[Swfdec-commits] 5 commits - README swfdec/Makefile.am swfdec/swfdec_blur_filter.c swfdec/swfdec_blur_filter.h swfdec/swfdec_button.c swfdec/swfdec_button_movie.c swfdec/swfdec_convolution_matrix.c swfdec/swfdec_convolution_matrix.h swfdec/swfdec_filter_as.c swfdec/swfdec_filter.c swfdec/swfdec_filter.h swfdec/swfdec_movie.c swfdec/swfdec_movie.h swfdec/swfdec_renderer.c swfdec/swfdec_sprite_movie_as.c swfdec/swfdec_sprite_movie.c

Benjamin Otte company at kemper.freedesktop.org
Sun Sep 14 14:39:04 PDT 2008


 README                             |    2 
 swfdec/Makefile.am                 |    2 
 swfdec/swfdec_blur_filter.c        |  160 ++++++++++++++++++++++++++++++++++++-
 swfdec/swfdec_blur_filter.h        |    4 
 swfdec/swfdec_button.c             |    6 -
 swfdec/swfdec_button_movie.c       |    6 -
 swfdec/swfdec_convolution_matrix.c |  152 +++++++++++++++++++++++++++++++++++
 swfdec/swfdec_convolution_matrix.h |   54 ++++++++++++
 swfdec/swfdec_filter.c             |  114 ++++++++++++++++++++++++--
 swfdec/swfdec_filter.h             |   24 ++++-
 swfdec/swfdec_filter_as.c          |   21 +---
 swfdec/swfdec_movie.c              |  104 ++++++++++++++++++------
 swfdec/swfdec_movie.h              |    3 
 swfdec/swfdec_renderer.c           |   23 +++--
 swfdec/swfdec_sprite_movie.c       |   17 +++
 swfdec/swfdec_sprite_movie_as.c    |   27 ++++++
 16 files changed, 646 insertions(+), 73 deletions(-)

New commits:
commit 89a900a9aaf4e0e2e15f9742fb6846575e1568c7
Author: Benjamin Otte <otte at gnome.org>
Date:   Sun Sep 14 23:22:48 2008 +0200

    make blur actually work
    
    It's probably still very slow, because cacheAsBitmap isn't implemented

diff --git a/swfdec/Makefile.am b/swfdec/Makefile.am
index 30c974b..f9d8dfe 100644
--- a/swfdec/Makefile.am
+++ b/swfdec/Makefile.am
@@ -68,6 +68,7 @@ libswfdec_source_files = \
 	swfdec_color_transform_as.c \
 	swfdec_constant_pool.c \
 	swfdec_convolution_filter.c \
+	swfdec_convolution_matrix.c \
 	swfdec_debug.c \
 	swfdec_decoder.c \
 	swfdec_displacement_map_filter.c \
@@ -269,6 +270,7 @@ noinst_HEADERS = \
 	swfdec_color.h \
 	swfdec_color_transform_as.h \
 	swfdec_constant_pool.h \
+	swfdec_convolution_matrix.h \
 	swfdec_debug.h \
 	swfdec_decoder.h \
 	swfdec_draw.h \
diff --git a/swfdec/swfdec_blur_filter.c b/swfdec/swfdec_blur_filter.c
index 68f817b..aab6386 100644
--- a/swfdec/swfdec_blur_filter.c
+++ b/swfdec/swfdec_blur_filter.c
@@ -22,13 +22,164 @@
 #endif
 
 #include "swfdec_blur_filter.h"
+
+#include <math.h>
+
 #include "swfdec_debug.h"
 
 G_DEFINE_TYPE (SwfdecBlurFilter, swfdec_blur_filter, SWFDEC_TYPE_FILTER)
 
 static void
+swfdec_blur_filter_clone (SwfdecFilter *dfilter, SwfdecFilter *sfilter)
+{
+  SwfdecBlurFilter *dest = SWFDEC_BLUR_FILTER (dfilter);
+  SwfdecBlurFilter *source = SWFDEC_BLUR_FILTER (sfilter);
+
+  dest->x = source->x;
+  dest->y = source->y;
+  dest->quality = source->quality;
+}
+
+static void
+swfdec_blur_filter_create_convolution_matrix (SwfdecBlurFilter *blur)
+{
+  guint x, y, w, h;
+  double div, blurx, blury;
+
+  if (blur->matrix)
+    return;
+
+  blurx = MAX (blur->x, 1);
+  blury = MAX (blur->y, 1);
+  w = ceil ((blurx - 1) / 2);
+  w = w * 2 + 1;
+  h = ceil ((blury - 1) / 2);
+  h = h * 2 + 1;
+
+  blur->matrix = swfdec_convolution_matrix_new (w, h);
+  div = 1.0 / (blurx * blury);
+  for (y = 0; y < h; y++) {
+    double val = div;
+    if (y == 0 || y == w - 1) {
+      val *= (1 - (h - MAX (blur->y, 1)) / 2);
+    }
+    for (x = 0; x < w; x++) {
+      if (x == 0 || x == w - 1) {
+	swfdec_convolution_matrix_set (blur->matrix, x, y,
+	    val * (1 - (w - MAX (blur->x, 1)) / 2));
+      } else {
+	swfdec_convolution_matrix_set (blur->matrix, x, y, val);
+      }
+    }
+  }
+}
+
+static void
+swfdec_blur_filter_get_rectangle (SwfdecFilter *filter, SwfdecRectangle *dest,
+    const SwfdecRectangle *source)
+{
+  SwfdecBlurFilter *blur = SWFDEC_BLUR_FILTER (filter);
+  guint x, y;
+
+  swfdec_blur_filter_create_convolution_matrix (blur);
+  x = swfdec_convolution_matrix_get_width (blur->matrix) / 2 * blur->quality;
+  y = swfdec_convolution_matrix_get_height (blur->matrix) / 2 * blur->quality;
+
+  dest->x = source->x - x;
+  dest->y = source->y - y;
+  dest->width = source->width + 2 * x;
+  dest->height = source->height + 2 * y;
+}
+
+static cairo_pattern_t *
+swfdec_blur_filter_apply (SwfdecFilter *filter, cairo_pattern_t *pattern,
+    const SwfdecRectangle *rect)
+{
+  SwfdecBlurFilter *blur = SWFDEC_BLUR_FILTER (filter);
+  cairo_surface_t *a, *b;
+  guint i, j, x, y;
+  guint8 *adata, *bdata;
+  guint astride, bstride;
+  cairo_t *cr;
+
+  if (blur->x <= 1.0 && blur->y <= 1.0)
+    return cairo_pattern_reference (pattern);
+
+  swfdec_blur_filter_create_convolution_matrix (blur);
+  x = swfdec_convolution_matrix_get_width (blur->matrix) / 2;
+  y = swfdec_convolution_matrix_get_height (blur->matrix) / 2;
+
+  /* FIXME: make this work in a single pass (requires smarter matrix construction) */
+  a = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+      rect->width + 2 * x * blur->quality,
+      rect->height + 2 * y * blur->quality);
+  cairo_surface_set_device_offset (a, 
+      - rect->x + x * blur->quality, - rect->y + y * blur->quality);
+  b = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+      rect->width + 2 * x * blur->quality,
+      rect->height + 2 * y * blur->quality);
+  cairo_surface_set_device_offset (b, 
+      - rect->x + x * blur->quality, - rect->y + y * blur->quality);
+
+  cr = cairo_create (b);
+  cairo_set_source (cr, pattern);
+  cairo_rectangle (cr, rect->x, rect->y, rect->width, rect->height);
+  cairo_fill (cr);
+  cairo_destroy (cr);
+  adata = cairo_image_surface_get_data (a);
+  astride = cairo_image_surface_get_stride (a);
+  bdata = cairo_image_surface_get_data (b);
+  bstride = cairo_image_surface_get_stride (b);
+  for (i = 1, j = blur->quality - 1; i <= blur->quality; i++, j--) {
+    swfdec_convolution_matrix_apply (blur->matrix,
+	rect->width + 2 * x * i, rect->height + 2 * y * i,
+	adata + 4 * x * j + astride * y * j, astride,
+	bdata + 4 * x * j + bstride * y * j, bstride);
+    i++;
+    j--;
+    if (i > blur->quality) {
+      cairo_surface_destroy (b);
+      goto out;
+    }
+    swfdec_convolution_matrix_apply (blur->matrix,
+	rect->width + 2 * x * i, rect->height + 2 * y * i,
+	bdata + 4 * x * j + bstride * y * j, bstride,
+	adata + 4 * x * j + astride * y * j, astride);
+  }
+
+  cairo_surface_destroy (a);
+  a = b;
+out:
+  pattern = cairo_pattern_create_for_surface (a);
+  cairo_surface_destroy (a);
+
+  return pattern;
+}
+
+static void
+swfdec_blur_filter_dispose (GObject *object)
+{
+  SwfdecBlurFilter *blur = SWFDEC_BLUR_FILTER (object);
+
+  if (blur->matrix) {
+    swfdec_convolution_matrix_free (blur->matrix);
+    blur->matrix = NULL;
+  }
+
+  G_OBJECT_CLASS (swfdec_blur_filter_parent_class)->dispose (object);
+}
+
+static void
 swfdec_blur_filter_class_init (SwfdecBlurFilterClass *klass)
 {
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  SwfdecFilterClass *filter_class = SWFDEC_FILTER_CLASS (klass);
+
+  object_class->dispose = swfdec_blur_filter_dispose;
+
+  filter_class->clone = swfdec_blur_filter_clone;
+  filter_class->get_rectangle = swfdec_blur_filter_get_rectangle;
+  filter_class->apply = swfdec_blur_filter_apply;
 }
 
 static void
@@ -40,8 +191,13 @@ swfdec_blur_filter_init (SwfdecBlurFilter *filter)
 }
 
 void
-swfdec_blur_filter_invalidate (SwfdecBlurFilter *filter)
+swfdec_blur_filter_invalidate (SwfdecBlurFilter *blur)
 {
-  g_return_if_fail (SWFDEC_IS_BLUR_FILTER (filter));
+  g_return_if_fail (SWFDEC_IS_BLUR_FILTER (blur));
+
+  if (blur->matrix) {
+    swfdec_convolution_matrix_free (blur->matrix);
+    blur->matrix = NULL;
+  }
 }
 
diff --git a/swfdec/swfdec_blur_filter.h b/swfdec/swfdec_blur_filter.h
index 6a78276..776d59f 100644
--- a/swfdec/swfdec_blur_filter.h
+++ b/swfdec/swfdec_blur_filter.h
@@ -21,6 +21,7 @@
 #define _SWFDEC_BLUR_FILTER_H_
 
 #include <swfdec/swfdec_filter.h>
+#include <swfdec/swfdec_convolution_matrix.h>
 
 G_BEGIN_DECLS
 
