source: trunk/third/libgnomecanvas/libgnomecanvas/gnome-canvas-pixbuf.c @ 19532

Revision 19532, 27.5 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r19531, which included commits to RCS files with non-trunk default branches.
Line 
1/* GNOME libraries - GdkPixbuf item for the GNOME canvas
2 *
3 * Copyright (C) 1999 The Free Software Foundation
4 *
5 * Author: Federico Mena-Quintero <federico@gimp.org>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 */
22
23#include <config.h>
24#include <math.h>
25#include <libgnomecanvas/gnome-canvas.h>
26#include <libgnomecanvas/gnome-canvas-util.h>
27#include <gdk-pixbuf/gdk-pixbuf.h>
28#include <libart_lgpl/art_rgb_affine.h>
29#include <libart_lgpl/art_rgb_rgba_affine.h>
30#include "gnome-canvas-pixbuf.h"
31
32/* Private part of the GnomeCanvasPixbuf structure */
33typedef struct {
34        /* Our gdk-pixbuf */
35        GdkPixbuf *pixbuf;
36
37        /* Width value */
38        double width;
39
40        /* Height value */
41        double height;
42
43        /* X translation */
44        double x;
45
46        /* Y translation */
47        double y;
48
49        /* Whether dimensions are set and whether they are in pixels or units */
50        guint width_set : 1;
51        guint width_in_pixels : 1;
52        guint height_set : 1;
53        guint height_in_pixels : 1;
54        guint x_in_pixels : 1;
55        guint y_in_pixels : 1;
56       
57        /* Whether the pixbuf has changed */
58        guint need_pixbuf_update : 1;
59
60        /* Whether the transformation or size have changed */
61        guint need_xform_update : 1;
62
63        /* Anchor */
64        GtkAnchorType anchor;
65} PixbufPrivate;
66
67/* Object argument IDs */
68enum {
69        PROP_0,
70        PROP_PIXBUF,
71        PROP_WIDTH,
72        PROP_WIDTH_SET,
73        PROP_WIDTH_IN_PIXELS,
74        PROP_HEIGHT,
75        PROP_HEIGHT_SET,
76        PROP_HEIGHT_IN_PIXELS,
77        PROP_X,
78        PROP_X_IN_PIXELS,
79        PROP_Y,
80        PROP_Y_IN_PIXELS,
81        PROP_ANCHOR
82};
83
84static void gnome_canvas_pixbuf_class_init (GnomeCanvasPixbufClass *class);
85static void gnome_canvas_pixbuf_init (GnomeCanvasPixbuf *cpb);
86static void gnome_canvas_pixbuf_destroy (GtkObject *object);
87static void gnome_canvas_pixbuf_set_property (GObject *object,
88                                              guint param_id,
89                                              const GValue *value,
90                                              GParamSpec *pspec);
91static void gnome_canvas_pixbuf_get_property (GObject *object,
92                                              guint param_id,
93                                              GValue *value,
94                                              GParamSpec *pspec);
95
96static void gnome_canvas_pixbuf_update (GnomeCanvasItem *item, double *affine,
97                                        ArtSVP *clip_path, int flags);
98static void gnome_canvas_pixbuf_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
99                                      int x, int y, int width, int height);
100static void gnome_canvas_pixbuf_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
101static double gnome_canvas_pixbuf_point (GnomeCanvasItem *item, double x, double y, int cx, int cy,
102                                         GnomeCanvasItem **actual_item);
103static void gnome_canvas_pixbuf_bounds (GnomeCanvasItem *item,
104                                        double *x1, double *y1, double *x2, double *y2);
105
106static GnomeCanvasItemClass *parent_class;
107
108
109
110/**
111 * gnome_canvas_pixbuf_get_type:
112 * @void:
113 *
114 * Registers the #GnomeCanvasPixbuf class if necessary, and returns the type ID
115 * associated to it.
116 *
117 * Return value: The type ID of the #GnomeCanvasPixbuf class.
118 **/
119GtkType
120gnome_canvas_pixbuf_get_type (void)
121{
122        static GType pixbuf_type;
123
124        if (!pixbuf_type) {
125                static const GTypeInfo object_info = {
126                        sizeof (GnomeCanvasPixbufClass),
127                        (GBaseInitFunc) NULL,
128                        (GBaseFinalizeFunc) NULL,
129                        (GClassInitFunc) gnome_canvas_pixbuf_class_init,
130                        (GClassFinalizeFunc) NULL,
131                        NULL,                   /* class_data */
132                        sizeof (GnomeCanvasPixbuf),
133                        0,                      /* n_preallocs */
134                        (GInstanceInitFunc) gnome_canvas_pixbuf_init,
135                        NULL                    /* value_table */
136                };
137
138                pixbuf_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasPixbuf",
139                                                     &object_info, 0);
140        }
141
142        return pixbuf_type;
143}
144
145/* Class initialization function for the pixbuf canvas item */
146static void
147gnome_canvas_pixbuf_class_init (GnomeCanvasPixbufClass *class)
148{
149        GObjectClass *gobject_class;
150        GtkObjectClass *object_class;
151        GnomeCanvasItemClass *item_class;
152
153        gobject_class = (GObjectClass *) class;
154        object_class = (GtkObjectClass *) class;
155        item_class = (GnomeCanvasItemClass *) class;
156
157        parent_class = g_type_class_peek_parent (class);
158
159        gobject_class->set_property = gnome_canvas_pixbuf_set_property;
160        gobject_class->get_property = gnome_canvas_pixbuf_get_property;
161
162        g_object_class_install_property
163                (gobject_class,
164                 PROP_PIXBUF,
165                 g_param_spec_object ("pixbuf", NULL, NULL,
166                                      GDK_TYPE_PIXBUF,
167                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
168        g_object_class_install_property
169                (gobject_class,
170                 PROP_WIDTH,
171                 g_param_spec_double ("width", NULL, NULL,
172                                      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
173                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
174        g_object_class_install_property
175                (gobject_class,
176                 PROP_WIDTH_SET,
177                 g_param_spec_boolean ("width_set", NULL, NULL,
178                                       FALSE,
179                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
180        g_object_class_install_property
181                (gobject_class,
182                 PROP_WIDTH_IN_PIXELS,
183                 g_param_spec_boolean ("width_in_pixels", NULL, NULL,
184                                       FALSE,
185                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
186        g_object_class_install_property
187                (gobject_class,
188                 PROP_HEIGHT,
189                 g_param_spec_double ("height", NULL, NULL,
190                                      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
191                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
192        g_object_class_install_property
193                (gobject_class,
194                 PROP_HEIGHT_SET,
195                 g_param_spec_boolean ("height_set", NULL, NULL,
196                                       FALSE,
197                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
198        g_object_class_install_property
199                (gobject_class,
200                 PROP_HEIGHT_IN_PIXELS,
201                 g_param_spec_boolean ("height_in_pixels", NULL, NULL,
202                                       FALSE,
203                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
204        g_object_class_install_property
205                (gobject_class,
206                 PROP_X,
207                 g_param_spec_double ("x", NULL, NULL,
208                                      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
209                                    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
210        g_object_class_install_property
211                (gobject_class,
212                 PROP_X_IN_PIXELS,
213                 g_param_spec_boolean ("x_in_pixels", NULL, NULL,
214                                       FALSE,
215                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
216        g_object_class_install_property
217                (gobject_class,
218                 PROP_Y,
219                 g_param_spec_double ("y", NULL, NULL,
220                                      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
221                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
222        g_object_class_install_property
223                (gobject_class,
224                 PROP_Y_IN_PIXELS,
225                 g_param_spec_boolean ("y_in_pixels", NULL, NULL,
226                                       FALSE,
227                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
228        g_object_class_install_property
229                (gobject_class,
230                 PROP_ANCHOR,
231                 g_param_spec_enum ("anchor", NULL, NULL,
232                                    GTK_TYPE_ANCHOR_TYPE,
233                                    GTK_ANCHOR_NW,
234                                    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
235
236        object_class->destroy = gnome_canvas_pixbuf_destroy;
237
238        item_class->update = gnome_canvas_pixbuf_update;
239        item_class->draw = gnome_canvas_pixbuf_draw;
240        item_class->render = gnome_canvas_pixbuf_render;
241        item_class->point = gnome_canvas_pixbuf_point;
242        item_class->bounds = gnome_canvas_pixbuf_bounds;
243}
244
245/* Object initialization function for the pixbuf canvas item */
246static void
247gnome_canvas_pixbuf_init (GnomeCanvasPixbuf *gcp)
248{
249        PixbufPrivate *priv;
250
251        priv = g_new0 (PixbufPrivate, 1);
252        gcp->priv = priv;
253
254        priv->width = 0.0;
255        priv->height = 0.0;
256        priv->x = 0.0;
257        priv->y = 0.0;
258        priv->anchor = GTK_ANCHOR_NW;
259}
260
261/* Destroy handler for the pixbuf canvas item */
262static void
263gnome_canvas_pixbuf_destroy (GtkObject *object)
264{
265        GnomeCanvasItem *item;
266        GnomeCanvasPixbuf *gcp;
267        PixbufPrivate *priv;
268
269        g_return_if_fail (object != NULL);
270        g_return_if_fail (GNOME_IS_CANVAS_PIXBUF (object));
271
272        item = GNOME_CANVAS_ITEM (object);
273        gcp = (GNOME_CANVAS_PIXBUF (object));
274        priv = gcp->priv;
275
276        /* remember, destroy can be run multiple times! */
277
278        if (priv) {
279            gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
280
281            if (priv->pixbuf)
282                gdk_pixbuf_unref (priv->pixbuf);
283
284            g_free (priv);
285            gcp->priv = NULL;
286        }
287
288        if (GTK_OBJECT_CLASS (parent_class)->destroy)
289                (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
290}
291
292
293
294/* Set_property handler for the pixbuf canvas item */
295static void
296gnome_canvas_pixbuf_set_property (GObject            *object,
297                                  guint               param_id,
298                                  const GValue       *value,
299                                  GParamSpec         *pspec)
300{
301        GnomeCanvasItem *item;
302        GnomeCanvasPixbuf *gcp;
303        PixbufPrivate *priv;
304        GdkPixbuf *pixbuf;
305        double val;
306
307        g_return_if_fail (object != NULL);
308        g_return_if_fail (GNOME_IS_CANVAS_PIXBUF (object));
309
310        item = GNOME_CANVAS_ITEM (object);
311        gcp = GNOME_CANVAS_PIXBUF (object);
312        priv = gcp->priv;
313
314        switch (param_id) {
315        case PROP_PIXBUF:
316                if (g_value_get_object (value))
317                        pixbuf = GDK_PIXBUF (g_value_get_object (value));
318                else
319                        pixbuf = NULL;
320                if (pixbuf != priv->pixbuf) {
321                        if (pixbuf) {
322                                g_return_if_fail
323                                    (gdk_pixbuf_get_colorspace (pixbuf) == GDK_COLORSPACE_RGB);
324                                g_return_if_fail
325                                    (gdk_pixbuf_get_n_channels (pixbuf) == 3
326                                     || gdk_pixbuf_get_n_channels (pixbuf) == 4);
327                                g_return_if_fail
328                                    (gdk_pixbuf_get_bits_per_sample (pixbuf) == 8);
329
330                                gdk_pixbuf_ref (pixbuf);
331                        }
332
333                        if (priv->pixbuf)
334                                gdk_pixbuf_unref (priv->pixbuf);
335
336                        priv->pixbuf = pixbuf;
337                }
338
339                priv->need_pixbuf_update = TRUE;
340                gnome_canvas_item_request_update (item);
341                break;
342
343        case PROP_WIDTH:
344                val = g_value_get_double (value);
345                g_return_if_fail (val >= 0.0);
346                priv->width = val;
347                priv->need_xform_update = TRUE;
348                gnome_canvas_item_request_update (item);
349                break;
350
351        case PROP_WIDTH_SET:
352                priv->width_set = g_value_get_boolean (value);
353                priv->need_xform_update = TRUE;
354                gnome_canvas_item_request_update (item);
355                break;
356
357        case PROP_WIDTH_IN_PIXELS:
358                priv->width_in_pixels = g_value_get_boolean (value);
359                priv->need_xform_update = TRUE;
360                gnome_canvas_item_request_update (item);
361                break;
362
363        case PROP_HEIGHT:
364                val = g_value_get_double (value);
365                g_return_if_fail (val >= 0.0);
366                priv->height = val;
367                priv->need_xform_update = TRUE;
368                gnome_canvas_item_request_update (item);
369                break;
370
371        case PROP_HEIGHT_SET:
372                priv->height_set = g_value_get_boolean (value);
373                priv->need_xform_update = TRUE;
374                gnome_canvas_item_request_update (item);
375                break;
376
377        case PROP_HEIGHT_IN_PIXELS:
378                priv->height_in_pixels = g_value_get_boolean (value);
379                priv->need_xform_update = TRUE;
380                gnome_canvas_item_request_update (item);
381                break;
382
383        case PROP_X:
384                priv->x = g_value_get_double (value);
385                priv->need_xform_update = TRUE;
386                gnome_canvas_item_request_update (item);
387                break;
388
389        case PROP_X_IN_PIXELS:
390                priv->x_in_pixels = g_value_get_boolean (value);
391                priv->need_xform_update = TRUE;
392                gnome_canvas_item_request_update (item);
393                break;
394
395        case PROP_Y:
396                priv->y = g_value_get_double (value);
397                priv->need_xform_update = TRUE;
398                gnome_canvas_item_request_update (item);
399                break;
400
401        case PROP_Y_IN_PIXELS:
402                priv->y_in_pixels = g_value_get_boolean (value);
403                priv->need_xform_update = TRUE;
404                gnome_canvas_item_request_update (item);
405                break;
406
407        case PROP_ANCHOR:
408                priv->anchor = g_value_get_enum (value);
409                priv->need_xform_update = TRUE;
410                gnome_canvas_item_request_update (item);
411                break;
412
413        default:
414                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
415                break;
416        }
417}
418
419/* Get_property handler for the pixbuf canvasi item */
420static void
421gnome_canvas_pixbuf_get_property (GObject            *object,
422                                  guint               param_id,
423                                  GValue             *value,
424                                  GParamSpec         *pspec)
425{
426        GnomeCanvasPixbuf *gcp;
427        PixbufPrivate *priv;
428
429        g_return_if_fail (object != NULL);
430        g_return_if_fail (GNOME_IS_CANVAS_PIXBUF (object));
431
432        gcp = GNOME_CANVAS_PIXBUF (object);
433        priv = gcp->priv;
434
435        switch (param_id) {
436        case PROP_PIXBUF:
437                g_value_set_object (value, G_OBJECT (priv->pixbuf));
438                break;
439
440        case PROP_WIDTH:
441                g_value_set_double (value, priv->width);
442                break;
443
444        case PROP_WIDTH_SET:
445                g_value_set_boolean (value, priv->width_set);
446                break;
447
448        case PROP_WIDTH_IN_PIXELS:
449                g_value_set_boolean (value, priv->width_in_pixels);
450                break;
451
452        case PROP_HEIGHT:
453                g_value_set_double (value, priv->height);
454                break;
455
456        case PROP_HEIGHT_SET:
457                g_value_set_boolean (value, priv->height_set);
458                break;
459
460        case PROP_HEIGHT_IN_PIXELS:
461                g_value_set_boolean (value, priv->height_in_pixels);
462                break;
463
464        case PROP_X:
465                g_value_set_double (value, priv->x);
466                break;
467
468        case PROP_X_IN_PIXELS:
469                g_value_set_boolean (value, priv->x_in_pixels);
470                break;
471
472        case PROP_Y:
473                g_value_set_double (value, priv->y);
474                break;
475
476        case PROP_Y_IN_PIXELS:
477                g_value_set_boolean (value, priv->y_in_pixels);
478                break;
479
480        case PROP_ANCHOR:
481                g_value_set_enum (value, priv->anchor);
482                break;
483
484        default:
485                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
486                break;
487        }
488}
489
490
491
492/* Bounds and utilities */
493
494/* Computes the amount by which the unit horizontal and vertical vectors will be
495 * scaled by an affine transformation.
496 */
497static void
498compute_xform_scaling (double *affine, ArtPoint *i_c, ArtPoint *j_c)
499{
500        ArtPoint orig, orig_c;
501        ArtPoint i, j;
502
503        /* Origin */
504
505        orig.x = 0.0;
506        orig.y = 0.0;
507        art_affine_point (&orig_c, &orig, affine);
508
509        /* Horizontal and vertical vectors */
510
511        i.x = 1.0;
512        i.y = 0.0;
513        art_affine_point (i_c, &i, affine);
514        i_c->x -= orig_c.x;
515        i_c->y -= orig_c.y;
516
517        j.x = 0.0;
518        j.y = 1.0;
519        art_affine_point (j_c, &j, affine);
520        j_c->x -= orig_c.x;
521        j_c->y -= orig_c.y;
522}
523
524/* computes the addtional resolution dependent affine needed to
525 * fit the image within its viewport defined by x,y,width and height
526 * args
527 */
528static void
529compute_viewport_affine (GnomeCanvasPixbuf *gcp, double *viewport_affine, double *i2c)
530{
531        PixbufPrivate *priv;
532        ArtPoint i_c, j_c;
533        double i_len, j_len;
534        double si_len, sj_len;
535        double ti_len, tj_len;
536        double scale[6], translate[6];
537        double w, h;
538        double x, y;
539
540        priv = gcp->priv;
541
542        /* Compute scaling vectors and required width/height */
543
544        compute_xform_scaling (i2c, &i_c, &j_c);
545
546        i_len = sqrt (i_c.x * i_c.x + i_c.y * i_c.y);
547        j_len = sqrt (j_c.x * j_c.x + j_c.y * j_c.y);
548
549        if (priv->width_set)
550                w = priv->width;
551        else
552                w = gdk_pixbuf_get_width (priv->pixbuf);
553
554        if (priv->height_set)
555                h = priv->height;
556        else
557                h = gdk_pixbuf_get_height (priv->pixbuf);
558
559        x = priv->x;
560        y = priv->y;
561
562        /* Convert i_len and j_len into scaling factors */
563
564        if (priv->width_in_pixels) {
565                if (i_len > GNOME_CANVAS_EPSILON)
566                        si_len = 1.0 / i_len;
567                else
568                        si_len = 0.0;
569        } else
570                si_len = 1.0;
571
572        si_len *= w / gdk_pixbuf_get_width (priv->pixbuf);
573
574        if (priv->height_in_pixels) {
575                if (j_len > GNOME_CANVAS_EPSILON)
576                        sj_len = 1.0 / j_len;
577                else
578                        sj_len = 0.0;
579        } else
580                sj_len = 1.0;
581
582        sj_len *= h / gdk_pixbuf_get_height (priv->pixbuf);
583
584        /* Calculate translation offsets */
585
586        if (priv->x_in_pixels) {
587                if (i_len > GNOME_CANVAS_EPSILON)
588                        ti_len = 1.0 / i_len;
589                else
590                        ti_len = 0.0;
591        } else
592                ti_len = 1.0;
593
594        switch (priv->anchor) {
595        case GTK_ANCHOR_NW:
596        case GTK_ANCHOR_W:
597        case GTK_ANCHOR_SW:
598                ti_len *= x;
599                break;
600
601        case GTK_ANCHOR_N:
602        case GTK_ANCHOR_CENTER:
603        case GTK_ANCHOR_S:
604                ti_len *= (x - (w/ 2));
605                break;
606
607        case GTK_ANCHOR_NE:
608        case GTK_ANCHOR_E:
609        case GTK_ANCHOR_SE:
610                ti_len *= (x - w);
611                break;
612
613        default:
614                break;
615        }
616
617        if (priv->y_in_pixels) {
618                if (j_len > GNOME_CANVAS_EPSILON)
619                        tj_len = 1.0 / j_len;
620                else
621                        tj_len = 0.0;
622        } else
623                tj_len = 1.0;
624
625        switch (priv->anchor) {
626        case GTK_ANCHOR_NW:
627        case GTK_ANCHOR_N:
628        case GTK_ANCHOR_NE:
629                tj_len *= y;
630                break;
631               
632        case GTK_ANCHOR_W:
633        case GTK_ANCHOR_CENTER:
634        case GTK_ANCHOR_E:
635                tj_len *= y - (h / 2);
636                break;
637
638        case GTK_ANCHOR_SW:
639        case GTK_ANCHOR_S:
640        case GTK_ANCHOR_SE:
641                tj_len *= y - h;
642                break;
643
644        default:
645                break;
646        }
647
648        /* Compute the final affine */
649
650        art_affine_scale (scale, si_len, sj_len);
651        art_affine_translate (translate, ti_len, tj_len);
652        art_affine_multiply (viewport_affine, scale, translate);
653}
654
655/* Computes the affine transformation with which the pixbuf needs to be
656 * transformed to render it on the canvas.  This is not the same as the
657 * item_to_canvas transformation because we may need to scale the pixbuf
658 * by some other amount.
659 */
660static void
661compute_render_affine (GnomeCanvasPixbuf *gcp, double *ra, double *i2c)
662{
663        double va[6];
664
665        compute_viewport_affine (gcp, va, i2c);
666#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
667        g_print ("va %g %g %g %g %g %g\n", va[0], va[1], va[2], va[3], va[4], va[5]);
668#endif
669        art_affine_multiply (ra, va, i2c);
670#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
671        g_print ("ra %g %g %g %g %g %g\n", ra[0], ra[1], ra[2], ra[3], ra[4], ra[5]);
672#endif
673}
674
675/* Recomputes the bounding box of a pixbuf canvas item.  The horizontal and
676 * vertical dimensions may be specified in units or pixels, separately, so we
677 * have to compute the components individually for each dimension.
678 */
679static void
680recompute_bounding_box (GnomeCanvasPixbuf *gcp, gdouble *i2c)
681{
682        GnomeCanvasItem *item;
683        PixbufPrivate *priv;
684        double ra[6];
685        ArtDRect rect;
686
687        item = GNOME_CANVAS_ITEM (gcp);
688        priv = gcp->priv;
689
690        if (!priv->pixbuf) {
691                item->x1 = item->y1 = item->x2 = item->y2 = 0.0;
692                return;
693        }
694
695        rect.x0 = 0.0;
696        rect.x1 = gdk_pixbuf_get_width (priv->pixbuf);
697
698        rect.y0 = 0.0;
699        rect.y1 = gdk_pixbuf_get_height (priv->pixbuf);
700
701#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
702        g_print ("i2c %g %g %g %g %g %g\n", i2c[0], i2c[1], i2c[2], i2c[3], i2c[4], i2c[5]);
703#endif
704        gnome_canvas_item_i2c_affine (item, i2c);
705#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
706        g_print ("i2c %g %g %g %g %g %g\n", i2c[0], i2c[1], i2c[2], i2c[3], i2c[4], i2c[5]);
707#endif
708        compute_render_affine (gcp, ra, i2c);
709#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
710        g_print ("ra %g %g %g %g %g %g\n", ra[0], ra[1], ra[2], ra[3], ra[4], ra[5]);
711#endif
712        art_drect_affine_transform (&rect, &rect, ra);
713
714        item->x1 = floor (rect.x0);
715        item->y1 = floor (rect.y0);
716        item->x2 = ceil (rect.x1);
717        item->y2 = ceil (rect.y1);
718}
719
720
721
722/* Update sequence */
723
724/* Update handler for the pixbuf canvas item */
725static void
726gnome_canvas_pixbuf_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
727{
728        GnomeCanvasPixbuf *gcp;
729        PixbufPrivate *priv;
730
731        gcp = GNOME_CANVAS_PIXBUF (item);
732        priv = gcp->priv;
733
734        if (parent_class->update)
735                (* parent_class->update) (item, affine, clip_path, flags);
736
737        /* the optimzations below cause rarely triggered redrawing bugs and
738         * don't seem to actually save much performance. so it's probably
739         * better to turn them off, than to chase subtle optimization bugs
740         * throughgout all of gnome-canvas-pixbuf.c - TIMJ
741         */
742#if USE_BROKEN_OPTIMIZATIONS
743        if (((flags & GNOME_CANVAS_UPDATE_VISIBILITY)
744             && !(GTK_OBJECT_FLAGS (item) & GNOME_CANVAS_ITEM_VISIBLE))
745            || (flags & GNOME_CANVAS_UPDATE_AFFINE)
746            || priv->need_pixbuf_update
747            || priv->need_xform_update) {
748                gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
749        }
750
751        /* If we need a pixbuf update, or if the item changed visibility to
752         * shown, recompute the bounding box.
753         */
754        if (priv->need_pixbuf_update
755            || priv->need_xform_update
756            || ((flags & GNOME_CANVAS_UPDATE_VISIBILITY)
757                && (GTK_OBJECT_FLAGS (gcp) & GNOME_CANVAS_ITEM_VISIBLE))
758            || (flags & GNOME_CANVAS_UPDATE_AFFINE)) {
759                recompute_bounding_box (gcp, affine);
760#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
761                g_print ("BBox is %g %g %g %g\n", item->x1, item->y1, item->x2, item->y2);
762#endif
763                gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
764                priv->need_pixbuf_update = FALSE;
765                priv->need_xform_update = FALSE;
766        }
767#else   /* ordinary update logic */
768        gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
769        recompute_bounding_box (gcp, affine);
770        gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
771        priv->need_pixbuf_update = FALSE;
772        priv->need_xform_update = FALSE;
773#endif
774}
775
776
777
778/* Rendering */
779
780/* This is private to libart, but we need it.  Sigh. */
781extern void art_rgb_affine_run (int *p_x0, int *p_x1, int y, int src_width, int src_height,
782                                const double affine[6]);
783
784/* Fills the specified buffer with the transformed version of a pixbuf */
785static void
786transform_pixbuf (guchar *dest, int x, int y, int width, int height, int rowstride,
787                  GdkPixbuf *pixbuf, double *affine)
788{
789        int xx, yy;
790        double inv[6];
791        guchar *src, *d;
792        ArtPoint src_p, dest_p;
793        int run_x1, run_x2;
794        int src_x, src_y;
795        int i;
796
797        art_affine_invert (inv, affine);
798
799        for (yy = 0; yy < height; yy++) {
800                dest_p.y = y + yy + 0.5;
801
802                run_x1 = x;
803                run_x2 = x + width;
804                art_rgb_affine_run (&run_x1, &run_x2, yy + y,
805                                    gdk_pixbuf_get_width (pixbuf),
806                                    gdk_pixbuf_get_height (pixbuf),
807                                    inv);
808
809                d = dest + yy * rowstride + (run_x1 - x) * 4;
810
811                for (xx = run_x1; xx < run_x2; xx++) {
812                        dest_p.x = xx + 0.5;
813                        art_affine_point (&src_p, &dest_p, inv);
814                        src_x = floor (src_p.x);
815                        src_y = floor (src_p.y);
816
817                        src =
818                            gdk_pixbuf_get_pixels (pixbuf) + src_y *
819                            gdk_pixbuf_get_rowstride (pixbuf) + src_x *
820                            gdk_pixbuf_get_n_channels (pixbuf);
821
822                        for (i = 0; i < gdk_pixbuf_get_n_channels (pixbuf); i++)
823                                *d++ = *src++;
824
825                        if (!gdk_pixbuf_get_has_alpha(pixbuf))
826                                *d++ = 255; /* opaque */
827                }
828        }
829}
830
831/* Draw handler for the pixbuf canvas item */
832static void
833gnome_canvas_pixbuf_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
834                          int x, int y, int width, int height)
835{
836        GnomeCanvasPixbuf *gcp;
837        PixbufPrivate *priv;
838        double i2c[6], render_affine[6];
839        guchar *buf;
840        GdkPixbuf *pixbuf;
841        ArtIRect p_rect, a_rect, d_rect;
842        int w, h;
843
844        gcp = GNOME_CANVAS_PIXBUF (item);
845        priv = gcp->priv;
846
847        if (!priv->pixbuf)
848                return;
849
850        gnome_canvas_item_i2c_affine (item, i2c);
851        compute_render_affine (gcp, render_affine, i2c);
852
853        /* Compute the area we need to repaint */
854
855        p_rect.x0 = item->x1;
856        p_rect.y0 = item->y1;
857        p_rect.x1 = item->x2;
858        p_rect.y1 = item->y2;
859
860        a_rect.x0 = x;
861        a_rect.y0 = y;
862        a_rect.x1 = x + width;
863        a_rect.y1 = y + height;
864
865        art_irect_intersect (&d_rect, &p_rect, &a_rect);
866        if (art_irect_empty (&d_rect))
867                return;
868
869        /* Create a temporary buffer and transform the pixbuf there */
870
871        w = d_rect.x1 - d_rect.x0;
872        h = d_rect.y1 - d_rect.y0;
873
874        buf = g_new0 (guchar, w * h * 4);
875        transform_pixbuf (buf,
876                          d_rect.x0, d_rect.y0,
877                          w, h,
878                          w * 4,
879                          priv->pixbuf, render_affine);
880
881        pixbuf = gdk_pixbuf_new_from_data (buf, GDK_COLORSPACE_RGB,
882                                           TRUE,
883                                           8, w, h,
884                                           w * 4,
885                                           NULL, NULL);
886
887        gdk_pixbuf_render_to_drawable_alpha (pixbuf, drawable,
888                                             0, 0,
889                                             d_rect.x0 - x, d_rect.y0 - y,
890                                             w, h,
891                                             GDK_PIXBUF_ALPHA_FULL,
892                                             0,
893                                             GDK_RGB_DITHER_MAX,
894                                             d_rect.x0, d_rect.y0);
895
896        gdk_pixbuf_unref (pixbuf);
897        g_free (buf);
898}
899
900/* Render handler for the pixbuf canvas item */
901static void
902gnome_canvas_pixbuf_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf)
903{
904        GnomeCanvasPixbuf *gcp;
905        PixbufPrivate *priv;
906        double i2c[6], render_affine[6];
907
908        gcp = GNOME_CANVAS_PIXBUF (item);
909        priv = gcp->priv;
910
911        if (!priv->pixbuf)
912                return;
913
914        gnome_canvas_item_i2c_affine (item, i2c);
915        compute_render_affine (gcp, render_affine, i2c);
916        gnome_canvas_buf_ensure_buf (buf);
917
918
919        if ((fabs (render_affine[1]) < GNOME_CANVAS_EPSILON) &&
920            (fabs (render_affine[2]) < GNOME_CANVAS_EPSILON) &&
921            render_affine[0] > 0.0 &&
922            render_affine[3] > 0.0)
923          {
924            GdkPixbuf *dest_pixbuf;
925            int x0, y0, x1, y1;
926
927            dest_pixbuf = gdk_pixbuf_new_from_data (buf->buf,
928                                                    GDK_COLORSPACE_RGB,
929                                                    FALSE,
930                                                    8,
931                                                    buf->rect.x1 - buf->rect.x0,
932                                                    buf->rect.y1 - buf->rect.y0,
933                                                    buf->buf_rowstride,
934                                                    NULL, NULL);
935
936
937            x0 = floor (render_affine[4] - buf->rect.x0 + 0.5);
938            y0 = floor (render_affine[5] - buf->rect.y0 + 0.5);
939
940            x1 = x0 + floor (gdk_pixbuf_get_width (priv->pixbuf) * render_affine[0] + 0.5);
941            y1 = y0 + floor (gdk_pixbuf_get_height (priv->pixbuf) * render_affine[3] + 0.5);
942
943            x0 = MAX (x0, 0);
944            x0 = MIN (x0, buf->rect.x1 - buf->rect.x0);
945            y0 = MAX (y0, 0);
946            y0 = MIN (y0, buf->rect.y1 - buf->rect.y0);
947           
948            x1 = MAX (x1, 0);
949            x1 = MIN (x1, buf->rect.x1 - buf->rect.x0);
950            y1 = MAX (y1, 0);
951            y1 = MIN (y1, buf->rect.y1 - buf->rect.y0);
952           
953            gdk_pixbuf_composite  (priv->pixbuf,
954                                   dest_pixbuf,
955                                   x0, y0,
956                                   x1 - x0, y1 - y0,
957                                   render_affine[4] - buf->rect.x0,
958                                   render_affine[5] - buf->rect.y0,
959                                   render_affine[0],
960                                   render_affine[3],
961                                   GDK_INTERP_NEAREST,
962                                   255);
963
964            gdk_pixbuf_unref (dest_pixbuf);
965          }
966        else if (gdk_pixbuf_get_has_alpha(priv->pixbuf))
967                art_rgb_rgba_affine (buf->buf,
968                                     buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1,
969                                     buf->buf_rowstride,
970                                     gdk_pixbuf_get_pixels(priv->pixbuf),
971                                     gdk_pixbuf_get_width(priv->pixbuf),
972                                     gdk_pixbuf_get_height(priv->pixbuf),
973                                     gdk_pixbuf_get_rowstride(priv->pixbuf),
974                                     render_affine,
975                                     ART_FILTER_NEAREST,
976                                     NULL);
977        else
978                art_rgb_affine (buf->buf,
979                                buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1,
980                                buf->buf_rowstride,
981                                gdk_pixbuf_get_pixels(priv->pixbuf),
982                                gdk_pixbuf_get_width(priv->pixbuf),
983                                gdk_pixbuf_get_height(priv->pixbuf),
984                                gdk_pixbuf_get_rowstride(priv->pixbuf),
985                                render_affine,
986                                ART_FILTER_NEAREST,
987                                NULL);
988
989        buf->is_bg = 0;
990}
991
992
993
994/* Point handler for the pixbuf canvas item */
995static double
996gnome_canvas_pixbuf_point (GnomeCanvasItem *item, double x, double y, int cx, int cy,
997                           GnomeCanvasItem **actual_item)
998{
999        GnomeCanvasPixbuf *gcp;
1000        PixbufPrivate *priv;
1001        double i2c[6], render_affine[6], inv[6];
1002        ArtPoint c, p;
1003        int px, py;
1004        double no_hit;
1005        guchar *src;
1006        GdkPixbuf *pixbuf;
1007
1008        gcp = GNOME_CANVAS_PIXBUF (item);
1009        priv = gcp->priv;
1010        pixbuf = priv->pixbuf;
1011
1012        *actual_item = item;
1013
1014        no_hit = item->canvas->pixels_per_unit * 2 + 10;
1015
1016        if (!priv->pixbuf)
1017                return no_hit;
1018
1019        gnome_canvas_item_i2c_affine (item, i2c);
1020        compute_render_affine (gcp, render_affine, i2c);
1021        art_affine_invert (inv, render_affine);
1022
1023        c.x = cx;
1024        c.y = cy;
1025        art_affine_point (&p, &c, inv);
1026        px = p.x;
1027        py = p.y;
1028
1029        if (px < 0 || px >= gdk_pixbuf_get_width (pixbuf) ||
1030            py < 0 || py >= gdk_pixbuf_get_height (pixbuf))
1031                return no_hit;
1032
1033        if (!gdk_pixbuf_get_has_alpha (pixbuf))
1034                return 0.0;
1035
1036        src = gdk_pixbuf_get_pixels (pixbuf) +
1037            py * gdk_pixbuf_get_rowstride (pixbuf) +
1038            px * gdk_pixbuf_get_n_channels (pixbuf);
1039
1040        if (src[3] < 128)
1041                return no_hit;
1042        else
1043                return 0.0;
1044}
1045
1046
1047
1048/* Bounds handler for the pixbuf canvas item */
1049static void
1050gnome_canvas_pixbuf_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1051{
1052        GnomeCanvasPixbuf *gcp;
1053        PixbufPrivate *priv;
1054        double i2c[6], viewport_affine[6];
1055        ArtDRect rect;
1056
1057        gcp = GNOME_CANVAS_PIXBUF (item);
1058        priv = gcp->priv;
1059
1060        if (!priv->pixbuf) {
1061                *x1 = *y1 = *x2 = *y2 = 0.0;
1062                return;
1063        }
1064
1065        rect.x0 = 0.0;
1066        rect.x1 = gdk_pixbuf_get_width (priv->pixbuf);
1067
1068        rect.y0 = 0.0;
1069        rect.y1 = gdk_pixbuf_get_height (priv->pixbuf);
1070
1071        gnome_canvas_item_i2c_affine (item, i2c);
1072        compute_viewport_affine (gcp, viewport_affine, i2c);
1073        art_drect_affine_transform (&rect, &rect, viewport_affine);
1074
1075        *x1 = rect.x0;
1076        *y1 = rect.y0;
1077        *x2 = rect.x1;
1078        *y2 = rect.y1;
1079}
Note: See TracBrowser for help on using the repository browser.