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.
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
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 */
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
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.
66 * The base renderer object. All its members are private.
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
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.
89 G_DEFINE_TYPE (SwfdecRenderer
, swfdec_renderer
, G_TYPE_OBJECT
)
97 swfdec_renderer_dispose (GObject
*object
)
99 SwfdecRendererPrivate
*priv
= SWFDEC_RENDERER (object
)->priv
;
102 cairo_surface_destroy (priv
->surface
);
103 priv
->surface
= NULL
;
105 if (priv
->cache_lookup
) {
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
) {
113 g_object_remove_weak_pointer (walk
->data
, &walk
->data
);
117 g_hash_table_destroy (priv
->cache_lookup
);
118 priv
->cache_lookup
= NULL
;
121 g_object_unref (priv
->cache
);
125 G_OBJECT_CLASS (swfdec_renderer_parent_class
)->dispose (object
);
129 swfdec_renderer_get_property (GObject
*object
, guint param_id
, GValue
*value
,
132 SwfdecRendererPrivate
*priv
= SWFDEC_RENDERER (object
)->priv
;
136 g_value_set_pointer (value
, priv
->surface
);
139 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, param_id
, pspec
);
145 swfdec_renderer_set_property (GObject
*object
, guint param_id
, const GValue
*value
,
148 SwfdecRendererPrivate
*priv
= SWFDEC_RENDERER (object
)->priv
;
152 priv
->surface
= g_value_get_pointer (value
);
153 g_assert (priv
->surface
!= NULL
);
154 cairo_surface_reference (priv
->surface
);
157 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, param_id
, pspec
);
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
;
169 priv
= renderer
->priv
;
170 if (cairo_surface_get_type (priv
->surface
) == CAIRO_SURFACE_TYPE_IMAGE
)
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);
183 cairo_surface_destroy (surface
);
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
);
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
;
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 ***/
233 swfdec_renderer_add_cache (SwfdecRenderer
*renderer
, gboolean replace
,
234 gpointer key
, SwfdecCached
*cached
)
236 SwfdecRendererPrivate
*priv
;
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
);
247 for (walk
= list
; walk
; walk
= walk
->next
) {
249 g_object_remove_weak_pointer (walk
->data
, &walk
->data
);
250 swfdec_cached_unuse (walk
->data
);
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
);
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
);
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
);
286 if (!func
|| func (walk
->data
, data
)) {
293 g_hash_table_insert (priv
->cache_lookup
, key
, list
);
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
);
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
);
319 static const cairo_user_data_key_t cr_key
;
320 static const cairo_user_data_key_t matrix_key
;
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");
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
);
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
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 */
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
);
404 swfdec_renderer_transform (SwfdecRenderer
*renderer
, cairo_surface_t
*surface
,
405 const SwfdecColorTransform
*trans
, const SwfdecRectangle
*rect
)
407 cairo_surface_t
*target
;
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
);
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);
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
];
448 color
= swfdec_color_apply_transform_premultiplied (color
, trans
);
449 ((guint32
*) (gpointer
) data
)[x
] = color
;
453 cairo_surface_mark_dirty (target
);
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
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
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
);
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
512 swfdec_renderer_get_surface (SwfdecRenderer
*renderer
)
514 g_return_val_if_fail (SWFDEC_IS_RENDERER (renderer
), NULL
);
516 return renderer
->priv
->surface
;