[goffice] Ensure that GOPixbuf keeps the original data around. [#745297]



commit ea67f35a2a16d7ba61572d9e35ebe98efdcc94e9
Author: Jean Brefort <jean brefort normalesup org>
Date:   Sat Feb 28 20:04:59 2015 +0100

    Ensure that GOPixbuf        keeps the original data around. [#745297]

 ChangeLog                 |    7 ++
 NEWS                      |    1 +
 goffice/utils/go-image.c  |    8 +--
 goffice/utils/go-pixbuf.c |  154 ++++++++++++++++++++++++++++++++++-----------
 goffice/utils/go-pixbuf.h |    4 +-
 5 files changed, 129 insertions(+), 45 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index d72b96d..f8ca46e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2015-02-28  Jean Brefort  <jean brefort normalesup org>
+
+       * goffice/utils/go-image.c (go_image_new_from_data): ensure that GOPixbuf
+       keeps the original data around. [#745297]
+       * goffice/utils/go-pixbuf.c: ditto.
+       * goffice/utils/go-pixbuf.h: ditto.
+
 2015-02-27  Morten Welinder  <terra gnome org>
 
        * goffice/utils/go-pixbuf.c (go_pixbuf_load_data): Set
diff --git a/NEWS b/NEWS
index 2c91c20..5d860b4 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,7 @@ goffice 0.10.21:
 
 Jean:
        * Ignore invalid values while checking data order. [#744200]
+       * Ensure that GOPixbuf  keeps the original data around. [#745297]
 
 Morten:
        * Fix problem with linear solver.
diff --git a/goffice/utils/go-image.c b/goffice/utils/go-image.c
index d80a5fd..8c46650 100644
--- a/goffice/utils/go-image.c
+++ b/goffice/utils/go-image.c
@@ -663,13 +663,7 @@ go_image_new_from_data (char const *type, guint8 const *data, gsize length, char
        } else if (!strcmp (type, "eps")) {
                image = go_spectre_new_from_data (data, length, error);
        } else {
-               GdkPixbufLoader *loader = gdk_pixbuf_loader_new_with_type (type, error);
-               if (loader) {
-                       if (gdk_pixbuf_loader_write (loader, data, length, error))
-                               image = go_pixbuf_new_from_pixbuf (gdk_pixbuf_loader_get_pixbuf (loader));
-                       gdk_pixbuf_loader_close (loader, error);
-                       g_object_unref (loader);
-               }
+               image = go_pixbuf_new_from_data (type, data, length, error);
        }
        if (image == NULL) {
 #ifdef GOFFICE_WITH_GTK
diff --git a/goffice/utils/go-pixbuf.c b/goffice/utils/go-pixbuf.c
index 6091d22..001b854 100644
--- a/goffice/utils/go-pixbuf.c
+++ b/goffice/utils/go-pixbuf.c
@@ -34,6 +34,9 @@ struct _GOPixbuf {
        unsigned rowstride;
        GdkPixbuf *pixbuf;
        cairo_surface_t *surface;
+       guint8 *cairo_pixels;
+       gsize length;
+       char *type;
 };
 
 typedef GOImageClass GOPixbufClass;
@@ -42,7 +45,8 @@ static GObjectClass *parent_klass;
 
 enum {
        PIXBUF_PROP_0,
-       PIXBUF_PROP_PIXBUF
+       PIXBUF_PROP_PIXBUF,
+       PIXBUF_PROP_TYPE
 };
 
 static void
@@ -51,10 +55,10 @@ pixbuf_to_cairo (GOPixbuf *pixbuf)
        unsigned char *src, *dst;
        GOImage *image = GO_IMAGE (pixbuf);
 
-       g_return_if_fail (GO_IS_PIXBUF (pixbuf) && image->data && pixbuf->pixbuf);
+       g_return_if_fail (GO_IS_PIXBUF (pixbuf) && pixbuf->cairo_pixels && pixbuf->pixbuf);
 
        src = gdk_pixbuf_get_pixels (pixbuf->pixbuf);
-       dst = image->data;
+       dst = pixbuf->cairo_pixels;
 
        g_return_if_fail (gdk_pixbuf_get_rowstride (pixbuf->pixbuf) == (int) pixbuf->rowstride);
 
@@ -67,10 +71,10 @@ cairo_to_pixbuf (GOPixbuf *pixbuf)
        unsigned char *src, *dst;
        GOImage *image = GO_IMAGE (pixbuf);
 
-       g_return_if_fail (GO_IS_PIXBUF (pixbuf) && image->data && pixbuf->pixbuf);
+       g_return_if_fail (GO_IS_PIXBUF (pixbuf) && pixbuf->cairo_pixels && pixbuf->pixbuf);
 
        dst = gdk_pixbuf_get_pixels (pixbuf->pixbuf);
-       src = image->data;
+       src = pixbuf->cairo_pixels;
 
        g_return_if_fail (gdk_pixbuf_get_rowstride (pixbuf->pixbuf) == (int) pixbuf->rowstride);
 
@@ -83,18 +87,16 @@ go_pixbuf_save (GOImage *image, GsfXMLOut *output)
        GOPixbuf *pixbuf;
        g_return_if_fail (GO_IS_PIXBUF (image));
        pixbuf = GO_PIXBUF (image);
-       gsf_xml_out_add_int (output, "rowstride", pixbuf->rowstride);
-       if (!image->data) {
-               image->data = g_try_new0 (guint8, image->height * pixbuf->rowstride);
-               if (image->data == NULL) {
-                       g_critical ("go_pixbuf_save: assertion `image->data != NULL' failed");
-                       return;
-               }
-               pixbuf_to_cairo (pixbuf);
+       if (pixbuf->type == NULL) {
+               pixbuf->type = g_strdup ("png");
+               g_free (image->data); /* just in case, but the pointer should be NULL */
+               gdk_pixbuf_save_to_buffer (pixbuf->pixbuf, (gchar **) &image->data,
+                                          &image->data_length, pixbuf->type, NULL,
+                                          "compression", 9, NULL);
        }
-       gsf_xml_out_add_base64
-               (output, NULL,
-                image->data, image->height * pixbuf->rowstride);
+       gsf_xml_out_add_cstr_unchecked (output, "image-type", pixbuf->type);
+       gsf_xml_out_add_base64 (output, NULL,
+                               image->data, image->data_length);
 }
 
 static void
@@ -106,7 +108,40 @@ go_pixbuf_load_attr (GOImage *image, xmlChar const *attr_name, xmlChar const *at
                long l = strtol (attr_value, NULL, 10);
                g_return_if_fail (l > 0 && l < G_MAXINT);
                pixbuf->rowstride = l;
+       } else if (!strcmp (attr_name, "image-type"))
+               pixbuf->type = g_strdup (attr_value);
+}
+
+static void
+go_pixbuf_create_pixbuf (GOPixbuf *pixbuf, GError **error)
+{
+       GOImage *image = GO_IMAGE (pixbuf);
+       GdkPixbufLoader *loader = gdk_pixbuf_loader_new_with_type (pixbuf->type, NULL);
+       if (loader) {
+               if (gdk_pixbuf_loader_write (loader, image->data, image->data_length, error))
+                       g_object_set (pixbuf,
+                                     "pixbuf", gdk_pixbuf_loader_get_pixbuf (loader),
+                                     NULL);
+               gdk_pixbuf_loader_close (loader, error);
+               g_object_unref (loader);
+       }
+}
+
+static void
+go_pixbuf_restore_data (GOPixbuf *pixbuf)
+{
+       GOImage *image = GO_IMAGE (pixbuf);
+       if (pixbuf->pixbuf == NULL) {
+               if (image->width == 0 || image->height == 0 || pixbuf->cairo_pixels == NULL)
+                       return;
+               pixbuf->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
+                                                image->width, image->height);
+               cairo_to_pixbuf (pixbuf);
        }
+       if (image->data == NULL && pixbuf->pixbuf && pixbuf->type)
+               gdk_pixbuf_save_to_buffer (pixbuf->pixbuf, (gchar **) &image->data,
+                                                  &image->data_length, pixbuf->type, NULL,
+                                                  NULL);
 }
 
 static void
@@ -114,24 +149,37 @@ go_pixbuf_load_data (GOImage *image, GsfXMLIn *xin)
 {
        size_t length, expected;
        int stride;
+       GOPixbuf *pixbuf = GO_PIXBUF (image);
 
        stride = go_pixbuf_get_rowstride (GO_PIXBUF (image));
-       g_return_if_fail (stride > 0);
-
        length = gsf_base64_decode_simple (xin->content->str, strlen(xin->content->str));
-       expected = image->height * (size_t)stride;
-       if (expected != length)
-               g_critical ("Invalid image size, expected %" G_GSIZE_FORMAT " bytes, got %" G_GSIZE_FORMAT,
-                           expected, length);
-       image->data = g_try_malloc (expected);
-       image->data_length = expected;
-       if (image->data == NULL) {
-               g_critical ("go_pixbuf_load_data: assertion `image->data != NULL' failed");
-               return;
+       if (stride > 0) {
+               /* load raw pixels */
+               expected = image->height * (size_t)stride;
+               if (expected != length)
+                       g_critical ("Invalid image size, expected %" G_GSIZE_FORMAT " bytes, got %" 
G_GSIZE_FORMAT,
+                                       expected, length);
+               pixbuf->cairo_pixels = g_try_malloc (expected);
+               pixbuf->length = expected;
+               if (pixbuf->cairo_pixels == NULL) {
+                       g_critical ("go_pixbuf_load_data: assertion `pixbuf->cairo_pixels != NULL' failed");
+                       return;
+               }
+               memcpy (pixbuf->cairo_pixels, xin->content->str, MIN (length, expected));
+               if (length < expected) /* fill with 0 */
+                       memset (pixbuf->cairo_pixels + length, 0, expected - length);
+               go_pixbuf_restore_data (pixbuf);
+       } else {
+               image->data = g_try_malloc (length);
+               image->data_length = length;
+               if (image->data == NULL) {
+                       g_critical ("go_pixbuf_load_data: assertion `image->data != NULL' failed");
+                       return;
+               }
+               memcpy (image->data, xin->content->str, length);
+               /* create the pixbuf */
+               go_pixbuf_create_pixbuf (pixbuf, NULL);
        }
-       memcpy (image->data, xin->content->str, MIN (length, expected));
-       if (length < expected) /* fill with 0 */
-               memset (image->data + length, 0, expected - length);
 }
 
 static void
@@ -140,16 +188,16 @@ go_pixbuf_draw (GOImage *image, cairo_t *cr)
        GOPixbuf *pixbuf = GO_PIXBUF (image);
        g_return_if_fail (pixbuf);
        if (pixbuf->surface == NULL) {
-               if (image->data == NULL) {
+               if (pixbuf->cairo_pixels == NULL) {
                        /* image built from a pixbuf */
-                       image->data = g_try_new0 (guint8, image->height * pixbuf->rowstride);
-                       if (image->data == NULL) {
+                       pixbuf->cairo_pixels = g_try_new0 (guint8, image->height * pixbuf->rowstride);
+                       if (pixbuf->cairo_pixels == NULL) {
                                g_critical ("go_pixbuf_load_data: assertion `image->data != NULL' failed");
                                return;
                        }
                        pixbuf_to_cairo (pixbuf);
                }
-               pixbuf->surface = cairo_image_surface_create_for_data (image->data,
+               pixbuf->surface = cairo_image_surface_create_for_data (pixbuf->cairo_pixels,
                                                                       CAIRO_FORMAT_ARGB32,
                                                                       image->width,
                                                                       image->height,
@@ -168,7 +216,7 @@ go_pixbuf_get_pixbuf (GOImage *image)
        GOPixbuf *pixbuf = GO_PIXBUF (image);
        g_return_val_if_fail (pixbuf, NULL);
        if (!pixbuf->pixbuf) {
-               if (image->width == 0 || image->height == 0 || image->data == NULL)
+               if (image->width == 0 || image->height == 0 || pixbuf->cairo_pixels == NULL)
                        return NULL;
                pixbuf->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
                                                                image->width, image->height);
@@ -243,12 +291,19 @@ go_pixbuf_set_property (GObject *obj, guint param_id,
                if (pixbuf->pixbuf)
                        g_object_unref (pixbuf->pixbuf);
                pixbuf->pixbuf = pix;
-               g_free (image->data); /* this should be in GOPixbuf */
-               image->data = NULL; /* this should be in GOPixbuf */
                _go_image_changed (image, gdk_pixbuf_get_width (pix), gdk_pixbuf_get_height (pix));
                pixbuf->rowstride = gdk_pixbuf_get_rowstride (pix); /* this should be in GOPixbuf */
        }
                break;
+       case PIXBUF_PROP_TYPE: {
+               char const *type = g_value_get_string (value);
+               if (pixbuf->type && !strcmp (type, pixbuf->type))
+                       break;
+               g_return_if_fail (pixbuf->type == NULL);
+               pixbuf->type = g_strdup (type);
+               go_pixbuf_restore_data (pixbuf);
+       }
+               break;
 
        default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
                return; /* NOTE : RETURN */
@@ -265,6 +320,9 @@ go_pixbuf_get_property (GObject *obj, guint param_id,
        case PIXBUF_PROP_PIXBUF:
                g_value_set_object (value, pixbuf->pixbuf);
                break;
+       case PIXBUF_PROP_TYPE:
+               g_value_set_string (value, pixbuf->type);
+               break;
 
        default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
                return; /* NOTE : RETURN */
@@ -279,6 +337,7 @@ go_pixbuf_finalize (GObject *obj)
                g_object_unref (pixbuf->pixbuf);
        if (pixbuf->surface)
                cairo_surface_destroy (pixbuf->surface);
+       g_free (pixbuf->type);
        (parent_klass->finalize) (obj);
 }
 
@@ -304,6 +363,12 @@ go_pixbuf_class_init (GObjectClass *klass)
                                         g_param_spec_object ("pixbuf", _("Pixbuf"),
                                                              _("GdkPixbuf object from which the GOPixbuf is 
built"),
                                                              GDK_TYPE_PIXBUF, G_PARAM_READWRITE));
+       g_object_class_install_property (klass, PIXBUF_PROP_TYPE,
+                g_param_spec_string ("image-type",
+                                     N_("Image type"),
+                                     N_("Type of image"),
+                                     NULL,
+                                     GSF_PARAM_STATIC | G_PARAM_READWRITE));
 }
 
 GSF_CLASS (GOPixbuf, go_pixbuf,
@@ -317,6 +382,21 @@ go_pixbuf_new_from_pixbuf (GdkPixbuf *pixbuf)
        return g_object_new (GO_TYPE_PIXBUF, "pixbuf", pixbuf, NULL);
 }
 
+GOImage *
+go_pixbuf_new_from_data (char const *type, guint8 const *data, gsize length, GError **error)
+{
+       GOImage *image = g_object_new (GO_TYPE_PIXBUF, NULL);
+       image->data = g_memdup (data, length);
+       image->data_length = length;
+       g_object_set (image, "image-type", type, NULL);
+       go_pixbuf_create_pixbuf (GO_PIXBUF (image), error);
+       if (*error != NULL) {
+               g_object_unref (image);
+               return NULL;
+       }
+       return image;
+}
+
 int
 go_pixbuf_get_rowstride (GOPixbuf *pixbuf)
 {
diff --git a/goffice/utils/go-pixbuf.h b/goffice/utils/go-pixbuf.h
index bd57c24..7657a22 100644
--- a/goffice/utils/go-pixbuf.h
+++ b/goffice/utils/go-pixbuf.h
@@ -33,7 +33,9 @@ G_BEGIN_DECLS
 
 GType go_pixbuf_get_type (void);
 
-GOImage                *go_pixbuf_new_from_pixbuf      (GdkPixbuf *pixbuf);
+GOImage                *go_pixbuf_new_from_pixbuf  (GdkPixbuf *pixbuf);
+GOImage                *go_pixbuf_new_from_data        (char const *type, guint8 const *data,
+                                                    gsize length, GError **error);
 int             go_pixbuf_get_rowstride        (GOPixbuf *pixbuf);
 
 G_END_DECLS


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]