[GTK][AC] Clutter required version up to 1.12
[WebKit-https.git] / Source / WebCore / platform / graphics / clutter / GraphicsLayerActor.cpp
1 /*
2  * Copyright 2011, 2012 Collabora Limited
3  * Copyright (C) 2012 Intel Corporation. All rights reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms and conditions of the GNU Lesser General Public License,
7  * version 2.1, as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope it will be useful, but WITHOUT ANY
10  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17  *
18  */
19
20 #include "config.h"
21
22 #if USE(ACCELERATED_COMPOSITING)
23
24 #include "GraphicsLayerActor.h"
25
26 #include "GraphicsContext.h"
27 #include "GraphicsLayerClutter.h"
28 #include "PlatformClutterLayerClient.h"
29 #include "PlatformContextCairo.h"
30 #include "RefPtrCairo.h"
31 #include <wtf/text/CString.h>
32
33 using namespace WebCore;
34
35 G_DEFINE_TYPE(GraphicsLayerActor, graphics_layer_actor, CLUTTER_TYPE_ACTOR)
36
37 #define GRAPHICS_LAYER_ACTOR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), GRAPHICS_LAYER_TYPE_ACTOR, GraphicsLayerActorPrivate))
38
39 struct _GraphicsLayerActorPrivate {
40     GraphicsLayerClutter::LayerType layerType;
41     gboolean allocating;
42
43     RefPtr<cairo_surface_t> surface;
44     CoglMatrix* matrix;
45
46     PlatformClutterLayerClient* layerClient;
47
48     gboolean drawsContent;
49
50     float anchorX;
51     float anchorY;
52     float anchorZ;
53
54     float scrollX;
55     float scrollY;
56
57     float translateX;
58     float translateY;
59 };
60
61 enum {
62     Property0,
63
64     PropertyTranslateX,
65     PropertyTranslateY,
66
67     PropertyLast
68 };
69
70 static void graphicsLayerActorAllocate(ClutterActor*, const ClutterActorBox*, ClutterAllocationFlags);
71 static void graphicsLayerActorApplyTransform(ClutterActor*, CoglMatrix*);
72 static void graphicsLayerActorDispose(GObject*);
73 static void graphicsLayerActorGetProperty(GObject*, guint propID, GValue*, GParamSpec*);
74 static void graphicsLayerActorSetProperty(GObject*, guint propID, const GValue*, GParamSpec*);
75 static void graphicsLayerActorPaint(ClutterActor*);
76
77 static void graphicsLayerActorAdded(ClutterContainer*, ClutterActor*, gpointer data);
78 static void graphicsLayerActorRemoved(ClutterContainer*, ClutterActor*, gpointer data);
79 static gboolean graphicsLayerActorDraw(ClutterCanvas*, cairo_t*, gint width, gint height, GraphicsLayerActor*);
80 static void graphicsLayerActorUpdateTexture(GraphicsLayerActor*);
81 static void drawLayerContents(ClutterActor*, GraphicsContext&);
82
83 static void graphics_layer_actor_class_init(GraphicsLayerActorClass* klass)
84 {
85     GObjectClass* objectClass = G_OBJECT_CLASS(klass);
86     ClutterActorClass* actorClass = CLUTTER_ACTOR_CLASS(klass);
87
88     objectClass->get_property = graphicsLayerActorGetProperty;
89     objectClass->set_property = graphicsLayerActorSetProperty;
90     objectClass->dispose = graphicsLayerActorDispose;
91     actorClass->apply_transform = graphicsLayerActorApplyTransform;
92     actorClass->allocate = graphicsLayerActorAllocate;
93     actorClass->paint = graphicsLayerActorPaint;
94
95     g_type_class_add_private(klass, sizeof(GraphicsLayerActorPrivate));
96
97     GParamSpec* pspec = g_param_spec_float("translate-x", "Translate X", "Translation value for the X axis", -G_MAXFLOAT, G_MAXFLOAT, 0.0, static_cast<GParamFlags>(G_PARAM_READWRITE));
98     g_object_class_install_property(objectClass, PropertyTranslateX, pspec);
99
100     pspec = g_param_spec_float("translate-y", "Translate Y", "Translation value for the Y ayis", -G_MAXFLOAT, G_MAXFLOAT, 0.0, static_cast<GParamFlags>(G_PARAM_READWRITE));
101     g_object_class_install_property(objectClass, PropertyTranslateY, pspec);
102 }
103
104 static void graphics_layer_actor_init(GraphicsLayerActor* self)
105 {
106     self->priv = GRAPHICS_LAYER_ACTOR_GET_PRIVATE(self);
107
108     clutter_actor_set_reactive(CLUTTER_ACTOR(self), FALSE);
109
110     // Default used by GraphicsLayer.
111     graphicsLayerActorSetAnchorPoint(self, 0.5, 0.5, 0.0);
112
113     g_signal_connect(self, "actor-added", G_CALLBACK(graphicsLayerActorAdded), 0);
114     g_signal_connect(self, "actor-removed", G_CALLBACK(graphicsLayerActorRemoved), 0);
115 }
116
117 static void graphicsLayerActorSetProperty(GObject* object, guint propID, const GValue* value, GParamSpec* pspec)
118 {
119     GraphicsLayerActor* layer = GRAPHICS_LAYER_ACTOR(object);
120
121     switch (propID) {
122     case PropertyTranslateX:
123         graphicsLayerActorSetTranslateX(layer, g_value_get_float(value));
124         break;
125     case PropertyTranslateY:
126         graphicsLayerActorSetTranslateY(layer, g_value_get_float(value));
127         break;
128     default:
129         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, pspec);
130     }
131 }
132
133 static void graphicsLayerActorGetProperty(GObject* object, guint propID, GValue* value, GParamSpec* pspec)
134 {
135     GraphicsLayerActor* layer = GRAPHICS_LAYER_ACTOR(object);
136
137     switch (propID) {
138     case PropertyTranslateX:
139         g_value_set_float(value, graphicsLayerActorGetTranslateX(layer));
140         break;
141     case PropertyTranslateY:
142         g_value_set_float(value, graphicsLayerActorGetTranslateY(layer));
143         break;
144     default:
145         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, pspec);
146     }
147 }
148
149
150 static void graphicsLayerActorDispose(GObject* object)
151 {
152     GraphicsLayerActor* layer = GRAPHICS_LAYER_ACTOR(object);
153     GraphicsLayerActorPrivate* priv = layer->priv;
154
155     priv->surface.clear();
156
157     if (priv->matrix)
158         cogl_matrix_free(priv->matrix);
159
160     G_OBJECT_CLASS(graphics_layer_actor_parent_class)->dispose(object);
161 }
162
163 // Copied from cairo.
164 #define MAX_IMAGE_SIZE 32767
165
166 static void graphicsLayerActorAllocate(ClutterActor* self, const ClutterActorBox* box, ClutterAllocationFlags flags)
167 {
168     GraphicsLayerActor* layer = GRAPHICS_LAYER_ACTOR(self);
169     GraphicsLayerActorPrivate* priv = layer->priv;
170
171     priv->allocating = TRUE;
172
173     CLUTTER_ACTOR_CLASS(graphics_layer_actor_parent_class)->allocate(self, box, flags);
174
175     ClutterContent* canvas = clutter_actor_get_content(self);
176     if (canvas)
177         clutter_canvas_set_size(CLUTTER_CANVAS(canvas), clutter_actor_get_width(self), clutter_actor_get_height(self));
178
179     // FIXME: maybe we can cache children allocation and not call
180     // allocate on them this often?
181     for (GList* list = layer->children; list; list = list->next) {
182         ClutterActor* child = CLUTTER_ACTOR(list->data);
183
184         float childWidth = clutter_actor_get_width(child);
185         float childHeight = clutter_actor_get_height(child);
186
187         ClutterActorBox childBox;
188         childBox.x1 = clutter_actor_get_x(child);
189         childBox.y1 = clutter_actor_get_y(child);
190         childBox.x2 = childBox.x1 + childWidth;
191         childBox.y2 = childBox.y1 + childHeight;
192
193         clutter_actor_allocate(child, &childBox, flags);
194     }
195
196     priv->allocating = FALSE;
197 }
198
199 static void graphicsLayerActorApplyTransform(ClutterActor* actor, CoglMatrix* matrix)
200 {
201     GraphicsLayerActorPrivate* priv = GRAPHICS_LAYER_ACTOR(actor)->priv;
202
203     // Apply translation and scrolling as a single translation. These
204     // need to come before anything else, otherwise they'll be
205     // affected by other operations such as scaling, which is not what
206     // we want.
207     float translateX = priv->scrollX + priv->translateX;
208     float translateY = priv->scrollY + priv->translateY;
209
210     if (translateX || translateY)
211         cogl_matrix_translate(matrix, translateX, translateY, 0);
212
213     CLUTTER_ACTOR_CLASS(graphics_layer_actor_parent_class)->apply_transform(actor, matrix);
214
215     float width = clutter_actor_get_width(actor);
216     float height = clutter_actor_get_height(actor);
217     if (width <= 1.0 || height <= 1.0)
218         return;
219
220     float pivotX, pivotY;
221     pivotX = width * priv->anchorX;
222     pivotY = height * priv->anchorY;
223
224     if (priv->matrix) {
225         CoglMatrix* localMatrix;
226         // CSS3 tranform-style can be changed on the fly, 
227         // so we have to copy priv->matrix in order to recover z-axis. 
228         localMatrix = cogl_matrix_copy(priv->matrix);
229
230         cogl_matrix_translate(matrix, pivotX, pivotY, priv->anchorZ);
231         cogl_matrix_multiply(matrix, matrix, localMatrix);
232         cogl_matrix_translate(matrix, -pivotX, -pivotY, -priv->anchorZ);
233         cogl_matrix_free(localMatrix);
234     }
235 }
236
237 static void graphicsLayerActorPaint(ClutterActor* actor)
238 {
239     GraphicsLayerActor* graphicsLayer = GRAPHICS_LAYER_ACTOR(actor);
240
241     GList* list;
242     for (list = graphicsLayer->children; list; list = list->next) {
243         ClutterActor* child = CLUTTER_ACTOR(list->data);
244         clutter_actor_paint(child);
245     }
246 }
247
248 static gboolean graphicsLayerActorDraw(ClutterCanvas* texture, cairo_t* cr, gint width, gint height, GraphicsLayerActor* layer)
249 {
250     ClutterActor* actor = CLUTTER_ACTOR(layer);
251
252     if (!width || !height)
253         return FALSE;
254
255     GraphicsLayerActorPrivate* priv = layer->priv;
256     GraphicsContext context(cr);
257     context.clearRect(FloatRect(0, 0, width, height));
258
259     if (priv->surface) {
260         gint surfaceWidth = cairo_image_surface_get_width(priv->surface.get());
261         gint surfaceHeight = cairo_image_surface_get_height(priv->surface.get());
262
263         FloatRect srcRect(0.0, 0.0, static_cast<float>(surfaceWidth), static_cast<float>(surfaceHeight));
264         FloatRect destRect(0.0, 0.0, width, height);
265         context.platformContext()->drawSurfaceToContext(priv->surface.get(), destRect, srcRect, &context);
266     }
267
268     if (priv->layerType == GraphicsLayerClutter::LayerTypeWebLayer)
269         drawLayerContents(actor, context);
270
271     return TRUE;
272 }
273
274 static void graphicsLayerActorAdded(ClutterContainer* container, ClutterActor* actor, gpointer data)
275 {
276     GraphicsLayerActor* graphicsLayer = GRAPHICS_LAYER_ACTOR(container);
277     graphicsLayer->children = g_list_append(graphicsLayer->children, actor);
278 }
279
280 static void graphicsLayerActorRemoved(ClutterContainer* container, ClutterActor* actor, gpointer data)
281 {
282     GraphicsLayerActor* graphicsLayer = GRAPHICS_LAYER_ACTOR(container);
283     graphicsLayer->children = g_list_remove(graphicsLayer->children, actor);
284 }
285
286 static void graphicsLayerActorUpdateTexture(GraphicsLayerActor* layer)
287 {
288     GraphicsLayerActorPrivate* priv = layer->priv;
289     ASSERT(priv->layerType != GraphicsLayerClutter::LayerTypeVideoLayer);
290     ClutterContent* canvas;
291     canvas = clutter_actor_get_content(CLUTTER_ACTOR(layer));
292
293     // Nothing needs a texture, remove the one we have, if any.
294     if (!priv->drawsContent && !priv->surface) {
295         if (!canvas)
296             return;
297
298         g_signal_handlers_disconnect_by_func(canvas, reinterpret_cast<void*>(graphicsLayerActorDraw), layer);
299         g_object_unref(canvas);
300         return;
301     }
302
303     // We need a texture, but already have one!
304     if (canvas)
305         return;
306
307     // We need a texture, so create it.
308     ClutterActor* actor = CLUTTER_ACTOR(layer);
309     int width = ceilf(clutter_actor_get_width(actor));
310     int height = ceilf(clutter_actor_get_height(actor));
311
312     canvas = clutter_canvas_new();
313     clutter_actor_set_content(actor, canvas);
314     clutter_canvas_set_size(CLUTTER_CANVAS(canvas), width > 0 ? width : 1, height > 0 ? height : 1);
315     g_object_unref(canvas);
316     
317     g_signal_connect(canvas, "draw", G_CALLBACK(graphicsLayerActorDraw), layer);
318 }
319
320 // Draw content into the layer.
321 static void drawLayerContents(ClutterActor* actor, GraphicsContext& context)
322 {
323     GraphicsLayerActorPrivate* priv = GRAPHICS_LAYER_ACTOR(actor)->priv;
324
325     if (!priv->drawsContent || !priv->layerClient)
326         return;
327
328     float width = clutter_actor_get_width(actor);
329     float height = clutter_actor_get_height(actor);
330     IntRect clip(0, 0, width, height);
331
332     // Apply the painted content to the layer.
333     priv->layerClient->platformClutterLayerPaintContents(context, clip);
334 }
335
336
337 GraphicsLayerActor* graphicsLayerActorNew(GraphicsLayerClutter::LayerType type)
338 {
339     GraphicsLayerActor* layer = GRAPHICS_LAYER_ACTOR(g_object_new(GRAPHICS_LAYER_TYPE_ACTOR, 0));
340     GraphicsLayerActorPrivate* priv = layer->priv;
341
342     priv->layerType = type;
343
344     return layer;
345 }
346
347 GraphicsLayerActor* graphicsLayerActorNewWithClient(GraphicsLayerClutter::LayerType type, PlatformClutterLayerClient* layerClient)
348 {
349     GraphicsLayerActor* layer = graphicsLayerActorNew(type);
350     graphicsLayerActorSetClient(layer, layerClient);
351
352     return layer;
353 }
354
355 void graphicsLayerActorSetClient(GraphicsLayerActor* layer, PlatformClutterLayerClient* client)
356 {
357     layer->priv->layerClient = client;
358 }
359
360 PlatformClutterLayerClient* graphicsLayerActorGetClient(GraphicsLayerActor* layer)
361 {
362     return layer->priv->layerClient;
363 }
364
365 void graphicsLayerActorRemoveAll(GraphicsLayerActor* layer)
366 {
367     g_return_if_fail(GRAPHICS_LAYER_IS_ACTOR(layer));
368
369     GList* children = clutter_actor_get_children(CLUTTER_ACTOR(layer));
370     for (; children; children = children->next)
371         clutter_actor_remove_child(CLUTTER_ACTOR(layer), CLUTTER_ACTOR(children->data));
372 }
373
374 cairo_surface_t* graphicsLayerActorGetSurface(GraphicsLayerActor* layer)
375 {
376     GraphicsLayerActorPrivate* priv = layer->priv;
377     ASSERT(priv->surface);
378     return priv->surface.get();
379 }
380
381 void graphicsLayerActorSetSurface(GraphicsLayerActor* layer, cairo_surface_t* surface)
382 {
383     GraphicsLayerActorPrivate* priv = layer->priv;
384     priv->surface = surface;
385     graphicsLayerActorUpdateTexture(layer);
386 }
387
388 void graphicsLayerActorInvalidateRectangle(GraphicsLayerActor* layer, const FloatRect& dirtyRect)
389 {
390     ClutterContent* canvas;
391     canvas = clutter_actor_get_content(CLUTTER_ACTOR(layer));
392     if (!canvas)
393         return;
394
395     // FIXME: Need to invalidate a specific area?
396     clutter_content_invalidate(canvas);
397 }
398
399 void graphicsLayerActorSetTransform(GraphicsLayerActor* layer, const CoglMatrix* matrix) 
400 {
401     GraphicsLayerActorPrivate* priv = layer->priv;
402
403     if (priv->matrix) {
404         cogl_matrix_free(priv->matrix);
405         priv->matrix = 0;
406         clutter_actor_queue_redraw(CLUTTER_ACTOR(layer));
407     }
408
409     CoglMatrix identity;
410     cogl_matrix_init_identity(&identity);
411     if (cogl_matrix_equal((CoglMatrix*)&identity, (CoglMatrix*)matrix))
412         return;
413
414     if (priv->matrix)
415         cogl_matrix_free(priv->matrix);
416
417     priv->matrix = cogl_matrix_copy(matrix);
418     clutter_actor_queue_redraw(CLUTTER_ACTOR(layer));
419 }
420
421 void graphicsLayerActorSetAnchorPoint(GraphicsLayerActor* layer, float x, float y, float z)
422 {
423     GraphicsLayerActorPrivate* priv = layer->priv;
424
425     priv->anchorX = x;
426     priv->anchorY = y;
427     priv->anchorZ = z;
428
429     ClutterActor* actor = CLUTTER_ACTOR(layer);
430     clutter_actor_set_pivot_point(actor, x, y);
431     clutter_actor_set_pivot_point_z(actor, z);
432 }
433
434 void graphicsLayerActorGetAnchorPoint(GraphicsLayerActor* layer, float* x, float* y, float* z)
435 {
436     GraphicsLayerActorPrivate* priv = layer->priv;
437     if (x)
438         *x = priv->anchorX;
439
440     if (y)
441         *y = priv->anchorY;
442
443     if (z)
444         *z = priv->anchorZ;
445 }
446
447 void graphicsLayerActorSetScrollPosition(GraphicsLayerActor* layer, float x, float y)
448 {
449     GraphicsLayerActorPrivate* priv = layer->priv;
450
451     if (x > 0 || y > 0)
452         return;
453
454     priv->scrollX = x;
455     priv->scrollY = y;
456
457     clutter_actor_queue_redraw(CLUTTER_ACTOR(layer));
458 }
459
460 gint graphicsLayerActorGetnChildren(GraphicsLayerActor* layer)
461 {
462     ASSERT(GRAPHICS_LAYER_IS_ACTOR(layer));
463
464     return g_list_length(layer->children);
465 }
466
467 void graphicsLayerActorReplaceSublayer(GraphicsLayerActor* layer, ClutterActor* oldChildLayer, ClutterActor* newChildLayer)
468 {
469     ASSERT(GRAPHICS_LAYER_IS_ACTOR(layer));
470     ASSERT(CLUTTER_IS_ACTOR(oldChildLayer));
471     ASSERT(CLUTTER_IS_ACTOR(newChildLayer));
472
473     clutter_actor_remove_child(CLUTTER_ACTOR(layer), oldChildLayer);
474     clutter_actor_add_child(CLUTTER_ACTOR(layer), newChildLayer);
475 }
476
477 void graphicsLayerActorInsertSublayer(GraphicsLayerActor* layer, ClutterActor* childLayer, gint index)
478 {
479     ASSERT(GRAPHICS_LAYER_IS_ACTOR(layer));
480     ASSERT(CLUTTER_IS_ACTOR(childLayer));
481
482     g_object_ref(childLayer);
483
484     layer->children = g_list_insert(layer->children, childLayer, index);
485     ASSERT(!clutter_actor_get_parent(childLayer));
486     clutter_actor_add_child(CLUTTER_ACTOR(layer), childLayer);
487     clutter_actor_queue_relayout(CLUTTER_ACTOR(layer));
488
489     g_object_unref(childLayer);
490 }
491
492 void graphicsLayerActorSetSublayers(GraphicsLayerActor* layer, GraphicsLayerActorList& subLayers)
493 {
494     if (!subLayers.size()) {
495         graphicsLayerActorRemoveAll(layer);
496         return;
497     }
498
499     for (size_t i = 0; i < subLayers.size(); ++i) {
500         ClutterActor* layerActor = CLUTTER_ACTOR(subLayers[i].get());
501         clutter_actor_add_child(CLUTTER_ACTOR(layer), layerActor);
502     }
503 }
504
505 GraphicsLayerClutter::LayerType graphicsLayerActorGetLayerType(GraphicsLayerActor* layer)
506 {
507     GraphicsLayerActorPrivate* priv = layer->priv;
508     return priv->layerType;
509 }
510
511 void graphicsLayerActorSetLayerType(GraphicsLayerActor* layer, GraphicsLayerClutter::LayerType layerType)
512 {
513     GraphicsLayerActorPrivate* priv = layer->priv;
514     priv->layerType = layerType;
515 }
516
517 void graphicsLayerActorSetTranslateX(GraphicsLayerActor* layer, float value)
518 {
519     GraphicsLayerActorPrivate* priv = layer->priv;
520     priv->translateX = value;
521     clutter_actor_queue_redraw(CLUTTER_ACTOR(layer));
522 }
523
524 float graphicsLayerActorGetTranslateX(GraphicsLayerActor* layer)
525 {
526     GraphicsLayerActorPrivate* priv = layer->priv;
527     return priv->translateX;
528 }
529
530 void graphicsLayerActorSetTranslateY(GraphicsLayerActor* layer, float value)
531 {
532     GraphicsLayerActorPrivate* priv = layer->priv;
533     priv->translateY = value;
534     clutter_actor_queue_redraw(CLUTTER_ACTOR(layer));
535 }
536
537 float graphicsLayerActorGetTranslateY(GraphicsLayerActor* layer)
538 {
539     GraphicsLayerActorPrivate* priv = layer->priv;
540     return priv->translateY;
541 }
542
543 void graphicsLayerActorSetDrawsContent(GraphicsLayerActor* layer, gboolean drawsContent)
544 {
545     GraphicsLayerActorPrivate* priv = layer->priv;
546
547     if (drawsContent == priv->drawsContent)
548         return;
549
550     priv->drawsContent = drawsContent;
551
552     graphicsLayerActorUpdateTexture(layer);
553 }
554
555 gboolean graphicsLayerActorGetDrawsContent(GraphicsLayerActor* layer)
556 {
557     return layer->priv->drawsContent;
558 }
559
560 WebCore::PlatformClutterAnimation* graphicsLayerActorGetAnimationForKey(GraphicsLayerActor* layer, const String key)
561 {
562     return static_cast<WebCore::PlatformClutterAnimation*>(g_object_get_data(G_OBJECT(layer), key.utf8().data()));
563 }
564
565 #endif // USE(ACCELERATED_COMPOSITING)