first stereo support
[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
1216 static void rt_tile_render(RendererTiles *rt, ImageTile *it,
1217                            gint x, gint y, gint w, gint h,
1218                            gboolean new_data, gboolean fast)
1219 {
1220         PixbufRenderer *pr = rt->pr;
1221         GtkWidget *box;
1222         gboolean has_alpha;
1223         gboolean draw = FALSE;
1224         gint stereo_pixbuf_off;
1225
1226         if (it->render_todo == TILE_RENDER_NONE && it->pixmap && !new_data) return;
1227
1228         if (it->render_done != TILE_RENDER_ALL)
1229                 {
1230                 x = 0;
1231                 y = 0;
1232                 w = it->w;
1233                 h = it->h;
1234                 if (!fast) it->render_done = TILE_RENDER_ALL;
1235                 }
1236         else if (it->render_todo != TILE_RENDER_AREA)
1237                 {
1238                 if (!fast) it->render_todo = TILE_RENDER_NONE;
1239                 return;
1240                 }
1241
1242         if (!fast) it->render_todo = TILE_RENDER_NONE;
1243
1244         if (new_data) it->blank = FALSE;
1245
1246         rt_tile_prepare(rt, it);
1247         has_alpha = (pr->pixbuf && gdk_pixbuf_get_has_alpha(pr->pixbuf));
1248
1249         stereo_pixbuf_off = (rt->stereo_mode & PR_STEREO_RIGHT) ? pr->stereo_pixbuf_off : 0;
1250         box = GTK_WIDGET(pr);
1251
1252         /* FIXME checker colors for alpha should be configurable,
1253          * also should be drawn for blank = TRUE
1254          */
1255
1256         if (it->blank)
1257                 {
1258                 /* no data, do fast rect fill */
1259                 gdk_draw_rectangle(it->pixmap, box->style->black_gc, TRUE,
1260                                    0, 0, it->w, it->h);
1261                 }
1262         else if (pr->source_tiles_enabled)
1263                 {
1264                 draw = rt_source_tile_render(rt, it, x, y, w, h, new_data, fast);
1265                 }
1266         else if (pr->zoom == 1.0 || pr->scale == 1.0)
1267                 {
1268
1269                 gdouble src_x, src_y;
1270                 gint pb_x, pb_y;
1271                 gint pb_w, pb_h;
1272                 pr_tile_coords_map_orientation(pr->orientation, it->x, it->y,
1273                                             pr->image_width, pr->image_height,
1274                                             rt->tile_width, rt->tile_height,
1275                                             &src_x, &src_y);
1276                 pr_tile_region_map_orientation(pr->orientation, x, y,
1277                                             rt->tile_width, rt->tile_height,
1278                                             w, h,
1279                                             &pb_x, &pb_y,
1280                                             &pb_w, &pb_h);
1281                 src_x += stereo_pixbuf_off;
1282
1283                 if (has_alpha)
1284                         {
1285                         gdk_pixbuf_composite_color(pr->pixbuf, it->pixbuf, pb_x, pb_y, pb_w, pb_h,
1286                                          (gdouble) 0.0 - src_x,
1287                                          (gdouble) 0.0 - src_y,
1288                                          1.0, 1.0, GDK_INTERP_NEAREST,
1289                                          255, it->x + pb_x, it->y + pb_y,
1290                                          PR_ALPHA_CHECK_SIZE, PR_ALPHA_CHECK1, PR_ALPHA_CHECK2);
1291                         rt_tile_apply_orientation(rt, &it->pixbuf, pb_x, pb_y, pb_w, pb_h);
1292                         draw = TRUE;
1293                         }
1294                 else
1295                         {
1296
1297
1298                         if (pr->orientation == EXIF_ORIENTATION_TOP_LEFT && !(pr->func_post_process && !(pr->post_process_slow && fast)))
1299                                 {
1300                                 /* faster, simple, base orientation, no postprocessing */
1301                                 gdk_draw_pixbuf(it->pixmap,
1302 #if GTK_CHECK_VERSION(2,20,0)
1303                                                 box->style->fg_gc[gtk_widget_get_state(box)],
1304 #else
1305                                                 box->style->fg_gc[GTK_WIDGET_STATE(box)],
1306 #endif
1307                                                 pr->pixbuf,
1308                                                 it->x + x + stereo_pixbuf_off, it->y + y,
1309                                                 x, y,
1310                                                 w, h,
1311                                                 pr->dither_quality, it->x + x, it->y + y);
1312                                 }
1313                         else
1314                                 {
1315                                 gdk_pixbuf_copy_area(pr->pixbuf,
1316                                                      src_x + pb_x, src_y + pb_y,
1317                                                      pb_w, pb_h,
1318                                                      it->pixbuf,
1319                                                      pb_x, pb_y);
1320                                 rt_tile_apply_orientation(rt, &it->pixbuf, pb_x, pb_y, pb_w, pb_h);
1321                                 draw = TRUE;
1322                                 }
1323                         }
1324                 }
1325         else
1326                 {
1327                 gdouble scale_x, scale_y;
1328                 gdouble src_x, src_y;
1329                 gint pb_x, pb_y;
1330                 gint pb_w, pb_h;
1331
1332                 if (pr->image_width == 0 || pr->image_height == 0) return;
1333
1334                 scale_x = (gdouble)pr->width / pr->image_width;
1335                 scale_y = (gdouble)pr->height / pr->image_height;
1336
1337                 pr_tile_coords_map_orientation(pr->orientation, it->x, it->y,
1338                                             pr->image_width * scale_x, pr->image_height * scale_y,
1339                                             rt->tile_width, rt->tile_height,
1340                                             &src_x, &src_y);
1341                 pr_tile_region_map_orientation(pr->orientation, x, y,
1342                                             rt->tile_width, rt->tile_height,
1343                                             w, h,
1344                                             &pb_x, &pb_y,
1345                                             &pb_w, &pb_h);
1346
1347                 switch (pr->orientation)
1348                         {
1349                         gdouble tmp;
1350                         case EXIF_ORIENTATION_LEFT_TOP:
1351                         case EXIF_ORIENTATION_RIGHT_TOP:
1352                         case EXIF_ORIENTATION_RIGHT_BOTTOM:
1353                         case EXIF_ORIENTATION_LEFT_BOTTOM:
1354                                 tmp = scale_x;
1355                                 scale_x = scale_y;
1356                                 scale_y = tmp;
1357                                 break;
1358                         default:
1359                                 /* nothing to do */
1360                                 break;
1361                         }
1362                 src_x += stereo_pixbuf_off * scale_x;
1363                 
1364                 /* HACK: The pixbuf scalers get kinda buggy(crash) with extremely
1365                  * small sizes for anything but GDK_INTERP_NEAREST
1366                  */
1367                 if (pr->width < PR_MIN_SCALE_SIZE || pr->height < PR_MIN_SCALE_SIZE) fast = TRUE;
1368
1369                 if (!has_alpha)
1370                         {
1371                         gdk_pixbuf_scale(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                         }
1377                 else
1378                         {
1379                         gdk_pixbuf_composite_color(pr->pixbuf, it->pixbuf, pb_x, pb_y, pb_w, pb_h,
1380                                          (gdouble) 0.0 - src_x,
1381                                          (gdouble) 0.0 - src_y,
1382                                          scale_x, scale_y,
1383                                          (fast) ? GDK_INTERP_NEAREST : pr->zoom_quality,
1384                                          255, it->x + pb_x, it->y + pb_y,
1385                                          PR_ALPHA_CHECK_SIZE, PR_ALPHA_CHECK1, PR_ALPHA_CHECK2);
1386                         }
1387                 rt_tile_apply_orientation(rt, &it->pixbuf, pb_x, pb_y, pb_w, pb_h);
1388                 draw = TRUE;
1389                 }
1390
1391         if (draw && it->pixbuf && !it->blank)
1392                 {
1393
1394                 if (pr->func_post_process && !(pr->post_process_slow && fast))
1395                         pr->func_post_process(pr, &it->pixbuf, x, y, w, h, pr->post_process_user_data);
1396
1397                 gdk_draw_pixbuf(it->pixmap,
1398 #if GTK_CHECK_VERSION(2,20,0)
1399                                 box->style->fg_gc[gtk_widget_get_state(box)],
1400 #else
1401                                 box->style->fg_gc[GTK_WIDGET_STATE(box)],
1402 #endif
1403                                 it->pixbuf,
1404                                 x, y,
1405                                 x, y,
1406                                 w, h,
1407                                 pr->dither_quality, it->x + x + rt->stereo_off_x, it->y + y + rt->stereo_off_y);
1408                 }
1409
1410 #if 0
1411         /* enable this line for debugging the edges of tiles */
1412         gdk_draw_rectangle(it->pixmap, box->style->white_gc,
1413                            FALSE, 0, 0, it->w, it->h);
1414         gdk_draw_rectangle(it->pixmap, box->style->white_gc,
1415                            FALSE, x, y, w, h);
1416 #endif
1417 }
1418
1419
1420 static void rt_tile_expose(RendererTiles *rt, ImageTile *it,
1421                            gint x, gint y, gint w, gint h,
1422                            gboolean new_data, gboolean fast)
1423 {
1424         PixbufRenderer *pr = rt->pr;
1425         GtkWidget *box;
1426
1427         rt_tile_render(rt, it, x, y, w, h, new_data, fast);
1428
1429         box = GTK_WIDGET(pr);
1430
1431 #if GTK_CHECK_VERSION(2,20,0)
1432         gdk_draw_drawable(box->window, box->style->fg_gc[gtk_widget_get_state(box)],
1433 #else
1434         gdk_draw_drawable(box->window, box->style->fg_gc[GTK_WIDGET_STATE(box)],
1435 #endif
1436                           it->pixmap, x, y,
1437                           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);
1438
1439         if (rt->overlay_list)
1440                 {
1441                 rt_overlay_draw(rt, pr->x_offset + (it->x - pr->x_scroll) + x,
1442                                 pr->y_offset + (it->y - pr->y_scroll) + y,
1443                                 w, h,
1444                                 it);
1445                 }
1446 }
1447
1448
1449 static gboolean rt_tile_is_visible(RendererTiles *rt, ImageTile *it)
1450 {
1451         PixbufRenderer *pr = rt->pr;
1452         return (it->x + it->w >= pr->x_scroll && it->x < pr->x_scroll + pr->vis_width &&
1453                 it->y + it->h >= pr->y_scroll && it->y < pr->y_scroll + pr->vis_height);
1454 }
1455
1456 /*
1457  *-------------------------------------------------------------------
1458  * draw queue
1459  *-------------------------------------------------------------------
1460  */
1461
1462 static gint rt_get_queued_area(GList *work)
1463 {
1464         gint area = 0;
1465         
1466         while (work) 
1467                 {
1468                 QueueData *qd = work->data;
1469                 area += qd->w * qd->h;
1470                 work = work->next;
1471                 }
1472         return area;
1473 }
1474
1475
1476 static gboolean rt_queue_schedule_next_draw(RendererTiles *rt, gboolean force_set)
1477 {
1478         PixbufRenderer *pr = rt->pr;
1479         gfloat percent;
1480         gint visible_area = pr->vis_width * pr->vis_height;
1481         
1482         if (!pr->loading)
1483                 {
1484                 /* 2pass prio */ 
1485                 DEBUG_2("redraw priority: 2pass");
1486                 rt->draw_idle_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, rt_queue_draw_idle_cb, rt, NULL);
1487                 return FALSE;
1488                 }
1489         
1490         if (visible_area == 0)
1491                 {
1492                 /* not known yet */
1493                 percent = 100.0;
1494                 }
1495         else
1496                 {
1497                 percent = 100.0 * rt_get_queued_area(rt->draw_queue) / visible_area;
1498                 }
1499         
1500         if (percent > 10.0)
1501                 {
1502                 /* we have enough data for starting intensive redrawing */
1503                 DEBUG_2("redraw priority: high %.2f %%", percent);
1504                 rt->draw_idle_id = g_idle_add_full(GDK_PRIORITY_REDRAW, rt_queue_draw_idle_cb, rt, NULL);
1505                 return FALSE;
1506                 }
1507         
1508         if (percent < 1.0 || force_set)
1509                 {
1510                 /* queue is (almost) empty, wait  50 ms*/
1511                 DEBUG_2("redraw priority: wait %.2f %%", percent);
1512                 rt->draw_idle_id = g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, 50, rt_queue_draw_idle_cb, rt, NULL);
1513                 return FALSE;
1514                 }
1515         
1516         /* keep the same priority as before */
1517         DEBUG_2("redraw priority: no change %.2f %%", percent);
1518         return TRUE;
1519 }
1520                 
1521
1522 static gboolean rt_queue_draw_idle_cb(gpointer data)
1523 {
1524         RendererTiles *rt = data;
1525         PixbufRenderer *pr = rt->pr;
1526         QueueData *qd;
1527         gboolean fast;
1528
1529
1530         if ((!pr->pixbuf && !pr->source_tiles_enabled) ||
1531             (!rt->draw_queue && !rt->draw_queue_2pass) ||
1532             !rt->draw_idle_id)
1533                 {
1534                 pr_render_complete_signal(pr);
1535
1536                 rt->draw_idle_id = 0;
1537                 return FALSE;
1538                 }
1539
1540         if (rt->draw_queue)
1541                 {
1542                 qd = rt->draw_queue->data;
1543                 fast = (pr->zoom_2pass && ((pr->zoom_quality != GDK_INTERP_NEAREST && pr->scale != 1.0) || pr->post_process_slow));
1544                 }
1545         else
1546                 {
1547                 if (pr->loading)
1548                         {
1549                         /* still loading, wait till done (also drops the higher priority) */
1550
1551                         return rt_queue_schedule_next_draw(rt, FALSE);
1552                         }
1553
1554                 qd = rt->draw_queue_2pass->data;
1555                 fast = FALSE;
1556                 }
1557
1558 #if GTK_CHECK_VERSION(2,20,0)
1559         if (gtk_widget_get_realized(pr))
1560 #else
1561         if (GTK_WIDGET_REALIZED(pr))
1562 #endif
1563                 {
1564                 if (rt_tile_is_visible(rt, qd->it))
1565                         {
1566                         rt_tile_expose(rt, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast);
1567                         }
1568                 else if (qd->new_data)
1569                         {
1570                         /* if new pixel data, and we already have a pixmap, update the tile */
1571                         qd->it->blank = FALSE;
1572                         if (qd->it->pixmap && qd->it->render_done == TILE_RENDER_ALL)
1573                                 {
1574                                 rt_tile_render(rt, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast);
1575                                 }
1576                         }
1577                 }
1578
1579         if (rt->draw_queue)
1580                 {
1581                 qd->it->qd = NULL;
1582                 rt->draw_queue = g_list_remove(rt->draw_queue, qd);
1583                 if (fast)
1584                         {
1585                         if (qd->it->qd2)
1586                                 {
1587                                 rt_queue_merge(qd->it->qd2, qd);
1588                                 g_free(qd);
1589                                 }
1590                         else
1591                                 {
1592                                 qd->it->qd2 = qd;
1593                                 rt->draw_queue_2pass = g_list_append(rt->draw_queue_2pass, qd);
1594                                 }
1595                         }
1596                 else
1597                         {
1598                         g_free(qd);
1599                         }
1600                 }
1601         else
1602                 {
1603                 qd->it->qd2 = NULL;
1604                 rt->draw_queue_2pass = g_list_remove(rt->draw_queue_2pass, qd);
1605                 g_free(qd);
1606                 }
1607
1608         if (!rt->draw_queue && !rt->draw_queue_2pass)
1609                 {
1610                 pr_render_complete_signal(pr);
1611
1612                 rt->draw_idle_id = 0;
1613                 return FALSE;
1614                 }
1615
1616                 return rt_queue_schedule_next_draw(rt, FALSE);
1617 }
1618
1619 static void rt_queue_list_free(GList *list)
1620 {
1621         GList *work;
1622
1623         work = list;
1624         while (work)
1625                 {
1626                 QueueData *qd;
1627
1628                 qd = work->data;
1629                 work = work->next;
1630
1631                 qd->it->qd = NULL;
1632                 qd->it->qd2 = NULL;
1633                 g_free(qd);
1634                 }
1635
1636         g_list_free(list);
1637 }
1638
1639 static void rt_queue_clear(RendererTiles *rt)
1640 {
1641         rt_queue_list_free(rt->draw_queue);
1642         rt->draw_queue = NULL;
1643
1644         rt_queue_list_free(rt->draw_queue_2pass);
1645         rt->draw_queue_2pass = NULL;
1646
1647         if (rt->draw_idle_id)
1648                 {
1649                 g_source_remove(rt->draw_idle_id);
1650                 rt->draw_idle_id = 0;
1651                 }
1652 }
1653
1654 static void rt_queue_merge(QueueData *parent, QueueData *qd)
1655 {
1656         if (parent->x + parent->w < qd->x + qd->w)
1657                 {
1658                 parent->w += (qd->x + qd->w) - (parent->x + parent->w);
1659                 }
1660         if (parent->x > qd->x)
1661                 {
1662                 parent->w += parent->x - qd->x;
1663                 parent->x = qd->x;
1664                 }
1665
1666         if (parent->y + parent->h < qd->y + qd->h)
1667                 {
1668                 parent->h += (qd->y + qd->h) - (parent->y + parent->h);
1669                 }
1670         if (parent->y > qd->y)
1671                 {
1672                 parent->h += parent->y - qd->y;
1673                 parent->y = qd->y;
1674                 }
1675
1676         parent->new_data |= qd->new_data;
1677 }
1678
1679 static gboolean rt_clamp_to_visible(RendererTiles *rt, gint *x, gint *y, gint *w, gint *h)
1680 {
1681         PixbufRenderer *pr = rt->pr;
1682         gint nx, ny;
1683         gint nw, nh;
1684         gint vx, vy;
1685         gint vw, vh;
1686
1687         vw = pr->vis_width;
1688         vh = pr->vis_height;
1689
1690         vx = pr->x_scroll;
1691         vy = pr->y_scroll;
1692
1693         if (*x + *w < vx || *x > vx + vw || *y + *h < vy || *y > vy + vh) return FALSE;
1694
1695         /* now clamp it */
1696         nx = CLAMP(*x, vx, vx + vw);
1697         nw = CLAMP(*w - (nx - *x), 1, vw);
1698
1699         ny = CLAMP(*y, vy, vy + vh);
1700         nh = CLAMP(*h - (ny - *y), 1, vh);
1701
1702         *x = nx;
1703         *y = ny;
1704         *w = nw;
1705         *h = nh;
1706
1707         return TRUE;
1708 }
1709
1710 static gboolean rt_queue_to_tiles(RendererTiles *rt, gint x, gint y, gint w, gint h,
1711                                   gboolean clamp, ImageRenderType render,
1712                                   gboolean new_data, gboolean only_existing)
1713 {
1714         PixbufRenderer *pr = rt->pr;
1715         gint i, j;
1716         gint x1, x2;
1717         gint y1, y2;
1718
1719         if (clamp && !rt_clamp_to_visible(rt, &x, &y, &w, &h)) return FALSE;
1720
1721         x1 = ROUND_DOWN(x, rt->tile_width);
1722         x2 = ROUND_UP(x + w, rt->tile_width);
1723
1724         y1 = ROUND_DOWN(y, rt->tile_height);
1725         y2 = ROUND_UP(y + h, rt->tile_height);
1726
1727         for (j = y1; j <= y2; j += rt->tile_height)
1728                 {
1729                 for (i = x1; i <= x2; i += rt->tile_width)
1730                         {
1731                         ImageTile *it;
1732
1733                         it = rt_tile_get(rt, i, j,
1734                                          (only_existing &&
1735                                           (i + rt->tile_width < pr->x_scroll ||
1736                                            i > pr->x_scroll + pr->vis_width ||
1737                                            j + rt->tile_height < pr->y_scroll ||
1738                                            j > pr->y_scroll + pr->vis_height)));
1739                         if (it)
1740                                 {
1741                                 QueueData *qd;
1742
1743                                 if ((render == TILE_RENDER_ALL && it->render_done != TILE_RENDER_ALL) ||
1744                                     (render == TILE_RENDER_AREA && it->render_todo != TILE_RENDER_ALL))
1745                                         {
1746                                         it->render_todo = render;
1747                                         }
1748
1749                                 qd = g_new(QueueData, 1);
1750                                 qd->it = it;
1751                                 qd->new_data = new_data;
1752
1753                                 if (i < x)
1754                                         {
1755                                         qd->x = x - i;
1756                                         }
1757                                 else
1758                                         {
1759                                         qd->x = 0;
1760                                         }
1761                                 qd->w = x + w - i - qd->x;
1762                                 if (qd->x + qd->w > rt->tile_width) qd->w = rt->tile_width - qd->x;
1763
1764                                 if (j < y)
1765                                         {
1766                                         qd->y = y - j;
1767                                         }
1768                                 else
1769                                         {
1770                                         qd->y = 0;
1771                                         }
1772                                 qd->h = y + h - j - qd->y;
1773                                 if (qd->y + qd->h > rt->tile_height) qd->h = rt->tile_height - qd->y;
1774
1775                                 if (qd->w < 1 || qd->h < 1)
1776                                         {
1777                                         g_free(qd);
1778                                         }
1779                                 else if (it->qd)
1780                                         {
1781                                         rt_queue_merge(it->qd, qd);
1782                                         g_free(qd);
1783                                         }
1784                                 else
1785                                         {
1786                                         it->qd = qd;
1787                                         rt->draw_queue = g_list_append(rt->draw_queue, qd);
1788                                         }
1789                                 }
1790                         }
1791                 }
1792
1793         return TRUE;
1794 }
1795
1796 static void rt_queue(RendererTiles *rt, gint x, gint y, gint w, gint h,
1797                      gboolean clamp, ImageRenderType render,
1798                      gboolean new_data, gboolean only_existing)
1799 {
1800         PixbufRenderer *pr = rt->pr;
1801         gint nx, ny;
1802
1803         nx = CLAMP(x, 0, pr->width - 1);
1804         ny = CLAMP(y, 0, pr->height - 1);
1805         w -= (nx - x);
1806         h -= (ny - y);
1807         w = CLAMP(w, 0, pr->width - nx);
1808         h = CLAMP(h, 0, pr->height - ny);
1809         if (w < 1 || h < 1) return;
1810
1811         if (rt_queue_to_tiles(rt, nx, ny, w, h, clamp, render, new_data, only_existing) &&
1812             ((!rt->draw_queue && !rt->draw_queue_2pass) || !rt->draw_idle_id))
1813                 {
1814                 if (rt->draw_idle_id)
1815                         {
1816                         g_source_remove(rt->draw_idle_id);
1817                         rt->draw_idle_id = 0;
1818                         }
1819                 rt_queue_schedule_next_draw(rt, TRUE);
1820                 }
1821 }
1822
1823 static void rt_redraw(RendererTiles *rt, gboolean new_data)
1824 {
1825         PixbufRenderer *pr = rt->pr;
1826         rt_queue_clear(rt);
1827         rt_queue(rt, 0, 0, pr->width, pr->height, TRUE, TILE_RENDER_ALL, new_data, FALSE);
1828 }
1829
1830
1831 static void rt_scroll(RendererTiles *rt, gint x_off, gint y_off)
1832 {
1833         PixbufRenderer *pr = rt->pr;
1834
1835         gint w = pr->vis_width - abs(x_off);
1836         gint h = pr->vis_height - abs(y_off);
1837
1838         if (w < 1 || h < 1)
1839                 {
1840                 /* scrolled completely to new material */
1841                 pr->renderer->queue(pr->renderer, 0, 0, pr->width, pr->height, TRUE, TILE_RENDER_ALL, FALSE, FALSE);
1842                 return;
1843                 }
1844         else
1845                 {
1846                 gint x1, y1;
1847                 gint x2, y2;
1848                 GtkWidget *box;
1849                 GdkGC *gc;
1850                 GdkEvent *event;
1851
1852                 if (x_off < 0)
1853                         {
1854                         x1 = abs(x_off);
1855                         x2 = 0;
1856                         }
1857                 else
1858                         {
1859                         x1 = 0;
1860                         x2 = abs(x_off);
1861                         }
1862
1863                 if (y_off < 0)
1864                         {
1865                         y1 = abs(y_off);
1866                         y2 = 0;
1867                         }
1868                 else
1869                         {
1870                         y1 = 0;
1871                         y2 = abs(y_off);
1872                         }
1873
1874                 box = GTK_WIDGET(pr);
1875
1876                 gc = gdk_gc_new(box->window);
1877                 gdk_gc_set_exposures(gc, TRUE);
1878                 gdk_draw_drawable(box->window, gc,
1879                                   box->window,
1880                                   x2 + pr->x_offset + rt->stereo_off_x, y2 + pr->y_offset + rt->stereo_off_y,
1881                                   x1 + pr->x_offset + rt->stereo_off_x, y1 + pr->y_offset + rt->stereo_off_y, w, h);
1882                 g_object_unref(gc);
1883
1884                 rt_overlay_queue_all(rt, x2, y2, x1, y1);
1885
1886                 w = pr->vis_width - w;
1887                 h = pr->vis_height - h;
1888
1889                 if (w > 0)
1890                         {
1891                         rt_queue(rt,
1892                                     x_off > 0 ? pr->x_scroll + (pr->vis_width - w) : pr->x_scroll, pr->y_scroll,
1893                                     w, pr->vis_height, TRUE, TILE_RENDER_ALL, FALSE, FALSE);
1894                         }
1895                 if (h > 0)
1896                         {
1897                         /* FIXME, to optimize this, remove overlap */
1898                         rt_queue(rt,
1899                                     pr->x_scroll, y_off > 0 ? pr->y_scroll + (pr->vis_height - h) : pr->y_scroll,
1900                                     pr->vis_width, h, TRUE, TILE_RENDER_ALL, FALSE, FALSE);
1901                         }
1902
1903                 /* process exposures here, "expose_event" seems to miss a few with obstructed windows */
1904 #if ! GTK_CHECK_VERSION(2,18,0)
1905                 while ((event = gdk_event_get_graphics_expose(box->window)) != NULL)
1906                         {
1907                         pixbuf_renderer_paint(pr, &event->expose.area);
1908
1909                         if (event->expose.count == 0)
1910                                 {
1911                                 gdk_event_free(event);
1912                                 break;
1913                                 }
1914                         gdk_event_free(event);
1915                         }
1916 #endif
1917                 }
1918 }
1919
1920 static void renderer_queue(void *renderer, gint x, gint y, gint w, gint h,
1921                      gint clamp, ImageRenderType render, gboolean new_data, gboolean only_existing)
1922 {
1923         rt_queue((RendererTiles *)renderer, x, y, w, h, clamp, render, new_data, only_existing);
1924 }
1925
1926 static void renderer_queue_clear(void *renderer)
1927 {
1928         rt_queue_clear((RendererTiles *)renderer);
1929 }
1930
1931 static void renderer_border_draw(void *renderer, gint x, gint y, gint w, gint h)
1932 {
1933         rt_border_draw((RendererTiles *)renderer, x, y, w, h);
1934 }
1935
1936
1937 static void renderer_invalidate_all(void *renderer)
1938 {
1939         rt_tile_invalidate_all((RendererTiles *)renderer);
1940 }
1941
1942 static void renderer_invalidate_region(void *renderer, gint x, gint y, gint w, gint h)
1943 {
1944         rt_tile_invalidate_region((RendererTiles *)renderer, x, y, w, h);
1945 }
1946
1947 static void renderer_overlay_draw(void *renderer, gint x, gint y, gint w, gint h)
1948 {
1949         rt_overlay_draw((RendererTiles *)renderer, x, y, w, h, NULL);
1950 }
1951
1952 static void renderer_update_sizes(void *renderer)
1953 {
1954         RendererTiles *rt = (RendererTiles *)renderer;
1955
1956         rt_overlay_update_sizes(rt);
1957
1958         rt->stereo_off_x = 0;
1959         rt->stereo_off_x = 0;
1960         
1961         if (rt->stereo_mode & PR_STEREO_RIGHT) 
1962                 {
1963                 if (rt->stereo_mode & PR_STEREO_HORIZ) 
1964                         {
1965                         rt->stereo_off_x = rt->pr->viewport_width;
1966                         }
1967                 else if (rt->stereo_mode & PR_STEREO_VERT) 
1968                         {
1969                         rt->stereo_off_y = rt->pr->viewport_height;
1970                         }
1971                 }
1972 }
1973
1974 static void renderer_free(void *renderer)
1975 {
1976         RendererTiles *rt = (RendererTiles *)renderer;
1977         rt_queue_clear(rt);
1978         rt_tile_free_all(rt);
1979         if (rt->spare_tile) g_object_unref(rt->spare_tile);
1980         if (rt->overlay_buffer) g_object_unref(rt->overlay_buffer);
1981         rt_overlay_list_clear(rt);
1982         /* disconnect "hierarchy-changed" */
1983         g_signal_handlers_disconnect_matched(G_OBJECT(rt->pr), G_SIGNAL_MATCH_DATA,
1984                                                      0, 0, 0, NULL, rt);
1985         g_free(rt);
1986 }
1987
1988 RendererFuncs *renderer_tiles_new(PixbufRenderer *pr, gint stereo_mode)
1989 {
1990         RendererTiles *rt = g_new0(RendererTiles, 1);
1991         
1992         rt->pr = pr;
1993         
1994         rt->f.queue = renderer_queue;
1995         rt->f.queue_clear = renderer_queue_clear;
1996         rt->f.border_draw = renderer_border_draw;
1997         rt->f.free = renderer_free;
1998         rt->f.invalidate_all = renderer_invalidate_all;
1999         rt->f.invalidate_region = renderer_invalidate_region;
2000         rt->f.scroll = rt_scroll;
2001         rt->f.update_sizes = renderer_update_sizes;
2002
2003
2004         rt->f.overlay_add = renderer_tiles_overlay_add;
2005         rt->f.overlay_set = renderer_tiles_overlay_set;
2006         rt->f.overlay_get = renderer_tiles_overlay_get;
2007         rt->f.overlay_draw = renderer_overlay_draw;
2008         
2009         rt->tile_width = PR_TILE_SIZE;
2010         rt->tile_height = PR_TILE_SIZE;
2011
2012         rt->tiles = NULL;
2013         rt->tile_cache_size = 0;
2014
2015         rt->tile_cache_max = PR_CACHE_SIZE_DEFAULT;
2016
2017         rt->draw_idle_id = 0;
2018         
2019         rt->stereo_mode = stereo_mode;
2020         rt->stereo_off_x = 0;
2021         rt->stereo_off_y = 0;
2022
2023         g_signal_connect(G_OBJECT(pr), "hierarchy-changed",
2024                          G_CALLBACK(rt_hierarchy_changed_cb), rt);
2025
2026         return (RendererFuncs *) rt;
2027 }
2028
2029
2030 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */