Bug 22803 - Make header conform to implementation
[swfdec.git] / swfdec / swfdec_renderer.c
blob59137199e3ea610ba69d608bb5122a817dc5c5f2
1 /* Swfdec
2 * Copyright (C) 2008 Benjamin Otte <otte@gnome.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301 USA
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
24 #include "swfdec_renderer.h"
25 #include "swfdec_renderer_internal.h"
26 #include "swfdec_cache.h"
27 #include "swfdec_player_internal.h"
29 struct _SwfdecRendererPrivate {
30 cairo_surface_t * surface; /* the surface we assume we render to */
31 SwfdecCache * cache; /* the cache we use for cached items */
32 GHashTable * cache_lookup; /* gpointer => GList mapping */
35 /*** GTK-DOC ***/
37 /**
38 * SECTION:SwfdecRenderer
39 * @title: SwfdecRenderer
40 * @short_description: provide accelerated rendering and caching abilities
42 * The #SwfdecRenderer object is used internally to improve rendering done by
43 * Swfdec.
45 * The first thing a #SwfdecRenderer does is provide a way to cache data relevant
46 * to rendering. This means it will cache surfaces that are expensive to create
47 * (like decoded JPEG images) in a format most suitable to quick rendering.
48 * Therefore, it's a good idea to keep renderers around as long as any drawing
49 * to the attached surface happens.
51 * The second thing a #SwfdecRenderer does is provide access to the surface
52 * that is used for rendering, even when not in the process of rendering. This
53 * is relevant for font backends, as different surfaces provide different
54 * native fonts. See swfdec_player_set_default_backend() for details about this.
56 * The third thing #SwfdecRenderer does is provide a list of virtual functions
57 * for critical operations that you can optimize using subclasses to provide
58 * faster implementations. Note that a working default implementation is
59 * provided, so you only need to override the functions you care about.
60 * See #SwfdecRendererClass for details about these functions.
63 /**
64 * SwfdecRenderer:
66 * The base renderer object. All its members are private.
69 /**
70 * SwfdecRendererClass:
71 * @create_similar: This function imitates cairo_surface_create_similar(). It
72 * is supposed to create a surface with identical contents as
73 * the given surface, but tuned for usage with the given
74 * renderer.
75 * @create_for_data: This function imitates cairo_surface_create_for_data(). It
76 * creates a surface for the given data, tuned for the given
77 * renderer. The function takes ownership of the passed in
78 * data and is responsible for freeing it with g_free() when
79 * it no longer needs it.
81 * The base class for the renderer. It contains some virtual functions that can
82 * be overridden in subclasses to accelerate certain time-critical Swfdec
83 * functions. For example, a subclass could make use of special hardware
84 * features on embedded devices.
87 /*** OBJECT ***/
89 G_DEFINE_TYPE (SwfdecRenderer, swfdec_renderer, G_TYPE_OBJECT)
91 enum {
92 PROP_0,
93 PROP_SURFACE
96 static void
97 swfdec_renderer_dispose (GObject *object)
99 SwfdecRendererPrivate *priv = SWFDEC_RENDERER (object)->priv;
101 if (priv->surface) {
102 cairo_surface_destroy (priv->surface);
103 priv->surface = NULL;
105 if (priv->cache_lookup) {
106 GHashTableIter iter;
107 GList *walk;
108 gpointer list;
109 g_hash_table_iter_init (&iter, priv->cache_lookup);
110 while (g_hash_table_iter_next (&iter, NULL, &list)) {
111 for (walk = list; walk; walk = walk->next) {
112 if (walk->data)
113 g_object_remove_weak_pointer (walk->data, &walk->data);
115 g_list_free (list);
117 g_hash_table_destroy (priv->cache_lookup);
118 priv->cache_lookup = NULL;
120 if (priv->cache) {
121 g_object_unref (priv->cache);
122 priv->cache = NULL;
125 G_OBJECT_CLASS (swfdec_renderer_parent_class)->dispose (object);
128 static void
129 swfdec_renderer_get_property (GObject *object, guint param_id, GValue *value,
130 GParamSpec *pspec)
132 SwfdecRendererPrivate *priv = SWFDEC_RENDERER (object)->priv;
134 switch (param_id) {
135 case PROP_SURFACE:
136 g_value_set_pointer (value, priv->surface);
137 break;
138 default:
139 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
140 break;
144 static void
145 swfdec_renderer_set_property (GObject *object, guint param_id, const GValue *value,
146 GParamSpec *pspec)
148 SwfdecRendererPrivate *priv = SWFDEC_RENDERER (object)->priv;
150 switch (param_id) {
151 case PROP_SURFACE:
152 priv->surface = g_value_get_pointer (value);
153 g_assert (priv->surface != NULL);
154 cairo_surface_reference (priv->surface);
155 break;
156 default:
157 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
158 break;
162 static cairo_surface_t *
163 swfdec_renderer_do_create_similar (SwfdecRenderer *renderer, cairo_surface_t *surface)
165 SwfdecRendererPrivate *priv;
166 cairo_surface_t *result;
167 cairo_t *cr;
169 priv = renderer->priv;
170 if (cairo_surface_get_type (priv->surface) == CAIRO_SURFACE_TYPE_IMAGE)
171 return surface;
173 result = cairo_surface_create_similar (priv->surface,
174 cairo_surface_get_content (surface),
175 cairo_image_surface_get_width (surface),
176 cairo_image_surface_get_height (surface));
177 cr = cairo_create (result);
179 cairo_set_source_surface (cr, surface, 0, 0);
180 cairo_paint (cr);
182 cairo_destroy (cr);
183 cairo_surface_destroy (surface);
184 return result;
187 static cairo_surface_t *
188 swfdec_renderer_do_create_for_data (SwfdecRenderer *renderer, guint8 *data,
189 cairo_format_t format, guint width, guint height, guint rowstride)
191 static const cairo_user_data_key_t key;
192 cairo_surface_t *surface;
194 surface = cairo_image_surface_create_for_data (data, format, width, height, rowstride);
195 cairo_surface_set_user_data (surface, &key, data, g_free);
196 return swfdec_renderer_create_similar (renderer, surface);
199 static void
200 swfdec_renderer_class_init (SwfdecRendererClass *klass)
202 GObjectClass *object_class = G_OBJECT_CLASS (klass);
204 g_type_class_add_private (klass, sizeof (SwfdecRendererPrivate));
206 object_class->dispose = swfdec_renderer_dispose;
207 object_class->get_property = swfdec_renderer_get_property;
208 object_class->set_property = swfdec_renderer_set_property;
210 /* FIXME: should be g_param_spec_size(), but no such thing exists */
211 g_object_class_install_property (object_class, PROP_SURFACE,
212 g_param_spec_pointer ("surface", "surface", "cairo surface in use by this renderer",
213 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
215 klass->create_similar = swfdec_renderer_do_create_similar;
216 klass->create_for_data = swfdec_renderer_do_create_for_data;
219 static void
220 swfdec_renderer_init (SwfdecRenderer *renderer)
222 SwfdecRendererPrivate *priv;
224 renderer->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (renderer, SWFDEC_TYPE_RENDERER, SwfdecRendererPrivate);
226 priv->cache = swfdec_cache_new (8 * 1024 * 1024);
227 priv->cache_lookup = g_hash_table_new (g_direct_hash, g_direct_equal);
230 /*** INTERNAL API ***/
232 void
233 swfdec_renderer_add_cache (SwfdecRenderer *renderer, gboolean replace,
234 gpointer key, SwfdecCached *cached)
236 SwfdecRendererPrivate *priv;
237 GList *list;
239 g_return_if_fail (SWFDEC_IS_RENDERER (renderer));
240 g_return_if_fail (key != NULL);
241 g_return_if_fail (SWFDEC_IS_CACHED (cached));
243 priv = renderer->priv;
244 list = g_hash_table_lookup (priv->cache_lookup, key);
245 if (replace) {
246 GList *walk;
247 for (walk = list; walk; walk = walk->next) {
248 if (walk->data) {
249 g_object_remove_weak_pointer (walk->data, &walk->data);
250 swfdec_cached_unuse (walk->data);
253 g_list_free (list);
254 list = NULL;
256 list = g_list_prepend (list, cached);
257 /* NB: empty list entries mean object was deleted */
258 g_object_add_weak_pointer (G_OBJECT (cached), &list->data);
259 g_hash_table_insert (priv->cache_lookup, key, list);
260 swfdec_cache_add (priv->cache, cached);
263 SwfdecCached *
264 swfdec_renderer_get_cache (SwfdecRenderer *renderer, gpointer key,
265 SwfdecRendererSearchFunc func, gpointer data)
267 SwfdecRendererPrivate *priv;
268 GList *list, *org, *walk;
269 SwfdecCached *result = NULL;
271 g_return_val_if_fail (SWFDEC_IS_RENDERER (renderer), NULL);
272 g_return_val_if_fail (key != NULL, NULL);
274 priv = renderer->priv;
275 org = g_hash_table_lookup (priv->cache_lookup, key);
276 list = org;
277 walk = list;
278 while (walk) {
279 /* NB: empty list entries mean object was deleted */
280 if (walk->data == NULL) {
281 GList *next = walk->next;
282 list = g_list_delete_link (list, walk);
283 walk = next;
284 continue;
286 if (!func || func (walk->data, data)) {
287 result = walk->data;
288 break;
290 walk = walk->next;
292 if (org != list)
293 g_hash_table_insert (priv->cache_lookup, key, list);
294 return result;
297 gsize
298 swfdec_renderer_get_max_cache_size (SwfdecRenderer *renderer)
300 g_return_val_if_fail (SWFDEC_IS_RENDERER (renderer), 0);
302 return swfdec_cache_get_max_cache_size (renderer->priv->cache);
305 SwfdecRenderer *
306 swfdec_renderer_new_default (SwfdecPlayer *player)
308 cairo_surface_t *surface;
309 SwfdecRenderer *renderer;
311 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
313 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 1, 1);
314 renderer = swfdec_renderer_new_for_player (surface, player);
315 cairo_surface_destroy (surface);
316 return renderer;
319 static const cairo_user_data_key_t cr_key;
320 static const cairo_user_data_key_t matrix_key;
322 void
323 swfdec_renderer_attach (SwfdecRenderer *renderer, cairo_t *cr)
325 cairo_matrix_t *matrix;
327 g_return_if_fail (SWFDEC_IS_RENDERER (renderer));
328 g_return_if_fail (cr != NULL);
330 g_object_ref (renderer);
331 if (cairo_set_user_data (cr, &cr_key, renderer, g_object_unref) != CAIRO_STATUS_SUCCESS) {
332 g_warning ("could not attach user data");
334 matrix = g_new (cairo_matrix_t, 1);
335 cairo_get_matrix (cr, matrix);
336 if (cairo_set_user_data (cr, &matrix_key, matrix, g_free) != CAIRO_STATUS_SUCCESS) {
337 g_warning ("could not attach user data");
341 SwfdecRenderer *
342 swfdec_renderer_get (cairo_t *cr)
344 g_return_val_if_fail (cr != NULL, NULL);
346 return cairo_get_user_data (cr, &cr_key);
349 void
350 swfdec_renderer_reset_matrix (cairo_t *cr)
352 cairo_matrix_t *matrix;
354 g_return_if_fail (cr != NULL);
356 matrix = cairo_get_user_data (cr, &matrix_key);
357 g_return_if_fail (matrix != NULL);
358 cairo_set_matrix (cr, matrix);
362 * swfdec_renderer_create_similar:
363 * @renderer: a renderer
364 * @surface: an image surface; this function takes ownership of the passed-in image.
366 * Creates a surface with the same contents and size as the given image
367 * @surface, but optimized for use in @renderer. You should use this function
368 * before caching a surface.
370 * Returns: A surface with the same contents as @surface. You own a reference to the
371 * returned surface.
373 cairo_surface_t *
374 swfdec_renderer_create_similar (SwfdecRenderer *renderer, cairo_surface_t *surface)
376 SwfdecRendererClass *klass;
378 g_return_val_if_fail (SWFDEC_IS_RENDERER (renderer), NULL);
379 g_return_val_if_fail (surface != NULL, NULL);
380 g_return_val_if_fail (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE, NULL);
382 klass = SWFDEC_RENDERER_GET_CLASS (renderer);
383 return klass->create_similar (renderer, surface);
386 /* FIXME: get data parameter const */
387 cairo_surface_t *
388 swfdec_renderer_create_for_data (SwfdecRenderer *renderer, guint8 *data,
389 cairo_format_t format, guint width, guint height, guint rowstride)
391 SwfdecRendererClass *klass;
393 g_return_val_if_fail (SWFDEC_IS_RENDERER (renderer), NULL);
394 g_return_val_if_fail (data != NULL, NULL);
395 g_return_val_if_fail (width > 0, NULL);
396 g_return_val_if_fail (height > 0, NULL);
397 g_return_val_if_fail (rowstride > 0, NULL);
399 klass = SWFDEC_RENDERER_GET_CLASS (renderer);
400 return klass->create_for_data (renderer, data, format, width, height, rowstride);
403 cairo_surface_t *
404 swfdec_renderer_transform (SwfdecRenderer *renderer, cairo_surface_t *surface,
405 const SwfdecColorTransform *trans, const SwfdecRectangle *rect)
407 cairo_surface_t *target;
408 guint stride, color;
409 SwfdecColor mask;
410 guint8 *data;
411 cairo_t *cr;
412 int x, y;
414 g_return_val_if_fail (SWFDEC_IS_RENDERER (renderer), NULL);
415 g_return_val_if_fail (surface != NULL, NULL);
416 g_return_val_if_fail (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE, NULL);
417 g_return_val_if_fail (trans != NULL, NULL);
418 g_return_val_if_fail (!swfdec_color_transform_is_mask (trans), NULL);
419 g_return_val_if_fail (rect != NULL, NULL);
420 g_return_val_if_fail (rect->x >= 0, NULL);
421 g_return_val_if_fail (rect->y >= 0, NULL);
422 g_return_val_if_fail (rect->x + rect->width <= cairo_image_surface_get_width (surface), NULL);
423 g_return_val_if_fail (rect->y + rect->height <= cairo_image_surface_get_height (surface), NULL);
425 /* FIXME: This function should likely be a vfunc.
426 * Or better: it should compile to a shader */
427 if (cairo_surface_get_content (surface) & CAIRO_CONTENT_ALPHA ||
428 trans->aa < 256 || trans->ab < 0) {
429 target = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, rect->width, rect->height);
430 mask = 0;
431 } else {
432 target = cairo_image_surface_create (CAIRO_FORMAT_RGB24, rect->width, rect->height);
433 mask = SWFDEC_COLOR_COMBINE (0, 0, 0, 0xFF);
435 cairo_surface_set_device_offset (target, -rect->x, -rect->y);
437 cr = cairo_create (target);
438 cairo_set_source_surface (cr, surface, 0, 0);
439 cairo_paint (cr);
440 cairo_destroy (cr);
442 data = cairo_image_surface_get_data (target);
443 stride = cairo_image_surface_get_stride (target);
444 for (y = 0; y < rect->height; y++) {
445 for (x = 0; x < rect->width; x++) {
446 color = ((guint32 *) (gpointer) data)[x];
447 color |= mask;
448 color = swfdec_color_apply_transform_premultiplied (color, trans);
449 ((guint32 *) (gpointer) data)[x] = color;
451 data += stride;
453 cairo_surface_mark_dirty (target);
455 return target;
458 /*** PUBLIC API ***/
461 * swfdec_renderer_new:
462 * @surface: a cairo surface
464 * Creates a new renderer to be used with the given @surface.
466 * Returns: a new renderer
468 SwfdecRenderer *
469 swfdec_renderer_new (cairo_surface_t *surface)
471 g_return_val_if_fail (surface != NULL, NULL);
473 return g_object_new (SWFDEC_TYPE_RENDERER, "surface", surface, NULL);
477 * swfdec_renderer_new_for_player:
478 * @surface: a cairo surface
479 * @player: the player this renderer should be used with
481 * Creates a renderer to be used with the given @surface and @player. The
482 * renderer will use the same cache as the @player.
484 * Returns: a new renderer
486 SwfdecRenderer *
487 swfdec_renderer_new_for_player (cairo_surface_t *surface, SwfdecPlayer *player)
489 SwfdecRendererPrivate *priv;
490 SwfdecRenderer *renderer;
492 g_return_val_if_fail (surface != NULL, NULL);
493 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
495 renderer = swfdec_renderer_new (surface);
496 priv = renderer->priv;
497 g_object_unref (priv->cache);
498 priv->cache = g_object_ref (player->priv->cache);
500 return renderer;
504 * swfdec_renderer_get_surface:
505 * @renderer: a renderer
507 * Gets the surface that was used when creating this surface.
509 * Returns: the surface used by this renderer
511 cairo_surface_t *
512 swfdec_renderer_get_surface (SwfdecRenderer *renderer)
514 g_return_val_if_fail (SWFDEC_IS_RENDERER (renderer), NULL);
516 return renderer->priv->surface;