1/* 2 * Copyright 2011, 2012, 2013 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 "TransformationMatrix.h" 32#include <algorithm> 33#include <wtf/gobject/GOwnPtr.h> 34#include <wtf/text/CString.h> 35 36using namespace WebCore; 37 38G_DEFINE_TYPE(GraphicsLayerActor, graphics_layer_actor, CLUTTER_TYPE_ACTOR) 39 40#define GRAPHICS_LAYER_ACTOR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), GRAPHICS_LAYER_TYPE_ACTOR, GraphicsLayerActorPrivate)) 41 42struct _GraphicsLayerActorPrivate { 43 PlatformClutterLayerClient* layerClient; 44 RefPtr<cairo_surface_t> surface; 45 46 GraphicsLayerClutter::LayerType layerType; 47 48 bool flatten; 49 bool drawsContent; 50 51 float scrollX; 52 float scrollY; 53 54 float translateX; 55 float translateY; 56}; 57 58enum { 59 Property0, 60 61 PropertyTranslateX, 62 PropertyTranslateY, 63 64 PropertyLast 65}; 66 67static void graphicsLayerActorAllocate(ClutterActor*, const ClutterActorBox*, ClutterAllocationFlags); 68static void graphicsLayerActorApplyTransform(ClutterActor*, CoglMatrix*); 69static void graphicsLayerActorDispose(GObject*); 70static void graphicsLayerActorGetProperty(GObject*, guint propID, GValue*, GParamSpec*); 71static void graphicsLayerActorSetProperty(GObject*, guint propID, const GValue*, GParamSpec*); 72static void graphicsLayerActorPaint(ClutterActor*); 73 74static gboolean graphicsLayerActorDraw(ClutterCanvas*, cairo_t*, gint width, gint height, GraphicsLayerActor*); 75static void graphicsLayerActorUpdateTexture(GraphicsLayerActor*); 76static void drawLayerContents(ClutterActor*, GraphicsContext&); 77 78static void graphics_layer_actor_class_init(GraphicsLayerActorClass* klass) 79{ 80 GObjectClass* objectClass = G_OBJECT_CLASS(klass); 81 ClutterActorClass* actorClass = CLUTTER_ACTOR_CLASS(klass); 82 83 objectClass->get_property = graphicsLayerActorGetProperty; 84 objectClass->set_property = graphicsLayerActorSetProperty; 85 objectClass->dispose = graphicsLayerActorDispose; 86 87 actorClass->allocate = graphicsLayerActorAllocate; 88 actorClass->apply_transform = graphicsLayerActorApplyTransform; 89 actorClass->paint = graphicsLayerActorPaint; 90 91 g_type_class_add_private(klass, sizeof(GraphicsLayerActorPrivate)); 92 93 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)); 94 g_object_class_install_property(objectClass, PropertyTranslateX, pspec); 95 96 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)); 97 g_object_class_install_property(objectClass, PropertyTranslateY, pspec); 98} 99 100static void graphics_layer_actor_init(GraphicsLayerActor* self) 101{ 102 self->priv = GRAPHICS_LAYER_ACTOR_GET_PRIVATE(self); 103 104 clutter_actor_set_reactive(CLUTTER_ACTOR(self), FALSE); 105 106 self->priv->flatten = true; 107 108 // Default used by GraphicsLayer. 109 graphicsLayerActorSetAnchorPoint(self, 0.5, 0.5, 0.0); 110} 111 112static void graphicsLayerActorSetProperty(GObject* object, guint propID, const GValue* value, GParamSpec* pspec) 113{ 114 GraphicsLayerActor* layer = GRAPHICS_LAYER_ACTOR(object); 115 116 switch (propID) { 117 case PropertyTranslateX: 118 graphicsLayerActorSetTranslateX(layer, g_value_get_float(value)); 119 break; 120 case PropertyTranslateY: 121 graphicsLayerActorSetTranslateY(layer, g_value_get_float(value)); 122 break; 123 default: 124 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, pspec); 125 } 126} 127 128static void graphicsLayerActorGetProperty(GObject* object, guint propID, GValue* value, GParamSpec* pspec) 129{ 130 GraphicsLayerActor* layer = GRAPHICS_LAYER_ACTOR(object); 131 132 switch (propID) { 133 case PropertyTranslateX: 134 g_value_set_float(value, graphicsLayerActorGetTranslateX(layer)); 135 break; 136 case PropertyTranslateY: 137 g_value_set_float(value, graphicsLayerActorGetTranslateY(layer)); 138 break; 139 default: 140 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, pspec); 141 } 142} 143 144 145static void graphicsLayerActorDispose(GObject* object) 146{ 147 GraphicsLayerActor* layer = GRAPHICS_LAYER_ACTOR(object); 148 GraphicsLayerActorPrivate* priv = layer->priv; 149 150 priv->surface.clear(); 151 152 G_OBJECT_CLASS(graphics_layer_actor_parent_class)->dispose(object); 153} 154 155// Copied from cairo. 156#define MAX_IMAGE_SIZE 32767 157 158static void graphicsLayerActorAllocate(ClutterActor* self, const ClutterActorBox* box, ClutterAllocationFlags flags) 159{ 160 CLUTTER_ACTOR_CLASS(graphics_layer_actor_parent_class)->allocate(self, box, flags); 161 162 ClutterContent* canvas = clutter_actor_get_content(self); 163 if (canvas) 164 clutter_canvas_set_size(CLUTTER_CANVAS(canvas), clutter_actor_get_width(self), clutter_actor_get_height(self)); 165 166 // FIXME: maybe we can cache children allocation and not call 167 // allocate on them this often? 168 GOwnPtr<GList> children(clutter_actor_get_children(self)); 169 for (GList* child = children.get(); child; child = child->next) { 170 ClutterActor* childActor = CLUTTER_ACTOR(child->data); 171 172 float childWidth = clutter_actor_get_width(childActor); 173 float childHeight = clutter_actor_get_height(childActor); 174 175 ClutterActorBox childBox; 176 childBox.x1 = clutter_actor_get_x(childActor); 177 childBox.y1 = clutter_actor_get_y(childActor); 178 childBox.x2 = childBox.x1 + childWidth; 179 childBox.y2 = childBox.y1 + childHeight; 180 181 clutter_actor_allocate(childActor, &childBox, flags); 182 } 183} 184 185static void graphicsLayerActorApplyTransform(ClutterActor* actor, CoglMatrix* matrix) 186{ 187 GraphicsLayerActorPrivate* priv = GRAPHICS_LAYER_ACTOR(actor)->priv; 188 189 // Apply translation and scrolling as a single translation. These 190 // need to come before anything else, otherwise they'll be 191 // affected by other operations such as scaling, which is not what 192 // we want. 193 float translateX = priv->scrollX + priv->translateX; 194 float translateY = priv->scrollY + priv->translateY; 195 196 if (translateX || translateY) 197 cogl_matrix_translate(matrix, translateX, translateY, 0); 198 199 CoglMatrix modelViewTransform = TransformationMatrix(); 200 CLUTTER_ACTOR_CLASS(graphics_layer_actor_parent_class)->apply_transform(actor, &modelViewTransform); 201 202 if (priv->flatten) 203 modelViewTransform = TransformationMatrix(&modelViewTransform).to2dTransform(); 204 cogl_matrix_multiply(matrix, matrix, &modelViewTransform); 205} 206 207static void graphicsLayerActorPaint(ClutterActor* actor) 208{ 209 GOwnPtr<GList> children(clutter_actor_get_children(actor)); 210 for (GList* child = children.get(); child; child = child->next) 211 clutter_actor_paint(CLUTTER_ACTOR(child->data)); 212} 213 214static gboolean graphicsLayerActorDraw(ClutterCanvas* texture, cairo_t* cr, gint width, gint height, GraphicsLayerActor* layer) 215{ 216 if (!width || !height) 217 return FALSE; 218 219 GraphicsContext context(cr); 220 context.clearRect(FloatRect(0, 0, width, height)); 221 222 GraphicsLayerActorPrivate* priv = layer->priv; 223 if (priv->surface) { 224 gint surfaceWidth = cairo_image_surface_get_width(priv->surface.get()); 225 gint surfaceHeight = cairo_image_surface_get_height(priv->surface.get()); 226 227 FloatRect srcRect(0.0, 0.0, static_cast<float>(surfaceWidth), static_cast<float>(surfaceHeight)); 228 FloatRect destRect(0.0, 0.0, width, height); 229 context.platformContext()->drawSurfaceToContext(priv->surface.get(), destRect, srcRect, &context); 230 } 231 232 if (priv->layerType == GraphicsLayerClutter::LayerTypeWebLayer) 233 drawLayerContents(CLUTTER_ACTOR(layer), context); 234 235 return TRUE; 236} 237 238static void graphicsLayerActorUpdateTexture(GraphicsLayerActor* layer) 239{ 240 GraphicsLayerActorPrivate* priv = layer->priv; 241 ASSERT(priv->layerType != GraphicsLayerClutter::LayerTypeVideoLayer); 242 243 ClutterActor* actor = CLUTTER_ACTOR(layer); 244 GRefPtr<ClutterContent> canvas = clutter_actor_get_content(actor); 245 if (canvas) { 246 // Nothing needs a texture, remove the one we have, if any. 247 if (!priv->drawsContent && !priv->surface) { 248 g_signal_handlers_disconnect_by_func(canvas.get(), reinterpret_cast<void*>(graphicsLayerActorDraw), layer); 249 clutter_actor_set_content(actor, 0); 250 } 251 return; 252 } 253 254 // We should have a texture, so create one. 255 canvas = adoptGRef(clutter_canvas_new()); 256 clutter_actor_set_content(actor, canvas.get()); 257 258 int width = std::max(static_cast<int>(ceilf(clutter_actor_get_width(actor))), 1); 259 int height = std::max(static_cast<int>(ceilf(clutter_actor_get_height(actor))), 1); 260 clutter_canvas_set_size(CLUTTER_CANVAS(canvas.get()), width, height); 261 262 g_signal_connect(canvas.get(), "draw", G_CALLBACK(graphicsLayerActorDraw), layer); 263} 264 265// Draw content into the layer. 266static void drawLayerContents(ClutterActor* actor, GraphicsContext& context) 267{ 268 GraphicsLayerActorPrivate* priv = GRAPHICS_LAYER_ACTOR(actor)->priv; 269 270 if (!priv->drawsContent || !priv->layerClient) 271 return; 272 273 int width = static_cast<int>(clutter_actor_get_width(actor)); 274 int height = static_cast<int>(clutter_actor_get_height(actor)); 275 IntRect clip(0, 0, width, height); 276 277 // Apply the painted content to the layer. 278 priv->layerClient->platformClutterLayerPaintContents(context, clip); 279} 280 281static GraphicsLayerActor* graphicsLayerActorNew(GraphicsLayerClutter::LayerType type) 282{ 283 GraphicsLayerActor* layer = GRAPHICS_LAYER_ACTOR(g_object_new(GRAPHICS_LAYER_TYPE_ACTOR, 0)); 284 GraphicsLayerActorPrivate* priv = layer->priv; 285 286 priv->layerType = type; 287 if (priv->layerType == GraphicsLayerClutter::LayerTypeTransformLayer) 288 priv->flatten = false; 289 290 return layer; 291} 292 293GraphicsLayerActor* graphicsLayerActorNewWithClient(GraphicsLayerClutter::LayerType type, PlatformClutterLayerClient* layerClient) 294{ 295 GraphicsLayerActor* layer = graphicsLayerActorNew(type); 296 graphicsLayerActorSetClient(layer, layerClient); 297 298 return layer; 299} 300 301void graphicsLayerActorSetClient(GraphicsLayerActor* layer, PlatformClutterLayerClient* client) 302{ 303 layer->priv->layerClient = client; 304} 305 306PlatformClutterLayerClient* graphicsLayerActorGetClient(GraphicsLayerActor* layer) 307{ 308 return layer->priv->layerClient; 309} 310 311void graphicsLayerActorSetSurface(GraphicsLayerActor* layer, cairo_surface_t* surface) 312{ 313 GraphicsLayerActorPrivate* priv = layer->priv; 314 priv->surface = surface; 315 graphicsLayerActorUpdateTexture(layer); 316} 317 318void graphicsLayerActorInvalidateRectangle(GraphicsLayerActor* layer, const FloatRect& dirtyRect) 319{ 320 ClutterContent* canvas = clutter_actor_get_content(CLUTTER_ACTOR(layer)); 321 if (!canvas) 322 return; 323 324 // FIXME: Need to invalidate a specific area? 325 clutter_content_invalidate(canvas); 326} 327 328void graphicsLayerActorSetAnchorPoint(GraphicsLayerActor* layer, float x, float y, float z) 329{ 330 ClutterActor* actor = CLUTTER_ACTOR(layer); 331 clutter_actor_set_pivot_point(actor, x, y); 332 clutter_actor_set_pivot_point_z(actor, z); 333} 334 335void graphicsLayerActorSetScrollPosition(GraphicsLayerActor* layer, float x, float y) 336{ 337 if (x > 0 || y > 0) 338 return; 339 340 GraphicsLayerActorPrivate* priv = layer->priv; 341 priv->scrollX = x; 342 priv->scrollY = y; 343 344 clutter_actor_queue_redraw(CLUTTER_ACTOR(layer)); 345} 346 347void graphicsLayerActorSetSublayers(GraphicsLayerActor* layer, GraphicsLayerActorList& subLayers) 348{ 349 if (subLayers.isEmpty()) { 350 clutter_actor_remove_all_children(CLUTTER_ACTOR(layer)); 351 return; 352 } 353 354 ClutterActor* newParentActor = CLUTTER_ACTOR(layer); 355 for (size_t i = 0; i < subLayers.size(); ++i) { 356 ClutterActor* childActor = CLUTTER_ACTOR(subLayers[i].get()); 357 ClutterActor* oldParentActor = clutter_actor_get_parent(childActor); 358 if (oldParentActor) { 359 if (oldParentActor == newParentActor) 360 continue; 361 clutter_actor_remove_child(oldParentActor, childActor); 362 } 363 clutter_actor_add_child(newParentActor, childActor); 364 } 365} 366 367void graphicsLayerActorRemoveFromSuperLayer(GraphicsLayerActor* layer) 368{ 369 ClutterActor* actor = CLUTTER_ACTOR(layer); 370 ClutterActor* parentActor = clutter_actor_get_parent(actor); 371 if (!parentActor) 372 return; 373 374 clutter_actor_remove_child(parentActor, actor); 375} 376 377GraphicsLayerClutter::LayerType graphicsLayerActorGetLayerType(GraphicsLayerActor* layer) 378{ 379 GraphicsLayerActorPrivate* priv = layer->priv; 380 return priv->layerType; 381} 382 383void graphicsLayerActorSetLayerType(GraphicsLayerActor* layer, GraphicsLayerClutter::LayerType layerType) 384{ 385 GraphicsLayerActorPrivate* priv = layer->priv; 386 priv->layerType = layerType; 387} 388 389void graphicsLayerActorSetTranslateX(GraphicsLayerActor* layer, float value) 390{ 391 GraphicsLayerActorPrivate* priv = layer->priv; 392 priv->translateX = value; 393 clutter_actor_queue_redraw(CLUTTER_ACTOR(layer)); 394} 395 396float graphicsLayerActorGetTranslateX(GraphicsLayerActor* layer) 397{ 398 GraphicsLayerActorPrivate* priv = layer->priv; 399 return priv->translateX; 400} 401 402void graphicsLayerActorSetTranslateY(GraphicsLayerActor* layer, float value) 403{ 404 GraphicsLayerActorPrivate* priv = layer->priv; 405 priv->translateY = value; 406 clutter_actor_queue_redraw(CLUTTER_ACTOR(layer)); 407} 408 409float graphicsLayerActorGetTranslateY(GraphicsLayerActor* layer) 410{ 411 GraphicsLayerActorPrivate* priv = layer->priv; 412 return priv->translateY; 413} 414 415void graphicsLayerActorSetDrawsContent(GraphicsLayerActor* layer, bool drawsContent) 416{ 417 GraphicsLayerActorPrivate* priv = layer->priv; 418 419 if (drawsContent == priv->drawsContent) 420 return; 421 422 priv->drawsContent = drawsContent; 423 424 graphicsLayerActorUpdateTexture(layer); 425} 426 427void graphicsLayerActorSetFlatten(GraphicsLayerActor* layer, bool flatten) 428{ 429 GraphicsLayerActorPrivate* priv = layer->priv; 430 if (flatten == priv->flatten) 431 return; 432 433 priv->flatten = flatten; 434} 435 436void graphicsLayerActorSetMasksToBounds(GraphicsLayerActor* layer, bool masksToBounds) 437{ 438 ClutterActor* actor = CLUTTER_ACTOR(layer); 439 if (masksToBounds) 440 clutter_actor_set_clip(actor, 0, 0, clutter_actor_get_width(actor), clutter_actor_get_height(actor)); 441 else 442 clutter_actor_remove_clip(actor); 443} 444 445WebCore::PlatformClutterAnimation* graphicsLayerActorGetAnimationForKey(GraphicsLayerActor* layer, const String key) 446{ 447 return static_cast<WebCore::PlatformClutterAnimation*>(g_object_get_data(G_OBJECT(layer), key.utf8().data())); 448} 449 450#endif // USE(ACCELERATED_COMPOSITING) 451