@@ -41,6 +42,7 @@ struct _SwfdecBlurFilter {
   double		x;		/* blur in horizontal direction */
   double		y;		/* blur in vertical direction */
   guint			quality;	/* number of passes */
+  SwfdecConvolutionMatrix *matrix;	/* matrix if computed or NULL */
 };
 
 struct _SwfdecBlurFilterClass {
@@ -49,7 +51,7 @@ struct _SwfdecBlurFilterClass {
 
 GType			swfdec_blur_filter_get_type	(void);
 
-void			swfdec_blur_filter_invalidate	(SwfdecBlurFilter *	filter);
+void			swfdec_blur_filter_invalidate	(SwfdecBlurFilter *	blur);
 
 
 G_END_DECLS
diff --git a/swfdec/swfdec_button.c b/swfdec/swfdec_button.c
index aa3a8c1..8957f81 100644
--- a/swfdec/swfdec_button.c
+++ b/swfdec/swfdec_button.c
@@ -165,10 +165,8 @@ tag_func_define_button_2 (SwfdecSwfDecoder * s, guint tag)
 	trans.x0, trans.y0);
     swfdec_bits_get_color_transform (&bits, &ctrans);
 
-    if (has_filters) {
-      GSList *list = swfdec_filter_parse (&bits);
-      g_slist_free (list);
-    }
+    if (has_filters)
+      swfdec_filter_skip (&bits);
     if (has_blend_mode) {
       guint blend_mode = swfdec_bits_get_u8 (&bits);
       SWFDEC_LOG ("  blend mode = %u", blend_mode);
diff --git a/swfdec/swfdec_button_movie.c b/swfdec/swfdec_button_movie.c
index d4d23f9..aa154ae 100644
--- a/swfdec/swfdec_button_movie.c
+++ b/swfdec/swfdec_button_movie.c
@@ -83,10 +83,8 @@ swfdec_button_movie_perform_place (SwfdecButtonMovie *button, SwfdecBits *bits)
     } else {
       blend_mode = 0;
     }
-    if (has_filters) {
-      GSList *filters = swfdec_filter_parse (bits);
-      g_slist_free (filters);
-    }
+    if (has_filters)
+      new->filters = swfdec_filter_parse (player, bits);
   } else {
     /* DefineButton1 record */
     v2 = FALSE;
diff --git a/swfdec/swfdec_convolution_matrix.c b/swfdec/swfdec_convolution_matrix.c
new file mode 100644
index 0000000..994d355
--- /dev/null
+++ b/swfdec/swfdec_convolution_matrix.c
@@ -0,0 +1,152 @@
+/* Swfdec
+ * Copyright (C) 2008 Benjamin Otte <otte at gnome.org>
+ *
+ * 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.1 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "swfdec_convolution_matrix.h"
+
+#include "swfdec_color.h"
+#include "swfdec_debug.h"
+
+struct _SwfdecConvolutionMatrix {
+  guint		width;
+  guint		height;
+  double      	matrix[1];
+};
+
+SwfdecConvolutionMatrix *
+swfdec_convolution_matrix_new (guint width, guint height)
+{
+  SwfdecConvolutionMatrix *matrix;
+
+  g_return_val_if_fail (width % 2 != 0, NULL);
+  g_return_val_if_fail (height % 2 != 0, NULL);
+
+  matrix = g_slice_alloc0 (sizeof (SwfdecConvolutionMatrix) + 
+      sizeof (guint8) * (width * height * 256 - 1));
+  matrix->width = width;
+  matrix->height = height;
+  return matrix;
+}
+
+void
+swfdec_convolution_matrix_free (SwfdecConvolutionMatrix *matrix)
+{
+  g_return_if_fail (matrix != NULL);
+
+  g_slice_free1 (sizeof (SwfdecConvolutionMatrix) + 
+      sizeof (guint8) * (matrix->width * matrix->height * 256 - 1), matrix);
+}
+
+void
+swfdec_convolution_matrix_set (SwfdecConvolutionMatrix *matrix,
+    guint x, guint y, double value)
+{
+  g_return_if_fail (matrix != NULL);
+  g_return_if_fail (x < matrix->width);
+  g_return_if_fail (y < matrix->height);
+
+  matrix->matrix[matrix->width * y + x] = value;
+}
+
+double
+swfdec_convolution_matrix_get (SwfdecConvolutionMatrix *matrix, guint x, guint y)
+{
+  g_return_val_if_fail (matrix != NULL, 0);
+  g_return_val_if_fail (x < matrix->width, 0);
+  g_return_val_if_fail (y < matrix->height, 0);
+
+  return matrix->matrix[matrix->width * y + x];
+}
+
+guint
+swfdec_convolution_matrix_get_width (SwfdecConvolutionMatrix *matrix)
+{
+  g_return_val_if_fail (matrix != NULL, 1);
+
+  return matrix->width;
+}
+
+guint
+swfdec_convolution_matrix_get_height (SwfdecConvolutionMatrix *matrix)
+{
+  g_return_val_if_fail (matrix != NULL, 1);
+
+  return matrix->height;
+}
+
+void
+swfdec_convolution_matrix_apply (SwfdecConvolutionMatrix *matrix, guint width, guint height, 
+    guint8 *tdata, guint tstride, const guint8 *sdata, guint sstride)
+{
+  double r, g, b, a;
+  guint x, y;
+  int offx, offy, offyend, offxend;
+
+  g_return_if_fail (matrix != NULL);
+  g_return_if_fail (width > 0);
+  g_return_if_fail (height > 0);
+  g_return_if_fail (tdata != NULL);
+  g_return_if_fail (tstride >= width * 4);
+  g_return_if_fail (sdata != NULL);
+  g_return_if_fail (sstride >= width * 4);
+
+  for (y = 0; y < height; y++) {
+    for (x = 0; x < width; x++) {
+      double *next = matrix->matrix;
+      a = r = g = b = 0;
+      offy = y - matrix->height / 2;
+      offyend = offy + matrix->height;
+      for (; offy < offyend; offy++) {
+	if (offy < 0 || (guint) offy >= height) {
+	  next += matrix->width;
+	  continue;
+	}
+	offx = x - matrix->width / 2;
+	offxend = offx + matrix->width;
+	for (; offx < offxend; offx++) {
+	  const guint8 *cur;
+	  if (offx < 0 || (guint) offx >= width) {
+	    next++;
+	    continue;
+	  }
+	  cur = &sdata[4 * offx + sstride * offy];
+	  g_assert (cur < sdata + sstride * height);
+	  a += *next * cur[SWFDEC_COLOR_INDEX_ALPHA];
+	  r += *next * cur[SWFDEC_COLOR_INDEX_RED];
+	  g += *next * cur[SWFDEC_COLOR_INDEX_GREEN];
+	  b += *next * cur[SWFDEC_COLOR_INDEX_BLUE];
+	  next++;
+	}
+      }
+      a = CLAMP (a, 0, 255);
+      r = CLAMP (r, 0, a);
+      g = CLAMP (g, 0, a);
+      b = CLAMP (b, 0, a);
+      tdata[4 * x + SWFDEC_COLOR_INDEX_ALPHA] = a;
+      tdata[4 * x + SWFDEC_COLOR_INDEX_RED] = r;
+      tdata[4 * x + SWFDEC_COLOR_INDEX_GREEN] = g;
+      tdata[4 * x + SWFDEC_COLOR_INDEX_BLUE] = b;
+    }
+    tdata += tstride;
+  }
+}
+
diff --git a/swfdec/swfdec_convolution_matrix.h b/swfdec/swfdec_convolution_matrix.h
new file mode 100644
index 0000000..12d578a
--- /dev/null
+++ b/swfdec/swfdec_convolution_matrix.h
@@ -0,0 +1,54 @@
+/* Swfdec
+ * Copyright (C) 2008 Benjamin Otte <otte at gnome.org>
+ *
+ * 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.1 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+#ifndef _SWFDEC_CONVOLUTION_MATRIX_H_
+#define _SWFDEC_CONVOLUTION_MATRIX_H_
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+
+typedef struct _SwfdecConvolutionMatrix SwfdecConvolutionMatrix;
+
+SwfdecConvolutionMatrix *
+		swfdec_convolution_matrix_new		(guint			  	width, 
+							 guint				height);
+void		swfdec_convolution_matrix_free		(SwfdecConvolutionMatrix *	matrix);
+
+void		swfdec_convolution_matrix_set		(SwfdecConvolutionMatrix *	matrix,
+							 guint				x,
+							 guint				y,
+							 double				value);
+double		swfdec_convolution_matrix_get		(SwfdecConvolutionMatrix *	matrix,
+							 guint				x,
+							 guint				y);
+guint		swfdec_convolution_matrix_get_width	(SwfdecConvolutionMatrix *	matrix);
+guint		swfdec_convolution_matrix_get_height	(SwfdecConvolutionMatrix *	matrix);
+void		swfdec_convolution_matrix_apply		(SwfdecConvolutionMatrix *	matrix,
+							 guint				width,
+							 guint				height, 
+							 guint8 *			tdata,
+							 guint				tstride,
+							 const guint8 *			sdata,
+							 guint				sstride);
+
+
+G_END_DECLS
+#endif
diff --git a/swfdec/swfdec_filter.c b/swfdec/swfdec_filter.c
index 8bff38c..d78154e 100644
--- a/swfdec/swfdec_filter.c
+++ b/swfdec/swfdec_filter.c
@@ -1,5 +1,5 @@
 /* Swfdec
- * Copyright (C) 2007 Benjamin Otte <otte at gnome.org>
+ * Copyright (C) 2007-2008 Benjamin Otte <otte at gnome.org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -22,6 +22,8 @@
 #endif
 
 #include "swfdec_filter.h"
+
+#include "swfdec_blur_filter.h"
 #include "swfdec_debug.h"
 
 G_DEFINE_ABSTRACT_TYPE (SwfdecFilter, swfdec_filter, SWFDEC_TYPE_AS_OBJECT)
@@ -36,29 +38,48 @@ swfdec_filter_init (SwfdecFilter *array)
 {
 }
 
+SwfdecFilter *
+swfdec_filter_clone (SwfdecFilter *filter)
+{
+  SwfdecFilter *clone;
+  SwfdecFilterClass *klass;
+
+  g_return_val_if_fail (SWFDEC_IS_FILTER (filter), NULL);
+
+  klass = SWFDEC_FILTER_GET_CLASS (filter);
+  clone = g_object_new (G_OBJECT_CLASS_TYPE (klass), "context", 
+      swfdec_gc_object_get_context (filter), NULL);
+  klass->clone (clone, filter);
+
+  return clone;
+}
+
 cairo_pattern_t *
-swfdec_filter_apply (SwfdecFilter *filter, cairo_pattern_t *pattern)
+swfdec_filter_apply (SwfdecFilter *filter, cairo_pattern_t *pattern,
+    const SwfdecRectangle *rect)
 {
   SwfdecFilterClass *klass;
   cairo_pattern_t *ret;
 
   g_return_val_if_fail (SWFDEC_IS_FILTER (filter), NULL);
   g_return_val_if_fail (pattern != NULL, NULL);
+  g_return_val_if_fail (rect != NULL, NULL);
 
   klass = SWFDEC_FILTER_GET_CLASS (filter);
   g_assert (klass->apply);
   
-  ret = klass->apply (filter, pattern);
+  ret = klass->apply (filter, pattern, rect);
   cairo_pattern_destroy (pattern);
   return ret;
 }
 
 GSList *
-swfdec_filter_parse (SwfdecBits *bits)
+swfdec_filter_parse (SwfdecPlayer *player, SwfdecBits *bits)
 {
   GSList *filters = NULL;
   guint i, n_filters, filter_id;
 
+  g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
   g_return_val_if_fail (bits != NULL, NULL);
 
   n_filters = swfdec_bits_get_u8 (bits);
@@ -71,8 +92,21 @@ swfdec_filter_parse (SwfdecBits *bits)
 	swfdec_bits_skip_bytes (bits, 16);
 	break;
       case 1:
-	SWFDEC_WARNING ("    blur");
-	swfdec_bits_skip_bytes (bits, 9);
+	{
+	  SwfdecBlurFilter *filter;
+	  
+	  filter = g_object_new (SWFDEC_TYPE_BLUR_FILTER, 
+	      "context", player, NULL);
+	  SWFDEC_LOG ("    blur");
+	  filter->x = swfdec_bits_get_u32 (bits) / 65536.0;
+	  filter->y = swfdec_bits_get_u32 (bits) / 65536.0;
+	  filter->quality = swfdec_bits_getbits (bits, 5);
+	  SWFDEC_LOG ("      x = %g", filter->x);
+	  SWFDEC_LOG ("      y = %g", filter->x);
+	  SWFDEC_LOG ("      quality = %u", filter->quality);
+	  swfdec_bits_getbits (bits, 3);
+	  filters = g_slist_prepend (filters, filter);
+	}
 	break;
       case 2:
 	SWFDEC_WARNING ("    glow");
@@ -121,3 +155,71 @@ swfdec_filter_parse (SwfdecBits *bits)
   return filters;
 }
 
+void
+swfdec_filter_skip (SwfdecBits *bits)
+{
+  guint i, n_filters, filter_id;
+
+  g_return_if_fail (bits != NULL);
+
+  n_filters = swfdec_bits_get_u8 (bits);
+  for (i = 0; i < n_filters && swfdec_bits_left (bits); i++) {
+    filter_id = swfdec_bits_get_u8 (bits);
+    switch (filter_id) {
+      case 0:
+	swfdec_bits_skip_bytes (bits, 16);
+	break;
+      case 1:
+	swfdec_bits_skip_bytes (bits, 9);
+	break;
+      case 2:
+	swfdec_bits_skip_bytes (bits, 15);
+	break;
+      case 3:
+	swfdec_bits_skip_bytes (bits, 27);
+	break;
+      case 4:
+	{
+	  guint n;
+	  n = swfdec_bits_get_u8 (bits);
+	  swfdec_bits_skip_bytes (bits, n * 5 + 19);
+	}
+	break;
+      case 5:
+	{
+	  guint x, y;
+	  x = swfdec_bits_get_u8 (bits);
+	  y = swfdec_bits_get_u8 (bits);
+	  swfdec_bits_skip_bytes (bits, (x + y) * 4 + 13);
+	}
+	break;
+      case 6:
+	swfdec_bits_skip_bytes (bits, 20 * 4);
+	break;
+      case 7:
+	{
+	  guint n;
+	  n = swfdec_bits_get_u8 (bits);
+	  swfdec_bits_skip_bytes (bits, n * 5 + 19);
+	}
+	break;
+      default:
+	SWFDEC_ERROR ("unknown filter id %u", filter_id);
+	break;
+    }
+  }
+}
+
+void
+swfdec_filter_get_rectangle (SwfdecFilter *filter, SwfdecRectangle *dest,
+    const SwfdecRectangle *source)
+{
+  SwfdecFilterClass *klass;
+
+  g_return_if_fail (SWFDEC_IS_FILTER (filter));
+  g_return_if_fail (dest != NULL);
+  g_return_if_fail (source != NULL);
+
+  klass = SWFDEC_FILTER_GET_CLASS (filter);
+  klass->get_rectangle (filter, dest, source);
+}
diff --git a/swfdec/swfdec_filter.h b/swfdec/swfdec_filter.h
index b15b9f7..6431dac 100644
--- a/swfdec/swfdec_filter.h
+++ b/swfdec/swfdec_filter.h
@@ -1,5 +1,5 @@
 /* Swfdec
- * Copyright (C) 2007 Benjamin Otte <otte at gnome.org>
+ * Copyright (C) 2007-2008 Benjamin Otte <otte at gnome.org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -43,17 +43,31 @@ struct _SwfdecFilter {
 struct _SwfdecFilterClass {
   SwfdecAsObjectClass	object_class;
 
-  SwfdecFilter *	(* clone)		(SwfdecFilter *		filter);
+  void			(* clone)		(SwfdecFilter *		dest,
+						 SwfdecFilter *		source);
+  void			(* get_rectangle)	(SwfdecFilter *		filter,
+						 SwfdecRectangle *	dest,
+						 const SwfdecRectangle *source);
   cairo_pattern_t *	(* apply)		(SwfdecFilter *		filter,
-						 cairo_pattern_t *	pattern);
+						 cairo_pattern_t *	pattern,
+						 const SwfdecRectangle *rect);
 };
 
 GType			swfdec_filter_get_type	(void);
 
+SwfdecFilter *		swfdec_filter_clone	(SwfdecFilter *		filter);
 cairo_pattern_t *	swfdec_filter_apply	(SwfdecFilter *		filter,
-						 cairo_pattern_t *	pattern);
-GSList *		swfdec_filter_parse	(SwfdecBits *		bits);
+						 cairo_pattern_t *	pattern,
+						 const SwfdecRectangle *source);
+void			swfdec_filter_get_rectangle
+						(SwfdecFilter *		filter,
+						 SwfdecRectangle *	dest,
+						 const SwfdecRectangle *source);
 
+GSList *		swfdec_filter_parse	(SwfdecPlayer *		player,
+						 SwfdecBits *		bits);
+void			swfdec_filter_skip	(SwfdecBits *		bits);
+						
 
 G_END_DECLS
 #endif
diff --git a/swfdec/swfdec_filter_as.c b/swfdec/swfdec_filter_as.c
index 0f00211..f02108d 100644
--- a/swfdec/swfdec_filter_as.c
+++ b/swfdec/swfdec_filter_as.c
@@ -1,5 +1,5 @@
 /* Swfdec
- * Copyright (C) 2006-2007 Benjamin Otte <otte at gnome.org>
+ * Copyright (C) 2006-2008 Benjamin Otte <otte at gnome.org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -22,26 +22,21 @@
 #endif
 
 #include "swfdec_filter.h"
+
 #include "swfdec_as_internal.h"
+#include "swfdec_as_native_function.h"
 #include "swfdec_debug.h"
 
-SWFDEC_AS_NATIVE (1112, 1, swfdec_filter_clone)
+SWFDEC_AS_NATIVE (1112, 1, swfdec_filter_do_clone)
 void
-swfdec_filter_clone (SwfdecAsContext *cx, SwfdecAsObject *object,
+swfdec_filter_do_clone (SwfdecAsContext *cx, SwfdecAsObject *object,
     guint argc, SwfdecAsValue *argv, SwfdecAsValue *retval)
 {
   SwfdecFilter *filter;
-  SwfdecFilterClass *klass;
-
-  if (!SWFDEC_IS_FILTER (object))
-    return;
 
-  filter = SWFDEC_FILTER (object);
-  klass = SWFDEC_FILTER_GET_CLASS (filter);
-  g_assert (klass->clone);
+  SWFDEC_AS_CHECK (SWFDEC_TYPE_FILTER, &filter, "");
 
-  filter = klass->clone (filter);
-  if (filter)
-    SWFDEC_AS_VALUE_SET_OBJECT (retval, SWFDEC_AS_OBJECT (filter));
+  filter = swfdec_filter_clone (filter);
+  SWFDEC_AS_VALUE_SET_OBJECT (retval, SWFDEC_AS_OBJECT (filter));
 }
 
diff --git a/swfdec/swfdec_movie.c b/swfdec/swfdec_movie.c
index 90f8cf0..a9c7feb 100644
--- a/swfdec/swfdec_movie.c
+++ b/swfdec/swfdec_movie.c
@@ -34,6 +34,7 @@
 #include "swfdec_debug.h"
 #include "swfdec_draw.h"
 #include "swfdec_event.h"
+#include "swfdec_filter.h"
 #include "swfdec_graphic.h"
 #include "swfdec_image.h"
 #include "swfdec_loader_internal.h"
@@ -695,14 +696,14 @@ typedef enum {
   SWFDEC_GROUP_NONE = 0,
   SWFDEC_GROUP_NORMAL,
   SWFDEC_GROUP_CACHED,
-  SWFDEC_GROUP_BITMAP
+  SWFDEC_GROUP_FILTERS
 } SwfdecGroup;
 
 static SwfdecGroup
 swfdec_movie_needs_group (SwfdecMovie *movie)
 {
   if (movie->filters)
-    return SWFDEC_GROUP_BITMAP;
+    return SWFDEC_GROUP_FILTERS;
   if (movie->cache_as_bitmap)
     return SWFDEC_GROUP_CACHED;
   if (movie->blend_mode > 1)
@@ -714,8 +715,8 @@ static cairo_operator_t
 swfdec_movie_get_operator_for_blend_mode (guint blend_mode)
 {
   switch (blend_mode) {
+    case SWFDEC_BLEND_MODE_NONE:
     case SWFDEC_BLEND_MODE_NORMAL:
-      SWFDEC_ERROR ("shouldn't need to get operator without blend mode?!");
     case SWFDEC_BLEND_MODE_LAYER:
       return CAIRO_OPERATOR_OVER;
     case SWFDEC_BLEND_MODE_ADD:
@@ -741,6 +742,31 @@ swfdec_movie_get_operator_for_blend_mode (guint blend_mode)
   }
 }
 
+static cairo_pattern_t *
+swfdec_movie_apply_filters (SwfdecMovie *movie, cairo_pattern_t *pattern)
+{
+  SwfdecRect rect;
+  SwfdecRectangle area;
+  GSList *walk;
+
+  if (movie->filters == NULL)
+    return pattern;
+
+  rect = movie->original_extents;
+  swfdec_movie_rect_local_to_global (movie, &rect);
+  swfdec_rect_transform (&rect, &rect,
+      &SWFDEC_PLAYER (swfdec_gc_object_get_context (movie))->priv->global_to_stage);
+  swfdec_rectangle_init_rect (&area, &rect);
+  /* FIXME: hack to make textfield borders work - looks like Adobe does this, too */
+  area.width++;
+  area.height++;
+  for (walk = movie->filters; walk; walk = walk->next) {
+    pattern = swfdec_filter_apply (walk->data, pattern, &area);
+    swfdec_filter_get_rectangle (walk->data, &area, &area);
+  }
+  return pattern;
+}
+
 /**
  * swfdec_movie_mask:
  * @movie: The movie to act as the mask
@@ -857,14 +883,28 @@ swfdec_movie_render (SwfdecMovie *movie, cairo_t *cr,
   } else {
     cairo_restore (cr);
   }
-  if (group != SWFDEC_GROUP_NONE) {
+  if (group == SWFDEC_GROUP_FILTERS) {
     cairo_pattern_t *pattern;
 
     pattern = cairo_pop_group (cr);
+    cairo_save (cr);
+    swfdec_renderer_reset_matrix (cr);
+    {
+      cairo_matrix_t mat;
+      cairo_get_matrix (cr, &mat);
+      cairo_matrix_invert (&mat);
+      cairo_pattern_set_matrix (pattern, &mat);
+    }
+    pattern = swfdec_movie_apply_filters (movie, pattern);
     cairo_set_source (cr, pattern);
     cairo_set_operator (cr, swfdec_movie_get_operator_for_blend_mode (movie->blend_mode));
     cairo_paint (cr);
     cairo_pattern_destroy (pattern);
+    cairo_restore (cr);
+  } else if (group != SWFDEC_GROUP_NONE) {
+    cairo_pop_group_to_source (cr);
+    cairo_set_operator (cr, swfdec_movie_get_operator_for_blend_mode (movie->blend_mode));
+    cairo_paint (cr);
   }
 }
 
@@ -992,16 +1032,15 @@ static void
 swfdec_movie_mark (SwfdecGcObject *object)
 {
   SwfdecMovie *movie = SWFDEC_MOVIE (object);
-  GList *walk;
   GSList *iter;
 
   if (movie->parent)
     swfdec_gc_object_mark (movie->parent);
   swfdec_as_string_mark (movie->original_name);
   swfdec_as_string_mark (movie->name);
-  for (walk = movie->list; walk; walk = walk->next) {
-    swfdec_gc_object_mark (walk->data);
-  }
+  g_list_foreach (movie->list, (GFunc) swfdec_gc_object_mark, NULL);
+  g_slist_foreach (movie->filters, (GFunc) swfdec_gc_object_mark, NULL);
+
   for (iter = movie->variable_listeners; iter != NULL; iter = iter->next) {
     SwfdecMovieVariableListener *listener = iter->data;
     swfdec_gc_object_mark (listener->object);
diff --git a/swfdec/swfdec_movie.h b/swfdec/swfdec_movie.h
index 1e93a50..f00098a 100644
--- a/swfdec/swfdec_movie.h
+++ b/swfdec/swfdec_movie.h
@@ -78,6 +78,7 @@ typedef enum {
   SWFDEC_MOVIE_PROPERTY_YMOUSE = 21
 } SwfdecMovieProperty;
 
+#define SWFDEC_BLEND_MODE_NONE		0
 #define SWFDEC_BLEND_MODE_NORMAL	1
 #define SWFDEC_BLEND_MODE_LAYER		2
 #define SWFDEC_BLEND_MODE_MULTIPLY	3
diff --git a/swfdec/swfdec_sprite_movie.c b/swfdec/swfdec_sprite_movie.c
index 451cdfa..e78efc2 100644
--- a/swfdec/swfdec_sprite_movie.c
+++ b/swfdec/swfdec_sprite_movie.c
@@ -159,6 +159,7 @@ swfdec_sprite_movie_perform_place (SwfdecSpriteMovie *movie, SwfdecBits *bits, g
   const char *name;
   guint blend_mode;
   SwfdecGraphic *graphic;
+  GSList *filters;
 
   dec = SWFDEC_SWF_DECODER (mov->resource->decoder);
   version = dec->version;
@@ -250,8 +251,9 @@ swfdec_sprite_movie_perform_place (SwfdecSpriteMovie *movie, SwfdecBits *bits, g
   }
 
   if (has_filter) {
-    GSList *filters = swfdec_filter_parse (bits);
-    g_slist_free (filters);
+    filters = swfdec_filter_parse (player, bits);
+  } else {
+    filters = NULL;
   }
 
   if (has_blend_mode) {
@@ -327,6 +329,8 @@ swfdec_sprite_movie_perform_place (SwfdecSpriteMovie *movie, SwfdecBits *bits, g
       SWFDEC_FIXME ("character %u is not a graphic (does it even exist?), aborting", id);
       if (events)
 	swfdec_event_list_free (events);
+      g_slist_foreach (filters, (GFunc) g_object_unref, NULL);
+      g_slist_free (filters);
       return FALSE;
     }
     cur = swfdec_movie_new (player, depth, mov, mov->resource, graphic, name);
@@ -340,8 +344,15 @@ swfdec_sprite_movie_perform_place (SwfdecSpriteMovie *movie, SwfdecBits *bits, g
     }
     swfdec_movie_initialize (cur);
   }
-
 out:
+  if (has_filter) {
+    if (cur->filters) {
+      g_slist_foreach (cur->filters, (GFunc) g_object_unref, NULL);
+      g_slist_free (cur->filters);
+    }
+    cur->filters = filters;
+  }
+
   if (events)
     swfdec_event_list_free (events);
   return TRUE;
diff --git a/swfdec/swfdec_sprite_movie_as.c b/swfdec/swfdec_sprite_movie_as.c
index b1bb452..181f2c1 100644
--- a/swfdec/swfdec_sprite_movie_as.c
+++ b/swfdec/swfdec_sprite_movie_as.c
@@ -31,6 +31,7 @@
 #include "swfdec_bits.h"
 #include "swfdec_debug.h"
 #include "swfdec_decoder.h"
+#include "swfdec_filter.h"
 #include "swfdec_internal.h"
 #include "swfdec_player_internal.h"
 #include "swfdec_sprite.h"
@@ -143,7 +144,31 @@ void
 swfdec_sprite_movie_set_filters (SwfdecAsContext *cx, SwfdecAsObject *object,
     guint argc, SwfdecAsValue *argv, SwfdecAsValue *rval)
 {
-  SWFDEC_STUB ("MovieClip.filters (set)");
+  SwfdecAsObject *array;
+  SwfdecAsValue val;
+  SwfdecFilter *filter;
+  SwfdecMovie *movie;
+  int i, length;
+  GSList *list;
+
+  SWFDEC_AS_CHECK (SWFDEC_TYPE_MOVIE, &movie, "o", &array);
+
+  swfdec_as_object_get_variable (array, SWFDEC_AS_STR_length, &val);
+  length = swfdec_as_value_to_integer (cx, &val);
+
+  list = NULL;
+  for (i = 0; i < length; i++) {
+    if (!swfdec_as_object_get_variable (array, 
+	  swfdec_as_integer_to_string (cx, i), &val) ||
+	!SWFDEC_AS_VALUE_IS_OBJECT (&val) ||
+	!SWFDEC_IS_FILTER (SWFDEC_AS_VALUE_GET_OBJECT (&val)))
+      continue;
+    filter = SWFDEC_FILTER (SWFDEC_AS_VALUE_GET_OBJECT (&val));
+    filter = swfdec_filter_clone (filter);
+    list = g_slist_prepend (list, filter);
+  }
+  g_slist_free (movie->filters);
+  movie->filters = list;
 }
 
 SWFDEC_AS_NATIVE (900, 419, swfdec_sprite_movie_get_transform)
commit a6c8fc9e8be525245a0afd93c476b76c269c87b4
Author: Benjamin Otte <otte at gnome.org>
Date:   Sun Sep 14 20:20:58 2008 +0200

    improve documentation for SwfdecRenderer
    
    It seems to be unclear that SwfdecRenderer objects should be kept around.

diff --git a/swfdec/swfdec_renderer.c b/swfdec/swfdec_renderer.c
index 7e4495c..259f28a 100644
--- a/swfdec/swfdec_renderer.c
+++ b/swfdec/swfdec_renderer.c
@@ -42,18 +42,21 @@ struct _SwfdecRendererPrivate {
  * The #SwfdecRenderer object is used internally to improve rendering done by
  * Swfdec.
  *
- * The first thing #SwfdecRenderer does is provide a way to cache data relevant
- * to rendering.
+ * The first thing a #SwfdecRenderer does is provide a way to cache data relevant
+ * to rendering. This means it will cache surfaces that are expensive to create
+ * (like decoded JPEG images) in a format most suitable to quick rendering.
+ * Therefore, it's a good idea to keep renderers around as long as any drawing 
+ * to the attached surface happens.
  *
- * The second thing it does is provide access to the surface that is used for 
- * rendering, even when not in the process of rendering. This is relevant for
- * font backends, as different surfaces provide different native fonts. See
- * swfdec_player_set_default_backend() for details about this.
+ * The second thing a #SwfdecRenderer does is provide access to the surface 
+ * that is used for rendering, even when not in the process of rendering. This 
+ * is relevant for font backends, as different surfaces provide different 
+ * native fonts. See swfdec_player_set_default_backend() for details about this.
  *
- * The third thing it does is provide a list of virtual functions for critical
- * operations that you can optimize using subclasses to provide faster 
- * implementations. Note that a working default implementation is provided, so
- * you only need to override the functions you care about. 
+ * The third thing #SwfdecRenderer does is provide a list of virtual functions 
+ * for critical operations that you can optimize using subclasses to provide 
+ * faster implementations. Note that a working default implementation is 
+ * provided, so you only need to override the functions you care about. 
  * See #SwfdecRendererClass for details about these functions.
  */
 
