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