d186e86f50bd85a88c9b67c721a21768f4848953
[ardour.git] / gtk2_ardour / canvas-imageframe.c
1 /* Image item type for GtkCanvas widget
2  *
3  * GtkCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
4  * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
5  *
6  * Copyright (C) 1998 The Free Software Foundation
7  *
8  * Author: Federico Mena <federico@nuclecu.unam.mx>
9  */
10  
11  
12 #include <string.h> /* for memcpy() */
13 #include <math.h>
14 #include <stdio.h>
15 #include "libart_lgpl/art_misc.h"
16 #include "libart_lgpl/art_affine.h"
17 #include "libart_lgpl/art_pixbuf.h"
18 #include "libart_lgpl/art_rgb_pixbuf_affine.h"
19 #include "canvas-imageframe.h"
20 #include <gtk-canvas/gtk-canvas-util.h>
21 #include <gtk-canvas/gtk-canvastypebuiltins.h>
22
23
24 enum {
25         ARG_0,
26         ARG_PIXBUF,
27         ARG_X,
28         ARG_Y,
29         ARG_WIDTH,
30         ARG_HEIGHT,
31         ARG_DRAWWIDTH,
32         ARG_ANCHOR
33 };
34
35
36 static void gtk_canvas_imageframe_class_init(GtkCanvasImageFrameClass* class) ;
37 static void gtk_canvas_imageframe_init(GtkCanvasImageFrame* image) ;
38 static void gtk_canvas_imageframe_destroy(GtkObject* object) ;
39 static void gtk_canvas_imageframe_set_arg(GtkObject* object, GtkArg* arg, guint arg_id) ;
40 static void gtk_canvas_imageframe_get_arg(GtkObject* object, GtkArg* arg, guint arg_id) ;
41
42 static void gtk_canvas_imageframe_update(GtkCanvasItem *item, double *affine, ArtSVP *clip_path, int flags) ;
43 static void gtk_canvas_imageframe_realize(GtkCanvasItem *item) ;
44 static void gtk_canvas_imageframe_unrealize(GtkCanvasItem *item) ;
45 static void gtk_canvas_imageframe_draw(GtkCanvasItem *item, GdkDrawable *drawable, int x, int y, int width, int height) ;
46 static double gtk_canvas_imageframe_point(GtkCanvasItem *item, double x, double y, int cx, int cy, GtkCanvasItem **actual_item) ;
47 static void gtk_canvas_imageframe_translate(GtkCanvasItem *item, double dx, double dy) ;
48 static void gtk_canvas_imageframe_bounds(GtkCanvasItem *item, double *x1, double *y1, double *x2, double *y2) ;
49 static void gtk_canvas_imageframe_render(GtkCanvasItem *item, GtkCanvasBuf *buf) ;
50
51 static GtkCanvasItemClass *parent_class;
52
53
54 GtkType
55 gtk_canvas_imageframe_get_type (void)
56 {
57         static GtkType imageframe_type = 0;
58
59         if (!imageframe_type) {
60                 GtkTypeInfo imageframe_info = {
61                         "GtkCanvasImageFrame",
62                         sizeof (GtkCanvasImageFrame),
63                         sizeof (GtkCanvasImageFrameClass),
64                         (GtkClassInitFunc) gtk_canvas_imageframe_class_init,
65                         (GtkObjectInitFunc) gtk_canvas_imageframe_init,
66                         NULL, /* reserved_1 */
67                         NULL, /* reserved_2 */
68                         (GtkClassInitFunc) NULL
69                 };
70
71                 imageframe_type = gtk_type_unique (gtk_canvas_item_get_type (), &imageframe_info);
72         }
73
74         return imageframe_type;
75 }
76
77 static void
78 gtk_canvas_imageframe_class_init (GtkCanvasImageFrameClass *class)
79 {
80         GtkObjectClass *object_class;
81         GtkCanvasItemClass *item_class;
82
83         object_class = (GtkObjectClass *) class;
84         item_class = (GtkCanvasItemClass *) class;
85
86         parent_class = gtk_type_class (gtk_canvas_item_get_type ());
87
88         gtk_object_add_arg_type ("GtkCanvasImageFrame::pixbuf", GTK_TYPE_BOXED, GTK_ARG_WRITABLE, ARG_PIXBUF);
89         gtk_object_add_arg_type ("GtkCanvasImageFrame::x", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_X);
90         gtk_object_add_arg_type ("GtkCanvasImageFrame::y", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_Y);
91         gtk_object_add_arg_type ("GtkCanvasImageFrame::width", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_WIDTH);
92         gtk_object_add_arg_type ("GtkCanvasImageFrame::drawwidth", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_DRAWWIDTH);
93         gtk_object_add_arg_type ("GtkCanvasImageFrame::height", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_HEIGHT);
94         gtk_object_add_arg_type ("GtkCanvasImageFrame::anchor", GTK_TYPE_ANCHOR_TYPE, GTK_ARG_READWRITE, ARG_ANCHOR);
95
96         object_class->destroy = gtk_canvas_imageframe_destroy;
97         object_class->set_arg = gtk_canvas_imageframe_set_arg;
98         object_class->get_arg = gtk_canvas_imageframe_get_arg;
99
100         item_class->update = gtk_canvas_imageframe_update;
101         item_class->realize = gtk_canvas_imageframe_realize;
102         item_class->unrealize = gtk_canvas_imageframe_unrealize;
103         item_class->draw = gtk_canvas_imageframe_draw;
104         item_class->point = gtk_canvas_imageframe_point;
105         item_class->translate = gtk_canvas_imageframe_translate;
106         item_class->bounds = gtk_canvas_imageframe_bounds;
107         item_class->render = gtk_canvas_imageframe_render;
108 }
109
110 static void
111 gtk_canvas_imageframe_init (GtkCanvasImageFrame *image)
112 {
113         image->x = 0.0;
114         image->y = 0.0;
115         image->width = 0.0;
116         image->height = 0.0;
117         image->drawwidth = 0.0;
118         image->anchor = GTK_ANCHOR_CENTER;
119         GTK_CANVAS_ITEM(image)->object.flags |= GTK_CANVAS_ITEM_NO_AUTO_REDRAW;
120 }
121
122 static void
123 gtk_canvas_imageframe_destroy (GtkObject *object)
124 {
125         GtkCanvasImageFrame *image;
126
127         g_return_if_fail (object != NULL);
128         g_return_if_fail (GTK_CANVAS_IS_CANVAS_IMAGEFRAME (object));
129
130         image = GTK_CANVAS_IMAGEFRAME (object);
131         
132         image->cwidth = 0;
133         image->cheight = 0;
134
135         if (image->pixbuf)
136         {
137                 art_pixbuf_free (image->pixbuf);
138                 image->pixbuf = NULL;
139         }
140
141         if(GTK_OBJECT_CLASS (parent_class)->destroy)
142         {
143                 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
144         }
145 }
146
147 /* Get's the image bounds expressed as item-relative coordinates. */
148 static void
149 get_bounds_item_relative (GtkCanvasImageFrame *image, double *px1, double *py1, double *px2, double *py2)
150 {
151         GtkCanvasItem *item;
152         double x, y;
153
154         item = GTK_CANVAS_ITEM (image);
155
156         /* Get item coordinates */
157
158         x = image->x;
159         y = image->y;
160
161         /* Anchor image */
162
163         switch (image->anchor) {
164         case GTK_ANCHOR_NW:
165         case GTK_ANCHOR_W:
166         case GTK_ANCHOR_SW:
167                 break;
168
169         case GTK_ANCHOR_N:
170         case GTK_ANCHOR_CENTER:
171         case GTK_ANCHOR_S:
172                 x -= image->width / 2;
173                 break;
174
175         case GTK_ANCHOR_NE:
176         case GTK_ANCHOR_E:
177         case GTK_ANCHOR_SE:
178                 x -= image->width;
179                 break;
180         }
181
182         switch (image->anchor) {
183         case GTK_ANCHOR_NW:
184         case GTK_ANCHOR_N:
185         case GTK_ANCHOR_NE:
186                 break;
187
188         case GTK_ANCHOR_W:
189         case GTK_ANCHOR_CENTER:
190         case GTK_ANCHOR_E:
191                 y -= image->height / 2;
192                 break;
193
194         case GTK_ANCHOR_SW:
195         case GTK_ANCHOR_S:
196         case GTK_ANCHOR_SE:
197                 y -= image->height;
198                 break;
199         }
200
201         /* Bounds */
202
203         *px1 = x;
204         *py1 = y;
205         *px2 = x + image->width;
206         *py2 = y + image->height;
207 }
208
209 static void
210 get_bounds (GtkCanvasImageFrame *image, double *px1, double *py1, double *px2, double *py2)
211 {
212         GtkCanvasItem *item;
213         double i2c[6];
214         ArtDRect i_bbox, c_bbox;
215
216         item = GTK_CANVAS_ITEM (image);
217
218         gtk_canvas_item_i2c_affine (item, i2c);
219
220         get_bounds_item_relative (image, &i_bbox.x0, &i_bbox.y0, &i_bbox.x1, &i_bbox.y1);
221         art_drect_affine_transform (&c_bbox, &i_bbox, i2c);
222
223         /* add a fudge factor */
224         *px1 = c_bbox.x0 - 1;
225         *py1 = c_bbox.y0 - 1;
226         *px2 = c_bbox.x1 + 1;
227         *py2 = c_bbox.y1 + 1;
228 }
229
230 /* deprecated */
231 static void
232 recalc_bounds (GtkCanvasImageFrame *image)
233 {
234         GtkCanvasItem *item;
235
236         item = GTK_CANVAS_ITEM (image);
237
238         get_bounds (image, &item->x1, &item->y1, &item->x2, &item->y2);
239
240         item->x1 = image->cx;
241         item->y1 = image->cy;
242         item->x2 = image->cx + image->cwidth;
243         item->y2 = image->cy + image->cheight;
244
245         gtk_canvas_group_child_bounds (GTK_CANVAS_GROUP (item->parent), item);
246 }
247
248 static void
249 gtk_canvas_imageframe_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
250 {
251         GtkCanvasItem *item;
252         GtkCanvasImageFrame *image;
253         int update;
254         int calc_bounds;
255
256         item = GTK_CANVAS_ITEM (object);
257         image = GTK_CANVAS_IMAGEFRAME (object);
258
259         update = FALSE;
260         calc_bounds = FALSE;
261
262         switch (arg_id) {
263         case ARG_PIXBUF:
264                 if (item->canvas->aa && GTK_VALUE_BOXED (*arg)) {
265                         if (image->pixbuf != NULL)
266                                 art_pixbuf_free (image->pixbuf);
267                         image->pixbuf = GTK_VALUE_BOXED (*arg);
268                 }
269                 update = TRUE;
270                 break;
271
272         case ARG_X:
273                 image->x = GTK_VALUE_DOUBLE (*arg);
274                 update = TRUE;
275                 break;
276
277         case ARG_Y:
278                 image->y = GTK_VALUE_DOUBLE (*arg);
279                 update = TRUE;
280                 break;
281
282         case ARG_WIDTH:
283                 image->width = fabs (GTK_VALUE_DOUBLE (*arg));
284                 update = TRUE;
285                 break;
286
287         case ARG_HEIGHT:
288                 image->height = fabs (GTK_VALUE_DOUBLE (*arg));
289                 update = TRUE;
290                 break;
291                 
292         case ARG_DRAWWIDTH:
293                 image->drawwidth = fabs (GTK_VALUE_DOUBLE (*arg));
294                 update = TRUE;
295                 break;
296
297         case ARG_ANCHOR:
298                 image->anchor = GTK_VALUE_ENUM (*arg);
299                 update = TRUE;
300                 break;
301
302         default:
303                 break;
304         }
305
306 #ifdef OLD_XFORM
307         if (update)
308                 (* GTK_CANVAS_ITEM_CLASS (item->object.klass)->update) (item, NULL, NULL, 0);
309
310         if (calc_bounds)
311                 recalc_bounds (image);
312 #else
313         if (update)
314                 gtk_canvas_item_request_update (item);
315 #endif
316 }
317
318 static void
319 gtk_canvas_imageframe_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
320 {
321         GtkCanvasImageFrame *image;
322
323         image = GTK_CANVAS_IMAGEFRAME (object);
324
325         switch (arg_id) {
326
327         case ARG_X:
328                 GTK_VALUE_DOUBLE (*arg) = image->x;
329                 break;
330
331         case ARG_Y:
332                 GTK_VALUE_DOUBLE (*arg) = image->y;
333                 break;
334
335         case ARG_WIDTH:
336                 GTK_VALUE_DOUBLE (*arg) = image->width;
337                 break;
338
339         case ARG_HEIGHT:
340                 GTK_VALUE_DOUBLE (*arg) = image->height;
341                 break;
342                 
343         case ARG_DRAWWIDTH:
344                 GTK_VALUE_DOUBLE (*arg) = image->drawwidth;
345                 break;
346
347         case ARG_ANCHOR:
348                 GTK_VALUE_ENUM (*arg) = image->anchor;
349                 break;
350
351         default:
352                 arg->type = GTK_TYPE_INVALID;
353                 break;
354         }
355 }
356
357 static void
358 gtk_canvas_imageframe_update (GtkCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
359 {
360         GtkCanvasImageFrame *image;
361         ArtDRect i_bbox, c_bbox;
362         int w = 0;
363         int h = 0;
364
365         image = GTK_CANVAS_IMAGEFRAME (item);
366
367         if (parent_class->update)
368                 (* parent_class->update) (item, affine, clip_path, flags);
369
370         /* only works for non-rotated, non-skewed transforms */
371         image->cwidth = (int) (image->width * affine[0] + 0.5);
372         image->cheight = (int) (image->height * affine[3] + 0.5);
373
374         if (image->pixbuf) {
375                 image->need_recalc = TRUE ;
376         }
377
378 #ifdef OLD_XFORM
379         recalc_bounds (image);
380 #else
381         get_bounds_item_relative (image, &i_bbox.x0, &i_bbox.y0, &i_bbox.x1, &i_bbox.y1);
382         art_drect_affine_transform (&c_bbox, &i_bbox, affine);
383
384         /* these values only make sense in the non-rotated, non-skewed case */
385         image->cx = c_bbox.x0;
386         image->cy = c_bbox.y0;
387
388         /* add a fudge factor */
389         c_bbox.x0--;
390         c_bbox.y0--;
391         c_bbox.x1++;
392         c_bbox.y1++;
393
394         gtk_canvas_update_bbox (item, c_bbox.x0, c_bbox.y0, c_bbox.x1, c_bbox.y1);
395
396         if (image->pixbuf) {
397                 w = image->pixbuf->width;
398                 h = image->pixbuf->height;
399         }
400
401         image->affine[0] = (affine[0] * image->width) / w;
402         image->affine[1] = (affine[1] * image->height) / h;
403         image->affine[2] = (affine[2] * image->width) / w;
404         image->affine[3] = (affine[3] * image->height) / h;
405         image->affine[4] = i_bbox.x0 * affine[0] + i_bbox.y0 * affine[2] + affine[4];
406         image->affine[5] = i_bbox.x0 * affine[1] + i_bbox.y0 * affine[3] + affine[5];
407         
408 #endif
409 }
410
411 static void
412 gtk_canvas_imageframe_realize (GtkCanvasItem *item)
413 {
414         GtkCanvasImageFrame *image;
415
416         image = GTK_CANVAS_IMAGEFRAME (item);
417
418         if (parent_class->realize)
419                 (* parent_class->realize) (item);
420
421 }
422
423 static void
424 gtk_canvas_imageframe_unrealize (GtkCanvasItem *item)
425 {
426         GtkCanvasImageFrame *image;
427
428         image = GTK_CANVAS_IMAGEFRAME(item);
429
430         if (parent_class->unrealize)
431                 (* parent_class->unrealize) (item);
432 }
433
434 static void
435 recalc_if_needed (GtkCanvasImageFrame *image)
436 {}
437
438 static void
439 gtk_canvas_imageframe_draw (GtkCanvasItem *item, GdkDrawable *drawable,
440                          int x, int y, int width, int height)
441 {
442         fprintf(stderr, "please don't use the CanvasImageFrame item in a non-aa Canvas\n") ;
443         abort() ;
444 }
445
446 static double
447 gtk_canvas_imageframe_point (GtkCanvasItem *item, double x, double y,
448                           int cx, int cy, GtkCanvasItem **actual_item)
449 {
450         GtkCanvasImageFrame *image;
451         int x1, y1, x2, y2;
452         int dx, dy;
453
454         image = GTK_CANVAS_IMAGEFRAME (item);
455
456         *actual_item = item;
457
458         recalc_if_needed (image);
459
460         x1 = image->cx - item->canvas->close_enough;
461         y1 = image->cy - item->canvas->close_enough;
462         x2 = image->cx + image->cwidth - 1 + item->canvas->close_enough;
463         y2 = image->cy + image->cheight - 1 + item->canvas->close_enough;
464
465         /* Hard case: is point inside image's gravity region? */
466
467         //if ((cx >= x1) && (cy >= y1) && (cx <= x2) && (cy <= y2))
468                 //return dist_to_mask (image, cx, cy) / item->canvas->pixels_per_unit;
469
470         /* Point is outside image */
471
472         x1 += item->canvas->close_enough;
473         y1 += item->canvas->close_enough;
474         x2 -= item->canvas->close_enough;
475         y2 -= item->canvas->close_enough;
476
477         if (cx < x1)
478                 dx = x1 - cx;
479         else if (cx > x2)
480                 dx = cx - x2;
481         else
482                 dx = 0;
483
484         if (cy < y1)
485                 dy = y1 - cy;
486         else if (cy > y2)
487                 dy = cy - y2;
488         else
489                 dy = 0;
490
491         return sqrt (dx * dx + dy * dy) / item->canvas->pixels_per_unit;
492 }
493
494 static void
495 gtk_canvas_imageframe_translate (GtkCanvasItem *item, double dx, double dy)
496 {
497 #ifdef OLD_XFORM
498         GtkCanvasImageFrame *image;
499
500         image = GTK_CANVAS_IMAGEFRAME (item);
501
502         image->x += dx;
503         image->y += dy;
504
505         recalc_bounds (image);
506 #endif
507 }
508
509 static void
510 gtk_canvas_imageframe_bounds (GtkCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
511 {
512         GtkCanvasImageFrame *image;
513
514         image = GTK_CANVAS_IMAGEFRAME (item);
515
516         *x1 = image->x;
517         *y1 = image->y;
518
519         switch (image->anchor) {
520         case GTK_ANCHOR_NW:
521         case GTK_ANCHOR_W:
522         case GTK_ANCHOR_SW:
523                 break;
524
525         case GTK_ANCHOR_N:
526         case GTK_ANCHOR_CENTER:
527         case GTK_ANCHOR_S:
528                 *x1 -= image->width / 2.0;
529                 break;
530
531         case GTK_ANCHOR_NE:
532         case GTK_ANCHOR_E:
533         case GTK_ANCHOR_SE:
534                 *x1 -= image->width;
535                 break;
536         }
537
538         switch (image->anchor) {
539         case GTK_ANCHOR_NW:
540         case GTK_ANCHOR_N:
541         case GTK_ANCHOR_NE:
542                 break;
543
544         case GTK_ANCHOR_W:
545         case GTK_ANCHOR_CENTER:
546         case GTK_ANCHOR_E:
547                 *y1 -= image->height / 2.0;
548                 break;
549
550         case GTK_ANCHOR_SW:
551         case GTK_ANCHOR_S:
552         case GTK_ANCHOR_SE:
553                 *y1 -= image->height;
554                 break;
555         }
556
557         *x2 = *x1 + image->width;
558         *y2 = *y1 + image->height;
559 }
560
561 static void
562 gtk_canvas_imageframe_render      (GtkCanvasItem *item, GtkCanvasBuf *buf)
563 {
564         GtkCanvasImageFrame *image;
565
566         image = GTK_CANVAS_IMAGEFRAME (item);
567
568         gtk_canvas_buf_ensure_buf (buf);
569
570 #ifdef VERBOSE
571         {
572                 char str[128];
573                 art_affine_to_string (str, image->affine);
574                 g_print ("gtk_canvas_imageframe_render %s\n", str);
575         }
576 #endif
577
578         art_rgb_pixbuf_affine (buf->buf,
579                         buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1,
580                         buf->buf_rowstride,
581                         image->pixbuf,
582                         image->affine,
583                         ART_FILTER_NEAREST, NULL);
584
585         buf->is_bg = 0;
586 }