2012-03-08 10:31:21 +00:00
|
|
|
/*
|
|
|
|
* Clutter.
|
|
|
|
*
|
|
|
|
* An OpenGL based 'interactive canvas' library.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2012 Intel Corporation.
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
* Author:
|
|
|
|
* Emmanuele Bassi <ebassi@linux.intel.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* SECTION:clutter-canvas
|
|
|
|
* @Title: ClutterCanvas
|
|
|
|
* @Short_Description: Content for 2D painting
|
|
|
|
* @See_Also: #ClutterContent
|
|
|
|
*
|
|
|
|
* The #ClutterCanvas class is a #ClutterContent implementation that allows
|
|
|
|
* drawing using the Cairo API on a 2D surface.
|
|
|
|
*
|
|
|
|
* In order to draw on a #ClutterCanvas, you should connect a handler to the
|
|
|
|
* #ClutterCanvas::draw signal; the signal will receive a #cairo_t context
|
|
|
|
* that can be used to draw. #ClutterCanvas will emit the #ClutterCanvas::draw
|
|
|
|
* signal when invalidated using clutter_content_invalidate().
|
|
|
|
*
|
2014-03-17 23:07:58 +00:00
|
|
|
* See [canvas.c](https://git.gnome.org/browse/clutter/tree/examples/canvas.c?h=clutter-1.18)
|
|
|
|
* for an example of how to use #ClutterCanvas.
|
2012-03-08 10:31:21 +00:00
|
|
|
*
|
|
|
|
* #ClutterCanvas is available since Clutter 1.10.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <cogl/cogl.h>
|
|
|
|
#include <cairo-gobject.h>
|
|
|
|
|
|
|
|
#include "clutter-canvas.h"
|
|
|
|
|
|
|
|
#define CLUTTER_ENABLE_EXPERIMENTAL_API
|
|
|
|
|
|
|
|
#include "clutter-backend.h"
|
2012-04-17 17:40:43 +00:00
|
|
|
#include "clutter-cairo.h"
|
2012-03-08 10:31:21 +00:00
|
|
|
#include "clutter-color.h"
|
|
|
|
#include "clutter-content-private.h"
|
2014-01-16 12:24:57 +00:00
|
|
|
#include "clutter-debug.h"
|
2012-03-08 10:31:21 +00:00
|
|
|
#include "clutter-marshal.h"
|
|
|
|
#include "clutter-paint-node.h"
|
|
|
|
#include "clutter-paint-nodes.h"
|
|
|
|
#include "clutter-private.h"
|
2014-01-16 12:24:57 +00:00
|
|
|
#include "clutter-settings.h"
|
2012-03-08 10:31:21 +00:00
|
|
|
|
|
|
|
struct _ClutterCanvasPrivate
|
|
|
|
{
|
|
|
|
cairo_t *cr;
|
|
|
|
|
|
|
|
int width;
|
|
|
|
int height;
|
|
|
|
|
2014-04-28 19:03:13 +00:00
|
|
|
CoglTexture *texture;
|
|
|
|
gboolean dirty;
|
|
|
|
|
2012-03-08 10:31:21 +00:00
|
|
|
CoglBitmap *buffer;
|
2014-01-17 11:03:15 +00:00
|
|
|
|
|
|
|
int scale_factor;
|
|
|
|
guint scale_factor_set : 1;
|
2012-03-08 10:31:21 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
PROP_0,
|
|
|
|
|
|
|
|
PROP_WIDTH,
|
|
|
|
PROP_HEIGHT,
|
2014-01-17 11:03:15 +00:00
|
|
|
PROP_SCALE_FACTOR,
|
|
|
|
PROP_SCALE_FACTOR_SET,
|
2012-03-08 10:31:21 +00:00
|
|
|
|
|
|
|
LAST_PROP
|
|
|
|
};
|
|
|
|
|
|
|
|
static GParamSpec *obj_props[LAST_PROP] = { NULL, };
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
DRAW,
|
|
|
|
|
|
|
|
LAST_SIGNAL
|
|
|
|
};
|
|
|
|
|
|
|
|
static guint canvas_signals[LAST_SIGNAL] = { 0, };
|
|
|
|
|
|
|
|
static void clutter_content_iface_init (ClutterContentIface *iface);
|
|
|
|
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (ClutterCanvas, clutter_canvas, G_TYPE_OBJECT,
|
2013-07-03 13:14:01 +00:00
|
|
|
G_ADD_PRIVATE (ClutterCanvas)
|
2012-03-08 10:31:21 +00:00
|
|
|
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT,
|
|
|
|
clutter_content_iface_init))
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_cairo_context_draw_marshaller (GClosure *closure,
|
|
|
|
GValue *return_value,
|
|
|
|
guint n_param_values,
|
|
|
|
const GValue *param_values,
|
|
|
|
gpointer invocation_hint,
|
|
|
|
gpointer marshal_data)
|
|
|
|
{
|
|
|
|
cairo_t *cr = g_value_get_boxed (¶m_values[1]);
|
|
|
|
|
|
|
|
cairo_save (cr);
|
|
|
|
|
2012-03-08 15:47:16 +00:00
|
|
|
_clutter_marshal_BOOLEAN__BOXED_INT_INT (closure,
|
|
|
|
return_value,
|
|
|
|
n_param_values,
|
|
|
|
param_values,
|
|
|
|
invocation_hint,
|
|
|
|
marshal_data);
|
2012-03-08 10:31:21 +00:00
|
|
|
|
|
|
|
cairo_restore (cr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_canvas_finalize (GObject *gobject)
|
|
|
|
{
|
|
|
|
ClutterCanvasPrivate *priv = CLUTTER_CANVAS (gobject)->priv;
|
|
|
|
|
|
|
|
if (priv->buffer != NULL)
|
|
|
|
{
|
|
|
|
cogl_object_unref (priv->buffer);
|
|
|
|
priv->buffer = NULL;
|
|
|
|
}
|
|
|
|
|
2014-04-28 19:03:13 +00:00
|
|
|
g_clear_pointer (&priv->texture, cogl_object_unref);
|
|
|
|
|
2012-03-08 10:31:21 +00:00
|
|
|
G_OBJECT_CLASS (clutter_canvas_parent_class)->finalize (gobject);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_canvas_set_property (GObject *gobject,
|
|
|
|
guint prop_id,
|
|
|
|
const GValue *value,
|
|
|
|
GParamSpec *pspec)
|
|
|
|
{
|
|
|
|
ClutterCanvasPrivate *priv = CLUTTER_CANVAS (gobject)->priv;
|
|
|
|
|
|
|
|
switch (prop_id)
|
|
|
|
{
|
|
|
|
case PROP_WIDTH:
|
2013-05-15 19:08:02 +00:00
|
|
|
{
|
|
|
|
gint new_size = g_value_get_int (value);
|
|
|
|
|
|
|
|
if (priv->width != new_size)
|
|
|
|
{
|
|
|
|
priv->width = new_size;
|
|
|
|
|
|
|
|
clutter_content_invalidate (CLUTTER_CONTENT (gobject));
|
|
|
|
}
|
|
|
|
}
|
2012-03-08 10:31:21 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_HEIGHT:
|
2013-05-15 19:08:02 +00:00
|
|
|
{
|
|
|
|
gint new_size = g_value_get_int (value);
|
|
|
|
|
|
|
|
if (priv->height != new_size)
|
|
|
|
{
|
|
|
|
priv->height = new_size;
|
|
|
|
|
|
|
|
clutter_content_invalidate (CLUTTER_CONTENT (gobject));
|
|
|
|
}
|
|
|
|
}
|
2012-03-08 10:31:21 +00:00
|
|
|
break;
|
|
|
|
|
2014-01-17 11:03:15 +00:00
|
|
|
case PROP_SCALE_FACTOR:
|
|
|
|
clutter_canvas_set_scale_factor (CLUTTER_CANVAS (gobject),
|
|
|
|
g_value_get_int (value));
|
|
|
|
break;
|
|
|
|
|
2012-03-08 10:31:21 +00:00
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_canvas_get_property (GObject *gobject,
|
|
|
|
guint prop_id,
|
|
|
|
GValue *value,
|
|
|
|
GParamSpec *pspec)
|
|
|
|
{
|
|
|
|
ClutterCanvasPrivate *priv = CLUTTER_CANVAS (gobject)->priv;
|
|
|
|
|
|
|
|
switch (prop_id)
|
|
|
|
{
|
|
|
|
case PROP_WIDTH:
|
|
|
|
g_value_set_int (value, priv->width);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_HEIGHT:
|
|
|
|
g_value_set_int (value, priv->height);
|
|
|
|
break;
|
|
|
|
|
2014-01-17 11:03:15 +00:00
|
|
|
case PROP_SCALE_FACTOR:
|
|
|
|
if (priv->scale_factor_set)
|
|
|
|
g_value_set_int (value, priv->scale_factor);
|
|
|
|
else
|
|
|
|
g_value_set_int (value, -1);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_SCALE_FACTOR_SET:
|
|
|
|
g_value_set_boolean (value, priv->scale_factor_set);
|
|
|
|
break;
|
|
|
|
|
2012-03-08 10:31:21 +00:00
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_canvas_class_init (ClutterCanvasClass *klass)
|
|
|
|
{
|
|
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ClutterCanvas:width:
|
|
|
|
*
|
|
|
|
* The width of the canvas.
|
|
|
|
*
|
|
|
|
* Since: 1.10
|
|
|
|
*/
|
|
|
|
obj_props[PROP_WIDTH] =
|
|
|
|
g_param_spec_int ("width",
|
|
|
|
P_("Width"),
|
|
|
|
P_("The width of the canvas"),
|
|
|
|
-1, G_MAXINT,
|
|
|
|
-1,
|
|
|
|
G_PARAM_READWRITE |
|
|
|
|
G_PARAM_STATIC_STRINGS);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ClutterCanvas:height:
|
|
|
|
*
|
|
|
|
* The height of the canvas.
|
|
|
|
*
|
|
|
|
* Since: 1.10
|
|
|
|
*/
|
|
|
|
obj_props[PROP_HEIGHT] =
|
|
|
|
g_param_spec_int ("height",
|
|
|
|
P_("Height"),
|
|
|
|
P_("The height of the canvas"),
|
|
|
|
-1, G_MAXINT,
|
|
|
|
-1,
|
|
|
|
G_PARAM_READWRITE |
|
|
|
|
G_PARAM_STATIC_STRINGS);
|
|
|
|
|
2014-01-17 11:03:15 +00:00
|
|
|
/**
|
|
|
|
* ClutterCanvas:scale-factor-set:
|
|
|
|
*
|
|
|
|
* Whether the #ClutterCanvas:scale-factor property is set.
|
|
|
|
*
|
|
|
|
* If the #ClutterCanvas:scale-factor-set property is %FALSE
|
|
|
|
* then #ClutterCanvas will use the #ClutterSettings:window-scaling-factor
|
|
|
|
* property.
|
|
|
|
*
|
|
|
|
* Since: 1.18
|
|
|
|
*/
|
|
|
|
obj_props[PROP_SCALE_FACTOR_SET] =
|
|
|
|
g_param_spec_boolean ("scale-factor-set",
|
|
|
|
P_("Scale Factor Set"),
|
|
|
|
P_("Whether the scale-factor property is set"),
|
|
|
|
FALSE,
|
|
|
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ClutterCanvas:scale-factor:
|
|
|
|
*
|
|
|
|
* The scaling factor to be applied to the Cairo surface used for
|
|
|
|
* drawing.
|
|
|
|
*
|
|
|
|
* If #ClutterCanvas:scale-factor is set to a negative value, the
|
|
|
|
* value of the #ClutterSettings:window-scaling-factor property is
|
|
|
|
* used instead.
|
|
|
|
*
|
|
|
|
* Use #ClutterCanvas:scale-factor-set to check if the scale factor
|
|
|
|
* is set.
|
|
|
|
*
|
|
|
|
* Since: 1.18
|
|
|
|
*/
|
|
|
|
obj_props[PROP_SCALE_FACTOR] =
|
|
|
|
g_param_spec_int ("scale-factor",
|
|
|
|
P_("Scale Factor"),
|
|
|
|
P_("The scaling factor for the surface"),
|
|
|
|
-1, 1000,
|
|
|
|
-1,
|
|
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
|
|
|
|
2012-03-08 10:31:21 +00:00
|
|
|
/**
|
|
|
|
* ClutterCanvas::draw:
|
|
|
|
* @canvas: the #ClutterCanvas that emitted the signal
|
|
|
|
* @cr: the Cairo context used to draw
|
2012-03-08 15:47:16 +00:00
|
|
|
* @width: the width of the @canvas
|
|
|
|
* @height: the height of the @canvas
|
2012-03-08 10:31:21 +00:00
|
|
|
*
|
|
|
|
* The #ClutterCanvas::draw signal is emitted each time a canvas is
|
|
|
|
* invalidated.
|
|
|
|
*
|
|
|
|
* It is safe to connect multiple handlers to this signal: each
|
|
|
|
* handler invocation will be automatically protected by cairo_save()
|
|
|
|
* and cairo_restore() pairs.
|
|
|
|
*
|
|
|
|
* Return value: %TRUE if the signal emission should stop, and
|
|
|
|
* %FALSE otherwise
|
|
|
|
*
|
|
|
|
* Since: 1.10
|
|
|
|
*/
|
|
|
|
canvas_signals[DRAW] =
|
|
|
|
g_signal_new (I_("draw"),
|
|
|
|
G_TYPE_FROM_CLASS (klass),
|
|
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
|
|
|
|
G_STRUCT_OFFSET (ClutterCanvasClass, draw),
|
|
|
|
_clutter_boolean_handled_accumulator, NULL,
|
|
|
|
clutter_cairo_context_draw_marshaller,
|
2012-03-08 15:47:16 +00:00
|
|
|
G_TYPE_BOOLEAN, 3,
|
|
|
|
CAIRO_GOBJECT_TYPE_CONTEXT,
|
|
|
|
G_TYPE_INT,
|
|
|
|
G_TYPE_INT);
|
2012-03-08 10:31:21 +00:00
|
|
|
|
|
|
|
gobject_class->set_property = clutter_canvas_set_property;
|
|
|
|
gobject_class->get_property = clutter_canvas_get_property;
|
|
|
|
gobject_class->finalize = clutter_canvas_finalize;
|
|
|
|
|
|
|
|
g_object_class_install_properties (gobject_class, LAST_PROP, obj_props);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_canvas_init (ClutterCanvas *self)
|
|
|
|
{
|
2013-07-03 13:14:01 +00:00
|
|
|
self->priv = clutter_canvas_get_instance_private (self);
|
2014-01-17 11:03:15 +00:00
|
|
|
|
2012-03-08 10:31:21 +00:00
|
|
|
self->priv->width = -1;
|
|
|
|
self->priv->height = -1;
|
2014-01-17 11:03:15 +00:00
|
|
|
self->priv->scale_factor = -1;
|
2012-03-08 10:31:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_canvas_paint_content (ClutterContent *content,
|
|
|
|
ClutterActor *actor,
|
|
|
|
ClutterPaintNode *root)
|
|
|
|
{
|
|
|
|
ClutterCanvas *self = CLUTTER_CANVAS (content);
|
2014-04-28 19:03:13 +00:00
|
|
|
ClutterCanvasPrivate *priv = self->priv;
|
2012-03-08 10:31:21 +00:00
|
|
|
ClutterPaintNode *node;
|
|
|
|
ClutterActorBox box;
|
|
|
|
ClutterColor color;
|
|
|
|
guint8 paint_opacity;
|
2012-03-08 17:19:12 +00:00
|
|
|
ClutterScalingFilter min_f, mag_f;
|
2012-06-08 11:49:07 +00:00
|
|
|
ClutterContentRepeat repeat;
|
2012-03-08 10:31:21 +00:00
|
|
|
|
2014-04-28 19:03:13 +00:00
|
|
|
if (priv->buffer == NULL)
|
2012-03-08 10:31:21 +00:00
|
|
|
return;
|
|
|
|
|
2014-04-28 19:03:13 +00:00
|
|
|
if (priv->texture && priv->dirty)
|
|
|
|
g_clear_pointer (&priv->texture, cogl_object_unref);
|
|
|
|
|
|
|
|
if (!priv->texture)
|
|
|
|
priv->texture = cogl_texture_new_from_bitmap (self->priv->buffer,
|
|
|
|
COGL_TEXTURE_NO_SLICING,
|
|
|
|
CLUTTER_CAIRO_FORMAT_ARGB32);
|
|
|
|
|
|
|
|
if (priv->texture == NULL)
|
2012-03-08 10:31:21 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
clutter_actor_get_content_box (actor, &box);
|
|
|
|
paint_opacity = clutter_actor_get_paint_opacity (actor);
|
2012-03-08 17:19:12 +00:00
|
|
|
clutter_actor_get_content_scaling_filters (actor, &min_f, &mag_f);
|
2012-06-08 11:49:07 +00:00
|
|
|
repeat = clutter_actor_get_content_repeat (actor);
|
2012-03-08 10:31:21 +00:00
|
|
|
|
2014-07-19 00:44:20 +00:00
|
|
|
color.red = 255;
|
|
|
|
color.green = 255;
|
|
|
|
color.blue = 255;
|
2012-03-08 10:31:21 +00:00
|
|
|
color.alpha = paint_opacity;
|
|
|
|
|
2014-04-28 19:03:13 +00:00
|
|
|
node = clutter_texture_node_new (priv->texture, &color, min_f, mag_f);
|
2012-03-08 10:31:21 +00:00
|
|
|
|
|
|
|
clutter_paint_node_set_name (node, "Canvas");
|
2012-06-08 11:49:07 +00:00
|
|
|
|
|
|
|
if (repeat == CLUTTER_REPEAT_NONE)
|
|
|
|
clutter_paint_node_add_rectangle (node, &box);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
float t_w = 1.f, t_h = 1.f;
|
|
|
|
|
|
|
|
if ((repeat & CLUTTER_REPEAT_X_AXIS) != FALSE)
|
2014-04-28 19:03:13 +00:00
|
|
|
t_w = (box.x2 - box.x1) / cogl_texture_get_width (priv->texture);
|
2012-06-08 11:49:07 +00:00
|
|
|
|
|
|
|
if ((repeat & CLUTTER_REPEAT_Y_AXIS) != FALSE)
|
2014-04-28 19:03:13 +00:00
|
|
|
t_h = (box.y2 - box.y1) / cogl_texture_get_height (priv->texture);
|
2012-06-08 11:49:07 +00:00
|
|
|
|
|
|
|
clutter_paint_node_add_texture_rectangle (node, &box,
|
|
|
|
0.f, 0.f,
|
|
|
|
t_w, t_h);
|
|
|
|
}
|
|
|
|
|
2012-03-08 10:31:21 +00:00
|
|
|
clutter_paint_node_add_child (root, node);
|
|
|
|
clutter_paint_node_unref (node);
|
2014-04-28 19:03:13 +00:00
|
|
|
|
|
|
|
priv->dirty = FALSE;
|
2012-03-08 10:31:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_canvas_emit_draw (ClutterCanvas *self)
|
|
|
|
{
|
|
|
|
ClutterCanvasPrivate *priv = self->priv;
|
2014-01-16 12:24:57 +00:00
|
|
|
int real_width, real_height;
|
2012-03-08 10:31:21 +00:00
|
|
|
cairo_surface_t *surface;
|
|
|
|
gboolean mapped_buffer;
|
|
|
|
unsigned char *data;
|
|
|
|
CoglBuffer *buffer;
|
2014-01-16 12:24:57 +00:00
|
|
|
int window_scale = 1;
|
2012-03-08 10:31:21 +00:00
|
|
|
gboolean res;
|
|
|
|
cairo_t *cr;
|
|
|
|
|
2012-03-29 12:35:50 +00:00
|
|
|
g_assert (priv->width > 0 && priv->width > 0);
|
2012-03-08 10:31:21 +00:00
|
|
|
|
2014-04-28 19:03:13 +00:00
|
|
|
priv->dirty = TRUE;
|
|
|
|
|
2014-01-17 11:03:15 +00:00
|
|
|
if (priv->scale_factor_set)
|
|
|
|
window_scale = priv->scale_factor;
|
|
|
|
else
|
|
|
|
g_object_get (clutter_settings_get_default (),
|
|
|
|
"window-scaling-factor", &window_scale,
|
|
|
|
NULL);
|
2014-01-16 12:24:57 +00:00
|
|
|
|
|
|
|
real_width = priv->width * window_scale;
|
|
|
|
real_height = priv->height * window_scale;
|
|
|
|
|
|
|
|
CLUTTER_NOTE (MISC, "Creating Cairo surface with size %d x %d (real: %d x %d, scale: %d)",
|
|
|
|
priv->width, priv->height,
|
|
|
|
real_width, real_height,
|
|
|
|
window_scale);
|
|
|
|
|
2012-03-08 10:31:21 +00:00
|
|
|
if (priv->buffer == NULL)
|
|
|
|
{
|
|
|
|
CoglContext *ctx;
|
|
|
|
|
|
|
|
ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
|
|
|
|
priv->buffer = cogl_bitmap_new_with_size (ctx,
|
2014-01-16 12:24:57 +00:00
|
|
|
real_width,
|
|
|
|
real_height,
|
2012-03-08 10:31:21 +00:00
|
|
|
CLUTTER_CAIRO_FORMAT_ARGB32);
|
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2014-01-16 12:24:57 +00:00
|
|
|
real_width,
|
|
|
|
real_height,
|
2012-03-08 10:31:21 +00:00
|
|
|
bitmap_stride);
|
|
|
|
mapped_buffer = TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
|
2014-01-16 12:24:57 +00:00
|
|
|
real_width,
|
|
|
|
real_height);
|
2012-03-08 10:31:21 +00:00
|
|
|
|
|
|
|
mapped_buffer = FALSE;
|
|
|
|
}
|
|
|
|
|
2014-01-16 12:24:57 +00:00
|
|
|
cairo_surface_set_device_scale (surface, window_scale, window_scale);
|
|
|
|
|
2012-03-08 10:31:21 +00:00
|
|
|
self->priv->cr = cr = cairo_create (surface);
|
|
|
|
|
2012-03-08 15:47:16 +00:00
|
|
|
g_signal_emit (self, canvas_signals[DRAW], 0,
|
|
|
|
cr, priv->width, priv->height,
|
|
|
|
&res);
|
2012-03-08 10:31:21 +00:00
|
|
|
|
2012-06-13 09:23:28 +00:00
|
|
|
#ifdef CLUTTER_ENABLE_DEBUG
|
|
|
|
if (_clutter_diagnostic_enabled () && cairo_status (cr))
|
|
|
|
{
|
|
|
|
g_warning ("Drawing failed for <ClutterCanvas>[%p]: %s",
|
|
|
|
self,
|
|
|
|
cairo_status_to_string (cairo_status (cr)));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-03-08 10:31:21 +00:00
|
|
|
self->priv->cr = NULL;
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_canvas_invalidate (ClutterContent *content)
|
|
|
|
{
|
|
|
|
ClutterCanvas *self = CLUTTER_CANVAS (content);
|
|
|
|
ClutterCanvasPrivate *priv = self->priv;
|
|
|
|
|
|
|
|
if (priv->buffer != NULL)
|
|
|
|
{
|
|
|
|
cogl_object_unref (priv->buffer);
|
|
|
|
priv->buffer = NULL;
|
|
|
|
}
|
|
|
|
|
2012-03-29 12:35:50 +00:00
|
|
|
if (priv->width <= 0 || priv->height <= 0)
|
2012-03-08 10:31:21 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
clutter_canvas_emit_draw (self);
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
clutter_canvas_get_preferred_size (ClutterContent *content,
|
|
|
|
gfloat *width,
|
|
|
|
gfloat *height)
|
|
|
|
{
|
|
|
|
ClutterCanvasPrivate *priv = CLUTTER_CANVAS (content)->priv;
|
|
|
|
|
|
|
|
if (priv->width < 0 || priv->height < 0)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (width != NULL)
|
|
|
|
*width = priv->width;
|
|
|
|
|
|
|
|
if (height != NULL)
|
|
|
|
*height = priv->height;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_content_iface_init (ClutterContentIface *iface)
|
|
|
|
{
|
|
|
|
iface->invalidate = clutter_canvas_invalidate;
|
|
|
|
iface->paint_content = clutter_canvas_paint_content;
|
|
|
|
iface->get_preferred_size = clutter_canvas_get_preferred_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_canvas_new:
|
|
|
|
*
|
|
|
|
* Creates a new instance of #ClutterCanvas.
|
|
|
|
*
|
|
|
|
* You should call clutter_canvas_set_size() to set the size of the canvas.
|
|
|
|
*
|
|
|
|
* You should call clutter_content_invalidate() every time you wish to
|
|
|
|
* draw the contents of the canvas.
|
|
|
|
*
|
|
|
|
* Return value: (transfer full): The newly allocated instance of
|
|
|
|
* #ClutterCanvas. Use g_object_unref() when done.
|
|
|
|
*
|
|
|
|
* Since: 1.10
|
|
|
|
*/
|
|
|
|
ClutterContent *
|
|
|
|
clutter_canvas_new (void)
|
|
|
|
{
|
|
|
|
return g_object_new (CLUTTER_TYPE_CANVAS, NULL);
|
|
|
|
}
|
|
|
|
|
2013-05-15 19:08:02 +00:00
|
|
|
static gboolean
|
canvas: Allow invalidating the content along with the size
Currently, clutter_canvas_set_size() causes invalidation of the canvas
contents only if the newly set size is different. There are cases when
we want to invalidate the content regardless of the size set, but we
cannot do that right now without possibly causing two invalidations,
for instance:
clutter_canvas_set_size (canvas, new_width, new_height);
clutter_content_invalidate (canvas);
will cause two invalidations if the newly set size is different than
the existing one. One way to work around it is to check the current
size of the canvas and either call set_size() or invalidate() depending
on whether the size differs or not, respectively:
g_object_get (canvas, "width", &width, "height", &height, NULL);
if (width != new_width || height != new_height)
clutter_canvas_set_size (canvas, new_width, new_height);
else
clutter_content_invalidate (canvas);
this, howevere, implies knowledge of the internals of ClutterCanvas,
and of its optimizations — and encodes a certain behaviour in third
party code, which makes changes further down the line harder.
We could remove the optimization, and just issue an invalidation
regardless of the surface size, but it's not something I'd be happy to
do. Instead, we can add a new function specifically for ClutterCanvas
that causes a forced invalidation regardless of the size change. If we
ever decide to remove the optimization further down the road, we can
simply deprecate the function, and make it an alias of invalidate()
or set_size().
2013-04-11 12:05:03 +00:00
|
|
|
clutter_canvas_invalidate_internal (ClutterCanvas *canvas,
|
|
|
|
int width,
|
2013-05-15 19:08:02 +00:00
|
|
|
int height)
|
2012-03-08 10:31:21 +00:00
|
|
|
{
|
|
|
|
gboolean width_changed = FALSE, height_changed = FALSE;
|
2013-05-15 19:08:02 +00:00
|
|
|
gboolean res = FALSE;
|
canvas: Allow invalidating the content along with the size
Currently, clutter_canvas_set_size() causes invalidation of the canvas
contents only if the newly set size is different. There are cases when
we want to invalidate the content regardless of the size set, but we
cannot do that right now without possibly causing two invalidations,
for instance:
clutter_canvas_set_size (canvas, new_width, new_height);
clutter_content_invalidate (canvas);
will cause two invalidations if the newly set size is different than
the existing one. One way to work around it is to check the current
size of the canvas and either call set_size() or invalidate() depending
on whether the size differs or not, respectively:
g_object_get (canvas, "width", &width, "height", &height, NULL);
if (width != new_width || height != new_height)
clutter_canvas_set_size (canvas, new_width, new_height);
else
clutter_content_invalidate (canvas);
this, howevere, implies knowledge of the internals of ClutterCanvas,
and of its optimizations — and encodes a certain behaviour in third
party code, which makes changes further down the line harder.
We could remove the optimization, and just issue an invalidation
regardless of the surface size, but it's not something I'd be happy to
do. Instead, we can add a new function specifically for ClutterCanvas
that causes a forced invalidation regardless of the size change. If we
ever decide to remove the optimization further down the road, we can
simply deprecate the function, and make it an alias of invalidate()
or set_size().
2013-04-11 12:05:03 +00:00
|
|
|
GObject *obj;
|
2012-03-08 10:31:21 +00:00
|
|
|
|
|
|
|
obj = G_OBJECT (canvas);
|
|
|
|
|
|
|
|
g_object_freeze_notify (obj);
|
|
|
|
|
|
|
|
if (canvas->priv->width != width)
|
|
|
|
{
|
|
|
|
canvas->priv->width = width;
|
|
|
|
width_changed = TRUE;
|
|
|
|
|
|
|
|
g_object_notify_by_pspec (obj, obj_props[PROP_WIDTH]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (canvas->priv->height != height)
|
|
|
|
{
|
|
|
|
canvas->priv->height = height;
|
|
|
|
height_changed = TRUE;
|
|
|
|
|
|
|
|
g_object_notify_by_pspec (obj, obj_props[PROP_HEIGHT]);
|
|
|
|
}
|
|
|
|
|
2013-05-15 19:08:02 +00:00
|
|
|
if (width_changed || height_changed)
|
|
|
|
{
|
|
|
|
clutter_content_invalidate (CLUTTER_CONTENT (canvas));
|
|
|
|
res = TRUE;
|
|
|
|
}
|
2012-03-08 10:31:21 +00:00
|
|
|
|
|
|
|
g_object_thaw_notify (obj);
|
2013-05-15 19:08:02 +00:00
|
|
|
|
|
|
|
return res;
|
2012-03-08 10:31:21 +00:00
|
|
|
}
|
canvas: Allow invalidating the content along with the size
Currently, clutter_canvas_set_size() causes invalidation of the canvas
contents only if the newly set size is different. There are cases when
we want to invalidate the content regardless of the size set, but we
cannot do that right now without possibly causing two invalidations,
for instance:
clutter_canvas_set_size (canvas, new_width, new_height);
clutter_content_invalidate (canvas);
will cause two invalidations if the newly set size is different than
the existing one. One way to work around it is to check the current
size of the canvas and either call set_size() or invalidate() depending
on whether the size differs or not, respectively:
g_object_get (canvas, "width", &width, "height", &height, NULL);
if (width != new_width || height != new_height)
clutter_canvas_set_size (canvas, new_width, new_height);
else
clutter_content_invalidate (canvas);
this, howevere, implies knowledge of the internals of ClutterCanvas,
and of its optimizations — and encodes a certain behaviour in third
party code, which makes changes further down the line harder.
We could remove the optimization, and just issue an invalidation
regardless of the surface size, but it's not something I'd be happy to
do. Instead, we can add a new function specifically for ClutterCanvas
that causes a forced invalidation regardless of the size change. If we
ever decide to remove the optimization further down the road, we can
simply deprecate the function, and make it an alias of invalidate()
or set_size().
2013-04-11 12:05:03 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_canvas_set_size:
|
|
|
|
* @canvas: a #ClutterCanvas
|
|
|
|
* @width: the width of the canvas, in pixels
|
|
|
|
* @height: the height of the canvas, in pixels
|
|
|
|
*
|
|
|
|
* Sets the size of the @canvas, and invalidates the content.
|
|
|
|
*
|
|
|
|
* This function will cause the @canvas to be invalidated only
|
|
|
|
* if the size of the canvas surface has changed.
|
|
|
|
*
|
2013-05-15 19:08:02 +00:00
|
|
|
* If you want to invalidate the contents of the @canvas when setting
|
|
|
|
* the size, you can use the return value of the function to conditionally
|
|
|
|
* call clutter_content_invalidate():
|
|
|
|
*
|
|
|
|
* |[
|
|
|
|
* if (!clutter_canvas_set_size (canvas, width, height))
|
|
|
|
* clutter_content_invalidate (CLUTTER_CONTENT (canvas));
|
|
|
|
* ]|
|
|
|
|
*
|
|
|
|
* Return value: this function returns %TRUE if the size change
|
|
|
|
* caused a content invalidation, and %FALSE otherwise
|
|
|
|
*
|
canvas: Allow invalidating the content along with the size
Currently, clutter_canvas_set_size() causes invalidation of the canvas
contents only if the newly set size is different. There are cases when
we want to invalidate the content regardless of the size set, but we
cannot do that right now without possibly causing two invalidations,
for instance:
clutter_canvas_set_size (canvas, new_width, new_height);
clutter_content_invalidate (canvas);
will cause two invalidations if the newly set size is different than
the existing one. One way to work around it is to check the current
size of the canvas and either call set_size() or invalidate() depending
on whether the size differs or not, respectively:
g_object_get (canvas, "width", &width, "height", &height, NULL);
if (width != new_width || height != new_height)
clutter_canvas_set_size (canvas, new_width, new_height);
else
clutter_content_invalidate (canvas);
this, howevere, implies knowledge of the internals of ClutterCanvas,
and of its optimizations — and encodes a certain behaviour in third
party code, which makes changes further down the line harder.
We could remove the optimization, and just issue an invalidation
regardless of the surface size, but it's not something I'd be happy to
do. Instead, we can add a new function specifically for ClutterCanvas
that causes a forced invalidation regardless of the size change. If we
ever decide to remove the optimization further down the road, we can
simply deprecate the function, and make it an alias of invalidate()
or set_size().
2013-04-11 12:05:03 +00:00
|
|
|
* Since: 1.10
|
|
|
|
*/
|
2013-05-15 19:08:02 +00:00
|
|
|
gboolean
|
canvas: Allow invalidating the content along with the size
Currently, clutter_canvas_set_size() causes invalidation of the canvas
contents only if the newly set size is different. There are cases when
we want to invalidate the content regardless of the size set, but we
cannot do that right now without possibly causing two invalidations,
for instance:
clutter_canvas_set_size (canvas, new_width, new_height);
clutter_content_invalidate (canvas);
will cause two invalidations if the newly set size is different than
the existing one. One way to work around it is to check the current
size of the canvas and either call set_size() or invalidate() depending
on whether the size differs or not, respectively:
g_object_get (canvas, "width", &width, "height", &height, NULL);
if (width != new_width || height != new_height)
clutter_canvas_set_size (canvas, new_width, new_height);
else
clutter_content_invalidate (canvas);
this, howevere, implies knowledge of the internals of ClutterCanvas,
and of its optimizations — and encodes a certain behaviour in third
party code, which makes changes further down the line harder.
We could remove the optimization, and just issue an invalidation
regardless of the surface size, but it's not something I'd be happy to
do. Instead, we can add a new function specifically for ClutterCanvas
that causes a forced invalidation regardless of the size change. If we
ever decide to remove the optimization further down the road, we can
simply deprecate the function, and make it an alias of invalidate()
or set_size().
2013-04-11 12:05:03 +00:00
|
|
|
clutter_canvas_set_size (ClutterCanvas *canvas,
|
|
|
|
int width,
|
|
|
|
int height)
|
|
|
|
{
|
2013-05-15 19:08:02 +00:00
|
|
|
g_return_val_if_fail (CLUTTER_IS_CANVAS (canvas), FALSE);
|
|
|
|
g_return_val_if_fail (width >= -1 && height >= -1, FALSE);
|
canvas: Allow invalidating the content along with the size
Currently, clutter_canvas_set_size() causes invalidation of the canvas
contents only if the newly set size is different. There are cases when
we want to invalidate the content regardless of the size set, but we
cannot do that right now without possibly causing two invalidations,
for instance:
clutter_canvas_set_size (canvas, new_width, new_height);
clutter_content_invalidate (canvas);
will cause two invalidations if the newly set size is different than
the existing one. One way to work around it is to check the current
size of the canvas and either call set_size() or invalidate() depending
on whether the size differs or not, respectively:
g_object_get (canvas, "width", &width, "height", &height, NULL);
if (width != new_width || height != new_height)
clutter_canvas_set_size (canvas, new_width, new_height);
else
clutter_content_invalidate (canvas);
this, howevere, implies knowledge of the internals of ClutterCanvas,
and of its optimizations — and encodes a certain behaviour in third
party code, which makes changes further down the line harder.
We could remove the optimization, and just issue an invalidation
regardless of the surface size, but it's not something I'd be happy to
do. Instead, we can add a new function specifically for ClutterCanvas
that causes a forced invalidation regardless of the size change. If we
ever decide to remove the optimization further down the road, we can
simply deprecate the function, and make it an alias of invalidate()
or set_size().
2013-04-11 12:05:03 +00:00
|
|
|
|
2013-05-15 19:08:02 +00:00
|
|
|
return clutter_canvas_invalidate_internal (canvas, width, height);
|
canvas: Allow invalidating the content along with the size
Currently, clutter_canvas_set_size() causes invalidation of the canvas
contents only if the newly set size is different. There are cases when
we want to invalidate the content regardless of the size set, but we
cannot do that right now without possibly causing two invalidations,
for instance:
clutter_canvas_set_size (canvas, new_width, new_height);
clutter_content_invalidate (canvas);
will cause two invalidations if the newly set size is different than
the existing one. One way to work around it is to check the current
size of the canvas and either call set_size() or invalidate() depending
on whether the size differs or not, respectively:
g_object_get (canvas, "width", &width, "height", &height, NULL);
if (width != new_width || height != new_height)
clutter_canvas_set_size (canvas, new_width, new_height);
else
clutter_content_invalidate (canvas);
this, howevere, implies knowledge of the internals of ClutterCanvas,
and of its optimizations — and encodes a certain behaviour in third
party code, which makes changes further down the line harder.
We could remove the optimization, and just issue an invalidation
regardless of the surface size, but it's not something I'd be happy to
do. Instead, we can add a new function specifically for ClutterCanvas
that causes a forced invalidation regardless of the size change. If we
ever decide to remove the optimization further down the road, we can
simply deprecate the function, and make it an alias of invalidate()
or set_size().
2013-04-11 12:05:03 +00:00
|
|
|
}
|
2014-01-17 11:03:15 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_canvas_set_scale_factor:
|
|
|
|
* @canvas: a #ClutterCanvas
|
|
|
|
* @scale: the scale factor, or -1 for the default
|
|
|
|
*
|
|
|
|
* Sets the scaling factor for the Cairo surface used by @canvas.
|
|
|
|
*
|
|
|
|
* This function should rarely be used.
|
|
|
|
*
|
|
|
|
* The default scaling factor of a #ClutterCanvas content uses the
|
|
|
|
* #ClutterSettings:window-scaling-factor property, which is set by
|
|
|
|
* the windowing system. By using this function it is possible to
|
|
|
|
* override that setting.
|
|
|
|
*
|
|
|
|
* Changing the scale factor will invalidate the @canvas.
|
|
|
|
*
|
|
|
|
* Since: 1.18
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
clutter_canvas_set_scale_factor (ClutterCanvas *canvas,
|
|
|
|
int scale)
|
|
|
|
{
|
|
|
|
ClutterCanvasPrivate *priv;
|
|
|
|
GObject *obj;
|
|
|
|
|
|
|
|
g_return_if_fail (CLUTTER_IS_CANVAS (canvas));
|
|
|
|
g_return_if_fail (scale != 0);
|
|
|
|
|
|
|
|
priv = canvas->priv;
|
|
|
|
|
|
|
|
if (scale < 0)
|
|
|
|
{
|
|
|
|
if (!priv->scale_factor_set)
|
|
|
|
return;
|
|
|
|
|
|
|
|
priv->scale_factor_set = FALSE;
|
|
|
|
priv->scale_factor = -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (priv->scale_factor_set && priv->scale_factor == scale)
|
|
|
|
return;
|
|
|
|
|
|
|
|
priv->scale_factor_set = TRUE;
|
|
|
|
priv->scale_factor = scale;
|
|
|
|
}
|
|
|
|
|
|
|
|
clutter_content_invalidate (CLUTTER_CONTENT (canvas));
|
|
|
|
|
|
|
|
obj = G_OBJECT (canvas);
|
|
|
|
|
|
|
|
g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_FACTOR]);
|
|
|
|
g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_FACTOR_SET]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_canvas_get_scale_factor:
|
|
|
|
* @canvas: a #ClutterCanvas
|
|
|
|
*
|
|
|
|
* Retrieves the scaling factor of @canvas, as set using
|
|
|
|
* clutter_canvas_set_scale_factor().
|
|
|
|
*
|
|
|
|
* Return value: the scaling factor, or -1 if the @canvas
|
|
|
|
* uses the default from #ClutterSettings
|
|
|
|
*
|
|
|
|
* Since: 1.18
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
clutter_canvas_get_scale_factor (ClutterCanvas *canvas)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_CANVAS (canvas), -1);
|
|
|
|
|
|
|
|
if (!canvas->priv->scale_factor_set)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return canvas->priv->scale_factor;
|
|
|
|
}
|