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