commit a0c5effa5511259fa85f370d8c70a9d1d1820266
Author: Benjamin Otte <otte at gnome.org>
Date:   Sun Sep 14 19:52:55 2008 +0200

    We require glib 2.16

diff --git a/README b/README
index f9e0632..09955b8 100644
--- a/README
+++ b/README
@@ -64,7 +64,7 @@ Limitations:
 Dependencies:
 
   cairo (>= 1.2.0 - >= 1.8.0 recommended) with png support enabled
-  glib (>= 2.14.0)
+  glib (>= 2.16.0)
   liboil (>= 0.3.6)
   Pangocairo (>= 1.16.0) - this is provided by Pango
   zlib (>= 1.1.4)
commit a21547aeb5666886f3d0a0201e2a6d5b406d606e
Author: Benjamin Otte <otte at gnome.org>
Date:   Wed Sep 10 18:56:29 2008 +0200

    only do save/restore when needed

diff --git a/swfdec/swfdec_movie.c b/swfdec/swfdec_movie.c
index a37537e..90f8cf0 100644
--- a/swfdec/swfdec_movie.c
+++ b/swfdec/swfdec_movie.c
@@ -806,8 +806,9 @@ swfdec_movie_render (SwfdecMovie *movie, cairo_t *cr,
   }
   if (movie->masked_by != NULL) {
     cairo_push_group (cr);
+  } else {
+    cairo_save (cr);
   }
