update the texture with lower priority
[geeqie.git] / src / renderer-clutter.c
1 /*
2  * Geeqie
3  * (C) 2006 John Ellis
4  * Copyright (C) 2008 - 2012 The Geeqie Team
5  *
6  * Author: John Ellis
7  * Author: Vladimir Nadvornik
8  *
9  * This software is released under the GNU General Public License (GNU GPL).
10  * Please read the included file COPYING for more information.
11  * This software comes with no warranty of any kind, use at your own risk!
12  */
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <math.h>
18
19 #include "main.h"
20 #include "pixbuf-renderer.h"
21 #include "renderer-clutter.h"
22
23 #include "intl.h"
24 #include "layout.h"
25
26 #include <gtk/gtk.h>
27
28 #ifdef HAVE_CLUTTER
29
30 #include <clutter/clutter.h>
31
32 #include <clutter-gtk/clutter-gtk.h>
33
34
35
36 #define GQ_BUILD 1
37
38 #ifdef GQ_BUILD
39 #include "main.h"
40 #include "pixbuf_util.h"
41 #include "exif.h"
42 #else
43 typedef enum {
44         EXIF_ORIENTATION_UNKNOWN        = 0,
45         EXIF_ORIENTATION_TOP_LEFT       = 1,
46         EXIF_ORIENTATION_TOP_RIGHT      = 2,
47         EXIF_ORIENTATION_BOTTOM_RIGHT   = 3,
48         EXIF_ORIENTATION_BOTTOM_LEFT    = 4,
49         EXIF_ORIENTATION_LEFT_TOP       = 5,
50         EXIF_ORIENTATION_RIGHT_TOP      = 6,
51         EXIF_ORIENTATION_RIGHT_BOTTOM   = 7,
52         EXIF_ORIENTATION_LEFT_BOTTOM    = 8
53 } ExifOrientationType;
54 #endif
55
56
57 typedef struct _OverlayData OverlayData;
58 struct _OverlayData
59 {
60         gint id;
61
62         GdkPixbuf *pixbuf;
63         GdkWindow *window;
64
65         gint x;
66         gint y;
67
68         OverlayRendererFlags flags;
69 };
70
71 typedef struct _RendererClutter RendererClutter;
72
73 struct _RendererClutter
74 {
75         RendererFuncs f;
76         PixbufRenderer *pr;
77
78         
79         gint stereo_mode;
80         gint stereo_off_x;
81         gint stereo_off_y;
82         
83         
84         GList *pending_updates;
85         gint idle_update;
86         
87         GtkWidget *widget; /* widget and stage may be shared with other renderers */
88         ClutterActor *stage;
89         ClutterActor *texture;
90         ClutterActor *group;
91 };
92
93 typedef struct _RendererClutterAreaParam RendererClutterAreaParam;
94 struct _RendererClutterAreaParam {
95         RendererClutter *rc;
96         gint x;
97         gint y;
98         gint w;
99         gint h;
100 };
101
102
103 static void rc_sync_actor(RendererClutter *rc)
104 {
105         PixbufRenderer *pr = rc->pr;
106         gint anchor_x = 0;
107         gint anchor_y = 0;
108         
109         clutter_actor_set_anchor_point(CLUTTER_ACTOR(rc->texture), 0, 0);
110
111         printf("scale %d %d\n", rc->pr->width, rc->pr->height);
112         printf("pos   %d %d\n", rc->pr->x_offset, rc->pr->y_offset);
113         
114         switch (pr->orientation)
115                 {
116                 case EXIF_ORIENTATION_TOP_LEFT:
117                         /* normal  */
118                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
119                                                 CLUTTER_Z_AXIS,
120                                                 0, 0, 0, 0);
121                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
122                                                 CLUTTER_Y_AXIS,
123                                                 0, 0, 0, 0);
124                         clutter_actor_set_size(CLUTTER_ACTOR(rc->texture), pr->width, pr->height);
125                         anchor_x = 0;
126                         anchor_y = 0;
127                         break;
128                 case EXIF_ORIENTATION_TOP_RIGHT:
129                         /* mirrored */
130                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
131                                                 CLUTTER_Z_AXIS,
132                                                 0, 0, 0, 0);
133                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
134                                                 CLUTTER_Y_AXIS,
135                                                 180, 0, 0, 0);
136                         clutter_actor_set_size(CLUTTER_ACTOR(rc->texture), pr->width, pr->height);
137                         anchor_x = pr->width;
138                         anchor_y = 0;
139                         break;
140                 case EXIF_ORIENTATION_BOTTOM_RIGHT:
141                         /* upside down */
142                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
143                                                 CLUTTER_Z_AXIS,
144                                                 180, 0, 0, 0);
145                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
146                                                 CLUTTER_Y_AXIS,
147                                                 0, 0, 0, 0);
148                         clutter_actor_set_size(CLUTTER_ACTOR(rc->texture), pr->width, pr->height);
149                         anchor_x = pr->width;
150                         anchor_y = pr->height;
151                         break;
152                 case EXIF_ORIENTATION_BOTTOM_LEFT:
153                         /* flipped */
154                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
155                                                 CLUTTER_Z_AXIS,
156                                                 180, 0, 0, 0);
157                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
158                                                 CLUTTER_Y_AXIS,
159                                                 180, 0, 0, 0);
160                         clutter_actor_set_size(CLUTTER_ACTOR(rc->texture), pr->width, pr->height);
161                         anchor_x = 0;
162                         anchor_y = pr->height;
163                         break;
164                 case EXIF_ORIENTATION_LEFT_TOP:
165                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
166                                                 CLUTTER_Z_AXIS,
167                                                 -90, 0, 0, 0);
168                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
169                                                 CLUTTER_Y_AXIS,
170                                                 180, 0, 0, 0);
171                         clutter_actor_set_size(CLUTTER_ACTOR(rc->texture), pr->height, pr->width);
172                         anchor_x = 0;
173                         anchor_y = 0;
174                         break;
175                 case EXIF_ORIENTATION_RIGHT_TOP:
176                         /* rotated -90 (270) */
177                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
178                                                 CLUTTER_Z_AXIS,
179                                                 -90, 0, 0, 0);
180                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
181                                                 CLUTTER_Y_AXIS,
182                                                 0, 0, 0, 0);
183                         clutter_actor_set_size(CLUTTER_ACTOR(rc->texture), pr->height, pr->width);
184                         anchor_x = 0;
185                         anchor_y = pr->height;
186                         break;
187                 case EXIF_ORIENTATION_RIGHT_BOTTOM:
188                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
189                                                 CLUTTER_Z_AXIS,
190                                                 90, 0, 0, 0);
191                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
192                                                 CLUTTER_Y_AXIS,
193                                                 180, 0, 0, 0);
194                         clutter_actor_set_size(CLUTTER_ACTOR(rc->texture), pr->height, pr->width);
195                         anchor_x = pr->width;
196                         anchor_y = pr->height;
197                         break;
198                 case EXIF_ORIENTATION_LEFT_BOTTOM:
199                         /* rotated 90 */
200                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
201                                                 CLUTTER_Z_AXIS,
202                                                 90, 0, 0, 0);
203                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
204                                                 CLUTTER_Y_AXIS,
205                                                 0, 0, 0, 0);
206                         clutter_actor_set_size(CLUTTER_ACTOR(rc->texture), pr->height, pr->width);
207                         anchor_x = pr->width;
208                         anchor_y = 0;
209                         break;
210                 default:
211                         /* The other values are out of range */
212                         break;
213                 }
214         
215         clutter_actor_set_position(CLUTTER_ACTOR(rc->texture), 
216                                 pr->x_offset - pr->x_scroll + anchor_x, 
217                                 pr->y_offset - pr->y_scroll + anchor_y);
218
219 }
220
221 #define MAX_REGION_AREA (8192 * 1024)
222
223 static gboolean renderer_area_changed_cb(gpointer data)
224 {
225         RendererClutter *rc = (RendererClutter *)data;
226         PixbufRenderer *pr = rc->pr;
227         
228         RendererClutterAreaParam *par = rc->pending_updates->data;
229         
230         gint h = MAX_REGION_AREA / par->w;
231         if (h == 0) h = 1;
232         if (h > par->h) h = par->h;
233         
234         
235         printf("renderer_area_changed_cb %d %d %d %d  (%d)\n", par->x, par->y, par->w, h, par->h);
236         if (pr->pixbuf)
237                 {
238                 CoglHandle texture = clutter_texture_get_cogl_texture(CLUTTER_TEXTURE(rc->texture));
239                 
240                 cogl_texture_set_region(texture,
241                                         par->x,
242                                         par->y,
243                                         par->x,
244                                         par->y,
245                                         par->w,
246                                         h,
247                                         par->w,
248                                         h,
249                                         gdk_pixbuf_get_has_alpha(pr->pixbuf) ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888,
250                                         gdk_pixbuf_get_rowstride(pr->pixbuf),
251                                         gdk_pixbuf_get_pixels(pr->pixbuf));
252                 }
253                 
254         par->y += h;
255         par->h -= h;
256         
257         if (par->h == 0)
258                 {
259                 rc->pending_updates = g_list_remove(rc->pending_updates, par);
260                 g_free(par);
261                 }
262         if (!rc->pending_updates)
263                 {
264                 clutter_actor_queue_redraw(CLUTTER_ACTOR(rc->texture));
265                 rc->idle_update = 0;
266                 return FALSE;
267                 }
268         return TRUE;
269 }
270
271
272 static void renderer_area_changed(void *renderer, gint src_x, gint src_y, gint src_w, gint src_h)
273 {
274         RendererClutter *rc = (RendererClutter *)renderer;
275         PixbufRenderer *pr = rc->pr;
276         
277         RendererClutterAreaParam *par = g_new0(RendererClutterAreaParam, 1);
278         par->rc = rc;
279         par->x = src_x;
280         par->y = src_y;
281         par->w = src_w;
282         par->h = src_h;
283         rc->pending_updates = g_list_append(rc->pending_updates, par);
284         if (!rc->idle_update) 
285                 {
286                 rc->idle_update = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, renderer_area_changed_cb, rc, NULL);
287                 }
288 }
289
290 static void renderer_remove_pending_updates(RendererClutter *rc)
291 {
292         if (rc->idle_update) g_idle_remove_by_data(rc);
293         rc->idle_update = 0;
294         while (rc->pending_updates)
295                 {
296                 RendererClutterAreaParam *par = rc->pending_updates->data;
297                 rc->pending_updates = g_list_remove(rc->pending_updates, par);
298                 g_free(par);
299                 }
300 }
301
302 static void renderer_update_pixbuf(void *renderer, gboolean lazy)
303 {
304         RendererClutter *rc = (RendererClutter *)renderer;
305         PixbufRenderer *pr = rc->pr;
306         
307         renderer_remove_pending_updates(rc);
308         
309         if (pr->pixbuf)
310                 {
311                 gint width = gdk_pixbuf_get_width(pr->pixbuf);
312                 gint height = gdk_pixbuf_get_height(pr->pixbuf);
313                 
314                 gint prev_width, prev_height;
315                 
316                 printf("renderer_update_pixbuf\n");
317                 clutter_texture_get_base_size(CLUTTER_TEXTURE(rc->texture), &prev_width, &prev_height);
318                 printf("change from %d %d to %d %d\n", prev_width, prev_height, width, height);
319                 
320                 if (width != prev_width || height != prev_height)
321                         {
322                         /* FIXME use CoglMaterial with multiple textures for background, color management, anaglyph, ... */
323                         CoglHandle texture = cogl_texture_new_with_size(width,
324                                                                         height,
325                                                                         COGL_TEXTURE_NONE,
326                                                                         gdk_pixbuf_get_has_alpha(pr->pixbuf) ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888);
327
328                         if (texture != COGL_INVALID_HANDLE)
329                                 {
330                                 clutter_texture_set_cogl_texture(CLUTTER_TEXTURE(rc->texture), texture);
331                                 cogl_handle_unref(texture);
332                                 }
333                         }
334                 if (!lazy)
335                         {
336                         renderer_area_changed(renderer, 0, 0, width, height);
337                         }
338                 }
339
340
341         printf("renderer_update_pixbuf\n");
342         rc_sync_actor(rc);
343 }
344
345
346
347 static void renderer_update_zoom(void *renderer, gboolean lazy)
348 {
349         RendererClutter *rc = (RendererClutter *)renderer;
350         PixbufRenderer *pr = rc->pr;
351
352         printf("renderer_update_zoom\n");
353         rc_sync_actor(rc);
354 }
355
356 static void renderer_invalidate_region(void *renderer, gint x, gint y, gint w, gint h)
357 {
358 }
359
360 static void renderer_overlay_draw(void *renderer, gint x, gint y, gint w, gint h)
361 {
362 }
363
364 static void renderer_overlay_add(void *renderer, gint x, gint y, gint w, gint h)
365 {
366 }
367
368 static void renderer_overlay_set(void *renderer, gint x, gint y, gint w, gint h)
369 {
370 }
371
372 static void renderer_overlay_get(void *renderer, gint x, gint y, gint w, gint h)
373 {
374 }
375
376 static void renderer_update_sizes(void *renderer)
377 {
378         RendererClutter *rc = (RendererClutter *)renderer;
379         ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff }; 
380
381         rc->stereo_off_x = 0;
382         rc->stereo_off_y = 0;
383         
384         if (rc->stereo_mode & PR_STEREO_RIGHT) 
385                 {
386                 if (rc->stereo_mode & PR_STEREO_HORIZ) 
387                         {
388                         rc->stereo_off_x = rc->pr->viewport_width;
389                         }
390                 else if (rc->stereo_mode & PR_STEREO_VERT) 
391                         {
392                         rc->stereo_off_y = rc->pr->viewport_height;
393                         }
394                 else if (rc->stereo_mode & PR_STEREO_FIXED) 
395                         {
396                         rc->stereo_off_x = rc->pr->stereo_fixed_x_right;
397                         rc->stereo_off_y = rc->pr->stereo_fixed_y_right;
398                         }
399                 }
400         else
401                 {
402                 if (rc->stereo_mode & PR_STEREO_FIXED) 
403                         {
404                         rc->stereo_off_x = rc->pr->stereo_fixed_x_left;
405                         rc->stereo_off_y = rc->pr->stereo_fixed_y_left;
406                         }
407                 }
408         DEBUG_1("update size: %p  %d %d   %d %d", rc, rc->stereo_off_x, rc->stereo_off_y, rc->pr->viewport_width, rc->pr->viewport_height);
409
410         printf("renderer_update_sizes  scale %d %d\n", rc->pr->width, rc->pr->height);
411
412         clutter_stage_set_color(CLUTTER_STAGE(rc->stage), &stage_color);
413
414
415         clutter_actor_set_size(rc->group, rc->pr->viewport_width, rc->pr->viewport_height);
416         clutter_actor_set_position(rc->group, rc->stereo_off_x, rc->stereo_off_y);
417         
418         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->group),
419                                                 CLUTTER_Y_AXIS,
420                                                 (rc->stereo_mode & PR_STEREO_MIRROR) ? 180 : 0, 
421                                                 rc->pr->viewport_width / 2.0, 0, 0);
422
423         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->group),
424                                                 CLUTTER_X_AXIS,
425                                                 (rc->stereo_mode & PR_STEREO_FLIP) ? 180 : 0,
426                                                 0, rc->pr->viewport_height / 2.0, 0);
427
428         rc_sync_actor(rc);
429 }
430
431 static void renderer_scroll(void *renderer, gint x_off, gint y_off)
432 {
433         printf("renderer_scroll\n");
434         RendererClutter *rc = (RendererClutter *)renderer;
435         PixbufRenderer *pr = rc->pr;
436
437         rc_sync_actor(rc);
438 }
439
440 static void renderer_stereo_set(void *renderer, gint stereo_mode)
441 {
442         RendererClutter *rc = (RendererClutter *)renderer;
443
444         rc->stereo_mode = stereo_mode;
445 }
446
447 static void renderer_free(void *renderer)
448 {
449         RendererClutter *rc = (RendererClutter *)renderer;
450         renderer_remove_pending_updates(rc);
451         
452         GtkWidget *widget = gtk_bin_get_child(GTK_BIN(rc->pr));
453         if (widget)
454                 {
455                 /* widget still exists */
456                 clutter_actor_destroy(rc->group);
457                 if (clutter_group_get_n_children(CLUTTER_GROUP(rc->stage)) == 0)
458                         {
459                         printf("destroy %p\n", rc->widget);
460                         /* this was the last user */
461                         gtk_widget_destroy(rc->widget);
462                         }
463                 else
464                         {
465                         printf("keep %p\n", rc->widget);
466                         g_object_unref(G_OBJECT(rc->widget));
467                         }
468                 }
469         g_free(rc);
470 }
471
472 RendererFuncs *renderer_clutter_new(PixbufRenderer *pr)
473 {
474         RendererClutter *rc = g_new0(RendererClutter, 1);
475         
476         rc->pr = pr;
477         
478         rc->f.area_changed = renderer_area_changed;
479         rc->f.update_pixbuf = renderer_update_pixbuf;
480         rc->f.free = renderer_free;
481         rc->f.update_zoom = renderer_update_zoom;
482         rc->f.invalidate_region = renderer_invalidate_region;
483         rc->f.scroll = renderer_scroll;
484         rc->f.update_sizes = renderer_update_sizes;
485
486
487         rc->f.overlay_add = renderer_overlay_add;
488         rc->f.overlay_set = renderer_overlay_set;
489         rc->f.overlay_get = renderer_overlay_get;
490         rc->f.overlay_draw = renderer_overlay_draw;
491
492         rc->f.stereo_set = renderer_stereo_set;
493         
494         
495         rc->stereo_mode = 0;
496         rc->stereo_off_x = 0;
497         rc->stereo_off_y = 0;
498
499         rc->idle_update = 0;
500         rc->pending_updates = NULL;
501
502         rc->widget = gtk_bin_get_child(GTK_BIN(rc->pr));
503         
504         if (rc->widget)
505                 {
506                 if (!GTK_CLUTTER_IS_EMBED(rc->widget))
507                         {
508                         g_free(rc);
509                         DEBUG_0("pixbuf renderer has a child of other type than gtk_clutter_embed");
510                         return NULL;
511                         }
512                 }
513         else 
514                 {
515                 rc->widget = gtk_clutter_embed_new();
516                 gtk_container_add(GTK_CONTAINER(rc->pr), rc->widget);
517                 }
518                 
519         gtk_event_box_set_above_child (GTK_EVENT_BOX(rc->pr), TRUE);
520         rc->stage = gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED (rc->widget));
521         
522         rc->group = clutter_group_new();
523         clutter_container_add_actor(CLUTTER_CONTAINER(rc->stage), rc->group);
524         clutter_actor_set_clip_to_allocation(CLUTTER_ACTOR(rc->group), TRUE);
525   
526         rc->texture = gtk_clutter_texture_new ();
527         clutter_container_add_actor(CLUTTER_CONTAINER(rc->group), rc->texture);
528         g_object_ref(G_OBJECT(rc->widget));
529   
530         gtk_widget_show(rc->widget);
531         return (RendererFuncs *) rc;
532 }
533
534 #endif 
535 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */