diff --git a/src/st/st-drawing-area.c b/src/st/st-drawing-area.c index 20b0d6def..f8d311c7b 100644 --- a/src/st/st-drawing-area.c +++ b/src/st/st-drawing-area.c @@ -39,6 +39,15 @@ typedef struct _StDrawingAreaPrivate StDrawingAreaPrivate; struct _StDrawingAreaPrivate { cairo_t *context; + + int width; + int height; + float scale_factor; + + CoglTexture *texture; + CoglBitmap *buffer; + + gboolean dirty; guint in_repaint : 1; }; @@ -53,53 +62,52 @@ enum static guint st_drawing_area_signals [LAST_SIGNAL] = { 0 }; -static gboolean -draw_content (ClutterCanvas *canvas, - cairo_t *cr, - int width, - int height, - gpointer user_data) -{ - StDrawingArea *area = ST_DRAWING_AREA (user_data); - StDrawingAreaPrivate *priv = st_drawing_area_get_instance_private (area); - - priv->context = cr; - priv->in_repaint = TRUE; - - cairo_save (cr); - - cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); - cairo_paint (cr); - - cairo_restore (cr); - g_signal_emit (area, st_drawing_area_signals[REPAINT], 0); - - priv->context = NULL; - priv->in_repaint = FALSE; - - return TRUE; -} - static void st_drawing_area_allocate (ClutterActor *self, const ClutterActorBox *box) { StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); - ClutterContent *content = clutter_actor_get_content (self); + StDrawingAreaPrivate *priv = + st_drawing_area_get_instance_private (ST_DRAWING_AREA (self)); ClutterActorBox content_box; - int width, height; - float resource_scale; - resource_scale = clutter_actor_get_resource_scale (self); + priv->scale_factor = clutter_actor_get_resource_scale (self); clutter_actor_set_allocation (self, box); st_theme_node_get_content_box (theme_node, box, &content_box); - width = (int)(0.5 + content_box.x2 - content_box.x1); - height = (int)(0.5 + content_box.y2 - content_box.y1); + priv->width = (int)(0.5 + content_box.x2 - content_box.x1); + priv->height = (int)(0.5 + content_box.y2 - content_box.y1); - clutter_canvas_set_scale_factor (CLUTTER_CANVAS (content), resource_scale); - clutter_canvas_set_size (CLUTTER_CANVAS (content), width, height); + st_drawing_area_queue_repaint (ST_DRAWING_AREA (self)); +} + +static void +st_drawing_area_paint_node (ClutterActor *actor, + ClutterPaintNode *root) +{ + StDrawingArea *area = ST_DRAWING_AREA (actor); + StDrawingAreaPrivate *priv = st_drawing_area_get_instance_private (area); + ClutterPaintNode *node; + + if (priv->buffer == NULL) + return; + + if (priv->dirty) + g_clear_object (&priv->texture); + + if (priv->texture == NULL) + priv->texture = cogl_texture_2d_new_from_bitmap (priv->buffer); + + if (priv->texture == NULL) + return; + + node = clutter_actor_create_texture_paint_node (actor, priv->texture); + clutter_paint_node_set_static_name (node, "Canvas Content"); + clutter_paint_node_add_child (root, node); + clutter_paint_node_unref (node); + + priv->dirty = FALSE; } static void @@ -113,23 +121,44 @@ st_drawing_area_style_changed (StWidget *self) static void st_drawing_area_resource_scale_changed (ClutterActor *self) { + StDrawingArea *area = ST_DRAWING_AREA (self); + StDrawingAreaPrivate *priv = st_drawing_area_get_instance_private (area); float resource_scale; - ClutterContent *content = clutter_actor_get_content (self); resource_scale = clutter_actor_get_resource_scale (self); - clutter_canvas_set_scale_factor (CLUTTER_CANVAS (content), resource_scale); + if (priv->scale_factor != resource_scale) + { + priv->scale_factor = resource_scale; - if (CLUTTER_ACTOR_CLASS (st_drawing_area_parent_class)->resource_scale_changed) - CLUTTER_ACTOR_CLASS (st_drawing_area_parent_class)->resource_scale_changed (self); + st_drawing_area_queue_repaint (area); + + if (CLUTTER_ACTOR_CLASS (st_drawing_area_parent_class)->resource_scale_changed) + CLUTTER_ACTOR_CLASS (st_drawing_area_parent_class)->resource_scale_changed (self); + } +} + +static void +st_drawing_area_finalize (GObject *self) +{ + StDrawingAreaPrivate *priv = + st_drawing_area_get_instance_private (ST_DRAWING_AREA (self)); + + g_clear_object (&priv->buffer); + g_clear_object (&priv->texture); + + G_OBJECT_CLASS (st_drawing_area_parent_class)->finalize (self); } static void st_drawing_area_class_init (StDrawingAreaClass *klass) { + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); StWidgetClass *widget_class = ST_WIDGET_CLASS (klass); + gobject_class->finalize = st_drawing_area_finalize; actor_class->allocate = st_drawing_area_allocate; + actor_class->paint_node = st_drawing_area_paint_node; widget_class->style_changed = st_drawing_area_style_changed; actor_class->resource_scale_changed = st_drawing_area_resource_scale_changed; @@ -145,10 +174,104 @@ st_drawing_area_class_init (StDrawingAreaClass *klass) static void st_drawing_area_init (StDrawingArea *area) { - ClutterContent *content = clutter_canvas_new (); - g_signal_connect (content, "draw", G_CALLBACK (draw_content), area); - clutter_actor_set_content (CLUTTER_ACTOR (area), content); - g_object_unref (content); + StDrawingAreaPrivate *priv = st_drawing_area_get_instance_private (area); + + priv->width = -1; + priv->height = -1; + priv->scale_factor = 1.0f; +} + +static void +st_drawing_area_emit_repaint (StDrawingArea *area) +{ + StDrawingAreaPrivate *priv = st_drawing_area_get_instance_private (area); + int real_width, real_height; + cairo_surface_t *surface; + gboolean mapped_buffer; + unsigned char *data; + CoglBuffer *buffer; + cairo_t *cr; + + g_assert (priv->height > 0 && priv->width > 0); + + priv->dirty = TRUE; + + real_width = ceilf (priv->width * priv->scale_factor); + real_height = ceilf (priv->height * priv->scale_factor); + + if (priv->buffer == NULL) + { + CoglContext *ctx; + + ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); + priv->buffer = cogl_bitmap_new_with_size (ctx, + real_width, + real_height, + COGL_PIXEL_FORMAT_CAIRO_ARGB32_COMPAT); + } + + buffer = COGL_BUFFER (cogl_bitmap_get_buffer (priv->buffer)); + if (buffer == NULL) + return; + + cogl_buffer_set_update_hint (buffer, COGL_BUFFER_UPDATE_HINT_DYNAMIC); + + data = cogl_buffer_map (buffer, + COGL_BUFFER_ACCESS_READ_WRITE, + COGL_BUFFER_MAP_HINT_DISCARD); + + if (data != NULL) + { + int bitmap_stride = cogl_bitmap_get_rowstride (priv->buffer); + + surface = cairo_image_surface_create_for_data (data, + CAIRO_FORMAT_ARGB32, + real_width, + real_height, + bitmap_stride); + mapped_buffer = TRUE; + } + else + { + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + real_width, + real_height); + + mapped_buffer = FALSE; + } + + cairo_surface_set_device_scale (surface, + priv->scale_factor, + priv->scale_factor); + + priv->context = cr = cairo_create (surface); + + priv->in_repaint = TRUE; + + cairo_save (priv->context); + cairo_set_operator (priv->context, CAIRO_OPERATOR_CLEAR); + cairo_paint (priv->context); + cairo_restore (priv->context); + + g_signal_emit (area, st_drawing_area_signals[REPAINT], 0); + + priv->context = NULL; + priv->in_repaint = FALSE; + + cairo_destroy (cr); + + if (mapped_buffer) + cogl_buffer_unmap (buffer); + else + { + int size = cairo_image_surface_get_stride (surface) * priv->height; + cogl_buffer_set_data (buffer, + 0, + cairo_image_surface_get_data (surface), + size); + } + + cairo_surface_destroy (surface); } /** @@ -164,9 +287,18 @@ st_drawing_area_init (StDrawingArea *area) void st_drawing_area_queue_repaint (StDrawingArea *area) { + StDrawingAreaPrivate *priv; + g_return_if_fail (ST_IS_DRAWING_AREA (area)); - clutter_content_invalidate (clutter_actor_get_content (CLUTTER_ACTOR (area))); + priv = st_drawing_area_get_instance_private (area); + + g_clear_object (&priv->buffer); + + if (priv->width <= 0 || priv->height <= 0) + return; + + st_drawing_area_emit_repaint (area); } /** @@ -224,24 +356,14 @@ st_drawing_area_get_surface_size (StDrawingArea *area, guint *height) { StDrawingAreaPrivate *priv; - ClutterContent *content; - float w, h, resource_scale; g_return_if_fail (ST_IS_DRAWING_AREA (area)); priv = st_drawing_area_get_instance_private (area); g_return_if_fail (priv->in_repaint); - content = clutter_actor_get_content (CLUTTER_ACTOR (area)); - clutter_content_get_preferred_size (content, &w, &h); - - resource_scale = clutter_actor_get_resource_scale (CLUTTER_ACTOR (area)); - - w /= resource_scale; - h /= resource_scale; - if (width) - *width = ceilf (w); + *width = priv->width; if (height) - *height = ceilf (h); + *height = priv->height; }