Tmp fix #397: GPU acceleration
[geeqie.git] / src / renderer-clutter.c
1 /*
2  * Copyright (C) 2006 John Ellis
3  * Copyright (C) 2008 - 2016 The Geeqie Team
4  *
5  * Author: John Ellis
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <math.h>
26
27 #include "main.h"
28 #include "pixbuf-renderer.h"
29 #include "renderer-clutter.h"
30
31 #include "intl.h"
32 #include "layout.h"
33
34 #include <gtk/gtk.h>
35
36 #ifdef HAVE_CLUTTER
37
38 /* for 3d texture */
39 #define COGL_ENABLE_EXPERIMENTAL_API
40 #define CLUTTER_ENABLE_EXPERIMENTAL_API
41
42 #include <clutter/clutter.h>
43
44 #include <clutter-gtk/clutter-gtk.h>
45
46
47
48 #define GQ_BUILD 1
49
50 #ifdef GQ_BUILD
51 #include "main.h"
52 #include "pixbuf_util.h"
53 #include "exif.h"
54 #else
55 typedef enum {
56         EXIF_ORIENTATION_UNKNOWN        = 0,
57         EXIF_ORIENTATION_TOP_LEFT       = 1,
58         EXIF_ORIENTATION_TOP_RIGHT      = 2,
59         EXIF_ORIENTATION_BOTTOM_RIGHT   = 3,
60         EXIF_ORIENTATION_BOTTOM_LEFT    = 4,
61         EXIF_ORIENTATION_LEFT_TOP       = 5,
62         EXIF_ORIENTATION_RIGHT_TOP      = 6,
63         EXIF_ORIENTATION_RIGHT_BOTTOM   = 7,
64         EXIF_ORIENTATION_LEFT_BOTTOM    = 8
65 } ExifOrientationType;
66 #endif
67
68 #define GET_RIGHT_PIXBUF_OFFSET(rc) \
69         (( (rc->stereo_mode & PR_STEREO_RIGHT) && !(rc->stereo_mode & PR_STEREO_SWAP)) || \
70          (!(rc->stereo_mode & PR_STEREO_RIGHT) &&  (rc->stereo_mode & PR_STEREO_SWAP)) ?  \
71           rc->pr->stereo_pixbuf_offset_right : rc->pr->stereo_pixbuf_offset_left )
72
73 #define GET_LEFT_PIXBUF_OFFSET(rc) \
74         ((!(rc->stereo_mode & PR_STEREO_RIGHT) && !(rc->stereo_mode & PR_STEREO_SWAP)) || \
75          ( (rc->stereo_mode & PR_STEREO_RIGHT) &&  (rc->stereo_mode & PR_STEREO_SWAP)) ?  \
76           rc->pr->stereo_pixbuf_offset_right : rc->pr->stereo_pixbuf_offset_left )
77
78
79 typedef struct _OverlayData OverlayData;
80 struct _OverlayData
81 {
82         gint id;
83
84         GdkPixbuf *pixbuf;
85         ClutterActor *actor;
86
87         gint x;
88         gint y;
89
90         OverlayRendererFlags flags;
91 };
92
93 typedef struct _RendererClutter RendererClutter;
94
95 struct _RendererClutter
96 {
97         RendererFuncs f;
98         PixbufRenderer *pr;
99
100
101         gint stereo_mode;
102         gint stereo_off_x;
103         gint stereo_off_y;
104
105
106         GList *pending_updates;
107         gint idle_update;
108
109         GList *overlay_list;
110
111         GtkWidget *widget; /* widget and stage may be shared with other renderers */
112         ClutterActor *stage;
113         ClutterActor *texture;
114         ClutterActor *group;
115
116         gboolean clut_updated;
117         gint64 last_pixbuf_change;
118
119         GdkPixbuf *display_pixbuf;
120 };
121
122 typedef struct _RendererClutterAreaParam RendererClutterAreaParam;
123 struct _RendererClutterAreaParam {
124         RendererClutter *rc;
125         gint x;
126         gint y;
127         gint w;
128         gint h;
129 };
130
131 typedef struct _RendererClutterShaderInfo RendererClutterShaderInfo;
132 struct _RendererClutterShaderInfo {
133         float checkersize;
134         float checkercolor0[3];
135         float checkercolor1[3];
136 };
137
138 #define CLUT_SIZE       32
139
140 static void rc_set_shader(CoglHandle material, const RendererClutterShaderInfo *shaderInfo)
141 {
142         CoglHandle shader;
143         CoglHandle program;
144         gint uniform_no;
145
146         shader = cogl_create_shader (COGL_SHADER_TYPE_FRAGMENT);
147         cogl_shader_source (shader,
148         "vec3 checker(vec2 texc, vec3 color0, vec3 color1)                                              \n"
149         "{                                                                                                                                              \n"
150         "  if (mod(floor(texc.x) + floor(texc.y), 2.0) == 0.0)                                  \n"
151         "    return color0;                                                                                                             \n"
152         "  else                                                                                                                                 \n"
153         "    return color1;                                                                                                             \n"
154         "}                                                                                                                                              \n"
155         "                                                                                                                                               \n"
156         "uniform sampler2D tex;                                                                                                 \n"
157         "uniform sampler3D clut;                                                                                                \n"
158         "uniform float scale;                                                                                                   \n"
159         "uniform float offset;                                                                                                  \n"
160         "uniform float checkersize;                                                                                             \n"
161         "uniform vec3 color0;                                                                                                   \n"
162         "uniform vec3 color1;                                                                                                   \n"
163         "                                                                                                                                               \n"
164         "void main(void)                                                                                                                \n"
165         "{                                                                                                                                              \n"
166         "    vec3 bg = checker(gl_FragCoord.xy / checkersize, color0, color1);  \n"
167         "    vec4 img4 = texture2D(tex, gl_TexCoord[0].xy);                                             \n"
168         "    vec3 img3 = img4.bgr;                                                                                              \n"
169         "    img3 = img3 * scale + offset;                                                                              \n"
170         "    img3 = texture3D(clut, img3).rgb;                                                                  \n"
171         "                                                                                                                                               \n"
172         "    gl_FragColor = vec4(img3 * img4.a + bg * (1.0 - img4.a), 1.0);             \n"
173         "}                                                                                                                                              \n"
174         );
175         cogl_shader_compile(shader);
176         gchar *err = cogl_shader_get_info_log(shader);
177         DEBUG_3("%s\n",err);
178         g_free(err);
179
180         program = cogl_create_program ();
181         cogl_program_attach_shader (program, shader);
182         cogl_handle_unref (shader);
183         cogl_program_link (program);
184
185         uniform_no = cogl_program_get_uniform_location (program, "tex");
186         cogl_program_set_uniform_1i (program, uniform_no, 0);
187
188         uniform_no = cogl_program_get_uniform_location (program, "clut");
189         cogl_program_set_uniform_1i (program, uniform_no, 1);
190
191         uniform_no = cogl_program_get_uniform_location (program, "scale");
192         cogl_program_set_uniform_1f (program, uniform_no, (double) (CLUT_SIZE - 1) / CLUT_SIZE);
193
194         uniform_no = cogl_program_get_uniform_location (program, "offset");
195         cogl_program_set_uniform_1f (program, uniform_no, 1.0 / (2 * CLUT_SIZE));
196
197         uniform_no = cogl_program_get_uniform_location (program, "checkersize");
198         cogl_program_set_uniform_1f (program, uniform_no, shaderInfo->checkersize);
199
200         uniform_no = cogl_program_get_uniform_location (program, "color0");
201         cogl_program_set_uniform_float (program, uniform_no, 3, 1, shaderInfo->checkercolor0);
202
203         uniform_no = cogl_program_get_uniform_location (program, "color1");
204         cogl_program_set_uniform_float (program, uniform_no, 3, 1, shaderInfo->checkercolor1);
205
206         cogl_material_set_user_program (material, program);
207         cogl_handle_unref (program);
208 }
209
210
211 static void rc_prepare_post_process_lut(RendererClutter *rc)
212 {
213         PixbufRenderer *pr = rc->pr;
214         static guchar clut[CLUT_SIZE * CLUT_SIZE * CLUT_SIZE * 3];
215         guint r, g, b;
216         GdkPixbuf *tmp_pixbuf;
217         CoglHandle material;
218         CoglHandle tex3d;
219
220         return; //FIXME
221
222         //~ DEBUG_3("%s clut start", get_exec_time());
223
224         //~ for (r = 0; r < CLUT_SIZE; r++)
225                 //~ {
226                 //~ for (g = 0; g < CLUT_SIZE; g++)
227                         //~ {
228                         //~ for (b = 0; b < CLUT_SIZE; b++)
229                                 //~ {
230                                 //~ guchar *ptr = clut + ((b * CLUT_SIZE + g) * CLUT_SIZE + r) * 3;
231                                 //~ ptr[0] = floor ((double) r / (CLUT_SIZE - 1) * 255.0 + 0.5);
232                                 //~ ptr[1] = floor ((double) g / (CLUT_SIZE - 1) * 255.0 + 0.5);
233                                 //~ ptr[2] = floor ((double) b / (CLUT_SIZE - 1) * 255.0 + 0.5);
234                                 //~ }
235                         //~ }
236                 //~ }
237         //~ tmp_pixbuf = gdk_pixbuf_new_from_data(clut, GDK_COLORSPACE_RGB, FALSE, 8,
238                                               //~ CLUT_SIZE * CLUT_SIZE,
239                                               //~ CLUT_SIZE,
240                                               //~ CLUT_SIZE * CLUT_SIZE * 3,
241                                               //~ NULL, NULL);
242         //~ if (pr->func_post_process)
243                 //~ {
244                 //~ pr->func_post_process(pr, &tmp_pixbuf, 0, 0, CLUT_SIZE * CLUT_SIZE, CLUT_SIZE, pr->post_process_user_data);
245                 //~ }
246         //~ g_object_unref(tmp_pixbuf);
247
248         //~ DEBUG_3("%s clut upload start", get_exec_time());
249 //~ #if COGL_VERSION_CHECK(1,18,2)
250         //~ {
251         //~ CoglContext *ctx = clutter_backend_get_cogl_context(clutter_get_default_backend ());
252
253         //~ tex3d = cogl_texture_3d_new_from_data(ctx,
254                                               //~ CLUT_SIZE, CLUT_SIZE, CLUT_SIZE,
255                                               //~ COGL_PIXEL_FORMAT_RGB_888,
256                                               //~ CLUT_SIZE * 3,
257                                               //~ CLUT_SIZE * CLUT_SIZE * 3,
258                                               //~ clut,
259                                               //~ NULL);
260         //~ }
261 //~ #elif COGL_VERSION_CHECK(1,10,4)
262         //~ {
263         //~ CoglContext *ctx = clutter_backend_get_cogl_context(clutter_get_default_backend ());
264
265         //~ tex3d = cogl_texture_3d_new_from_data(ctx,
266                                               //~ CLUT_SIZE, CLUT_SIZE, CLUT_SIZE,
267                                               //~ COGL_PIXEL_FORMAT_RGB_888,
268                                               //~ COGL_PIXEL_FORMAT_RGB_888,
269                                               //~ CLUT_SIZE * 3,
270                                               //~ CLUT_SIZE * CLUT_SIZE * 3,
271                                               //~ clut,
272                                               //~ NULL);
273         //~ }
274 //~ #else
275         //~ tex3d = cogl_texture_3d_new_from_data(CLUT_SIZE, CLUT_SIZE, CLUT_SIZE,
276                                               //~ COGL_TEXTURE_NONE,
277                                               //~ COGL_PIXEL_FORMAT_RGB_888,
278                                               //~ COGL_PIXEL_FORMAT_RGB_888,
279                                               //~ CLUT_SIZE * 3,
280                                               //~ CLUT_SIZE * CLUT_SIZE * 3,
281                                               //~ clut,
282                                               //~ NULL);
283 //~ #endif
284         //~ material = clutter_texture_get_cogl_material(CLUTTER_TEXTURE(rc->texture));
285         //~ cogl_material_set_layer(material, 1, tex3d);
286         //~ cogl_handle_unref(tex3d);
287         //~ DEBUG_3("%s clut end", get_exec_time());
288         //~ rc->clut_updated = TRUE;
289 }
290
291
292
293 static void rc_sync_actor(RendererClutter *rc)
294 {
295         PixbufRenderer *pr = rc->pr;
296         gint anchor_x = 0;
297         gint anchor_y = 0;
298         gint rot_z = 0;
299         gint rot_y = 0;
300
301         clutter_actor_set_anchor_point(CLUTTER_ACTOR(rc->texture), 0, 0);
302
303         DEBUG_3("scale %d %d", rc->pr->width, rc->pr->height);
304         DEBUG_3("pos   %d %d", rc->pr->x_offset, rc->pr->y_offset);
305
306         clutter_actor_set_scale(CLUTTER_ACTOR(rc->texture),
307                                 (gfloat)pr->width / pr->image_width,
308                                 (gfloat)pr->height / pr->image_height);
309
310         switch (pr->orientation)
311                 {
312                 case EXIF_ORIENTATION_TOP_LEFT:
313                         /* 1 - Horizontal (normal)  */
314                         break;
315                 case EXIF_ORIENTATION_TOP_RIGHT:
316                         /* 2 - Mirror horizontal */
317                         rot_y = 180;
318                         anchor_x = pr->width;
319                         break;
320                 case EXIF_ORIENTATION_BOTTOM_RIGHT:
321                         /* 3 - Rotate 180 */
322                         rot_z = 180;
323                         anchor_x = pr->width;
324                         anchor_y = pr->height;
325                         break;
326                 case EXIF_ORIENTATION_BOTTOM_LEFT:
327                         /* 4 - Mirror vertical */
328                         rot_z = 180;
329                         rot_y = 180;
330                         anchor_y = pr->height;
331                         break;
332                 case EXIF_ORIENTATION_LEFT_TOP:
333                         /* 5 - Mirror horizontal and rotate 270 CW */
334                         rot_z = 270;
335                         rot_y = 180;
336                         break;
337                 case EXIF_ORIENTATION_RIGHT_TOP:
338                         /* 6 - Rotate 90 CW */
339                         rot_z = 90;
340                         anchor_x = pr->width;
341                         break;
342                 case EXIF_ORIENTATION_RIGHT_BOTTOM:
343                         /* 7 - Mirror horizontal and rotate 90 CW */
344                         rot_z = 90;
345                         rot_y = 180;
346                         anchor_x = pr->width;
347                         anchor_y = pr->height;
348                         break;
349                 case EXIF_ORIENTATION_LEFT_BOTTOM:
350                         /* 8 - Rotate 270 CW */
351                         rot_z = 270;
352                         anchor_y = pr->height;
353                         break;
354                 default:
355                         /* The other values are out of range */
356                         break;
357                 }
358
359         clutter_actor_set_rotation(     CLUTTER_ACTOR(rc->texture),
360                                                                 CLUTTER_Z_AXIS,
361                                                                 rot_z, 0, 0, 0);
362         clutter_actor_set_rotation(     CLUTTER_ACTOR(rc->texture),
363                                                                 CLUTTER_Y_AXIS,
364                                                                 rot_y, 0, 0, 0);
365
366         clutter_actor_set_position(CLUTTER_ACTOR(rc->texture),
367                                 pr->x_offset - pr->x_scroll + anchor_x,
368                                 pr->y_offset - pr->y_scroll + anchor_y);
369
370 }
371
372
373 static void rc_area_clip_add(RendererClutter *rc, gfloat x, gfloat y, gfloat w, gfloat h)
374 {
375         gfloat x2, y2;
376         gfloat clip_x, clip_y, clip_w, clip_h, clip_x2, clip_y2;
377
378         x2 = x + w;
379         y2 = y + h;
380
381         clutter_actor_get_clip(rc->texture, &clip_x, &clip_y, &clip_w, &clip_h);
382
383         clip_x2 = clip_x + clip_w;
384         clip_y2 = clip_y + clip_h;
385
386         if (clip_x > x) clip_x = x;
387         if (clip_x2 < x2) clip_x2 = x2;
388         if (clip_y > y) clip_y = y;
389         if (clip_y2 < y2) clip_y2 = y2;
390
391         clip_w = clip_x2 - clip_x;
392         clip_h = clip_y2 - clip_y;
393
394         clutter_actor_set_clip(rc->texture, clip_x, clip_y, clip_w, clip_h);
395 }
396
397 #define MAX_REGION_AREA (32768 * 1024)
398
399 static gboolean rc_area_changed_cb(gpointer data);
400
401 static void rc_schedule_texture_upload(RendererClutter *rc)
402 {
403         if (g_get_monotonic_time() - rc->last_pixbuf_change < 50000)
404                 {
405                 /* delay clutter redraw until the texture has some data
406                    set priority between gtk redraw and clutter redraw */
407                 DEBUG_3("%s tex upload high prio", get_exec_time());
408                 rc->idle_update = g_idle_add_full(CLUTTER_PRIORITY_REDRAW - 10, rc_area_changed_cb, rc, NULL);
409                 }
410         else
411                 {
412                 /* higher prio than histogram */
413                 DEBUG_3("%s tex upload low prio", get_exec_time());
414
415                 rc->idle_update = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE - 5, rc_area_changed_cb, rc, NULL);
416                 }
417 }
418
419 static void update_display_pixbuf(RendererClutter *rc);
420
421 static gboolean rc_area_changed_cb(gpointer data)
422 {
423         RendererClutter *rc = (RendererClutter *)data;
424         PixbufRenderer *pr = rc->pr;
425
426         RendererClutterAreaParam *par = rc->pending_updates->data;
427
428         gint h = MAX_REGION_AREA / par->w;
429         if (h == 0) h = 1;
430         if (h > par->h) h = par->h;
431
432         update_display_pixbuf(rc);
433
434         if (pr->func_post_process)
435                 {
436                 pr->func_post_process(pr, &rc->display_pixbuf, 0, 0, gdk_pixbuf_get_width(pr->pixbuf), gdk_pixbuf_get_height(pr->pixbuf), pr->post_process_user_data);
437                 }
438
439         DEBUG_3("%s upload start", get_exec_time());
440         if (pr->pixbuf)
441                 {
442                 CoglHandle texture = clutter_texture_get_cogl_texture(CLUTTER_TEXTURE(rc->texture));
443
444                 cogl_texture_set_region(texture,
445                                         par->x + GET_RIGHT_PIXBUF_OFFSET(rc),
446                                         par->y,
447                                         par->x,
448                                         par->y,
449                                         par->w,
450                                         h,
451                                         par->w,
452                                         h,
453                                         gdk_pixbuf_get_has_alpha(pr->pixbuf) ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888,
454                                         gdk_pixbuf_get_rowstride(pr->pixbuf),
455                                         gdk_pixbuf_get_pixels(rc->display_pixbuf));
456                 }
457         DEBUG_3("%s upload end", get_exec_time());
458         rc_area_clip_add(rc, par->x, par->y, par->w, h);
459
460
461         par->y += h;
462         par->h -= h;
463
464         if (par->h == 0)
465                 {
466                 rc->pending_updates = g_list_remove(rc->pending_updates, par);
467                 g_free(par);
468                 }
469         if (!rc->pending_updates)
470                 {
471                 clutter_actor_queue_redraw(CLUTTER_ACTOR(rc->texture));
472                 rc->idle_update = 0;
473
474                 /* FIXME: find a better place for this */
475                 if (!rc->clut_updated) rc_prepare_post_process_lut(rc);
476
477                 return FALSE;
478                 }
479
480         rc_schedule_texture_upload(rc);
481
482         return FALSE; /* it was rescheduled, possibly with different prio */
483 }
484
485
486 static void rc_area_changed(void *renderer, gint src_x, gint src_y, gint src_w, gint src_h)
487 {
488         RendererClutter *rc = (RendererClutter *)renderer;
489         PixbufRenderer *pr = rc->pr;
490         RendererClutterAreaParam *par;
491
492         gint width = gdk_pixbuf_get_width(pr->pixbuf);
493         gint height = gdk_pixbuf_get_height(pr->pixbuf);
494
495         if (pr->stereo_data == STEREO_PIXBUF_SBS || pr->stereo_data == STEREO_PIXBUF_CROSS)
496                         {
497                         width /= 2;
498                         }
499
500         if (!pr_clip_region(src_x, src_y, src_w, src_h,
501                                                 GET_RIGHT_PIXBUF_OFFSET(rc), 0, width, height,
502                                                 &src_x, &src_y, &src_w, &src_h)) return;
503
504         par = g_new0(RendererClutterAreaParam, 1);
505         par->rc = rc;
506         par->x = src_x - GET_RIGHT_PIXBUF_OFFSET(rc);
507         par->y = src_y;
508         par->w = src_w;
509         par->h = src_h;
510         rc->pending_updates = g_list_append(rc->pending_updates, par);
511         if (!rc->idle_update)
512                 {
513                 rc_schedule_texture_upload(rc);
514                 }
515 }
516
517 static void rc_remove_pending_updates(RendererClutter *rc)
518 {
519         if (rc->idle_update) g_idle_remove_by_data(rc);
520         rc->idle_update = 0;
521         while (rc->pending_updates)
522                 {
523                 RendererClutterAreaParam *par = rc->pending_updates->data;
524                 rc->pending_updates = g_list_remove(rc->pending_updates, par);
525                 g_free(par);
526                 }
527 }
528
529 static void update_display_pixbuf(RendererClutter *rc)
530 {
531         PixbufRenderer *pr = rc->pr;
532         GdkPixbuf* tmppixbuf = NULL;
533
534         if (pr->pixbuf)
535                 {
536                 if (rc->display_pixbuf)
537                         {
538                         g_object_unref(rc->display_pixbuf);
539                         }
540
541                 if (!gdk_pixbuf_get_has_alpha(pr->pixbuf))
542                         {
543                         rc->display_pixbuf = gdk_pixbuf_copy(pr->pixbuf);
544                         }
545                 else
546                         {
547                         if (pr->ignore_alpha)
548                                 {
549                                 tmppixbuf = gdk_pixbuf_add_alpha(pr->pixbuf, FALSE, 0, 0, 0);
550
551                                 pixbuf_ignore_alpha_rect(tmppixbuf, 0, 0, gdk_pixbuf_get_width(pr->pixbuf), gdk_pixbuf_get_height(pr->pixbuf));
552
553                                 rc->display_pixbuf = gdk_pixbuf_composite_color_simple (tmppixbuf,
554                                                 gdk_pixbuf_get_width(pr->pixbuf),
555                                                 gdk_pixbuf_get_height(pr->pixbuf),
556                                                 GDK_INTERP_HYPER,
557                                                 255,
558                                                 PR_ALPHA_CHECK_SIZE,
559                                                 ((options->image.alpha_color_1.red << 8 & 0x00FF0000) +
560                                                 (options->image.alpha_color_1.green & 0x00FF00) +
561                                                 (options->image.alpha_color_1.blue >> 8 & 0x00FF)),
562                                                 ((options->image.alpha_color_2.red << 8 & 0x00FF0000) +
563                                                 (options->image.alpha_color_2.green & 0x00FF00) +
564                                                 (options->image.alpha_color_2.blue >> 8 & 0x00FF)));
565                                 g_object_unref(tmppixbuf);
566                                 }
567                         else
568                                 {
569                                 rc->display_pixbuf = gdk_pixbuf_composite_color_simple (pr->pixbuf,
570                                                 gdk_pixbuf_get_width(pr->pixbuf),
571                                                 gdk_pixbuf_get_height(pr->pixbuf),
572                                                 GDK_INTERP_HYPER,
573                                                 255,
574                                                 PR_ALPHA_CHECK_SIZE,
575                                                 ((options->image.alpha_color_1.red << 8 & 0x00FF0000) +
576                                                 (options->image.alpha_color_1.green & 0x00FF00) +
577                                                 (options->image.alpha_color_1.blue >> 8 & 0x00FF)),
578                                                 ((options->image.alpha_color_2.red << 8 & 0x00FF0000) +
579                                                 (options->image.alpha_color_2.green & 0x00FF00) +
580                                                 (options->image.alpha_color_2.blue >> 8 & 0x00FF)));
581                                 }
582                         }
583                 }
584 }
585
586 static void rc_update_pixbuf(void *renderer, gboolean lazy)
587 {
588         RendererClutter *rc = (RendererClutter *)renderer;
589         PixbufRenderer *pr = rc->pr;
590
591         DEBUG_3("rc_update_pixbuf");
592
593         update_display_pixbuf(rc);
594         rc_remove_pending_updates(rc);
595
596         rc->last_pixbuf_change = g_get_monotonic_time();
597         DEBUG_3("%s change time reset", get_exec_time());
598
599         if (pr->pixbuf)
600                 {
601                 if (pr->func_post_process)
602                         {
603                         pr->func_post_process(pr, &rc->display_pixbuf, 0, 0, gdk_pixbuf_get_width(pr->pixbuf), gdk_pixbuf_get_height(pr->pixbuf), pr->post_process_user_data);
604                         }
605
606                         gint width = gdk_pixbuf_get_width(pr->pixbuf);
607                         gint height = gdk_pixbuf_get_height(pr->pixbuf);
608
609                         DEBUG_3("pixbuf size %d x %d (%d)", width, height, gdk_pixbuf_get_has_alpha(pr->pixbuf) ? 32 : 24);
610
611                         gint prev_width, prev_height;
612
613                         if (pr->stereo_data == STEREO_PIXBUF_SBS || pr->stereo_data == STEREO_PIXBUF_CROSS)
614                                 {
615                                 width /= 2;
616                                 }
617
618
619                         clutter_texture_get_base_size(CLUTTER_TEXTURE(rc->texture), &prev_width, &prev_height);
620
621                         if (width != prev_width || height != prev_height)
622                                 {
623                                 /* FIXME use CoglMaterial with multiple textures for background, color management, anaglyph, ... */
624                                 CoglHandle texture = cogl_texture_new_with_size(width,
625                                                                                 height,
626                                                                                 COGL_TEXTURE_NO_AUTO_MIPMAP,
627                                                                                 gdk_pixbuf_get_has_alpha(pr->pixbuf) ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888);
628
629                                 if (texture != COGL_INVALID_HANDLE)
630                                         {
631                                         clutter_texture_set_cogl_texture(CLUTTER_TEXTURE(rc->texture), texture);
632                                         cogl_handle_unref(texture);
633                                         }
634                                 }
635                         clutter_actor_set_clip(rc->texture, 0, 0, 0, 0); /* visible area is extended as area_changed events arrive */
636                         if (!lazy)
637                                 {
638                                 rc_area_changed(renderer, GET_RIGHT_PIXBUF_OFFSET(rc), 0, width, height);
639                                 }
640
641                 rc->clut_updated = FALSE;
642                 }
643 }
644
645
646
647 static void rc_update_zoom(void *renderer, gboolean lazy)
648 {
649         RendererClutter *rc = (RendererClutter *)renderer;
650
651         DEBUG_3("rc_update_zoom");
652         rc_sync_actor(rc);
653 }
654
655 static void rc_invalidate_region(void *renderer, gint x, gint y, gint w, gint h)
656 {
657 }
658
659 static OverlayData *rc_overlay_find(RendererClutter *rc, gint id)
660 {
661         GList *work;
662
663         work = rc->overlay_list;
664         while (work)
665                 {
666                 OverlayData *od = work->data;
667                 work = work->next;
668
669                 if (od->id == id) return od;
670                 }
671
672         return NULL;
673 }
674
675 static void rc_overlay_actor_destroy_cb(ClutterActor *actor, gpointer user_data)
676 {
677         OverlayData *od = user_data;
678         od->actor = NULL;
679 }
680
681 static void rc_overlay_free(RendererClutter *rc, OverlayData *od)
682 {
683         rc->overlay_list = g_list_remove(rc->overlay_list, od);
684
685         if (od->pixbuf) g_object_unref(G_OBJECT(od->pixbuf));
686         if (od->actor) clutter_actor_destroy(od->actor);
687         g_free(od);
688 }
689
690 static void rc_overlay_update_position(RendererClutter *rc, OverlayData *od)
691 {
692         gint px, py, pw, ph;
693
694         pw = gdk_pixbuf_get_width(od->pixbuf);
695         ph = gdk_pixbuf_get_height(od->pixbuf);
696         px = od->x;
697         py = od->y;
698
699         if (od->flags & OVL_RELATIVE)
700                 {
701                 if (px < 0) px = rc->pr->viewport_width - pw + px;
702                 if (py < 0) py = rc->pr->viewport_height - ph + py;
703                 }
704         if (od->actor) clutter_actor_set_position(od->actor, px, py);
705 }
706
707 static void rc_overlay_update_positions(RendererClutter *rc)
708 {
709         GList *work;
710
711         work = rc->overlay_list;
712         while (work)
713                 {
714                 OverlayData *od = work->data;
715                 work = work->next;
716
717                 rc_overlay_update_position(rc, od);
718                 }
719 }
720
721 static void rc_overlay_free_all(RendererClutter *rc)
722 {
723         GList *work;
724
725         work = rc->overlay_list;
726         while (work)
727                 {
728                 OverlayData *od = work->data;
729                 work = work->next;
730
731                 rc_overlay_free(rc, od);
732                 }
733 }
734
735 static gint rc_overlay_add(void *renderer, GdkPixbuf *pixbuf, gint x, gint y, OverlayRendererFlags flags)
736 {
737         RendererClutter *rc = (RendererClutter *)renderer;
738         PixbufRenderer *pr = rc->pr;
739         OverlayData *od;
740         gint id;
741
742         g_return_val_if_fail(IS_PIXBUF_RENDERER(pr), -1);
743         g_return_val_if_fail(pixbuf != NULL, -1);
744
745         id = 1;
746         while (rc_overlay_find(rc, id)) id++;
747
748         od = g_new0(OverlayData, 1);
749         od->id = id;
750         od->pixbuf = pixbuf;
751         g_object_ref(G_OBJECT(od->pixbuf));
752         od->x = x;
753         od->y = y;
754         od->flags = flags;
755
756         od->actor = gtk_clutter_texture_new();
757         g_signal_connect (od->actor, "destroy", G_CALLBACK(rc_overlay_actor_destroy_cb), od);
758
759         gtk_clutter_texture_set_from_pixbuf(GTK_CLUTTER_TEXTURE (od->actor), pixbuf, NULL);
760         clutter_container_add_actor(CLUTTER_CONTAINER(rc->group), od->actor);
761
762         rc->overlay_list = g_list_append(rc->overlay_list, od);
763         rc_overlay_update_position(rc, od);
764
765         return od->id;
766 }
767
768 static void rc_overlay_set(void *renderer, gint id, GdkPixbuf *pixbuf, gint x, gint y)
769 {
770         RendererClutter *rc = (RendererClutter *)renderer;
771         PixbufRenderer *pr = rc->pr;
772         OverlayData *od;
773
774         g_return_if_fail(IS_PIXBUF_RENDERER(pr));
775
776         od = rc_overlay_find(rc, id);
777         if (!od) return;
778
779         if (pixbuf)
780                 {
781                 g_object_ref(G_OBJECT(pixbuf));
782                 g_object_unref(G_OBJECT(od->pixbuf));
783                 od->pixbuf = pixbuf;
784
785                 od->x = x;
786                 od->y = y;
787
788                 if (od->actor) gtk_clutter_texture_set_from_pixbuf(GTK_CLUTTER_TEXTURE(od->actor), pixbuf, NULL);
789                 rc_overlay_update_position(rc, od);
790                 }
791         else
792                 {
793                 rc_overlay_free(rc, od);
794                 }
795 }
796
797 static gboolean rc_overlay_get(void *renderer, gint id, GdkPixbuf **pixbuf, gint *x, gint *y)
798 {
799         RendererClutter *rc = (RendererClutter *)renderer;
800
801         PixbufRenderer *pr = rc->pr;
802         OverlayData *od;
803
804         g_return_val_if_fail(IS_PIXBUF_RENDERER(pr), FALSE);
805
806         od = rc_overlay_find(rc, id);
807         if (!od) return FALSE;
808
809         if (pixbuf) *pixbuf = od->pixbuf;
810         if (x) *x = od->x;
811         if (y) *y = od->y;
812
813         return TRUE;
814 }
815
816
817 static void rc_update_viewport(void *renderer)
818 {
819         RendererClutter *rc = (RendererClutter *)renderer;
820         ClutterColor stage_color = { rc->pr->color.red / 256, rc->pr->color.green / 256, rc->pr->color.blue / 256, 0xff };
821
822         rc->stereo_off_x = 0;
823         rc->stereo_off_y = 0;
824
825         if (rc->stereo_mode & PR_STEREO_RIGHT)
826                 {
827                 if (rc->stereo_mode & PR_STEREO_HORIZ)
828                         {
829                         rc->stereo_off_x = rc->pr->viewport_width;
830                         }
831                 else if (rc->stereo_mode & PR_STEREO_VERT)
832                         {
833                         rc->stereo_off_y = rc->pr->viewport_height;
834                         }
835                 else if (rc->stereo_mode & PR_STEREO_FIXED)
836                         {
837                         rc->stereo_off_x = rc->pr->stereo_fixed_x_right;
838                         rc->stereo_off_y = rc->pr->stereo_fixed_y_right;
839                         }
840                 }
841         else
842                 {
843                 if (rc->stereo_mode & PR_STEREO_FIXED)
844                         {
845                         rc->stereo_off_x = rc->pr->stereo_fixed_x_left;
846                         rc->stereo_off_y = rc->pr->stereo_fixed_y_left;
847                         }
848                 }
849         DEBUG_3("rc_update_viewport  scale %d %d", rc->pr->width, rc->pr->height);
850
851         clutter_stage_set_color(CLUTTER_STAGE(rc->stage), &stage_color);
852
853
854         clutter_actor_set_size(rc->group, rc->pr->viewport_width, rc->pr->viewport_height);
855         clutter_actor_set_position(rc->group, rc->stereo_off_x, rc->stereo_off_y);
856
857         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->group),
858                                                 CLUTTER_Y_AXIS,
859                                                 (rc->stereo_mode & PR_STEREO_MIRROR) ? 180 : 0,
860                                                 rc->pr->viewport_width / 2.0, 0, 0);
861
862         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->group),
863                                                 CLUTTER_X_AXIS,
864                                                 (rc->stereo_mode & PR_STEREO_FLIP) ? 180 : 0,
865                                                 0, rc->pr->viewport_height / 2.0, 0);
866
867         rc_sync_actor(rc);
868         rc_overlay_update_positions(rc);
869 }
870
871 static void rc_scroll(void *renderer, gint x_off, gint y_off)
872 {
873         DEBUG_3("rc_scroll");
874         RendererClutter *rc = (RendererClutter *)renderer;
875
876         rc_sync_actor(rc);
877 }
878
879 static void rc_stereo_set(void *renderer, gint stereo_mode)
880 {
881         RendererClutter *rc = (RendererClutter *)renderer;
882
883         rc->stereo_mode = stereo_mode;
884 }
885
886 static void rc_free(void *renderer)
887 {
888         RendererClutter *rc = (RendererClutter *)renderer;
889         GtkWidget *widget = gtk_bin_get_child(GTK_BIN(rc->pr));
890
891         if (rc->display_pixbuf)
892                 {
893                 g_object_unref(rc->display_pixbuf);
894                 }
895
896         rc_remove_pending_updates(rc);
897
898         rc_overlay_free_all(rc);
899
900         if (widget)
901                 {
902                 /* widget still exists */
903                 clutter_actor_destroy(rc->group);
904                 if (clutter_group_get_n_children(CLUTTER_GROUP(rc->stage)) == 0)
905                         {
906                         DEBUG_1("destroy %p", rc->widget);
907                         /* this was the last user */
908                         gtk_widget_destroy(rc->widget);
909                         }
910                 else
911                         {
912                         DEBUG_1("keep %p", rc->widget);
913                         g_object_unref(G_OBJECT(rc->widget));
914                         }
915                 }
916         g_free(rc);
917 }
918
919 /* initialize shader for transparency background checker */
920 static void renderer_clutter_init_checker_shader(RendererClutter *rc)
921 {
922         const RendererClutterShaderInfo info = {
923                 16.0,                           /* checker size */
924                 {0.6, 0.6, 0.6},        /* color 0 */
925                 {0.4, 0.4, 0.4}         /* color 1 */
926         };
927         rc_set_shader(clutter_texture_get_cogl_material(CLUTTER_TEXTURE(rc->texture)), &info);
928 }
929
930 RendererFuncs *renderer_clutter_new(PixbufRenderer *pr)
931 {
932         RendererClutter *rc = g_new0(RendererClutter, 1);
933
934         rc->pr = pr;
935
936         rc->f.area_changed = rc_area_changed;
937         rc->f.update_pixbuf = rc_update_pixbuf;
938         rc->f.free = rc_free;
939         rc->f.update_zoom = rc_update_zoom;
940         rc->f.invalidate_region = rc_invalidate_region;
941         rc->f.scroll = rc_scroll;
942         rc->f.update_viewport = rc_update_viewport;
943
944
945         rc->f.overlay_add = rc_overlay_add;
946         rc->f.overlay_set = rc_overlay_set;
947         rc->f.overlay_get = rc_overlay_get;
948
949         rc->f.stereo_set = rc_stereo_set;
950
951
952         rc->stereo_mode = 0;
953         rc->stereo_off_x = 0;
954         rc->stereo_off_y = 0;
955
956         rc->idle_update = 0;
957         rc->pending_updates = NULL;
958
959         rc->display_pixbuf = NULL;
960
961         rc->widget = gtk_bin_get_child(GTK_BIN(rc->pr));
962
963         if (rc->widget)
964                 {
965                 if (!GTK_CLUTTER_IS_EMBED(rc->widget))
966                         {
967                         g_free(rc);
968                         DEBUG_3("pixbuf renderer has a child of other type than gtk_clutter_embed");
969                         return NULL;
970                         }
971                 }
972         else
973                 {
974                 rc->widget = gtk_clutter_embed_new();
975                 gtk_container_add(GTK_CONTAINER(rc->pr), rc->widget);
976                 }
977
978         gtk_event_box_set_above_child (GTK_EVENT_BOX(rc->pr), TRUE);
979         rc->stage = gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED (rc->widget));
980
981         rc->group = clutter_group_new();
982         clutter_container_add_actor(CLUTTER_CONTAINER(rc->stage), rc->group);
983         clutter_actor_set_clip_to_allocation(CLUTTER_ACTOR(rc->group), TRUE);
984
985         rc->texture = clutter_texture_new ();
986         clutter_container_add_actor(CLUTTER_CONTAINER(rc->group), rc->texture);
987
988         //~ renderer_clutter_init_checker_shader(rc);  //FIXME
989         g_object_ref(G_OBJECT(rc->widget));
990
991         gtk_widget_show(rc->widget);
992         return (RendererFuncs *) rc;
993 }
994
995 #endif
996 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */