d3d8ee69fdb8bb2ede7cebf28b0c2d2f9224d5f6
[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
431     float width, height;
432     clutter_actor_get_size(actor, &width, &height);
433     clutter_actor_set_anchor_point(actor, width * priv->anchorX, height * priv->anchorY);
434 }
435
436 void graphicsLayerActorGetAnchorPoint(GraphicsLayerActor* layer, float* x, float* y, float* z)
437 {
438     GraphicsLayerActorPrivate* priv = layer->priv;
439     if (x)
440         *x = priv->anchorX;
441
442     if (y)
443         *y = priv->anchorY;
444
445     if (z)
446         *z = priv->anchorZ;
447 }
448
449 void graphicsLayerActorSetScrollPosition(GraphicsLayerActor* layer, float x, float y)
450 {
451     GraphicsLayerActorPrivate* priv = layer->priv;
452
453     if (x > 0 || y > 0)
454         return;
455
456     priv->scrollX = x;
457     priv->scrollY = y;
458
459     clutter_actor_queue_redraw(CLUTTER_ACTOR(layer));
460 }
461
462 gint graphicsLayerActorGetnChildren(GraphicsLayerActor* layer)
463 {
464     ASSERT(GRAPHICS_LAYER_IS_ACTOR(layer));
465
466     return g_list_length(layer->children);
467 }
468
469 void graphicsLayerActorReplaceSublayer(GraphicsLayerActor* layer, ClutterActor* oldChildLayer, ClutterActor* newChildLayer)
470 {
471     ASSERT(GRAPHICS_LAYER_IS_ACTOR(layer));
472     ASSERT(CLUTTER_IS_ACTOR(oldChildLayer));
473     ASSERT(CLUTTER_IS_ACTOR(newChildLayer));
474
475     clutter_actor_remove_child(CLUTTER_ACTOR(layer), oldChildLayer);
476     clutter_actor_add_child(CLUTTER_ACTOR(layer), newChildLayer);
477 }
478
479 void graphicsLayerActorInsertSublayer(GraphicsLayerActor* layer, ClutterActor* childLayer, gint index)
480 {
481     ASSERT(GRAPHICS_LAYER_IS_ACTOR(layer));
482     ASSERT(CLUTTER_IS_ACTOR(childLayer));
483
484     g_object_ref(childLayer);
485
486     layer->children = g_list_insert(layer->children, childLayer, index);
487     ASSERT(!clutter_actor_get_parent(childLayer));
488     clutter_actor_add_child(CLUTTER_ACTOR(layer), childLayer);
489     clutter_actor_queue_relayout(CLUTTER_ACTOR(layer));
490
491     g_object_unref(childLayer);
492 }
493
494 void graphicsLayerActorSetSublayers(GraphicsLayerActor* layer, GraphicsLayerActorList& subLayers)
495 {
496     if (!subLayers.size()) {
497         graphicsLayerActorRemoveAll(layer);
498         return;
499     }
500
501     for (size_t i = 0; i < subLayers.size(); ++i) {
502         ClutterActor* layerActor = CLUTTER_ACTOR(subLayers[i].get());
503         clutter_actor_add_child(CLUTTER_ACTOR(layer), layerActor);
504     }
505 }
506
507 GraphicsLayerClutter::LayerType graphicsLayerActorGetLayerType(GraphicsLayerActor* layer)
508 {
509     GraphicsLayerActorPrivate* priv = layer->priv;
510     return priv->layerType;
511 }
512
513 void graphicsLayerActorSetLayerType(GraphicsLayerActor* layer, GraphicsLayerClutter::LayerType layerType)
514 {
515     GraphicsLayerActorPrivate* priv = layer->priv;
516     priv->layerType = layerType;
517 }
518
519 void graphicsLayerActorSetTranslateX(GraphicsLayerActor* layer, float value)
520 {
521     GraphicsLayerActorPrivate* priv = layer->priv;
522     priv->translateX = value;
523     clutter_actor_queue_redraw(CLUTTER_ACTOR(layer));
524 }
525
526 float graphicsLayerActorGetTranslateX(GraphicsLayerActor* layer)
527 {
528     GraphicsLayerActorPrivate* priv = layer->priv;
529     return priv->translateX;
530 }
531
532 void graphicsLayerActorSetTranslateY(GraphicsLayerActor* layer, float value)
533 {
534     GraphicsLayerActorPrivate* priv = layer->priv;
535     priv->translateY = value;
536     clutter_actor_queue_redraw(CLUTTER_ACTOR(layer));
537 }
538
539 float graphicsLayerActorGetTranslateY(GraphicsLayerActor* layer)
540 {
541     GraphicsLayerActorPrivate* priv = layer->priv;
542     return priv->translateY;
543 }
544
545 void graphicsLayerActorSetDrawsContent(GraphicsLayerActor* layer, gboolean drawsContent)
546 {
547     GraphicsLayerActorPrivate* priv = layer->priv;
548
549     if (drawsContent == priv->drawsContent)
550         return;
551
552     priv->drawsContent = drawsContent;
553
554     graphicsLayerActorUpdateTexture(layer);
555 }
556
557 gboolean graphicsLayerActorGetDrawsContent(GraphicsLayerActor* layer)
558 {
559     return layer->priv->drawsContent;
560 }
561
562 WebCore::PlatformClutterAnimation* graphicsLayerActorGetAnimationForKey(GraphicsLayerActor* layer, const String key)
563 {
564     return static_cast<WebCore::PlatformClutterAnimation*>(g_object_get_data(G_OBJECT(layer), key.utf8().data()));
565 }
566
567 #endif // USE(ACCELERATED_COMPOSITING)