separated tile renderer
[geeqie.git] / src / renderer-tiles.c
1 /*
2  * Geeqie
3  * (C) 2006 John Ellis
4  * Copyright (C) 2008 - 2010 The Geeqie Team
5  *
6  * Author: John Ellis
7  *
8  * This software is released under the GNU General Public License (GNU GPL).
9  * Please read the included file COPYING for more information.
10  * This software comes with no warranty of any kind, use at your own risk!
11  */
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <math.h>
17
18 #include "main.h"
19 #include "pixbuf-renderer.h"
20 #include "renderer-tiles.h"
21
22 #include "intl.h"
23 #include "layout.h"
24
25 #include <gtk/gtk.h>
26
27
28 /* comment this out if not using this from within Geeqie
29  * defining GQ_BUILD does these things:
30  *   - Sets the shift-click scroller pixbuf to a nice icon instead of a black box
31  */
32 #define GQ_BUILD 1
33
34 #ifdef GQ_BUILD
35 #include "main.h"
36 #include "pixbuf_util.h"
37 #include "exif.h"
38 #else
39 typedef enum {
40         EXIF_ORIENTATION_UNKNOWN        = 0,
41         EXIF_ORIENTATION_TOP_LEFT       = 1,
42         EXIF_ORIENTATION_TOP_RIGHT      = 2,
43         EXIF_ORIENTATION_BOTTOM_RIGHT   = 3,
44         EXIF_ORIENTATION_BOTTOM_LEFT    = 4,
45         EXIF_ORIENTATION_LEFT_TOP       = 5,
46         EXIF_ORIENTATION_RIGHT_TOP      = 6,
47         EXIF_ORIENTATION_RIGHT_BOTTOM   = 7,
48         EXIF_ORIENTATION_LEFT_BOTTOM    = 8
49 } ExifOrientationType;
50 #endif
51
52
53 /* size to use when breaking up image pane for rendering */
54 #define PR_TILE_SIZE 128
55
56 typedef struct _ImageTile ImageTile;
57 typedef struct _QueueData QueueData;
58
59 struct _ImageTile
60 {
61         GdkPixmap *pixmap;      /* off screen buffer */
62         GdkPixbuf *pixbuf;      /* pixbuf area for zooming */
63         gint x;                 /* x offset into image */
64         gint y;                 /* y offset into image */
65         gint w;                 /* width that is visible (may be less if at edge of image) */
66         gint h;                 /* height '' */
67
68         gboolean blank;
69
70 /* render_todo: (explanation)
71         NONE    do nothing
72         AREA    render area of tile, usually only used when loading an image
73                 note: will jump to an ALL if render_done is not ALL.
74         ALL     render entire tile, if never done before w/ ALL, for expose events *only*
75 */
76
77         ImageRenderType render_todo;    /* what to do (see above) */
78         ImageRenderType render_done;    /* highest that has been done before on tile */
79
80         QueueData *qd;
81         QueueData *qd2;
82
83         guint size;             /* est. memory used by pixmap and pixbuf */
84 };
85
86 struct _QueueData
87 {
88         ImageTile *it;
89         gint x;
90         gint y;
91         gint w;
92         gint h;
93         gboolean new_data;
94 };
95
96 typedef struct _OverlayData OverlayData;
97 struct _OverlayData
98 {
99         gint id;
100
101         GdkPixbuf *pixbuf;
102         GdkWindow *window;
103
104         gint x;
105         gint y;
106
107         OverlayRendererFlags flags;
108 };
109
110 typedef struct _RendererTiles RendererTiles;
111
112 struct _RendererTiles
113 {
114         RendererFuncs f;
115         PixbufRenderer *pr;
116
117         gint tile_cache_max;            /* max mb to use for offscreen buffer */
118
119         gint tile_width;
120         gint tile_height;
121         gint tile_cols;         /* count of tile columns */
122         GList *tiles;           /* list of buffer tiles */
123         gint tile_cache_size;   /* allocated size of pixmaps/pixbufs */
124         GList *draw_queue;      /* list of areas to redraw */
125         GList *draw_queue_2pass;/* list when 2 pass is enabled */
126
127         GList *overlay_list;
128         GdkPixmap *overlay_buffer;
129         
130         guint draw_idle_id; /* event source id */
131
132         GdkPixbuf *spare_tile;
133
134 };
135
136
137 static void rt_border_draw(RendererTiles *rt, gint x, gint y, gint w, gint h);
138 static void rt_overlay_draw(RendererTiles *rt, gint x, gint y, gint w, gint h, ImageTile *it);
139
140
141 static void rt_tile_free_all(RendererTiles *rt);
142 static void rt_tile_invalidate_region(RendererTiles *rt, gint x, gint y, gint w, gint h);
143 static gboolean rt_tile_is_visible(RendererTiles *rt, ImageTile *it);
144 static void rt_queue_clear(RendererTiles *rt);
145 static void rt_queue_merge(QueueData *parent, QueueData *qd);
146 static void rt_queue(RendererTiles *rt, gint x, gint y, gint w, gint h,
147                      gint clamp, ImageRenderType render, gboolean new_data, gboolean only_existing);
148
149 static void rt_redraw(RendererTiles *rt, gboolean new_data);
150
151
152 static void rt_signals_connect(RendererTiles *rt);
153 static void rt_size_cb(GtkWidget *widget, GtkAllocation *allocation, gpointer data);
154 static void rt_hierarchy_changed_cb(GtkWidget *widget, GtkWidget *previous_toplevel, gpointer data);
155 static void pixbuf_renderer_paint(RendererTiles *rt, GdkRectangle *area);
156 static gint rt_queue_draw_idle_cb(gpointer data);
157
158
159 /*
160  *-------------------------------------------------------------------
161  * borders
162  *-------------------------------------------------------------------
163  */
164
165 static void rt_border_draw(RendererTiles *rt, gint x, gint y, gint w, gint h)
166 {
167         PixbufRenderer *pr = rt->pr;
168         GtkWidget *box;
169         gint rx, ry, rw, rh;
170
171         box = GTK_WIDGET(pr);
172
173         if (!box->window) return;
174
175         if (!pr->pixbuf && !pr->source_tiles_enabled)
176                 {
177                 if (pr_clip_region(x, y, w, h,
178                                    0, 0,
179                                    pr->window_width, pr->window_height,
180                                    &rx, &ry, &rw, &rh))
181                         {
182                         gdk_window_clear_area(box->window, rx, ry, rw, rh);
183                         rt_overlay_draw(rt, rx, ry, rw, rh, NULL);
184                         }
185                 return;
186                 }
187
188         if (pr->vis_width < pr->window_width)
189                 {
190                 if (pr->x_offset > 0 &&
191                     pr_clip_region(x, y, w, h,
192                                    0, 0,
193                                    pr->x_offset, pr->window_height,
194                                    &rx, &ry, &rw, &rh))
195                         {
196                         gdk_window_clear_area(box->window, rx, ry, rw, rh);
197                         rt_overlay_draw(rt, rx, ry, rw, rh, NULL);
198                         }
199                 if (pr->window_width - pr->vis_width - pr->x_offset > 0 &&
200                     pr_clip_region(x, y, w, h,
201                                    pr->x_offset + pr->vis_width, 0,
202                                    pr->window_width - pr->vis_width - pr->x_offset, pr->window_height,
203                                    &rx, &ry, &rw, &rh))
204                         {
205                         gdk_window_clear_area(box->window, rx, ry, rw, rh);
206                         rt_overlay_draw(rt, rx, ry, rw, rh, NULL);
207                         }
208                 }
209         if (pr->vis_height < pr->window_height)
210                 {
211                 if (pr->y_offset > 0 &&
212                     pr_clip_region(x, y, w, h,
213                                    pr->x_offset, 0,
214                                    pr->vis_width, pr->y_offset,
215                                    &rx, &ry, &rw, &rh))
216                         {
217                         gdk_window_clear_area(box->window, rx, ry, rw, rh);
218                         rt_overlay_draw(rt, rx, ry, rw, rh, NULL);
219                         }
220                 if (pr->window_height - pr->vis_height - pr->y_offset > 0 &&
221                     pr_clip_region(x, y, w, h,
222                                    pr->x_offset, pr->y_offset + pr->vis_height,
223                                    pr->vis_width, pr->window_height - pr->vis_height - pr->y_offset,
224                                    &rx, &ry, &rw, &rh))
225                         {
226                         gdk_window_clear_area(box->window, rx, ry, rw, rh);
227                         rt_overlay_draw(rt, rx, ry, rw, rh, NULL);
228                         }
229                 }
230 }
231
232 static void rt_border_clear(RendererTiles *rt)
233 {
234         PixbufRenderer *pr = rt->pr;
235         rt_border_draw(rt, 0, 0, pr->window_width, pr->window_height);
236 }
237
238
239 /*
240  *-------------------------------------------------------------------
241  * display tiles
242  *-------------------------------------------------------------------
243  */
244
245 static ImageTile *rt_tile_new(gint x, gint y, gint width, gint height)
246 {
247         ImageTile *it;
248
249         it = g_new0(ImageTile, 1);
250
251         it->x = x;
252         it->y = y;
253         it->w = width;
254         it->h = height;
255
256         it->render_done = TILE_RENDER_NONE;
257
258         return it;
259 }
260
261 static void rt_tile_free(ImageTile *it)
262 {
263         if (!it) return;
264
265         if (it->pixbuf) g_object_unref(it->pixbuf);
266         if (it->pixmap) g_object_unref(it->pixmap);
267
268         g_free(it);
269 }
270
271 static void rt_tile_free_all(RendererTiles *rt)
272 {
273         GList *work;
274
275         work = rt->tiles;
276         while (work)
277                 {
278                 ImageTile *it;
279
280                 it = work->data;
281                 work = work->next;
282
283                 rt_tile_free(it);
284                 }
285
286         g_list_free(rt->tiles);
287         rt->tiles = NULL;
288         rt->tile_cache_size = 0;
289 }
290
291 static ImageTile *rt_tile_add(RendererTiles *rt, gint x, gint y)
292 {
293         PixbufRenderer *pr = rt->pr;
294         ImageTile *it;
295
296         it = rt_tile_new(x, y, rt->tile_width, rt->tile_height);
297
298         if (it->x + it->w > pr->width) it->w = pr->width - it->x;
299         if (it->y + it->h > pr->height) it->h = pr->height - it->y;
300
301         rt->tiles = g_list_prepend(rt->tiles, it);
302         rt->tile_cache_size += it->size;
303
304         return it;
305 }
306
307 static void rt_tile_remove(RendererTiles *rt, ImageTile *it)
308 {
309         if (it->qd)
310                 {
311                 QueueData *qd = it->qd;
312
313                 it->qd = NULL;
314                 rt->draw_queue = g_list_remove(rt->draw_queue, qd);
315                 g_free(qd);
316                 }
317
318         if (it->qd2)
319                 {
320                 QueueData *qd = it->qd2;
321
322                 it->qd2 = NULL;
323                 rt->draw_queue_2pass = g_list_remove(rt->draw_queue_2pass, qd);
324                 g_free(qd);
325                 }
326
327         rt->tiles = g_list_remove(rt->tiles, it);
328         rt->tile_cache_size -= it->size;
329
330         rt_tile_free(it);
331 }
332
333 static void rt_tile_free_space(RendererTiles *rt, guint space, ImageTile *it)
334 {
335         PixbufRenderer *pr = rt->pr;
336         GList *work;
337         guint tile_max;
338
339         work = g_list_last(rt->tiles);
340
341         if (pr->source_tiles_enabled && pr->scale < 1.0)
342                 {
343                 gint tiles;
344
345                 tiles = (pr->vis_width / rt->tile_width + 1) * (pr->vis_height / rt->tile_height + 1);
346                 tile_max = MAX(tiles * rt->tile_width * rt->tile_height * 3,
347                                (gint)((gdouble)rt->tile_cache_max * 1048576.0 * pr->scale));
348                 }
349         else
350                 {
351                 tile_max = rt->tile_cache_max * 1048576;
352                 }
353
354         while (work && rt->tile_cache_size + space > tile_max)
355                 {
356                 ImageTile *needle;
357
358                 needle = work->data;
359                 work = work->prev;
360                 if (needle != it &&
361                     ((!needle->qd && !needle->qd2) || !rt_tile_is_visible(rt, needle))) rt_tile_remove(rt, needle);
362                 }
363 }
364
365 static void rt_tile_invalidate_all(RendererTiles *rt)
366 {
367         PixbufRenderer *pr = rt->pr;
368         GList *work;
369
370         work = rt->tiles;
371         while (work)
372                 {
373                 ImageTile *it;
374
375                 it = work->data;
376                 work = work->next;
377
378                 it->render_done = TILE_RENDER_NONE;
379                 it->render_todo = TILE_RENDER_ALL;
380                 it->blank = FALSE;
381
382                 it->w = MIN(rt->tile_width, pr->width - it->x);
383                 it->h = MIN(rt->tile_height, pr->height - it->y);
384                 }
385 }
386
387 static void rt_tile_invalidate_region(RendererTiles *rt, gint x, gint y, gint w, gint h)
388 {
389         gint x1, x2;
390         gint y1, y2;
391         GList *work;
392
393         x1 = ROUND_DOWN(x, rt->tile_width);
394         x2 = ROUND_UP(x + w, rt->tile_width);
395
396         y1 = ROUND_DOWN(y, rt->tile_height);
397         y2 = ROUND_UP(y + h, rt->tile_height);
398
399         work = rt->tiles;
400         while (work)
401                 {
402                 ImageTile *it;
403
404                 it = work->data;
405                 work = work->next;
406
407                 if (it->x < x2 && it->x + it->w > x1 &&
408                     it->y < y2 && it->y + it->h > y1)
409                         {
410                         it->render_done = TILE_RENDER_NONE;
411                         it->render_todo = TILE_RENDER_ALL;
412                         }
413                 }
414 }
415
416 static ImageTile *rt_tile_get(RendererTiles *rt, gint x, gint y, gboolean only_existing)
417 {
418         GList *work;
419
420         work = rt->tiles;
421         while (work)
422                 {
423                 ImageTile *it;
424
425                 it = work->data;
426                 if (it->x == x && it->y == y)
427                         {
428                         rt->tiles = g_list_delete_link(rt->tiles, work);
429                         rt->tiles = g_list_prepend(rt->tiles, it);
430                         return it;
431                         }
432
433                 work = work->next;
434                 }
435
436         if (only_existing) return NULL;
437
438         return rt_tile_add(rt, x, y);
439 }
440
441 static gint pixmap_calc_size(GdkPixmap *pixmap)
442 {
443         gint w, h, d;
444
445         d = gdk_drawable_get_depth(pixmap);
446         gdk_drawable_get_size(pixmap, &w, &h);
447         return w * h * (d / 8);
448 }
449
450 static void rt_tile_prepare(RendererTiles *rt, ImageTile *it)
451 {
452         PixbufRenderer *pr = rt->pr;
453         if (!it->pixmap)
454                 {
455                 GdkPixmap *pixmap;
456                 guint size;
457
458                 pixmap = gdk_pixmap_new(((GtkWidget *)pr)->window, rt->tile_width, rt->tile_height, -1);
459
460                 size = pixmap_calc_size(pixmap);
461                 rt_tile_free_space(rt, size, it);
462
463                 it->pixmap = pixmap;
464                 it->size += size;
465                 rt->tile_cache_size += size;
466                 }
467
468         if ((pr->zoom != 1.0 || pr->source_tiles_enabled || (pr->pixbuf && gdk_pixbuf_get_has_alpha(pr->pixbuf)) ||
469              pr->orientation != EXIF_ORIENTATION_TOP_LEFT || pr->func_post_process) && !it->pixbuf)
470                 {
471                 GdkPixbuf *pixbuf;
472                 guint size;
473                         {
474                         pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, rt->tile_width, rt->tile_height);
475                         }
476
477                 size = gdk_pixbuf_get_rowstride(pixbuf) * rt->tile_height;
478                 rt_tile_free_space(rt, size, it);
479
480                 it->pixbuf = pixbuf;
481                 it->size += size;
482                 rt->tile_cache_size += size;
483                 }
484 }
485
486 /*
487  *-------------------------------------------------------------------
488  * overlays
489  *-------------------------------------------------------------------
490  */
491
492 static void rt_overlay_get_position(RendererTiles *rt, OverlayData *od,
493                                     gint *x, gint *y, gint *w, gint *h)
494 {
495         PixbufRenderer *pr = rt->pr;
496         gint px, py, pw, ph;
497
498         pw = gdk_pixbuf_get_width(od->pixbuf);
499         ph = gdk_pixbuf_get_height(od->pixbuf);
500         px = od->x;
501         py = od->y;
502
503         if (od->flags & OVL_RELATIVE)
504                 {
505                 if (px < 0) px = pr->window_width - pw + px;
506                 if (py < 0) py = pr->window_height - ph + py;
507                 }
508
509         if (x) *x = px;
510         if (y) *y = py;
511         if (w) *w = pw;
512         if (h) *h = ph;
513 }
514
515 static void rt_overlay_init_window(RendererTiles *rt, OverlayData *od)
516 {
517         PixbufRenderer *pr = rt->pr;
518         gint px, py, pw, ph;
519         GdkWindowAttr attributes;
520         gint attributes_mask;
521
522         rt_overlay_get_position(rt, od, &px, &py, &pw, &ph);
523
524         attributes.window_type = GDK_WINDOW_CHILD;
525         attributes.wclass = GDK_INPUT_OUTPUT;
526         attributes.width = pw;
527         attributes.height = ph;
528         attributes.event_mask = GDK_EXPOSURE_MASK;
529         attributes_mask = 0;
530
531         od->window = gdk_window_new(GTK_WIDGET(pr)->window, &attributes, attributes_mask);
532         gdk_window_set_user_data(od->window, pr);
533         gdk_window_move(od->window, px, py);
534         gdk_window_show(od->window);
535 }
536
537 static void rt_overlay_draw(RendererTiles *rt, gint x, gint y, gint w, gint h,
538                             ImageTile *it)
539 {
540         PixbufRenderer *pr = rt->pr;
541         GtkWidget *box;
542         GList *work;
543
544         box = GTK_WIDGET(pr);
545
546         work = rt->overlay_list;
547         while (work)
548                 {
549                 OverlayData *od;
550                 gint px, py, pw, ph;
551                 gint rx, ry, rw, rh;
552
553                 od = work->data;
554                 work = work->next;
555
556                 if (!od->window) rt_overlay_init_window(rt, od);
557                 
558                 rt_overlay_get_position(rt, od, &px, &py, &pw, &ph);
559                 if (pr_clip_region(x, y, w, h, px, py, pw, ph, &rx, &ry, &rw, &rh))
560                         {
561                         if (!rt->overlay_buffer)
562                                 {
563                                 rt->overlay_buffer = gdk_pixmap_new(((GtkWidget *)pr)->window, rt->tile_width, rt->tile_height, -1);
564                                 }
565
566                         if (it)
567                                 {
568 #if GTK_CHECK_VERSION(2,20,0)
569                                 gdk_draw_drawable(rt->overlay_buffer, box->style->fg_gc[gtk_widget_get_state(box)],
570 #else
571                                 gdk_draw_drawable(rt->overlay_buffer, box->style->fg_gc[GTK_WIDGET_STATE(box)],
572 #endif
573                                                   it->pixmap,
574                                                   rx - (pr->x_offset + (it->x - pr->x_scroll)),
575                                                   ry - (pr->y_offset + (it->y - pr->y_scroll)),
576                                                   0, 0, rw, rh);
577                                 gdk_draw_pixbuf(rt->overlay_buffer,
578 #if GTK_CHECK_VERSION(2,20,0)
579                                                 box->style->fg_gc[gtk_widget_get_state(box)],
580 #else
581                                                 box->style->fg_gc[GTK_WIDGET_STATE(box)],
582 #endif
583                                                 od->pixbuf,
584                                                 rx - px, ry - py,
585                                                 0, 0, rw, rh,
586                                                 pr->dither_quality, rx, ry);
587 #if GTK_CHECK_VERSION(2,20,0)
588                                 gdk_draw_drawable(od->window, box->style->fg_gc[gtk_widget_get_state(box)],
589 #else
590                                 gdk_draw_drawable(od->window, box->style->fg_gc[GTK_WIDGET_STATE(box)],
591 #endif
592                                                   rt->overlay_buffer,
593                                                   0, 0,
594                                                   rx - px, ry - py, rw, rh);
595                                 }
596                         else
597                                 {
598                                 /* no ImageTile means region may be larger than our scratch buffer */
599                                 gint sx, sy;
600
601                                 for (sx = rx; sx < rx + rw; sx += rt->tile_width)
602                                     for (sy = ry; sy < ry + rh; sy += rt->tile_height)
603                                         {
604                                         gint sw, sh;
605
606                                         sw = MIN(rx + rw - sx, rt->tile_width);
607                                         sh = MIN(ry + rh - sy, rt->tile_height);
608
609                                         gdk_draw_rectangle(rt->overlay_buffer,
610 #if GTK_CHECK_VERSION(2,20,0)
611                                                            box->style->bg_gc[gtk_widget_get_state(box)], TRUE,
612 #else
613                                                            box->style->bg_gc[GTK_WIDGET_STATE(box)], TRUE,
614 #endif
615                                                            0, 0, sw, sh);
616                                         gdk_draw_pixbuf(rt->overlay_buffer,
617 #if GTK_CHECK_VERSION(2,20,0)
618                                                         box->style->fg_gc[gtk_widget_get_state(box)],
619 #else
620                                                         box->style->fg_gc[GTK_WIDGET_STATE(box)],
621 #endif
622                                                         od->pixbuf,
623                                                         sx - px, sy - py,
624                                                         0, 0, sw, sh,
625                                                         pr->dither_quality, sx, sy);
626 #if GTK_CHECK_VERSION(2,20,0)
627                                         gdk_draw_drawable(od->window, box->style->fg_gc[gtk_widget_get_state(box)],
628 #else
629                                         gdk_draw_drawable(od->window, box->style->fg_gc[GTK_WIDGET_STATE(box)],
630 #endif
631                                                           rt->overlay_buffer,
632                                                           0, 0,
633                                                           sx - px, sy - py, sw, sh);
634                                         }
635                                 }
636                         }
637                 }
638 }
639
640 static void rt_overlay_queue_draw(RendererTiles *rt, OverlayData *od, gint x1, gint y1, gint x2, gint y2)
641 {
642         PixbufRenderer *pr = rt->pr;
643         gint x, y, w, h;
644
645         rt_overlay_get_position(rt, od, &x, &y, &w, &h);
646         
647         /* add borders */
648         x -= x1;
649         y -= y1;
650         w += x1 + x2;
651         h += y1 + y2;
652         
653         rt_queue(rt, pr->x_scroll - pr->x_offset + x,
654                  pr->y_scroll - pr->y_offset + y,
655                  w, h,
656                  FALSE, TILE_RENDER_ALL, FALSE, FALSE);
657
658         rt_border_draw(rt, x, y, w, h);
659 }
660
661 static void rt_overlay_queue_all(RendererTiles *rt, gint x1, gint y1, gint x2, gint y2)
662 {
663         GList *work;
664
665         work = rt->overlay_list;
666         while (work)
667                 {
668                 OverlayData *od = work->data;
669                 work = work->next;
670
671                 rt_overlay_queue_draw(rt, od, x1, y1, x2, y2);
672                 }
673 }
674
675 static void rt_overlay_update_sizes(RendererTiles *rt)
676 {
677         GList *work;
678
679         work = rt->overlay_list;
680         while (work)
681                 {
682                 OverlayData *od = work->data;
683                 work = work->next;
684                 
685                 if (!od->window) rt_overlay_init_window(rt, od);
686                 
687                 if (od->flags & OVL_RELATIVE)
688                         {
689                         gint x, y, w, h;
690
691                         rt_overlay_get_position(rt, od, &x, &y, &w, &h);
692                         gdk_window_move_resize(od->window, x, y, w, h);
693                         }
694                 }
695 }
696
697 static OverlayData *rt_overlay_find(RendererTiles *rt, gint id)
698 {
699         GList *work;
700
701         work = rt->overlay_list;
702         while (work)
703                 {
704                 OverlayData *od = work->data;
705                 work = work->next;
706
707                 if (od->id == id) return od;
708                 }
709
710         return NULL;
711 }
712
713
714 gint renderer_tiles_overlay_add(RendererTiles *rt, GdkPixbuf *pixbuf, gint x, gint y,
715                                  OverlayRendererFlags flags)
716 {
717         PixbufRenderer *pr = rt->pr;
718         OverlayData *od;
719         gint id;
720
721         g_return_val_if_fail(IS_PIXBUF_RENDERER(pr), -1);
722         g_return_val_if_fail(pixbuf != NULL, -1);
723
724         id = 1;
725         while (rt_overlay_find(rt, id)) id++;
726
727         od = g_new0(OverlayData, 1);
728         od->id = id;
729         od->pixbuf = pixbuf;
730         g_object_ref(G_OBJECT(od->pixbuf));
731         od->x = x;
732         od->y = y;
733         od->flags = flags;
734
735         rt_overlay_init_window(rt, od);
736         
737         rt->overlay_list = g_list_append(rt->overlay_list, od);
738
739         rt_overlay_queue_draw(rt, od, 0, 0, 0, 0);
740
741         return od->id;
742 }
743
744 static void rt_overlay_free(RendererTiles *rt, OverlayData *od)
745 {
746         rt->overlay_list = g_list_remove(rt->overlay_list, od);
747
748         if (od->pixbuf) g_object_unref(G_OBJECT(od->pixbuf));
749         if (od->window) gdk_window_destroy(od->window);
750         g_free(od);
751
752         if (!rt->overlay_list && rt->overlay_buffer)
753                 {
754                 g_object_unref(rt->overlay_buffer);
755                 rt->overlay_buffer = NULL;
756                 }
757 }
758
759 static void rt_overlay_list_clear(RendererTiles *rt)
760 {
761         while (rt->overlay_list)
762                 {
763                 OverlayData *od;
764
765                 od = rt->overlay_list->data;
766                 rt_overlay_free(rt, od);
767                 }
768 }
769
770 static void rt_overlay_list_reset_window(RendererTiles *rt)
771 {
772         GList *work;
773
774         if (rt->overlay_buffer) g_object_unref(rt->overlay_buffer);
775         rt->overlay_buffer = NULL;
776
777         work = rt->overlay_list;
778         while (work)
779                 {
780                 OverlayData *od = work->data;
781                 work = work->next;
782                 if (od->window) gdk_window_destroy(od->window);
783                 od->window = NULL;
784                 }
785 }
786
787 void renderer_tiles_overlay_set(RendererTiles *rt, gint id, GdkPixbuf *pixbuf, gint x, gint y)
788 {
789         PixbufRenderer *pr = rt->pr;
790         OverlayData *od;
791
792         g_return_if_fail(IS_PIXBUF_RENDERER(pr));
793
794         od = rt_overlay_find(rt, id);
795         if (!od) return;
796
797         if (pixbuf)
798                 {
799                 gint px, py, pw, ph;
800
801                 g_object_ref(G_OBJECT(pixbuf));
802                 g_object_unref(G_OBJECT(od->pixbuf));
803                 od->pixbuf = pixbuf;
804
805                 od->x = x;
806                 od->y = y;
807
808                 if (!od->window) rt_overlay_init_window(rt, od);
809
810                 rt_overlay_queue_draw(rt, od, 0, 0, 0, 0);
811                 rt_overlay_get_position(rt, od, &px, &py, &pw, &ph);
812                 gdk_window_move_resize(od->window, px, py, pw, ph);
813                 }
814         else
815                 {
816                 rt_overlay_queue_draw(rt, od, 0, 0, 0, 0);
817                 rt_overlay_free(rt, od);
818                 }
819 }
820
821 gboolean renderer_tiles_overlay_get(RendererTiles *rt, gint id, GdkPixbuf **pixbuf, gint *x, gint *y)
822 {
823         PixbufRenderer *pr = rt->pr;
824         OverlayData *od;
825
826         g_return_val_if_fail(IS_PIXBUF_RENDERER(pr), FALSE);
827
828         od = rt_overlay_find(rt, id);
829         if (!od) return FALSE;
830
831         if (pixbuf) *pixbuf = od->pixbuf;
832         if (x) *x = od->x;
833         if (y) *y = od->y;
834
835         return TRUE;
836 }
837
838 static void rt_hierarchy_changed_cb(GtkWidget *widget, GtkWidget *previous_toplevel, gpointer data)
839 {
840         RendererTiles *rt = data;
841         rt_overlay_list_reset_window(rt);
842 }
843
844 /*
845  *-------------------------------------------------------------------
846  * drawing
847  *-------------------------------------------------------------------
848  */
849
850 static GdkPixbuf *rt_get_spare_tile(RendererTiles *rt)
851 {
852         if (!rt->spare_tile) rt->spare_tile = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, rt->tile_width, rt->tile_height);
853         return rt->spare_tile;
854 }
855
856 #define COLOR_BYTES 3   /* rgb */
857
858 static void rt_tile_rotate_90_clockwise(RendererTiles *rt, GdkPixbuf **tile, gint x, gint y, gint w, gint h)
859 {
860         GdkPixbuf *src = *tile;
861         GdkPixbuf *dest;
862         gint srs, drs;
863         guchar *s_pix, *d_pix;
864         guchar *sp, *dp;
865         guchar *ip, *spi, *dpi;
866         gint i, j;
867         gint tw = rt->tile_width;
868
869         srs = gdk_pixbuf_get_rowstride(src);
870         s_pix = gdk_pixbuf_get_pixels(src);
871         spi = s_pix + (x * COLOR_BYTES);
872
873         dest = rt_get_spare_tile(rt);
874         drs = gdk_pixbuf_get_rowstride(dest);
875         d_pix = gdk_pixbuf_get_pixels(dest);
876         dpi = d_pix + (tw - 1) * COLOR_BYTES;
877
878         for (i = y; i < y + h; i++)
879                 {
880                 sp = spi + (i * srs);
881                 ip = dpi - (i * COLOR_BYTES);
882                 for (j = x; j < x + w; j++)
883                         {
884                         dp = ip + (j * drs);
885                         memcpy(dp, sp, COLOR_BYTES);
886                         sp += COLOR_BYTES;
887                         }
888                 }
889
890         rt->spare_tile = src;
891         *tile = dest;
892 }
893
894 static void rt_tile_rotate_90_counter_clockwise(RendererTiles *rt, GdkPixbuf **tile, gint x, gint y, gint w, gint h)
895 {
896         GdkPixbuf *src = *tile;
897         GdkPixbuf *dest;
898         gint srs, drs;
899         guchar *s_pix, *d_pix;
900         guchar *sp, *dp;
901         guchar *ip, *spi, *dpi;
902         gint i, j;
903         gint th = rt->tile_height;
904
905         srs = gdk_pixbuf_get_rowstride(src);
906         s_pix = gdk_pixbuf_get_pixels(src);
907         spi = s_pix + (x * COLOR_BYTES);
908
909         dest = rt_get_spare_tile(rt);
910         drs = gdk_pixbuf_get_rowstride(dest);
911         d_pix = gdk_pixbuf_get_pixels(dest);
912         dpi = d_pix + (th - 1) * drs;
913
914         for (i = y; i < y + h; i++)
915                 {
916                 sp = spi + (i * srs);
917                 ip = dpi + (i * COLOR_BYTES);
918                 for (j = x; j < x + w; j++)
919                         {
920                         dp = ip - (j * drs);
921                         memcpy(dp, sp, COLOR_BYTES);
922                         sp += COLOR_BYTES;
923                         }
924                 }
925
926         rt->spare_tile = src;
927         *tile = dest;
928 }
929
930 static void rt_tile_mirror_only(RendererTiles *rt, GdkPixbuf **tile, gint x, gint y, gint w, gint h)
931 {
932         GdkPixbuf *src = *tile;
933         GdkPixbuf *dest;
934         gint srs, drs;
935         guchar *s_pix, *d_pix;
936         guchar *sp, *dp;
937         guchar *spi, *dpi;
938         gint i, j;
939
940         gint tw = rt->tile_width;
941
942         srs = gdk_pixbuf_get_rowstride(src);
943         s_pix = gdk_pixbuf_get_pixels(src);
944         spi = s_pix + (x * COLOR_BYTES);
945
946         dest = rt_get_spare_tile(rt);
947         drs = gdk_pixbuf_get_rowstride(dest);
948         d_pix = gdk_pixbuf_get_pixels(dest);
949         dpi =  d_pix + (tw - x - 1) * COLOR_BYTES;
950
951         for (i = y; i < y + h; i++)
952                 {
953                 sp = spi + (i * srs);
954                 dp = dpi + (i * drs);
955                 for (j = 0; j < w; j++)
956                         {
957                         memcpy(dp, sp, COLOR_BYTES);
958                         sp += COLOR_BYTES;
959                         dp -= COLOR_BYTES;
960                         }
961                 }
962
963         rt->spare_tile = src;
964         *tile = dest;
965 }
966
967 static void rt_tile_mirror_and_flip(RendererTiles *rt, GdkPixbuf **tile, gint x, gint y, gint w, gint h)
968 {
969         GdkPixbuf *src = *tile;
970         GdkPixbuf *dest;
971         gint srs, drs;
972         guchar *s_pix, *d_pix;
973         guchar *sp, *dp;
974         guchar *spi, *dpi;
975         gint i, j;
976         gint tw = rt->tile_width;
977         gint th = rt->tile_height;
978
979         srs = gdk_pixbuf_get_rowstride(src);
980         s_pix = gdk_pixbuf_get_pixels(src);
981         spi = s_pix + (x * COLOR_BYTES);
982
983         dest = rt_get_spare_tile(rt);
984         drs = gdk_pixbuf_get_rowstride(dest);
985         d_pix = gdk_pixbuf_get_pixels(dest);
986         dpi = d_pix + (th - 1) * drs + (tw - 1) * COLOR_BYTES;
987
988         for (i = y; i < y + h; i++)
989                 {
990                 sp = s_pix + (i * srs) + (x * COLOR_BYTES);
991                 dp = dpi - (i * drs) - (x * COLOR_BYTES);
992                 for (j = 0; j < w; j++)
993                         {
994                         memcpy(dp, sp, COLOR_BYTES);
995                         sp += COLOR_BYTES;
996                         dp -= COLOR_BYTES;
997                         }
998                 }
999
1000         rt->spare_tile = src;
1001         *tile = dest;
1002 }
1003
1004 static void rt_tile_flip_only(RendererTiles *rt, GdkPixbuf **tile, gint x, gint y, gint w, gint h)
1005 {
1006         GdkPixbuf *src = *tile;
1007         GdkPixbuf *dest;
1008         gint srs, drs;
1009         guchar *s_pix, *d_pix;
1010         guchar *sp, *dp;
1011         guchar *spi, *dpi;
1012         gint i;
1013         gint th = rt->tile_height;
1014
1015         srs = gdk_pixbuf_get_rowstride(src);
1016         s_pix = gdk_pixbuf_get_pixels(src);
1017         spi = s_pix + (x * COLOR_BYTES);
1018
1019         dest = rt_get_spare_tile(rt);
1020         drs = gdk_pixbuf_get_rowstride(dest);
1021         d_pix = gdk_pixbuf_get_pixels(dest);
1022         dpi = d_pix + (th - 1) * drs + (x * COLOR_BYTES);
1023
1024         for (i = y; i < y + h; i++)
1025                 {
1026                 sp = spi + (i * srs);
1027                 dp = dpi - (i * drs);
1028                 memcpy(dp, sp, w * COLOR_BYTES);
1029                 }
1030
1031         rt->spare_tile = src;
1032         *tile = dest;
1033 }
1034
1035 static void rt_tile_apply_orientation(RendererTiles *rt, GdkPixbuf **pixbuf, gint x, gint y, gint w, gint h)
1036 {
1037         PixbufRenderer *pr = rt->pr;
1038         switch (pr->orientation)
1039                 {
1040                 case EXIF_ORIENTATION_TOP_LEFT:
1041                         /* normal -- nothing to do */
1042                         break;
1043                 case EXIF_ORIENTATION_TOP_RIGHT:
1044                         /* mirrored */
1045                         {
1046                                 rt_tile_mirror_only(rt, pixbuf, x, y, w, h);
1047                         }
1048                         break;
1049                 case EXIF_ORIENTATION_BOTTOM_RIGHT:
1050                         /* upside down */
1051                         {
1052                                 rt_tile_mirror_and_flip(rt, pixbuf, x, y, w, h);
1053                         }
1054                         break;
1055                 case EXIF_ORIENTATION_BOTTOM_LEFT:
1056                         /* flipped */
1057                         {
1058                                 rt_tile_flip_only(rt, pixbuf, x, y, w, h);
1059                         }
1060                         break;
1061                 case EXIF_ORIENTATION_LEFT_TOP:
1062                         {
1063                                 rt_tile_flip_only(rt, pixbuf, x, y, w, h);
1064                                 rt_tile_rotate_90_clockwise(rt, pixbuf, x, rt->tile_height - y - h, w, h);
1065                         }
1066                         break;
1067                 case EXIF_ORIENTATION_RIGHT_TOP:
1068                         /* rotated -90 (270) */
1069                         {
1070                                 rt_tile_rotate_90_clockwise(rt, pixbuf, x, y, w, h);
1071                         }
1072                         break;
1073                 case EXIF_ORIENTATION_RIGHT_BOTTOM:
1074                         {
1075                                 rt_tile_flip_only(rt, pixbuf, x, y, w, h);
1076                                 rt_tile_rotate_90_counter_clockwise(rt, pixbuf, x, rt->tile_height - y - h, w, h);
1077                         }
1078                         break;
1079                 case EXIF_ORIENTATION_LEFT_BOTTOM:
1080                         /* rotated 90 */
1081                         {
1082                                 rt_tile_rotate_90_counter_clockwise(rt, pixbuf, x, y, w, h);
1083                         }
1084                         break;
1085                 default:
1086                         /* The other values are out of range */
1087                         break;
1088                 }
1089 }
1090
1091 static gboolean rt_source_tile_render(RendererTiles *rt, ImageTile *it,
1092                                       gint x, gint y, gint w, gint h,
1093                                       gboolean new_data, gboolean fast)
1094 {
1095         PixbufRenderer *pr = rt->pr;
1096         GtkWidget *box;
1097         GList *list;
1098         GList *work;
1099         gboolean draw = FALSE;
1100
1101         box = GTK_WIDGET(pr);
1102
1103         if (pr->zoom == 1.0 || pr->scale == 1.0)
1104                 {
1105                 list = pr_source_tile_compute_region(pr, it->x + x, it->y + y, w, h, TRUE);
1106                 work = list;
1107                 while (work)
1108                         {
1109                         SourceTile *st;
1110                         gint rx, ry, rw, rh;
1111
1112                         st = work->data;
1113                         work = work->next;
1114
1115                         if (pr_clip_region(st->x, st->y, pr->source_tile_width, pr->source_tile_height,
1116                                            it->x + x, it->y + y, w, h,
1117                                            &rx, &ry, &rw, &rh))
1118                                 {
1119                                 if (st->blank)
1120                                         {
1121                                         gdk_draw_rectangle(it->pixmap, box->style->black_gc, TRUE,
1122                                                            rx - st->x, ry - st->y, rw, rh);
1123                                         }
1124                                 else /* (pr->zoom == 1.0 || pr->scale == 1.0) */
1125                                         {
1126                                         gdk_draw_pixbuf(it->pixmap,
1127 #if GTK_CHECK_VERSION(2,20,0)
1128                                                         box->style->fg_gc[gtk_widget_get_state(box)],
1129 #else
1130                                                         box->style->fg_gc[GTK_WIDGET_STATE(box)],
1131 #endif
1132                                                         st->pixbuf,
1133                                                         rx - st->x, ry - st->y,
1134                                                         rx - it->x, ry - it->y,
1135                                                         rw, rh,
1136                                                         pr->dither_quality, rx, ry);
1137                                         }
1138                                 }
1139                         }
1140                 }
1141         else
1142                 {
1143                 gdouble scale_x, scale_y;
1144                 gint sx, sy, sw, sh;
1145
1146                 if (pr->image_width == 0 || pr->image_height == 0) return FALSE;
1147                 scale_x = (gdouble)pr->width / pr->image_width;
1148                 scale_y = (gdouble)pr->height / pr->image_height;
1149
1150                 sx = (gdouble)(it->x + x) / scale_x;
1151                 sy = (gdouble)(it->y + y) / scale_y;
1152                 sw = (gdouble)w / scale_x;
1153                 sh = (gdouble)h / scale_y;
1154
1155                 if (pr->width < PR_MIN_SCALE_SIZE || pr->height < PR_MIN_SCALE_SIZE) fast = TRUE;
1156
1157 #if 0
1158                 /* draws red over draw region, to check for leaks (regions not filled) */
1159                 pixbuf_set_rect_fill(it->pixbuf, x, y, w, h, 255, 0, 0, 255);
1160 #endif
1161
1162                 list = pr_source_tile_compute_region(pr, sx, sy, sw, sh, TRUE);
1163                 work = list;
1164                 while (work)
1165                         {
1166                         SourceTile *st;
1167                         gint rx, ry, rw, rh;
1168                         gint stx, sty, stw, sth;
1169
1170                         st = work->data;
1171                         work = work->next;
1172
1173                         stx = floor((gdouble)st->x * scale_x);
1174                         sty = floor((gdouble)st->y * scale_y);
1175                         stw = ceil((gdouble)(st->x + pr->source_tile_width) * scale_x) - stx;
1176                         sth = ceil((gdouble)(st->y + pr->source_tile_height) * scale_y) - sty;
1177
1178                         if (pr_clip_region(stx, sty, stw, sth,
1179                                            it->x + x, it->y + y, w, h,
1180                                            &rx, &ry, &rw, &rh))
1181                                 {
1182                                 if (st->blank)
1183                                         {
1184                                         gdk_draw_rectangle(it->pixmap, box->style->black_gc, TRUE,
1185                                                            rx - st->x, ry - st->y, rw, rh);
1186                                         }
1187                                 else
1188                                         {
1189                                         gdouble offset_x;
1190                                         gdouble offset_y;
1191
1192                                         /* may need to use unfloored stx,sty values here */
1193                                         offset_x = (gdouble)(stx - it->x);
1194                                         offset_y = (gdouble)(sty - it->y);
1195
1196                                         gdk_pixbuf_scale(st->pixbuf, it->pixbuf, rx - it->x, ry - it->y, rw, rh,
1197                                                  (gdouble) 0.0 + offset_x,
1198                                                  (gdouble) 0.0 + offset_y,
1199                                                  scale_x, scale_y,
1200                                                  (fast) ? GDK_INTERP_NEAREST : pr->zoom_quality);
1201                                         draw = TRUE;
1202                                         }
1203                                 }
1204                         }
1205                 }
1206
1207         g_list_free(list);
1208
1209         return draw;
1210 }
1211
1212
1213 static void rt_tile_render(RendererTiles *rt, ImageTile *it,
1214                            gint x, gint y, gint w, gint h,
1215                            gboolean new_data, gboolean fast)
1216 {
1217         PixbufRenderer *pr = rt->pr;
1218         GtkWidget *box;
1219         gboolean has_alpha;
1220         gboolean draw = FALSE;
1221
1222         if (it->render_todo == TILE_RENDER_NONE && it->pixmap && !new_data) return;
1223
1224         if (it->render_done != TILE_RENDER_ALL)
1225                 {
1226                 x = 0;
1227                 y = 0;
1228                 w = it->w;
1229                 h = it->h;
1230                 if (!fast) it->render_done = TILE_RENDER_ALL;
1231                 }
1232         else if (it->render_todo != TILE_RENDER_AREA)
1233                 {
1234                 if (!fast) it->render_todo = TILE_RENDER_NONE;
1235                 return;
1236                 }
1237
1238         if (!fast) it->render_todo = TILE_RENDER_NONE;
1239
1240         if (new_data) it->blank = FALSE;
1241
1242         rt_tile_prepare(rt, it);
1243         has_alpha = (pr->pixbuf && gdk_pixbuf_get_has_alpha(pr->pixbuf));
1244
1245         box = GTK_WIDGET(pr);
1246
1247         /* FIXME checker colors for alpha should be configurable,
1248          * also should be drawn for blank = TRUE
1249          */
1250
1251         if (it->blank)
1252                 {
1253                 /* no data, do fast rect fill */
1254                 gdk_draw_rectangle(it->pixmap, box->style->black_gc, TRUE,
1255                                    0, 0, it->w, it->h);
1256                 }
1257         else if (pr->source_tiles_enabled)
1258                 {
1259                 draw = rt_source_tile_render(rt, it, x, y, w, h, new_data, fast);
1260                 }
1261         else if (pr->zoom == 1.0 || pr->scale == 1.0)
1262                 {
1263
1264                 gdouble src_x, src_y;
1265                 gint pb_x, pb_y;
1266                 gint pb_w, pb_h;
1267                 pr_tile_coords_map_orientation(pr->orientation, it->x, it->y,
1268                                             pr->image_width, pr->image_height,
1269                                             rt->tile_width, rt->tile_height,
1270                                             &src_x, &src_y);
1271                 pr_tile_region_map_orientation(pr->orientation, x, y,
1272                                             rt->tile_width, rt->tile_height,
1273                                             w, h,
1274                                             &pb_x, &pb_y,
1275                                             &pb_w, &pb_h);
1276
1277                 if (has_alpha)
1278                         {
1279                         gdk_pixbuf_composite_color(pr->pixbuf, it->pixbuf, pb_x, pb_y, pb_w, pb_h,
1280                                          (gdouble) 0.0 - src_x,
1281                                          (gdouble) 0.0 - src_y,
1282                                          1.0, 1.0, GDK_INTERP_NEAREST,
1283                                          255, it->x + pb_x, it->y + pb_y,
1284                                          PR_ALPHA_CHECK_SIZE, PR_ALPHA_CHECK1, PR_ALPHA_CHECK2);
1285                         rt_tile_apply_orientation(rt, &it->pixbuf, pb_x, pb_y, pb_w, pb_h);
1286                         draw = TRUE;
1287                         }
1288                 else
1289                         {
1290
1291
1292                         if (pr->orientation == EXIF_ORIENTATION_TOP_LEFT && !(pr->func_post_process && !(pr->post_process_slow && fast)))
1293                                 {
1294                                 /* faster, simple, base orientation, no postprocessing */
1295                                 gdk_draw_pixbuf(it->pixmap,
1296 #if GTK_CHECK_VERSION(2,20,0)
1297                                                 box->style->fg_gc[gtk_widget_get_state(box)],
1298 #else
1299                                                 box->style->fg_gc[GTK_WIDGET_STATE(box)],
1300 #endif
1301                                                 pr->pixbuf,
1302                                                 it->x + x, it->y + y,
1303                                                 x, y,
1304                                                 w, h,
1305                                                 pr->dither_quality, it->x + x, it->y + y);
1306                                 }
1307                         else
1308                                 {
1309                                 gdk_pixbuf_copy_area(pr->pixbuf,
1310                                                      src_x + pb_x, src_y + pb_y,
1311                                                      pb_w, pb_h,
1312                                                      it->pixbuf,
1313                                                      pb_x, pb_y);
1314                                 rt_tile_apply_orientation(rt, &it->pixbuf, pb_x, pb_y, pb_w, pb_h);
1315                                 draw = TRUE;
1316                                 }
1317                         }
1318                 }
1319         else
1320                 {
1321                 gdouble scale_x, scale_y;
1322                 gdouble src_x, src_y;
1323                 gint pb_x, pb_y;
1324                 gint pb_w, pb_h;
1325
1326                 if (pr->image_width == 0 || pr->image_height == 0) return;
1327
1328                 scale_x = (gdouble)pr->width / pr->image_width;
1329                 scale_y = (gdouble)pr->height / pr->image_height;
1330
1331                 pr_tile_coords_map_orientation(pr->orientation, it->x, it->y,
1332                                             pr->image_width * scale_x, pr->image_height * scale_y,
1333                                             rt->tile_width, rt->tile_height,
1334                                             &src_x, &src_y);
1335                 pr_tile_region_map_orientation(pr->orientation, x, y,
1336                                             rt->tile_width, rt->tile_height,
1337                                             w, h,
1338                                             &pb_x, &pb_y,
1339                                             &pb_w, &pb_h);
1340                 switch (pr->orientation)
1341                         {
1342                         gdouble tmp;
1343                         case EXIF_ORIENTATION_LEFT_TOP:
1344                         case EXIF_ORIENTATION_RIGHT_TOP:
1345                         case EXIF_ORIENTATION_RIGHT_BOTTOM:
1346                         case EXIF_ORIENTATION_LEFT_BOTTOM:
1347                                 tmp = scale_x;
1348                                 scale_x = scale_y;
1349                                 scale_y = tmp;
1350                                 break;
1351                         default:
1352                                 /* nothing to do */
1353                                 break;
1354                         }
1355
1356                 /* HACK: The pixbuf scalers get kinda buggy(crash) with extremely
1357                  * small sizes for anything but GDK_INTERP_NEAREST
1358                  */
1359                 if (pr->width < PR_MIN_SCALE_SIZE || pr->height < PR_MIN_SCALE_SIZE) fast = TRUE;
1360
1361                 if (!has_alpha)
1362                         {
1363                         gdk_pixbuf_scale(pr->pixbuf, it->pixbuf, pb_x, pb_y, pb_w, pb_h,
1364                                          (gdouble) 0.0 - src_x,
1365                                          (gdouble) 0.0 - src_y,
1366                                          scale_x, scale_y,
1367                                          (fast) ? GDK_INTERP_NEAREST : pr->zoom_quality);
1368                         }
1369                 else
1370                         {
1371                         gdk_pixbuf_composite_color(pr->pixbuf, it->pixbuf, pb_x, pb_y, pb_w, pb_h,
1372                                          (gdouble) 0.0 - src_x,
1373                                          (gdouble) 0.0 - src_y,
1374                                          scale_x, scale_y,
1375                                          (fast) ? GDK_INTERP_NEAREST : pr->zoom_quality,
1376                                          255, it->x + pb_x, it->y + pb_y,
1377                                          PR_ALPHA_CHECK_SIZE, PR_ALPHA_CHECK1, PR_ALPHA_CHECK2);
1378                         }
1379                 rt_tile_apply_orientation(rt, &it->pixbuf, pb_x, pb_y, pb_w, pb_h);
1380                 draw = TRUE;
1381                 }
1382
1383         if (draw && it->pixbuf && !it->blank)
1384                 {
1385
1386                 if (pr->func_post_process && !(pr->post_process_slow && fast))
1387                         pr->func_post_process(pr, &it->pixbuf, x, y, w, h, pr->post_process_user_data);
1388
1389                 gdk_draw_pixbuf(it->pixmap,
1390 #if GTK_CHECK_VERSION(2,20,0)
1391                                 box->style->fg_gc[gtk_widget_get_state(box)],
1392 #else
1393                                 box->style->fg_gc[GTK_WIDGET_STATE(box)],
1394 #endif
1395                                 it->pixbuf,
1396                                 x, y,
1397                                 x, y,
1398                                 w, h,
1399                                 pr->dither_quality, it->x + x, it->y + y);
1400                 }
1401
1402 #if 0
1403         /* enable this line for debugging the edges of tiles */
1404         gdk_draw_rectangle(it->pixmap, box->style->white_gc,
1405                            FALSE, 0, 0, it->w, it->h);
1406         gdk_draw_rectangle(it->pixmap, box->style->white_gc,
1407                            FALSE, x, y, w, h);
1408 #endif
1409 }
1410
1411
1412 static void rt_tile_expose(RendererTiles *rt, ImageTile *it,
1413                            gint x, gint y, gint w, gint h,
1414                            gboolean new_data, gboolean fast)
1415 {
1416         PixbufRenderer *pr = rt->pr;
1417         GtkWidget *box;
1418
1419         rt_tile_render(rt, it, x, y, w, h, new_data, fast);
1420
1421         box = GTK_WIDGET(pr);
1422
1423 #if GTK_CHECK_VERSION(2,20,0)
1424         gdk_draw_drawable(box->window, box->style->fg_gc[gtk_widget_get_state(box)],
1425 #else
1426         gdk_draw_drawable(box->window, box->style->fg_gc[GTK_WIDGET_STATE(box)],
1427 #endif
1428                           it->pixmap, x, y,
1429                           pr->x_offset + (it->x - pr->x_scroll) + x, pr->y_offset + (it->y - pr->y_scroll) + y, w, h);
1430
1431         if (rt->overlay_list)
1432                 {
1433                 rt_overlay_draw(rt, pr->x_offset + (it->x - pr->x_scroll) + x,
1434                                 pr->y_offset + (it->y - pr->y_scroll) + y,
1435                                 w, h,
1436                                 it);
1437                 }
1438 }
1439
1440
1441 static gboolean rt_tile_is_visible(RendererTiles *rt, ImageTile *it)
1442 {
1443         PixbufRenderer *pr = rt->pr;
1444         return (it->x + it->w >= pr->x_scroll && it->x < pr->x_scroll + pr->vis_width &&
1445                 it->y + it->h >= pr->y_scroll && it->y < pr->y_scroll + pr->vis_height);
1446 }
1447
1448 /*
1449  *-------------------------------------------------------------------
1450  * draw queue
1451  *-------------------------------------------------------------------
1452  */
1453
1454 static gint rt_get_queued_area(GList *work)
1455 {
1456         gint area = 0;
1457         
1458         while (work) 
1459                 {
1460                 QueueData *qd = work->data;
1461                 area += qd->w * qd->h;
1462                 work = work->next;
1463                 }
1464         return area;
1465 }
1466
1467
1468 static gboolean rt_queue_schedule_next_draw(RendererTiles *rt, gboolean force_set)
1469 {
1470         PixbufRenderer *pr = rt->pr;
1471         gfloat percent;
1472         gint visible_area = pr->vis_width * pr->vis_height;
1473         
1474         if (!pr->loading)
1475                 {
1476                 /* 2pass prio */ 
1477                 DEBUG_2("redraw priority: 2pass");
1478                 rt->draw_idle_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, rt_queue_draw_idle_cb, rt, NULL);
1479                 return FALSE;
1480                 }
1481         
1482         if (visible_area == 0)
1483                 {
1484                 /* not known yet */
1485                 percent = 100.0;
1486                 }
1487         else
1488                 {
1489                 percent = 100.0 * rt_get_queued_area(rt->draw_queue) / visible_area;
1490                 }
1491         
1492         if (percent > 10.0)
1493                 {
1494                 /* we have enough data for starting intensive redrawing */
1495                 DEBUG_2("redraw priority: high %.2f %%", percent);
1496                 rt->draw_idle_id = g_idle_add_full(GDK_PRIORITY_REDRAW, rt_queue_draw_idle_cb, rt, NULL);
1497                 return FALSE;
1498                 }
1499         
1500         if (percent < 1.0 || force_set)
1501                 {
1502                 /* queue is (almost) empty, wait  50 ms*/
1503                 DEBUG_2("redraw priority: wait %.2f %%", percent);
1504                 rt->draw_idle_id = g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, 50, rt_queue_draw_idle_cb, rt, NULL);
1505                 return FALSE;
1506                 }
1507         
1508         /* keep the same priority as before */
1509         DEBUG_2("redraw priority: no change %.2f %%", percent);
1510         return TRUE;
1511 }
1512                 
1513
1514 static gboolean rt_queue_draw_idle_cb(gpointer data)
1515 {
1516         RendererTiles *rt = data;
1517         PixbufRenderer *pr = rt->pr;
1518         QueueData *qd;
1519         gboolean fast;
1520
1521
1522         if ((!pr->pixbuf && !pr->source_tiles_enabled) ||
1523             (!rt->draw_queue && !rt->draw_queue_2pass) ||
1524             !rt->draw_idle_id)
1525                 {
1526                 pr_render_complete_signal(pr);
1527
1528                 rt->draw_idle_id = 0;
1529                 return FALSE;
1530                 }
1531
1532         if (rt->draw_queue)
1533                 {
1534                 qd = rt->draw_queue->data;
1535                 fast = (pr->zoom_2pass && ((pr->zoom_quality != GDK_INTERP_NEAREST && pr->scale != 1.0) || pr->post_process_slow));
1536                 }
1537         else
1538                 {
1539                 if (pr->loading)
1540                         {
1541                         /* still loading, wait till done (also drops the higher priority) */
1542
1543                         return rt_queue_schedule_next_draw(rt, FALSE);
1544                         }
1545
1546                 qd = rt->draw_queue_2pass->data;
1547                 fast = FALSE;
1548                 }
1549
1550 #if GTK_CHECK_VERSION(2,20,0)
1551         if (gtk_widget_get_realized(pr))
1552 #else
1553         if (GTK_WIDGET_REALIZED(pr))
1554 #endif
1555                 {
1556                 if (rt_tile_is_visible(rt, qd->it))
1557                         {
1558                         rt_tile_expose(rt, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast);
1559                         }
1560                 else if (qd->new_data)
1561                         {
1562                         /* if new pixel data, and we already have a pixmap, update the tile */
1563                         qd->it->blank = FALSE;
1564                         if (qd->it->pixmap && qd->it->render_done == TILE_RENDER_ALL)
1565                                 {
1566                                 rt_tile_render(rt, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast);
1567                                 }
1568                         }
1569                 }
1570
1571         if (rt->draw_queue)
1572                 {
1573                 qd->it->qd = NULL;
1574                 rt->draw_queue = g_list_remove(rt->draw_queue, qd);
1575                 if (fast)
1576                         {
1577                         if (qd->it->qd2)
1578                                 {
1579                                 rt_queue_merge(qd->it->qd2, qd);
1580                                 g_free(qd);
1581                                 }
1582                         else
1583                                 {
1584                                 qd->it->qd2 = qd;
1585                                 rt->draw_queue_2pass = g_list_append(rt->draw_queue_2pass, qd);
1586                                 }
1587                         }
1588                 else
1589                         {
1590                         g_free(qd);
1591                         }
1592                 }
1593         else
1594                 {
1595                 qd->it->qd2 = NULL;
1596                 rt->draw_queue_2pass = g_list_remove(rt->draw_queue_2pass, qd);
1597                 g_free(qd);
1598                 }
1599
1600         if (!rt->draw_queue && !rt->draw_queue_2pass)
1601                 {
1602                 pr_render_complete_signal(pr);
1603
1604                 rt->draw_idle_id = 0;
1605                 return FALSE;
1606                 }
1607
1608                 return rt_queue_schedule_next_draw(rt, FALSE);
1609 }
1610
1611 static void rt_queue_list_free(GList *list)
1612 {
1613         GList *work;
1614
1615         work = list;
1616         while (work)
1617                 {
1618                 QueueData *qd;
1619
1620                 qd = work->data;
1621                 work = work->next;
1622
1623                 qd->it->qd = NULL;
1624                 qd->it->qd2 = NULL;
1625                 g_free(qd);
1626                 }
1627
1628         g_list_free(list);
1629 }
1630
1631 static void rt_queue_clear(RendererTiles *rt)
1632 {
1633         rt_queue_list_free(rt->draw_queue);
1634         rt->draw_queue = NULL;
1635
1636         rt_queue_list_free(rt->draw_queue_2pass);
1637         rt->draw_queue_2pass = NULL;
1638
1639         if (rt->draw_idle_id)
1640                 {
1641                 g_source_remove(rt->draw_idle_id);
1642                 rt->draw_idle_id = 0;
1643                 }
1644 }
1645
1646 static void rt_queue_merge(QueueData *parent, QueueData *qd)
1647 {
1648         if (parent->x + parent->w < qd->x + qd->w)
1649                 {
1650                 parent->w += (qd->x + qd->w) - (parent->x + parent->w);
1651                 }
1652         if (parent->x > qd->x)
1653                 {
1654                 parent->w += parent->x - qd->x;
1655                 parent->x = qd->x;
1656                 }
1657
1658         if (parent->y + parent->h < qd->y + qd->h)
1659                 {
1660                 parent->h += (qd->y + qd->h) - (parent->y + parent->h);
1661                 }
1662         if (parent->y > qd->y)
1663                 {
1664                 parent->h += parent->y - qd->y;
1665                 parent->y = qd->y;
1666                 }
1667
1668         parent->new_data |= qd->new_data;
1669 }
1670
1671 static gboolean rt_clamp_to_visible(RendererTiles *rt, gint *x, gint *y, gint *w, gint *h)
1672 {
1673         PixbufRenderer *pr = rt->pr;
1674         gint nx, ny;
1675         gint nw, nh;
1676         gint vx, vy;
1677         gint vw, vh;
1678
1679         vw = pr->vis_width;
1680         vh = pr->vis_height;
1681
1682         vx = pr->x_scroll;
1683         vy = pr->y_scroll;
1684
1685         if (*x + *w < vx || *x > vx + vw || *y + *h < vy || *y > vy + vh) return FALSE;
1686
1687         /* now clamp it */
1688         nx = CLAMP(*x, vx, vx + vw);
1689         nw = CLAMP(*w - (nx - *x), 1, vw);
1690
1691         ny = CLAMP(*y, vy, vy + vh);
1692         nh = CLAMP(*h - (ny - *y), 1, vh);
1693
1694         *x = nx;
1695         *y = ny;
1696         *w = nw;
1697         *h = nh;
1698
1699         return TRUE;
1700 }
1701
1702 static gboolean rt_queue_to_tiles(RendererTiles *rt, gint x, gint y, gint w, gint h,
1703                                   gboolean clamp, ImageRenderType render,
1704                                   gboolean new_data, gboolean only_existing)
1705 {
1706         PixbufRenderer *pr = rt->pr;
1707         gint i, j;
1708         gint x1, x2;
1709         gint y1, y2;
1710
1711         if (clamp && !rt_clamp_to_visible(rt, &x, &y, &w, &h)) return FALSE;
1712
1713         x1 = ROUND_DOWN(x, rt->tile_width);
1714         x2 = ROUND_UP(x + w, rt->tile_width);
1715
1716         y1 = ROUND_DOWN(y, rt->tile_height);
1717         y2 = ROUND_UP(y + h, rt->tile_height);
1718
1719         for (j = y1; j <= y2; j += rt->tile_height)
1720                 {
1721                 for (i = x1; i <= x2; i += rt->tile_width)
1722                         {
1723                         ImageTile *it;
1724
1725                         it = rt_tile_get(rt, i, j,
1726                                          (only_existing &&
1727                                           (i + rt->tile_width < pr->x_scroll ||
1728                                            i > pr->x_scroll + pr->vis_width ||
1729                                            j + rt->tile_height < pr->y_scroll ||
1730                                            j > pr->y_scroll + pr->vis_height)));
1731                         if (it)
1732                                 {
1733                                 QueueData *qd;
1734
1735                                 if ((render == TILE_RENDER_ALL && it->render_done != TILE_RENDER_ALL) ||
1736                                     (render == TILE_RENDER_AREA && it->render_todo != TILE_RENDER_ALL))
1737                                         {
1738                                         it->render_todo = render;
1739                                         }
1740
1741                                 qd = g_new(QueueData, 1);
1742                                 qd->it = it;
1743                                 qd->new_data = new_data;
1744
1745                                 if (i < x)
1746                                         {
1747                                         qd->x = x - i;
1748                                         }
1749                                 else
1750                                         {
1751                                         qd->x = 0;
1752                                         }
1753                                 qd->w = x + w - i - qd->x;
1754                                 if (qd->x + qd->w > rt->tile_width) qd->w = rt->tile_width - qd->x;
1755
1756                                 if (j < y)
1757                                         {
1758                                         qd->y = y - j;
1759                                         }
1760                                 else
1761                                         {
1762                                         qd->y = 0;
1763                                         }
1764                                 qd->h = y + h - j - qd->y;
1765                                 if (qd->y + qd->h > rt->tile_height) qd->h = rt->tile_height - qd->y;
1766
1767                                 if (qd->w < 1 || qd->h < 1)
1768                                         {
1769                                         g_free(qd);
1770                                         }
1771                                 else if (it->qd)
1772                                         {
1773                                         rt_queue_merge(it->qd, qd);
1774                                         g_free(qd);
1775                                         }
1776                                 else
1777                                         {
1778                                         it->qd = qd;
1779                                         rt->draw_queue = g_list_append(rt->draw_queue, qd);
1780                                         }
1781                                 }
1782                         }
1783                 }
1784
1785         return TRUE;
1786 }
1787
1788 static void rt_queue(RendererTiles *rt, gint x, gint y, gint w, gint h,
1789                      gboolean clamp, ImageRenderType render,
1790                      gboolean new_data, gboolean only_existing)
1791 {
1792         PixbufRenderer *pr = rt->pr;
1793         gint nx, ny;
1794
1795         nx = CLAMP(x, 0, pr->width - 1);
1796         ny = CLAMP(y, 0, pr->height - 1);
1797         w -= (nx - x);
1798         h -= (ny - y);
1799         w = CLAMP(w, 0, pr->width - nx);
1800         h = CLAMP(h, 0, pr->height - ny);
1801         if (w < 1 || h < 1) return;
1802
1803         if (rt_queue_to_tiles(rt, nx, ny, w, h, clamp, render, new_data, only_existing) &&
1804             ((!rt->draw_queue && !rt->draw_queue_2pass) || !rt->draw_idle_id))
1805                 {
1806                 if (rt->draw_idle_id)
1807                         {
1808                         g_source_remove(rt->draw_idle_id);
1809                         rt->draw_idle_id = 0;
1810                         }
1811                 rt_queue_schedule_next_draw(rt, TRUE);
1812                 }
1813 }
1814
1815 static void rt_redraw(RendererTiles *rt, gboolean new_data)
1816 {
1817         PixbufRenderer *pr = rt->pr;
1818         rt_queue_clear(rt);
1819         rt_queue(rt, 0, 0, pr->width, pr->height, TRUE, TILE_RENDER_ALL, new_data, FALSE);
1820 }
1821
1822
1823 static void rt_scroll(RendererTiles *rt, gint x_off, gint y_off)
1824 {
1825         PixbufRenderer *pr = rt->pr;
1826
1827         gint w = pr->vis_width - abs(x_off);
1828         gint h = pr->vis_height - abs(y_off);
1829
1830         if (w < 1 || h < 1)
1831                 {
1832                 /* scrolled completely to new material */
1833                 pr->renderer->queue(pr->renderer, 0, 0, pr->width, pr->height, TRUE, TILE_RENDER_ALL, FALSE, FALSE);
1834                 return;
1835                 }
1836         else
1837                 {
1838                 gint x1, y1;
1839                 gint x2, y2;
1840                 GtkWidget *box;
1841                 GdkGC *gc;
1842                 GdkEvent *event;
1843
1844                 if (x_off < 0)
1845                         {
1846                         x1 = abs(x_off);
1847                         x2 = 0;
1848                         }
1849                 else
1850                         {
1851                         x1 = 0;
1852                         x2 = abs(x_off);
1853                         }
1854
1855                 if (y_off < 0)
1856                         {
1857                         y1 = abs(y_off);
1858                         y2 = 0;
1859                         }
1860                 else
1861                         {
1862                         y1 = 0;
1863                         y2 = abs(y_off);
1864                         }
1865
1866                 box = GTK_WIDGET(pr);
1867
1868                 gc = gdk_gc_new(box->window);
1869                 gdk_gc_set_exposures(gc, TRUE);
1870                 gdk_draw_drawable(box->window, gc,
1871                                   box->window,
1872                                   x2 + pr->x_offset, y2 + pr->y_offset,
1873                                   x1 + pr->x_offset, y1 + pr->y_offset, w, h);
1874                 g_object_unref(gc);
1875
1876                 rt_overlay_queue_all(rt, x2, y2, x1, y1);
1877
1878                 w = pr->vis_width - w;
1879                 h = pr->vis_height - h;
1880
1881                 if (w > 0)
1882                         {
1883                         rt_queue(rt,
1884                                     x_off > 0 ? pr->x_scroll + (pr->vis_width - w) : pr->x_scroll, pr->y_scroll,
1885                                     w, pr->vis_height, TRUE, TILE_RENDER_ALL, FALSE, FALSE);
1886                         }
1887                 if (h > 0)
1888                         {
1889                         /* FIXME, to optimize this, remove overlap */
1890                         rt_queue(rt,
1891                                     pr->x_scroll, y_off > 0 ? pr->y_scroll + (pr->vis_height - h) : pr->y_scroll,
1892                                     pr->vis_width, h, TRUE, TILE_RENDER_ALL, FALSE, FALSE);
1893                         }
1894
1895                 /* process exposures here, "expose_event" seems to miss a few with obstructed windows */
1896 #if ! GTK_CHECK_VERSION(2,18,0)
1897                 while ((event = gdk_event_get_graphics_expose(box->window)) != NULL)
1898                         {
1899                         pixbuf_renderer_paint(pr, &event->expose.area);
1900
1901                         if (event->expose.count == 0)
1902                                 {
1903                                 gdk_event_free(event);
1904                                 break;
1905                                 }
1906                         gdk_event_free(event);
1907                         }
1908 #endif
1909                 }
1910 }
1911
1912 static void renderer_queue(void *renderer, gint x, gint y, gint w, gint h,
1913                      gint clamp, ImageRenderType render, gboolean new_data, gboolean only_existing)
1914 {
1915         rt_queue((RendererTiles *)renderer, x, y, w, h, clamp, render, new_data, only_existing);
1916 }
1917
1918 static void renderer_queue_clear(void *renderer)
1919 {
1920         rt_queue_clear((RendererTiles *)renderer);
1921 }
1922
1923 static void renderer_border_draw(void *renderer, gint x, gint y, gint w, gint h)
1924 {
1925         rt_border_draw((RendererTiles *)renderer, x, y, w, h);
1926 }
1927
1928
1929 static void renderer_invalidate_all(void *renderer)
1930 {
1931         rt_tile_invalidate_all((RendererTiles *)renderer);
1932 }
1933
1934 static void renderer_invalidate_region(void *renderer, gint x, gint y, gint w, gint h)
1935 {
1936         rt_tile_invalidate_region((RendererTiles *)renderer, x, y, w, h);
1937 }
1938
1939 static void renderer_overlay_draw(void *renderer, gint x, gint y, gint w, gint h)
1940 {
1941         rt_overlay_draw((RendererTiles *)renderer, x, y, w, h, NULL);
1942 }
1943
1944 static void renderer_free(void *renderer)
1945 {
1946         RendererTiles *rt = (RendererTiles *)renderer;
1947         rt_queue_clear(rt);
1948         rt_tile_free_all(rt);
1949         if (rt->spare_tile) g_object_unref(rt->spare_tile);
1950         if (rt->overlay_buffer) g_object_unref(rt->overlay_buffer);
1951         rt_overlay_list_clear(rt);
1952         /* disconnect "hierarchy-changed" */
1953         g_signal_handlers_disconnect_matched(G_OBJECT(rt->pr), G_SIGNAL_MATCH_DATA,
1954                                                      0, 0, 0, NULL, rt);
1955         g_free(rt);
1956 }
1957
1958 RendererFuncs *renderer_tiles_new(PixbufRenderer *pr)
1959 {
1960         RendererTiles *rt = g_new0(RendererTiles, 1);
1961         
1962         rt->pr = pr;
1963         
1964         rt->f.queue = renderer_queue;
1965         rt->f.queue_clear = renderer_queue_clear;
1966         rt->f.border_draw = renderer_border_draw;
1967         rt->f.free = renderer_free;
1968         rt->f.invalidate_all = renderer_invalidate_all;
1969         rt->f.invalidate_region = renderer_invalidate_region;
1970         rt->f.scroll = rt_scroll;
1971
1972
1973         rt->f.overlay_add = renderer_tiles_overlay_add;
1974         rt->f.overlay_set = renderer_tiles_overlay_set;
1975         rt->f.overlay_get = renderer_tiles_overlay_get;
1976         rt->f.overlay_update_sizes = rt_overlay_update_sizes;
1977         rt->f.overlay_draw = renderer_overlay_draw;
1978         
1979         rt->tile_width = PR_TILE_SIZE;
1980         rt->tile_height = PR_TILE_SIZE;
1981
1982         rt->tiles = NULL;
1983         rt->tile_cache_size = 0;
1984
1985         rt->tile_cache_max = PR_CACHE_SIZE_DEFAULT;
1986
1987         rt->draw_idle_id = 0;
1988
1989         g_signal_connect(G_OBJECT(pr), "hierarchy-changed",
1990                          G_CALLBACK(rt_hierarchy_changed_cb), rt);
1991
1992         return (RendererFuncs *) rt;
1993 }
1994
1995
1996 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */