Learn how easy it is to sync an existing GitHub or Google Code repo to a SourceForge project! See Demo

Close

Diff of /cbrpager-maemo/branches/MAEMO_0_9_20/src/image_view_drawer.c [000000] .. [r4] Maximize Restore

  Switch to side-by-side view

--- a
+++ b/cbrpager-maemo/branches/MAEMO_0_9_20/src/image_view_drawer.c
@@ -0,0 +1,311 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; coding: utf-8 -*- 
+ *
+ * Copyright © 2007 Björn Lindqvist <bjourne@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#include "image_view_drawer.h"
+#include "utils.h"
+#include <string.h>
+
+static gboolean
+gdk_rectangle_contains_rect (GdkRectangle r1, GdkRectangle r2)
+{
+    return
+        r1.x <= r2.x &&
+        r1.y <= r2.y &&
+        (r2.x + r2.width) <= (r1.x + r1.width) &&
+        (r2.y + r2.height) <= (r1.y + r1.height);
+}
+
+static void
+gdk_pixbuf_copy_area_intact (GdkPixbuf *src,
+                             int        src_x,
+                             int        src_y,
+                             int        width,
+                             int        height,
+                             GdkPixbuf *dst,
+                             int        dst_x,
+                             int        dst_y)
+{
+    if (src_x == dst_x && src_y == dst_y && src == dst)
+        return;
+    
+    int src_stride = gdk_pixbuf_get_rowstride (src);
+    int dst_stride = gdk_pixbuf_get_rowstride (dst);
+    int chans = gdk_pixbuf_get_n_channels (src);
+
+    int linelen = width * chans;
+
+    guchar *src_base = gdk_pixbuf_get_pixels (src);
+    guchar *dst_base = gdk_pixbuf_get_pixels (dst);
+
+    int src_y_ofs = src_y * src_stride;
+    int dst_y_ofs = dst_y * dst_stride;
+    int y;
+    if (dst_y > src_y)
+    {
+        src_y_ofs = (src_y + height - 1) * src_stride;
+        dst_y_ofs = (dst_y + height - 1) * dst_stride;
+        src_stride = -src_stride;
+        dst_stride = -dst_stride;
+    }
+    guchar *src_ofs = src_base + src_y_ofs + src_x * chans;
+    guchar *dst_ofs = dst_base + dst_y_ofs + dst_x * chans;
+
+    void (*copy_func)(void *, void *, size_t) = (void *) memcpy;
+    if (dst_x > src_x)
+        copy_func = (void *) memmove;
+
+    for (y = 0; y < height; y++)
+    {
+        copy_func (dst_ofs, src_ofs, linelen);
+        src_ofs += src_stride;
+        dst_ofs += dst_stride;
+    }
+}
+
+/**
+ * draw_settings_get_method:
+ * @old: The draw settings used in the last draw operation.
+ * @new_: The draw settings used in the current draw operation.
+ * @last_pixbuf: The pixbuf to be drawn.
+ *
+ * Returns which method that needs to be carried out in this draw.
+ **/
+DrawMethod
+draw_settings_get_method (DrawSettings *old,
+                          DrawSettings *new_,
+                          GdkPixbuf    *last_pixbuf)
+{
+    if (new_->zoom != old->zoom ||
+        new_->interp != old->interp ||
+        new_->check_color1 != old->check_color1 ||
+        new_->check_color2 != old->check_color2 ||
+        new_->pixbuf != old->pixbuf)
+        return DRAW_METHOD_SCALE;
+
+    if (gdk_rectangle_contains_rect (old->zoom_rect, new_->zoom_rect))
+        return DRAW_METHOD_CONTAINS;
+
+    return DRAW_METHOD_SCROLL;
+}
+
+ImageViewDrawer *
+image_view_drawer_new ()
+{
+    ImageViewDrawer *drawer = g_new0 (ImageViewDrawer, 1);
+    drawer->last_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 1, 1);
+    drawer->check_size = 16;
+    drawer->old = (DrawSettings){0,
+                                 {0, 0, 0, 0},
+                                 0, 0,
+                                 GDK_INTERP_NEAREST, drawer->last_pixbuf,
+                                 0, 0};
+    return drawer;
+}
+
+void
+image_view_drawer_free (ImageViewDrawer *drawer)
+{
+    g_object_unref (drawer->last_pixbuf);
+    g_free (drawer);
+}
+
+/**
+ * image_view_drawer_force_scale:
+ *
+ * This method forces the drawer to perform a %DRAW_FLAGS_SCALE
+ * operation the next time image_view_drawer_draw() is called.
+ *
+ * ImageViewDrawer tries to minimize the number of scale operations
+ * needed by caching the last drawn pixbuf. It would be inefficient to
+ * check the individual pixels inside the pixbuf so it assumes that if
+ * the memory address of the pixbuf has not changed, then the cache is
+ * good to use.
+ *
+ * Use this method to override that assumption.
+ **/
+void
+image_view_drawer_force_scale (ImageViewDrawer *drawer)
+{
+    /* Set the cached zoom to a bogus value, to force a
+       DRAW_FLAGS_SCALE but not a DRAW_FLAGS_ALLOCATE. */
+    drawer->old.zoom = -1234.0;
+}
+
+static GdkPixbuf *
+image_view_drawer_scroll_intersection (GdkPixbuf    *pixbuf,
+                                       int           new_width,
+                                       int           new_height,
+                                       int           src_x,
+                                       int           src_y,
+                                       int           inter_width,
+                                       int           inter_height,
+                                       int           dst_x,
+                                       int           dst_y)
+{
+    int last_width = gdk_pixbuf_get_width (pixbuf);
+    int last_height = gdk_pixbuf_get_height (pixbuf);
+    
+    int width = MAX (last_width, new_width);
+    int height = MAX (last_height, new_height);
+    if (width > last_width || height > last_height)
+    {
+        GdkColorspace cs = gdk_pixbuf_get_colorspace (pixbuf);
+        int bps = gdk_pixbuf_get_bits_per_sample (pixbuf);
+        gboolean alpha = gdk_pixbuf_get_has_alpha (pixbuf);
+        GdkPixbuf *tmp = gdk_pixbuf_new (cs, alpha, bps, width, height);
+        
+        gdk_pixbuf_copy_area_intact (pixbuf,
+                                     src_x, src_y,
+                                     inter_width, inter_height,
+                                     tmp,
+                                     dst_x, dst_y);
+        g_object_unref (pixbuf);
+        return tmp;
+    }
+    gdk_pixbuf_copy_area_intact (pixbuf,
+                                 src_x, src_y,
+                                 inter_width, inter_height,
+                                 pixbuf,
+                                 dst_x, dst_y);
+    return pixbuf;
+}
+
+/**
+ * image_view_drawer_intersect_draw:
+ *
+ * Updates the cache by first scrolling the still valid area in the
+ * cache. Then the newly exposed areas in the cache is sampled from
+ * the pixbuf.
+ **/
+static void
+image_view_drawer_intersect_draw (ImageViewDrawer *drawer,
+                                  DrawSettings    *ds,
+                                  GdkDrawable     *drawable)
+{
+    GdkRectangle this = ds->zoom_rect;
+    GdkRectangle cache = drawer->old.zoom_rect;
+    int n;
+
+    /* If there is no intersection, we have to scale the whole area
+       from the source pixbuf. */
+    GdkRectangle inter;
+    GdkRectangle around[4] = {
+        this,
+        {0, 0, 0, 0},
+        {0, 0, 0, 0},
+        {0, 0, 0, 0}
+    };
+    if (gdk_rectangle_intersect (&cache, &this, &inter))
+        gdk_rectangle_get_rects_around (&this, &inter, around);
+
+    drawer->last_pixbuf =
+        image_view_drawer_scroll_intersection (drawer->last_pixbuf,
+                                               this.width,
+                                               this.height,
+                                               inter.x - cache.x,
+                                               inter.y - cache.y,
+                                               inter.width,
+                                               inter.height,
+                                               around[1].width,
+                                               around[0].height);
+    
+    for (n = 0; n < 4; n++)
+    {
+        if (!around[n].width || !around[n].height)
+            continue;
+        gdk_pixbuf_scale_blend (ds->pixbuf,
+                                drawer->last_pixbuf,
+                                around[n].x - this.x,
+                                around[n].y - this.y,
+                                around[n].width, around[n].height,
+                                -this.x, -this.y,
+                                ds->zoom,
+                                ds->interp,
+                                around[n].x, around[n].y,
+                                drawer->check_size,
+                                ds->check_color1,
+                                ds->check_color2);
+    }
+}
+
+/**
+ * image_view_drawer_draw:
+ * @drawer: An #ImageViewDrawer.
+ * @ds: The #DrawSettings to use for this redraw.
+ **/
+void
+image_view_drawer_draw (ImageViewDrawer *drawer,
+                        DrawSettings    *ds,
+                        GdkDrawable     *drawable)
+{
+    GdkRectangle this = ds->zoom_rect;
+    DrawMethod method = draw_settings_get_method (&drawer->old, ds,
+                                                  drawer->last_pixbuf);
+    int deltax = 0;
+    int deltay = 0;
+    if (method == DRAW_METHOD_CONTAINS)
+    {
+        deltax = this.x - drawer->old.zoom_rect.x;
+        deltay = this.y - drawer->old.zoom_rect.y;
+    }
+    else if (method == DRAW_METHOD_SCROLL)
+    {
+        image_view_drawer_intersect_draw (drawer, ds, drawable);
+    }
+    else if (method == DRAW_METHOD_SCALE)
+    {
+        int last_width = gdk_pixbuf_get_width (drawer->last_pixbuf);
+        int last_height = gdk_pixbuf_get_height (drawer->last_pixbuf);
+        GdkColorspace new_cs = gdk_pixbuf_get_colorspace (ds->pixbuf);
+        GdkColorspace last_cs = gdk_pixbuf_get_colorspace (drawer->last_pixbuf);
+        int new_bps = gdk_pixbuf_get_bits_per_sample (ds->pixbuf);
+        int last_bps = gdk_pixbuf_get_bits_per_sample (drawer->last_pixbuf);
+        
+        if (this.width > last_width || this.height > last_height ||
+            new_cs != last_cs || new_bps != last_bps)
+        {
+            g_object_unref (drawer->last_pixbuf);
+            drawer->last_pixbuf = gdk_pixbuf_new (new_cs, FALSE, new_bps,
+                                                  this.width, this.height);
+        }
+        
+        gdk_pixbuf_scale_blend (ds->pixbuf,
+                                drawer->last_pixbuf,
+                                0, 0,
+                                this.width, this.height,
+                                (double) -this.x, (double) -this.y,
+                                ds->zoom,
+                                ds->interp,
+                                this.x, this.y,
+                                drawer->check_size,
+                                ds->check_color1,
+                                ds->check_color2);
+    }
+    gdk_draw_pixbuf (drawable,
+                     NULL,
+                     drawer->last_pixbuf,
+                     deltax, deltay,
+                     ds->widget_x, ds->widget_y,
+                     this.width, this.height,
+                     GDK_RGB_DITHER_MAX,
+                     ds->widget_x, ds->widget_y);
+    if (method != DRAW_METHOD_CONTAINS)
+        drawer->old = *ds;
+}
+