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