-  cairo_save (cr);
 
   SWFDEC_LOG ("transforming movie, transform: %g %g  %g %g   %g %g",
       movie->matrix.xx, movie->matrix.yy,
@@ -836,7 +837,6 @@ swfdec_movie_render (SwfdecMovie *movie, cairo_t *cr,
   if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) {
     g_warning ("error rendering with cairo: %s", cairo_status_to_string (cairo_status (cr)));
   }
-  cairo_restore (cr);
   if (movie->masked_by) {
     cairo_pattern_t *mask;
     cairo_matrix_t mat;
@@ -854,6 +854,8 @@ swfdec_movie_render (SwfdecMovie *movie, cairo_t *cr,
     cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
     cairo_mask (cr, mask);
     cairo_pattern_destroy (mask);
+  } else {
+    cairo_restore (cr);
   }
   if (group != SWFDEC_GROUP_NONE) {
     cairo_pattern_t *pattern;
commit b520fea5127b93a614a214eab0662ff915a31bbf
Author: Benjamin Otte <otte at gnome.org>
Date:   Wed Sep 10 10:55:33 2008 +0200

    render mask before blend mode
    
    also add some features required for filters/cacheAsBitmap

diff --git a/swfdec/swfdec_movie.c b/swfdec/swfdec_movie.c
index adbe5a7..a37537e 100644
--- a/swfdec/swfdec_movie.c
+++ b/swfdec/swfdec_movie.c
@@ -690,10 +690,24 @@ swfdec_movie_do_contains (SwfdecMovie *movie, double x, double y, gboolean event
   return NULL;
 }
 
-static gboolean
+/* NB: order is important */
+typedef enum {
+  SWFDEC_GROUP_NONE = 0,
+  SWFDEC_GROUP_NORMAL,
+  SWFDEC_GROUP_CACHED,
+  SWFDEC_GROUP_BITMAP
+} SwfdecGroup;
+
+static SwfdecGroup
 swfdec_movie_needs_group (SwfdecMovie *movie)
 {
-  return (movie->blend_mode > 1);
+  if (movie->filters)
+    return SWFDEC_GROUP_BITMAP;
+  if (movie->cache_as_bitmap)
+    return SWFDEC_GROUP_CACHED;
+  if (movie->blend_mode > 1)
+    return SWFDEC_GROUP_NORMAL;
+  return SWFDEC_GROUP_NONE;
 }
 
 static cairo_operator_t
@@ -767,7 +781,7 @@ swfdec_movie_render (SwfdecMovie *movie, cairo_t *cr,
 {
   SwfdecMovieClass *klass;
   SwfdecColorTransform trans;
-  gboolean group;
+  SwfdecGroup group;
 
   g_return_if_fail (SWFDEC_IS_MOVIE (movie));
   g_return_if_fail (cr != NULL);
@@ -782,13 +796,16 @@ swfdec_movie_render (SwfdecMovie *movie, cairo_t *cr,
     return;
   }
 
-  if (movie->masked_by != NULL) {
-    cairo_push_group (cr);
-  }
   group = swfdec_movie_needs_group (movie);
-  if (group) {
+  if (group == SWFDEC_GROUP_NORMAL) {
     SWFDEC_DEBUG ("pushing group for blend mode %u", movie->blend_mode);
     cairo_push_group (cr);
+  } else if (group != SWFDEC_GROUP_NONE) {
+    SWFDEC_FIXME ("implement cache-as-bitmap and filters here");
+    cairo_push_group (cr);
+  }
+  if (movie->masked_by != NULL) {
+    cairo_push_group (cr);
   }
   cairo_save (cr);
 
@@ -820,15 +837,6 @@ swfdec_movie_render (SwfdecMovie *movie, cairo_t *cr,
     g_warning ("error rendering with cairo: %s", cairo_status_to_string (cairo_status (cr)));
   }
   cairo_restore (cr);
-  if (group) {
-    cairo_pattern_t *pattern;
-
-    pattern = cairo_pop_group (cr);
-    cairo_set_source (cr, pattern);
-    cairo_set_operator (cr, swfdec_movie_get_operator_for_blend_mode (movie->blend_mode));
-    cairo_paint (cr);
-    cairo_pattern_destroy (pattern);
-  }
   if (movie->masked_by) {
     cairo_pattern_t *mask;
     cairo_matrix_t mat;
@@ -847,6 +855,15 @@ swfdec_movie_render (SwfdecMovie *movie, cairo_t *cr,
     cairo_mask (cr, mask);
     cairo_pattern_destroy (mask);
   }
+  if (group != SWFDEC_GROUP_NONE) {
+    cairo_pattern_t *pattern;
+
+    pattern = cairo_pop_group (cr);
+    cairo_set_source (cr, pattern);
+    cairo_set_operator (cr, swfdec_movie_get_operator_for_blend_mode (movie->blend_mode));
+    cairo_paint (cr);
+    cairo_pattern_destroy (pattern);
+  }
 }
 
 static void
diff --git a/swfdec/swfdec_movie.h b/swfdec/swfdec_movie.h
index 512abf0..1e93a50 100644
--- a/swfdec/swfdec_movie.h
+++ b/swfdec/swfdec_movie.h
@@ -156,6 +156,8 @@ struct _SwfdecMovie {
   /* drawing state */
   SwfdecMovie *		mask_of;		/* movie this movie is a mask of or NULL if none */
   SwfdecMovie *		masked_by;		/* movie we are masked by or NULL if none */
+  GSList *		filters;		/* filters to apply to movie */
+  gboolean		cache_as_bitmap;	/* the movie should be cached */
   /* FIXME: could it be that shape drawing (SwfdecGraphicMovie etc) uses these same objects? */
   SwfdecImage *		image;			/* image loaded via loadMovie */
   SwfdecRect		draw_extents;		/* extents of the items in the following list */


More information about the Swfdec-commits mailing list