simplified mirrored stereo drawing
[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_actor(RendererClutter *rc)
90 {
91         PixbufRenderer *pr = rc->pr;
92         gint anchor_x = 0;
93         gint anchor_y = 0;
94         
95         clutter_actor_set_anchor_point(CLUTTER_ACTOR(rc->texture), 0, 0);
96
97         printf("scale %d %d\n", rc->pr->width, rc->pr->height);
98         printf("pos   %d %d        %d %d\n", rc->pr->x_offset, rc->pr->y_offset, rc->x_scroll, rc->y_scroll);
99         
100         switch (pr->orientation)
101                 {
102                 case EXIF_ORIENTATION_TOP_LEFT:
103                         /* normal  */
104                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
105                                                 CLUTTER_Z_AXIS,
106                                                 0, 0, 0, 0);
107                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
108                                                 CLUTTER_Y_AXIS,
109                                                 0, 0, 0, 0);
110                         clutter_actor_set_size(CLUTTER_ACTOR(rc->texture), pr->width, pr->height);
111                         anchor_x = 0;
112                         anchor_y = 0;
113                         break;
114                 case EXIF_ORIENTATION_TOP_RIGHT:
115                         /* mirrored */
116                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
117                                                 CLUTTER_Z_AXIS,
118                                                 0, 0, 0, 0);
119                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
120                                                 CLUTTER_Y_AXIS,
121                                                 180, 0, 0, 0);
122                         clutter_actor_set_size(CLUTTER_ACTOR(rc->texture), pr->width, pr->height);
123                         anchor_x = pr->width;
124                         anchor_y = 0;
125                         break;
126                 case EXIF_ORIENTATION_BOTTOM_RIGHT:
127                         /* upside down */
128                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
129                                                 CLUTTER_Z_AXIS,
130                                                 180, 0, 0, 0);
131                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
132                                                 CLUTTER_Y_AXIS,
133                                                 0, 0, 0, 0);
134                         clutter_actor_set_size(CLUTTER_ACTOR(rc->texture), pr->width, pr->height);
135                         anchor_x = pr->width;
136                         anchor_y = pr->height;
137                         break;
138                 case EXIF_ORIENTATION_BOTTOM_LEFT:
139                         /* flipped */
140                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
141                                                 CLUTTER_Z_AXIS,
142                                                 180, 0, 0, 0);
143                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
144                                                 CLUTTER_Y_AXIS,
145                                                 180, 0, 0, 0);
146                         clutter_actor_set_size(CLUTTER_ACTOR(rc->texture), pr->width, pr->height);
147                         anchor_x = 0;
148                         anchor_y = pr->height;
149                         break;
150                 case EXIF_ORIENTATION_LEFT_TOP:
151                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
152                                                 CLUTTER_Z_AXIS,
153                                                 -90, 0, 0, 0);
154                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
155                                                 CLUTTER_Y_AXIS,
156                                                 180, 0, 0, 0);
157                         clutter_actor_set_size(CLUTTER_ACTOR(rc->texture), pr->height, pr->width);
158                         anchor_x = 0;
159                         anchor_y = 0;
160                         break;
161                 case EXIF_ORIENTATION_RIGHT_TOP:
162                         /* rotated -90 (270) */
163                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
164                                                 CLUTTER_Z_AXIS,
165                                                 -90, 0, 0, 0);
166                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
167                                                 CLUTTER_Y_AXIS,
168                                                 0, 0, 0, 0);
169                         clutter_actor_set_size(CLUTTER_ACTOR(rc->texture), pr->height, pr->width);
170                         anchor_x = 0;
171                         anchor_y = pr->height;
172                         break;
173                 case EXIF_ORIENTATION_RIGHT_BOTTOM:
174                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
175                                                 CLUTTER_Z_AXIS,
176                                                 90, 0, 0, 0);
177                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
178                                                 CLUTTER_Y_AXIS,
179                                                 180, 0, 0, 0);
180                         clutter_actor_set_size(CLUTTER_ACTOR(rc->texture), pr->height, pr->width);
181                         anchor_x = pr->width;
182                         anchor_y = pr->height;
183                         break;
184                 case EXIF_ORIENTATION_LEFT_BOTTOM:
185                         /* rotated 90 */
186                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
187                                                 CLUTTER_Z_AXIS,
188                                                 90, 0, 0, 0);
189                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
190                                                 CLUTTER_Y_AXIS,
191                                                 0, 0, 0, 0);
192                         clutter_actor_set_size(CLUTTER_ACTOR(rc->texture), pr->height, pr->width);
193                         anchor_x = pr->width;
194                         anchor_y = 0;
195                         break;
196                 default:
197                         /* The other values are out of range */
198                         break;
199                 }
200         
201         clutter_actor_set_position(CLUTTER_ACTOR(rc->texture), 
202                                 pr->x_offset - pr->x_scroll + anchor_x, 
203                                 pr->y_offset - pr->y_scroll + anchor_y);
204
205 }
206
207
208 static void renderer_area_changed(void *renderer, gint src_x, gint src_y, gint src_w, gint src_h)
209 {
210         RendererClutter *rc = (RendererClutter *)renderer;
211         PixbufRenderer *pr = rc->pr;
212         
213         
214         
215         printf("renderer_area_changed %d %d %d %d\n", src_x, src_y, src_w, src_h);
216         if (pr->pixbuf)
217                 {
218                 CoglHandle texture = clutter_texture_get_cogl_texture(CLUTTER_TEXTURE(rc->texture));
219                 
220                 cogl_texture_set_region(texture,
221                                         src_x,
222                                         src_y,
223                                         src_x,
224                                         src_y,
225                                         src_w,
226                                         src_h,
227                                         src_w,
228                                         src_h,
229                                         gdk_pixbuf_get_has_alpha(pr->pixbuf) ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888,
230                                         gdk_pixbuf_get_rowstride(pr->pixbuf),
231                                         gdk_pixbuf_get_pixels(pr->pixbuf));
232                 clutter_actor_queue_redraw(CLUTTER_ACTOR(rc->texture));
233                 }
234
235 }
236
237 static void renderer_update_pixbuf(void *renderer, gboolean lazy)
238 {
239         RendererClutter *rc = (RendererClutter *)renderer;
240         PixbufRenderer *pr = rc->pr;
241         
242         if (pr->pixbuf)
243                 {
244                 printf("renderer_update_pixbuf\n");
245                 
246                 /* FIXME use CoglMaterial with multiple textures for background, color management, anaglyph, ... */
247                 CoglHandle texture = cogl_texture_new_with_size(gdk_pixbuf_get_width(pr->pixbuf),
248                                                                 gdk_pixbuf_get_height(pr->pixbuf),
249                                                                 COGL_TEXTURE_NONE,
250                                                                 gdk_pixbuf_get_has_alpha(pr->pixbuf) ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888);
251
252                 if (texture != COGL_INVALID_HANDLE)
253                         {
254                         clutter_texture_set_cogl_texture(CLUTTER_TEXTURE(rc->texture), texture);
255                         cogl_handle_unref(texture);
256                         }
257                 if (!lazy)
258                         {
259                         renderer_area_changed(renderer, 0, 0, gdk_pixbuf_get_width(pr->pixbuf), gdk_pixbuf_get_height(pr->pixbuf));
260                         }
261                 }
262
263
264         printf("renderer_update_pixbuf\n");
265         rc_sync_actor(rc);
266 }
267
268
269
270 static void renderer_update_zoom(void *renderer, gboolean lazy)
271 {
272         RendererClutter *rc = (RendererClutter *)renderer;
273         PixbufRenderer *pr = rc->pr;
274
275         printf("renderer_update_zoom\n");
276         rc_sync_actor(rc);
277 }
278
279 static void renderer_invalidate_region(void *renderer, gint x, gint y, gint w, gint h)
280 {
281 }
282
283 static void renderer_overlay_draw(void *renderer, gint x, gint y, gint w, gint h)
284 {
285 }
286
287 static void renderer_overlay_add(void *renderer, gint x, gint y, gint w, gint h)
288 {
289 }
290
291 static void renderer_overlay_set(void *renderer, gint x, gint y, gint w, gint h)
292 {
293 }
294
295 static void renderer_overlay_get(void *renderer, gint x, gint y, gint w, gint h)
296 {
297 }
298
299 static void renderer_update_sizes(void *renderer)
300 {
301         RendererClutter *rc = (RendererClutter *)renderer;
302         ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff }; 
303
304         rc->stereo_off_x = 0;
305         rc->stereo_off_y = 0;
306         
307         if (rc->stereo_mode & PR_STEREO_RIGHT) 
308                 {
309                 if (rc->stereo_mode & PR_STEREO_HORIZ) 
310                         {
311                         rc->stereo_off_x = rc->pr->viewport_width;
312                         }
313                 else if (rc->stereo_mode & PR_STEREO_VERT) 
314                         {
315                         rc->stereo_off_y = rc->pr->viewport_height;
316                         }
317                 else if (rc->stereo_mode & PR_STEREO_FIXED) 
318                         {
319                         rc->stereo_off_x = rc->pr->stereo_fixed_x_right;
320                         rc->stereo_off_y = rc->pr->stereo_fixed_y_right;
321                         }
322                 }
323         else
324                 {
325                 if (rc->stereo_mode & PR_STEREO_FIXED) 
326                         {
327                         rc->stereo_off_x = rc->pr->stereo_fixed_x_left;
328                         rc->stereo_off_y = rc->pr->stereo_fixed_y_left;
329                         }
330                 }
331         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);
332
333         printf("renderer_update_sizes  scale %d %d\n", rc->pr->width, rc->pr->height);
334
335         clutter_stage_set_color(CLUTTER_STAGE(rc->stage), &stage_color);
336
337
338         clutter_actor_set_size(rc->group, rc->pr->viewport_width, rc->pr->viewport_height);
339         clutter_actor_set_position(rc->group, rc->stereo_off_x, rc->stereo_off_y);
340         
341         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->group),
342                                                 CLUTTER_Y_AXIS,
343                                                 (rc->stereo_mode & PR_STEREO_MIRROR) ? 180 : 0, 
344                                                 rc->pr->viewport_width / 2.0, 0, 0);
345
346         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->group),
347                                                 CLUTTER_X_AXIS,
348                                                 (rc->stereo_mode & PR_STEREO_FLIP) ? 180 : 0,
349                                                 0, rc->pr->viewport_height / 2.0, 0);
350
351         rc_sync_actor(rc);
352 }
353
354 static void renderer_scroll(void *renderer, gint x_off, gint y_off)
355 {
356         printf("renderer_scroll\n");
357         RendererClutter *rc = (RendererClutter *)renderer;
358         PixbufRenderer *pr = rc->pr;
359
360         rc_sync_actor(rc);
361 }
362
363 static void renderer_stereo_set(void *renderer, gint stereo_mode)
364 {
365         RendererClutter *rc = (RendererClutter *)renderer;
366
367         rc->stereo_mode = stereo_mode;
368 }
369
370 static void renderer_free(void *renderer)
371 {
372         RendererClutter *rc = (RendererClutter *)renderer;
373         GtkWidget *widget = gtk_bin_get_child(GTK_BIN(rc->pr));
374         if (widget)
375                 {
376                 /* widget still exists */
377                 clutter_actor_destroy(rc->group);
378                 if (clutter_group_get_n_children(CLUTTER_GROUP(rc->stage)) == 0)
379                         {
380                         printf("destroy %p\n", rc->widget);
381                         /* this was the last user */
382                         gtk_widget_destroy(rc->widget);
383                         }
384                 else
385                         {
386                         printf("keep %p\n", rc->widget);
387                         g_object_unref(G_OBJECT(rc->widget));
388                         }
389                 }
390         g_free(rc);
391 }
392
393 RendererFuncs *renderer_clutter_new(PixbufRenderer *pr)
394 {
395         RendererClutter *rc = g_new0(RendererClutter, 1);
396         
397         rc->pr = pr;
398         
399         rc->f.area_changed = renderer_area_changed;
400         rc->f.update_pixbuf = renderer_update_pixbuf;
401         rc->f.free = renderer_free;
402         rc->f.update_zoom = renderer_update_zoom;
403         rc->f.invalidate_region = renderer_invalidate_region;
404         rc->f.scroll = renderer_scroll;
405         rc->f.update_sizes = renderer_update_sizes;
406
407
408         rc->f.overlay_add = renderer_overlay_add;
409         rc->f.overlay_set = renderer_overlay_set;
410         rc->f.overlay_get = renderer_overlay_get;
411         rc->f.overlay_draw = renderer_overlay_draw;
412
413         rc->f.stereo_set = renderer_stereo_set;
414         
415         
416         rc->stereo_mode = 0;
417         rc->stereo_off_x = 0;
418         rc->stereo_off_y = 0;
419
420
421         rc->widget = gtk_bin_get_child(GTK_BIN(rc->pr));
422         
423         if (rc->widget)
424                 {
425                 if (!GTK_CLUTTER_IS_EMBED(rc->widget))
426                         {
427                         g_free(rc);
428                         DEBUG_0("pixbuf renderer has a child of other type than gtk_clutter_embed");
429                         return NULL;
430                         }
431                 }
432         else 
433                 {
434                 rc->widget = gtk_clutter_embed_new();
435                 gtk_container_add(GTK_CONTAINER(rc->pr), rc->widget);
436                 }
437                 
438         gtk_event_box_set_above_child (GTK_EVENT_BOX(rc->pr), TRUE);
439         rc->stage = gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED (rc->widget));
440         
441         rc->group = clutter_group_new();
442         clutter_container_add_actor(CLUTTER_CONTAINER(rc->stage), rc->group);
443         clutter_actor_set_clip_to_allocation(CLUTTER_ACTOR(rc->group), TRUE);
444   
445         rc->texture = gtk_clutter_texture_new ();
446         clutter_container_add_actor(CLUTTER_CONTAINER(rc->group), rc->texture);
447         g_object_ref(G_OBJECT(rc->widget));
448   
449         gtk_widget_show(rc->widget);
450         return (RendererFuncs *) rc;
451 }
452
453
454 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */