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