aboutsummaryrefslogblamecommitdiffstats
path: root/libgnomecanvas/gnome-canvas-pixbuf.c
blob: 6a3d433c070c0ed9569523e48660eecb3dd9fcd0 (plain) (tree)




































                                                                    
                      

                          
                       

                           
                  

                           
                  







                                                                                
 






































                                                                           

                                                                               
                                                                                   
                                                                               
                                                                                    




                                                                

                                                                        
                                                                                            













































































































































































                                                                                                  

                                                                      
























                                                                      
                    































































































































































































                                                                                               
                                                                     





























                                                                    


                                                  


                            





                                       



























































































                                                               
 



















                                                         
                                                                







                                                                        
                                                                         
 
                      



















                                                                                     
                      








































                                                                                            



                                                  








































                                                                                                   

                                                                      
                                             

                                                                      









                                                       





                                                         


                                                                         







                                    
 

                       

                               


                            





































                                                                                
                                                                  


                               
                                         


                                        
                  




























































                                                                       
                                         










                                                        





                                                               
                                









                                                                                









                                                                                            
 



                                                       
 










































                                                                                            





                                                         


                               
                                                 
                      

                       












































                                                            




                                                  


                               
                                           
























                                                                   
/* GNOME libraries - GdkPixbuf item for the GNOME canvas
 *
 * Copyright (C) 1999 The Free Software Foundation
 *
 * Author: Federico Mena-Quintero <federico@gimp.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library 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 <config.h>
#include <math.h>
#include <libgnomecanvas/gnome-canvas.h>
#include <libgnomecanvas/gnome-canvas-util.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <libart_lgpl/art_rgb_affine.h>
#include <libart_lgpl/art_rgb_rgba_affine.h>
#include "gnome-canvas-pixbuf.h"

/* Private part of the GnomeCanvasPixbuf structure */
typedef struct {
    /* Our gdk-pixbuf */
    GdkPixbuf *pixbuf;

    /* Width value */
    gdouble width;

    /* Height value */
    gdouble height;

    /* X translation */
    gdouble x;

    /* Y translation */
    gdouble y;

    /* Whether dimensions are set and whether they are in pixels or units */
    guint width_set : 1;
    guint width_in_pixels : 1;
    guint height_set : 1;
    guint height_in_pixels : 1;
    guint x_in_pixels : 1;
    guint y_in_pixels : 1;

    /* Whether the pixbuf has changed */
    guint need_pixbuf_update : 1;

    /* Whether the transformation or size have changed */
    guint need_xform_update : 1;

    /* Anchor */
    GtkAnchorType anchor;
} PixbufPrivate;

/* Object argument IDs */
enum {
    PROP_0,
    PROP_PIXBUF,
    PROP_WIDTH,
    PROP_WIDTH_SET,
    PROP_WIDTH_IN_PIXELS,
    PROP_HEIGHT,
    PROP_HEIGHT_SET,
    PROP_HEIGHT_IN_PIXELS,
    PROP_X,
    PROP_X_IN_PIXELS,
    PROP_Y,
    PROP_Y_IN_PIXELS,
    PROP_ANCHOR
};

static void gnome_canvas_pixbuf_class_init (GnomeCanvasPixbufClass *class);
static void gnome_canvas_pixbuf_init (GnomeCanvasPixbuf *cpb);
static void gnome_canvas_pixbuf_destroy (GtkObject *object);
static void gnome_canvas_pixbuf_set_property (GObject *object,
                          guint param_id,
                          const GValue *value,
                          GParamSpec *pspec);
static void gnome_canvas_pixbuf_get_property (GObject *object,
                          guint param_id,
                          GValue *value,
                          GParamSpec *pspec);

static void gnome_canvas_pixbuf_update (GnomeCanvasItem *item, gdouble *affine,
                    ArtSVP *clip_path, gint flags);
static void gnome_canvas_pixbuf_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
                      gint x, gint y, gint width, gint height);
static void gnome_canvas_pixbuf_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
static gdouble gnome_canvas_pixbuf_point (GnomeCanvasItem *item,
                     gdouble x,
                     gdouble y,
                     gint cx,
                     gint cy,
                     GnomeCanvasItem **actual_item);
static void gnome_canvas_pixbuf_bounds (GnomeCanvasItem *item,
                    gdouble *x1, gdouble *y1, gdouble *x2, gdouble *y2);

static GnomeCanvasItemClass *parent_class;



/**
 * gnome_canvas_pixbuf_get_type:
 * @void:
 *
 * Registers the #GnomeCanvasPixbuf class if necessary, and returns the type ID
 * associated to it.
 *
 * Return value: The type ID of the #GnomeCanvasPixbuf class.
 **/
GType
gnome_canvas_pixbuf_get_type (void)
{
    static GType pixbuf_type;

    if (!pixbuf_type) {
        const GTypeInfo object_info = {
            sizeof (GnomeCanvasPixbufClass),
            (GBaseInitFunc) NULL,
            (GBaseFinalizeFunc) NULL,
            (GClassInitFunc) gnome_canvas_pixbuf_class_init,
            (GClassFinalizeFunc) NULL,
            NULL,           /* class_data */
            sizeof (GnomeCanvasPixbuf),
            0,          /* n_preallocs */
            (GInstanceInitFunc) gnome_canvas_pixbuf_init,
            NULL            /* value_table */
        };

        pixbuf_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasPixbuf",
                             &object_info, 0);
    }

    return pixbuf_type;
}

/* Class initialization function for the pixbuf canvas item */
static void
gnome_canvas_pixbuf_class_init (GnomeCanvasPixbufClass *class)
{
        GObjectClass *gobject_class;
    GtkObjectClass *object_class;
    GnomeCanvasItemClass *item_class;

        gobject_class = (GObjectClass *) class;
    object_class = (GtkObjectClass *) class;
    item_class = (GnomeCanvasItemClass *) class;

    parent_class = g_type_class_peek_parent (class);

    gobject_class->set_property = gnome_canvas_pixbuf_set_property;
    gobject_class->get_property = gnome_canvas_pixbuf_get_property;

        g_object_class_install_property
                (gobject_class,
                 PROP_PIXBUF,
                 g_param_spec_object ("pixbuf", NULL, NULL,
                                      GDK_TYPE_PIXBUF,
                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
        g_object_class_install_property
                (gobject_class,
                 PROP_WIDTH,
                 g_param_spec_double ("width", NULL, NULL,
                      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
        g_object_class_install_property
                (gobject_class,
                 PROP_WIDTH_SET,
                 g_param_spec_boolean ("width_set", NULL, NULL,
                       FALSE,
                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
        g_object_class_install_property
                (gobject_class,
                 PROP_WIDTH_IN_PIXELS,
                 g_param_spec_boolean ("width_in_pixels", NULL, NULL,
                       FALSE,
                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
        g_object_class_install_property
                (gobject_class,
                 PROP_HEIGHT,
                 g_param_spec_double ("height", NULL, NULL,
                      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
        g_object_class_install_property
                (gobject_class,
                 PROP_HEIGHT_SET,
                 g_param_spec_boolean ("height_set", NULL, NULL,
                       FALSE,
                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
        g_object_class_install_property
                (gobject_class,
                 PROP_HEIGHT_IN_PIXELS,
                 g_param_spec_boolean ("height_in_pixels", NULL, NULL,
                       FALSE,
                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
        g_object_class_install_property
                (gobject_class,
                 PROP_X,
                 g_param_spec_double ("x", NULL, NULL,
                      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
        g_object_class_install_property
                (gobject_class,
                 PROP_X_IN_PIXELS,
                 g_param_spec_boolean ("x_in_pixels", NULL, NULL,
                       FALSE,
                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
        g_object_class_install_property
                (gobject_class,
                 PROP_Y,
                 g_param_spec_double ("y", NULL, NULL,
                      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
        g_object_class_install_property
                (gobject_class,
                 PROP_Y_IN_PIXELS,
                 g_param_spec_boolean ("y_in_pixels", NULL, NULL,
                       FALSE,
                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
        g_object_class_install_property
                (gobject_class,
                 PROP_ANCHOR,
                 g_param_spec_enum ("anchor", NULL, NULL,
                                    GTK_TYPE_ANCHOR_TYPE,
                                    GTK_ANCHOR_NW,
                                    (G_PARAM_READABLE | G_PARAM_WRITABLE)));

    object_class->destroy = gnome_canvas_pixbuf_destroy;

    item_class->update = gnome_canvas_pixbuf_update;
    item_class->draw = gnome_canvas_pixbuf_draw;
    item_class->render = gnome_canvas_pixbuf_render;
    item_class->point = gnome_canvas_pixbuf_point;
    item_class->bounds = gnome_canvas_pixbuf_bounds;
}

/* Object initialization function for the pixbuf canvas item */
static void
gnome_canvas_pixbuf_init (GnomeCanvasPixbuf *gcp)
{
    PixbufPrivate *priv;

    priv = g_new0 (PixbufPrivate, 1);
    gcp->priv = priv;

    priv->width = 0.0;
    priv->height = 0.0;
    priv->x = 0.0;
    priv->y = 0.0;
    priv->anchor = GTK_ANCHOR_NW;
}

/* Destroy handler for the pixbuf canvas item */
static void
gnome_canvas_pixbuf_destroy (GtkObject *object)
{
    GnomeCanvasItem *item;
    GnomeCanvasPixbuf *gcp;
    PixbufPrivate *priv;

    g_return_if_fail (object != NULL);
    g_return_if_fail (GNOME_IS_CANVAS_PIXBUF (object));

    item = GNOME_CANVAS_ITEM (object);
    gcp = (GNOME_CANVAS_PIXBUF (object));
    priv = gcp->priv;

    /* remember, destroy can be run multiple times! */

    if (priv) {
        gnome_canvas_request_redraw (
        item->canvas, item->x1, item->y1, item->x2, item->y2);

        if (priv->pixbuf)
        g_object_unref (priv->pixbuf);

        g_free (priv);
        gcp->priv = NULL;
    }

    if (GTK_OBJECT_CLASS (parent_class)->destroy)
        (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
}



/* Set_property handler for the pixbuf canvas item */
static void
gnome_canvas_pixbuf_set_property (GObject            *object,
                  guint               param_id,
                  const GValue       *value,
                  GParamSpec         *pspec)
{
    GnomeCanvasItem *item;
    GnomeCanvasPixbuf *gcp;
    PixbufPrivate *priv;
    GdkPixbuf *pixbuf;
    gdouble val;

    g_return_if_fail (object != NULL);
    g_return_if_fail (GNOME_IS_CANVAS_PIXBUF (object));

    item = GNOME_CANVAS_ITEM (object);
    gcp = GNOME_CANVAS_PIXBUF (object);
    priv = gcp->priv;

    switch (param_id) {
    case PROP_PIXBUF:
        if (g_value_get_object (value))
            pixbuf = GDK_PIXBUF (g_value_get_object (value));
        else
            pixbuf = NULL;
        if (pixbuf != priv->pixbuf) {
            if (pixbuf) {
                g_return_if_fail
                    (gdk_pixbuf_get_colorspace (pixbuf) == GDK_COLORSPACE_RGB);
                g_return_if_fail
                    (gdk_pixbuf_get_n_channels (pixbuf) == 3
                     || gdk_pixbuf_get_n_channels (pixbuf) == 4);
                g_return_if_fail
                    (gdk_pixbuf_get_bits_per_sample (pixbuf) == 8);

                g_object_ref (pixbuf);
            }

            if (priv->pixbuf)
                g_object_unref (priv->pixbuf);

            priv->pixbuf = pixbuf;
        }

        priv->need_pixbuf_update = TRUE;
        gnome_canvas_item_request_update (item);
        break;

    case PROP_WIDTH:
        val = g_value_get_double (value);
        g_return_if_fail (val >= 0.0);
        priv->width = val;
        priv->need_xform_update = TRUE;
        gnome_canvas_item_request_update (item);
        break;

    case PROP_WIDTH_SET:
        priv->width_set = g_value_get_boolean (value);
        priv->need_xform_update = TRUE;
        gnome_canvas_item_request_update (item);
        break;

    case PROP_WIDTH_IN_PIXELS:
        priv->width_in_pixels = g_value_get_boolean (value);
        priv->need_xform_update = TRUE;
        gnome_canvas_item_request_update (item);
        break;

    case PROP_HEIGHT:
        val = g_value_get_double (value);
        g_return_if_fail (val >= 0.0);
        priv->height = val;
        priv->need_xform_update = TRUE;
        gnome_canvas_item_request_update (item);
        break;

    case PROP_HEIGHT_SET:
        priv->height_set = g_value_get_boolean (value);
        priv->need_xform_update = TRUE;
        gnome_canvas_item_request_update (item);
        break;

    case PROP_HEIGHT_IN_PIXELS:
        priv->height_in_pixels = g_value_get_boolean (value);
        priv->need_xform_update = TRUE;
        gnome_canvas_item_request_update (item);
        break;

    case PROP_X:
        priv->x = g_value_get_double (value);
        priv->need_xform_update = TRUE;
        gnome_canvas_item_request_update (item);
        break;

    case PROP_X_IN_PIXELS:
        priv->x_in_pixels = g_value_get_boolean (value);
        priv->need_xform_update = TRUE;
        gnome_canvas_item_request_update (item);
        break;

    case PROP_Y:
        priv->y = g_value_get_double (value);
        priv->need_xform_update = TRUE;
        gnome_canvas_item_request_update (item);
        break;

    case PROP_Y_IN_PIXELS:
        priv->y_in_pixels = g_value_get_boolean (value);
        priv->need_xform_update = TRUE;
        gnome_canvas_item_request_update (item);
        break;

    case PROP_ANCHOR:
        priv->anchor = g_value_get_enum (value);
        priv->need_xform_update = TRUE;
        gnome_canvas_item_request_update (item);
        break;

    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
        break;
    }
}

/* Get_property handler for the pixbuf canvasi item */
static void
gnome_canvas_pixbuf_get_property (GObject            *object,
                  guint               param_id,
                  GValue             *value,
                  GParamSpec         *pspec)
{
    GnomeCanvasPixbuf *gcp;
    PixbufPrivate *priv;

    g_return_if_fail (object != NULL);
    g_return_if_fail (GNOME_IS_CANVAS_PIXBUF (object));

    gcp = GNOME_CANVAS_PIXBUF (object);
    priv = gcp->priv;

    switch (param_id) {
    case PROP_PIXBUF:
        g_value_set_object (value, G_OBJECT (priv->pixbuf));
        break;

    case PROP_WIDTH:
        g_value_set_double (value, priv->width);
        break;

    case PROP_WIDTH_SET:
        g_value_set_boolean (value, priv->width_set);
        break;

    case PROP_WIDTH_IN_PIXELS:
        g_value_set_boolean (value, priv->width_in_pixels);
        break;

    case PROP_HEIGHT:
        g_value_set_double (value, priv->height);
        break;

    case PROP_HEIGHT_SET:
        g_value_set_boolean (value, priv->height_set);
        break;

    case PROP_HEIGHT_IN_PIXELS:
        g_value_set_boolean (value, priv->height_in_pixels);
        break;

    case PROP_X:
        g_value_set_double (value, priv->x);
        break;

    case PROP_X_IN_PIXELS:
        g_value_set_boolean (value, priv->x_in_pixels);
        break;

    case PROP_Y:
        g_value_set_double (value, priv->y);
        break;

    case PROP_Y_IN_PIXELS:
        g_value_set_boolean (value, priv->y_in_pixels);
        break;

    case PROP_ANCHOR:
        g_value_set_enum (value, priv->anchor);
        break;

    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
        break;
    }
}



/* Bounds and utilities */

/* Computes the amount by which the unit horizontal and vertical vectors will be
 * scaled by an affine transformation.
 */
static void
compute_xform_scaling (gdouble *affine, ArtPoint *i_c, ArtPoint *j_c)
{
    ArtPoint orig, orig_c;
    ArtPoint i, j;

    /* Origin */

    orig.x = 0.0;
    orig.y = 0.0;
    art_affine_point (&orig_c, &orig, affine);

    /* Horizontal and vertical vectors */

    i.x = 1.0;
    i.y = 0.0;
    art_affine_point (i_c, &i, affine);
    i_c->x -= orig_c.x;
    i_c->y -= orig_c.y;

    j.x = 0.0;
    j.y = 1.0;
    art_affine_point (j_c, &j, affine);
    j_c->x -= orig_c.x;
    j_c->y -= orig_c.y;
}

/* computes the addtional resolution dependent affine needed to
 * fit the image within its viewport defined by x,y,width and height
 * args
 */
static void
compute_viewport_affine (GnomeCanvasPixbuf *gcp,
                         gdouble *viewport_affine,
                         gdouble *i2c)
{
    PixbufPrivate *priv;
    ArtPoint i_c, j_c;
    gdouble i_len, j_len;
    gdouble si_len, sj_len;
    gdouble ti_len, tj_len;
    gdouble scale[6], translate[6];
    gdouble w, h;
    gdouble x, y;

    priv = gcp->priv;

    /* Compute scaling vectors and required width/height */

    compute_xform_scaling (i2c, &i_c, &j_c);

    i_len = sqrt (i_c.x * i_c.x + i_c.y * i_c.y);
    j_len = sqrt (j_c.x * j_c.x + j_c.y * j_c.y);

    if (priv->width_set)
        w = priv->width;
    else
        w = gdk_pixbuf_get_width (priv->pixbuf);

    if (priv->height_set)
        h = priv->height;
    else
        h = gdk_pixbuf_get_height (priv->pixbuf);

    x = priv->x;
    y = priv->y;

    /* Convert i_len and j_len into scaling factors */

    if (priv->width_in_pixels) {
        if (i_len > GNOME_CANVAS_EPSILON)
            si_len = 1.0 / i_len;
        else
            si_len = 0.0;
    } else
        si_len = 1.0;

    si_len *= w / gdk_pixbuf_get_width (priv->pixbuf);

    if (priv->height_in_pixels) {
        if (j_len > GNOME_CANVAS_EPSILON)
            sj_len = 1.0 / j_len;
        else
            sj_len = 0.0;
    } else
        sj_len = 1.0;

    sj_len *= h / gdk_pixbuf_get_height (priv->pixbuf);

    /* Calculate translation offsets */

    if (priv->x_in_pixels) {
        if (i_len > GNOME_CANVAS_EPSILON)
            ti_len = 1.0 / i_len;
        else
            ti_len = 0.0;
    } else
        ti_len = 1.0;

    switch (priv->anchor) {
    case GTK_ANCHOR_NW:
    case GTK_ANCHOR_W:
    case GTK_ANCHOR_SW:
        ti_len *= x;
        break;

    case GTK_ANCHOR_N:
    case GTK_ANCHOR_CENTER:
    case GTK_ANCHOR_S:
        ti_len *= x - w * si_len / 2;
        break;

    case GTK_ANCHOR_NE:
    case GTK_ANCHOR_E:
    case GTK_ANCHOR_SE:
        ti_len *= x - w * si_len;
        break;

        default:
                break;
    }

    if (priv->y_in_pixels) {
        if (j_len > GNOME_CANVAS_EPSILON)
            tj_len = 1.0 / j_len;
        else
            tj_len = 0.0;
    } else
        tj_len = 1.0;

    switch (priv->anchor) {
    case GTK_ANCHOR_NW:
    case GTK_ANCHOR_N:
    case GTK_ANCHOR_NE:
        tj_len *= y;
        break;

    case GTK_ANCHOR_W:
    case GTK_ANCHOR_CENTER:
    case GTK_ANCHOR_E:
        tj_len *= y - h * sj_len / 2;
        break;

    case GTK_ANCHOR_SW:
    case GTK_ANCHOR_S:
    case GTK_ANCHOR_SE:
        tj_len *= y - h * sj_len;
        break;

        default:
                break;
    }

    /* Compute the final affine */

    art_affine_scale (scale, si_len, sj_len);
    art_affine_translate (translate, ti_len, tj_len);
    art_affine_multiply (viewport_affine, scale, translate);
}

/* Computes the affine transformation with which the pixbuf needs to be
 * transformed to render it on the canvas.  This is not the same as the
 * item_to_canvas transformation because we may need to scale the pixbuf
 * by some other amount.
 */
static void
compute_render_affine (GnomeCanvasPixbuf *gcp, gdouble *ra, gdouble *i2c)
{
    gdouble va[6];

    compute_viewport_affine (gcp, va, i2c);
#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
    g_print ("va %g %g %g %g %g %g\n", va[0], va[1], va[2], va[3], va[4], va[5]);
#endif
    art_affine_multiply (ra, va, i2c);
#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
    g_print ("ra %g %g %g %g %g %g\n", ra[0], ra[1], ra[2], ra[3], ra[4], ra[5]);
#endif
}

/* Recomputes the bounding box of a pixbuf canvas item.  The horizontal and
 * vertical dimensions may be specified in units or pixels, separately, so we
 * have to compute the components individually for each dimension.
 */
static void
recompute_bounding_box (GnomeCanvasPixbuf *gcp, gdouble *i2c)
{
    GnomeCanvasItem *item;
    PixbufPrivate *priv;
    gdouble ra[6];
    ArtDRect rect;

    item = GNOME_CANVAS_ITEM (gcp);
    priv = gcp->priv;

    if (!priv->pixbuf) {
        item->x1 = item->y1 = item->x2 = item->y2 = 0.0;
        return;
    }

    rect.x0 = 0.0;
    rect.x1 = gdk_pixbuf_get_width (priv->pixbuf);

    rect.y0 = 0.0;
    rect.y1 = gdk_pixbuf_get_height (priv->pixbuf);

#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
    g_print ("i2c %g %g %g %g %g %g\n", i2c[0], i2c[1], i2c[2], i2c[3], i2c[4], i2c[5]);
#endif
    gnome_canvas_item_i2c_affine (item, i2c);
#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
    g_print ("i2c %g %g %g %g %g %g\n", i2c[0], i2c[1], i2c[2], i2c[3], i2c[4], i2c[5]);
#endif
    compute_render_affine (gcp, ra, i2c);
#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
    g_print ("ra %g %g %g %g %g %g\n", ra[0], ra[1], ra[2], ra[3], ra[4], ra[5]);
#endif
    art_drect_affine_transform (&rect, &rect, ra);

    item->x1 = floor (rect.x0);
    item->y1 = floor (rect.y0);
    item->x2 = ceil (rect.x1);
    item->y2 = ceil (rect.y1);
}



/* Update sequence */

/* Update handler for the pixbuf canvas item */
static void
gnome_canvas_pixbuf_update (GnomeCanvasItem *item,
                            gdouble *affine,
                            ArtSVP *clip_path,
                            gint flags)
{
    GnomeCanvasPixbuf *gcp;
    PixbufPrivate *priv;

    gcp = GNOME_CANVAS_PIXBUF (item);
    priv = gcp->priv;

    if (parent_class->update)
        (* parent_class->update) (item, affine, clip_path, flags);

        /* the optimzations below cause rarely triggered redrawing bugs and
     * don't seem to actually save much performance. so it's probably
     * better to turn them off, than to chase subtle optimization bugs
     * throughgout all of gnome-canvas-pixbuf.c - TIMJ
     */
#ifdef USE_BROKEN_OPTIMIZATIONS
    if (((flags & GNOME_CANVAS_UPDATE_VISIBILITY)
         && !(GTK_OBJECT_FLAGS (item) & GNOME_CANVAS_ITEM_VISIBLE))
        || (flags & GNOME_CANVAS_UPDATE_AFFINE)
        || priv->need_pixbuf_update
        || priv->need_xform_update) {
        gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
    }

    /* If we need a pixbuf update, or if the item changed visibility to
     * shown, recompute the bounding box.
     */
    if (priv->need_pixbuf_update
        || priv->need_xform_update
        || ((flags & GNOME_CANVAS_UPDATE_VISIBILITY)
        && (GTK_OBJECT_FLAGS (gcp) & GNOME_CANVAS_ITEM_VISIBLE))
        || (flags & GNOME_CANVAS_UPDATE_AFFINE)) {
        recompute_bounding_box (gcp, affine);
#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
        g_print ("BBox is %g %g %g %g\n", item->x1, item->y1, item->x2, item->y2);
#endif
        gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
        priv->need_pixbuf_update = FALSE;
        priv->need_xform_update = FALSE;
    }
#else   /* ordinary update logic */
        gnome_canvas_request_redraw (
        item->canvas, item->x1, item->y1, item->x2, item->y2);
        recompute_bounding_box (gcp, affine);
        gnome_canvas_request_redraw (
        item->canvas, item->x1, item->y1, item->x2, item->y2);
        priv->need_pixbuf_update = FALSE;
        priv->need_xform_update = FALSE;
#endif
}



/* Rendering */

/* This is private to libart, but we need it.  Sigh. */
extern void art_rgb_affine_run (gint *p_x0,
                                gint *p_x1,
                                gint y,
                                gint src_width,
                                gint src_height,
                                const gdouble affine[6]);

/* Fills the specified buffer with the transformed version of a pixbuf */
static void
transform_pixbuf (guchar *dest,
                  gint x,
                  gint y,
                  gint width,
                  gint height,
                  gint rowstride,
                  GdkPixbuf *pixbuf,
                  gdouble *affine)
{
    gint xx, yy;
    gdouble inv[6];
    guchar *src, *d;
    ArtPoint src_p, dest_p;
    gint run_x1, run_x2;
    gint src_x, src_y;
    gint i;

    art_affine_invert (inv, affine);

    for (yy = 0; yy < height; yy++) {
        dest_p.y = y + yy + 0.5;

        run_x1 = x;
        run_x2 = x + width;
        art_rgb_affine_run (&run_x1, &run_x2, yy + y,
                    gdk_pixbuf_get_width (pixbuf),
                    gdk_pixbuf_get_height (pixbuf),
                    inv);

        d = dest + yy * rowstride + (run_x1 - x) * 4;

        for (xx = run_x1; xx < run_x2; xx++) {
            dest_p.x = xx + 0.5;
            art_affine_point (&src_p, &dest_p, inv);
            src_x = floor (src_p.x);
            src_y = floor (src_p.y);

            src =
                gdk_pixbuf_get_pixels (pixbuf) + src_y *
                gdk_pixbuf_get_rowstride (pixbuf) + src_x *
                gdk_pixbuf_get_n_channels (pixbuf);

            for (i = 0; i < gdk_pixbuf_get_n_channels (pixbuf); i++)
                *d++ = *src++;

            if (!gdk_pixbuf_get_has_alpha(pixbuf))
                *d++ = 255; /* opaque */
        }
    }
}

/* Draw handler for the pixbuf canvas item */
static void
gnome_canvas_pixbuf_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
              gint x, gint y, gint width, gint height)
{
    GnomeCanvasPixbuf *gcp;
    PixbufPrivate *priv;
    gdouble i2c[6], render_affine[6];
    guchar *buf;
    GdkPixbuf *pixbuf;
    ArtIRect p_rect, a_rect, d_rect;
    gint w, h;

    gcp = GNOME_CANVAS_PIXBUF (item);
    priv = gcp->priv;

    if (!priv->pixbuf)
        return;

    gnome_canvas_item_i2c_affine (item, i2c);
    compute_render_affine (gcp, render_affine, i2c);

    /* Compute the area we need to repaint */

    p_rect.x0 = item->x1;
    p_rect.y0 = item->y1;
    p_rect.x1 = item->x2;
    p_rect.y1 = item->y2;

    a_rect.x0 = x;
    a_rect.y0 = y;
    a_rect.x1 = x + width;
    a_rect.y1 = y + height;

    art_irect_intersect (&d_rect, &p_rect, &a_rect);
    if (art_irect_empty (&d_rect))
        return;

    /* Create a temporary buffer and transform the pixbuf there */

    w = d_rect.x1 - d_rect.x0;
    h = d_rect.y1 - d_rect.y0;

    buf = g_new0 (guchar, w * h * 4);
    transform_pixbuf (buf,
              d_rect.x0, d_rect.y0,
              w, h,
              w * 4,
              priv->pixbuf, render_affine);

    pixbuf = gdk_pixbuf_new_from_data (buf, GDK_COLORSPACE_RGB,
                       TRUE,
                       8, w, h,
                       w * 4,
                       NULL, NULL);

    gdk_draw_pixbuf (drawable, NULL, pixbuf,
             0, 0,
             d_rect.x0 - x, d_rect.y0 - y,
             w, h,
             GDK_RGB_DITHER_MAX,
             d_rect.x0, d_rect.y0);

    g_object_unref (pixbuf);
    g_free (buf);
}

/* Render handler for the pixbuf canvas item */
static void
gnome_canvas_pixbuf_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf)
{
    GnomeCanvasPixbuf *gcp;
    PixbufPrivate *priv;
    gdouble i2c[6], render_affine[6];

    gcp = GNOME_CANVAS_PIXBUF (item);
    priv = gcp->priv;

    if (!priv->pixbuf)
        return;

    gnome_canvas_item_i2c_affine (item, i2c);
    compute_render_affine (gcp, render_affine, i2c);
        gnome_canvas_buf_ensure_buf (buf);

    if ((fabs (render_affine[1]) < GNOME_CANVAS_EPSILON) &&
        (fabs (render_affine[2]) < GNOME_CANVAS_EPSILON) &&
        render_affine[0] > 0.0 &&
        render_affine[3] > 0.0)
      {
        GdkPixbuf *dest_pixbuf;
        gint x0, y0, x1, y1;

        dest_pixbuf = gdk_pixbuf_new_from_data (buf->buf,
                            GDK_COLORSPACE_RGB,
                            FALSE,
                            8,
                            buf->rect.x1 - buf->rect.x0,
                            buf->rect.y1 - buf->rect.y0,
                            buf->buf_rowstride,
                            NULL, NULL);

        x0 = floor (render_affine[4] - buf->rect.x0 + 0.5);
        y0 = floor (render_affine[5] - buf->rect.y0 + 0.5);

        x1 = x0 + floor (gdk_pixbuf_get_width (priv->pixbuf) * render_affine[0] + 0.5);
        y1 = y0 + floor (gdk_pixbuf_get_height (priv->pixbuf) * render_affine[3] + 0.5);

        x0 = MAX (x0, 0);
        x0 = MIN (x0, buf->rect.x1 - buf->rect.x0);
        y0 = MAX (y0, 0);
        y0 = MIN (y0, buf->rect.y1 - buf->rect.y0);

        x1 = MAX (x1, 0);
        x1 = MIN (x1, buf->rect.x1 - buf->rect.x0);
        y1 = MAX (y1, 0);
        y1 = MIN (y1, buf->rect.y1 - buf->rect.y0);

        gdk_pixbuf_composite  (priv->pixbuf,
                   dest_pixbuf,
                   x0, y0,
                   x1 - x0, y1 - y0,
                   render_affine[4] - buf->rect.x0,
                   render_affine[5] - buf->rect.y0,
                   render_affine[0],
                   render_affine[3],
                   GDK_INTERP_BILINEAR,
                   255);

        g_object_unref (dest_pixbuf);
      }
    else if (gdk_pixbuf_get_has_alpha(priv->pixbuf))
        art_rgb_rgba_affine (buf->buf,
                     buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1,
                     buf->buf_rowstride,
                     gdk_pixbuf_get_pixels(priv->pixbuf),
                     gdk_pixbuf_get_width(priv->pixbuf),
                     gdk_pixbuf_get_height(priv->pixbuf),
                     gdk_pixbuf_get_rowstride(priv->pixbuf),
                     render_affine,
                     ART_FILTER_NEAREST,
                     NULL);
    else
        art_rgb_affine (buf->buf,
                buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1,
                buf->buf_rowstride,
                gdk_pixbuf_get_pixels(priv->pixbuf),
                gdk_pixbuf_get_width(priv->pixbuf),
                gdk_pixbuf_get_height(priv->pixbuf),
                gdk_pixbuf_get_rowstride(priv->pixbuf),
                render_affine,
                ART_FILTER_NEAREST,
                NULL);

    buf->is_bg = 0;
}



/* Point handler for the pixbuf canvas item */
static double
gnome_canvas_pixbuf_point (GnomeCanvasItem *item,
                           gdouble x,
                           gdouble y,
                           gint cx,
                           gint cy,
                           GnomeCanvasItem **actual_item)
{
    GnomeCanvasPixbuf *gcp;
    PixbufPrivate *priv;
    gdouble i2c[6], render_affine[6], inv[6];
    ArtPoint c, p;
    gint px, py;
    gdouble no_hit;
    guchar *src;
    GdkPixbuf *pixbuf;

    gcp = GNOME_CANVAS_PIXBUF (item);
    priv = gcp->priv;
    pixbuf = priv->pixbuf;

    *actual_item = item;

    no_hit = item->canvas->pixels_per_unit * 2 + 10;

    if (!priv->pixbuf)
        return no_hit;

    gnome_canvas_item_i2c_affine (item, i2c);
    compute_render_affine (gcp, render_affine, i2c);
    art_affine_invert (inv, render_affine);

    c.x = cx;
    c.y = cy;
    art_affine_point (&p, &c, inv);
    px = p.x;
    py = p.y;

    if (px < 0 || px >= gdk_pixbuf_get_width (pixbuf) ||
        py < 0 || py >= gdk_pixbuf_get_height (pixbuf))
        return no_hit;

    if (!gdk_pixbuf_get_has_alpha (pixbuf))
        return 0.0;

    src = gdk_pixbuf_get_pixels (pixbuf) +
        py * gdk_pixbuf_get_rowstride (pixbuf) +
        px * gdk_pixbuf_get_n_channels (pixbuf);

    if (src[3] < 128)
        return no_hit;
    else
        return 0.0;
}



/* Bounds handler for the pixbuf canvas item */
static void
gnome_canvas_pixbuf_bounds (GnomeCanvasItem *item,
                            gdouble *x1,
                            gdouble *y1,
                            gdouble *x2,
                            gdouble *y2)
{
    GnomeCanvasPixbuf *gcp;
    PixbufPrivate *priv;
    gdouble i2c[6], viewport_affine[6];
    ArtDRect rect;

    gcp = GNOME_CANVAS_PIXBUF (item);
    priv = gcp->priv;

    if (!priv->pixbuf) {
        *x1 = *y1 = *x2 = *y2 = 0.0;
        return;
    }

    rect.x0 = 0.0;
    rect.x1 = gdk_pixbuf_get_width (priv->pixbuf);

    rect.y0 = 0.0;
    rect.y1 = gdk_pixbuf_get_height (priv->pixbuf);

    gnome_canvas_item_i2c_affine (item, i2c);
    compute_viewport_affine (gcp, viewport_affine, i2c);
    art_drect_affine_transform (&rect, &rect, viewport_affine);

    *x1 = rect.x0;
    *y1 = rect.y0;
    *x2 = rect.x1;
    *y2 = rect.y1;
}