]> gitweb.michael.orlitzky.com - libsvgtiny-pixbuf.git/blobdiff - io-svg.c
io-svg.c: simplify memory management for context->svg_data
[libsvgtiny-pixbuf.git] / io-svg.c
index 341d9e5939ed74f0fe9bb32803513bb7a2420941..543162f6c0b21f8357679f3e03ce76f408e91da9 100644 (file)
--- a/io-svg.c
+++ b/io-svg.c
@@ -16,8 +16,6 @@
 #define VIEWPORT_WIDTH 512
 #define VIEWPORT_HEIGHT 512
 
-#define SVG_BUFFER_INCREMENT (size_t)4194304
-
 /* Convenient typedefs for libsvgtiny */
 typedef struct svgtiny_diagram diagram_t;
 typedef struct svgtiny_shape shape_t;
@@ -29,10 +27,13 @@ typedef struct {
   GdkPixbufModuleSizeFunc     size_func;
   gpointer                    user_data;
 
-  /* The "file" */
+  /* The SVG "file" that we're building in memory. */
   char*                       svg_data;
-  size_t                      svg_data_size;
-  size_t                      svg_data_max;
+
+  /* How far into svg_data are we? This should always point to the
+     next empty byte. If (for example) svg_data_size is 2, then
+     svg_data[0] and svg_data[1] are used, but svg_data[2] is free. */
+  size_t svg_data_size;
 
 } SvgTinyContext;
 
@@ -55,8 +56,8 @@ static void render_path(cairo_t* cr, shape_t* path) {
     switch ((int) path->path[j]) {
     case svgtiny_PATH_MOVE:
       cairo_move_to(cr,
-                   path->path[j + 1],
-                   path->path[j + 2]);
+                    path->path[j + 1],
+                    path->path[j + 2]);
       j += 3;
       break;
     case svgtiny_PATH_CLOSE:
@@ -113,24 +114,23 @@ static void render_path(cairo_t* cr, shape_t* path) {
  *   svgtiny_free the result if it is valid.
  */
 static diagram_t* svgtiny_diagram_from_buffer(char* buffer,
-                                             size_t bytecount,
-                                             int width,
-                                             int height,
-                                             GError** error) {
+                                              size_t bytecount,
+                                              int width,
+                                              int height,
+                                              GError** error) {
   diagram_t* diagram;
   svgtiny_code code;
 
   diagram = svgtiny_create();
   if (!diagram) {
     g_set_error_literal(error,
-                        GDK_PIXBUF_ERROR,
-                        GDK_PIXBUF_ERROR_FAILED,
-                        "svgtiny_create() failed");
+                        G_FILE_ERROR,
+                        G_FILE_ERROR_NOMEM,
+                        "out of memory in svgtiny_create()");
     return NULL;
   }
 
   code = svgtiny_parse(diagram, buffer, bytecount, "", width, height);
-  free(buffer);
 
   if (code != svgtiny_OK) {
     switch (code) {
@@ -144,19 +144,19 @@ static diagram_t* svgtiny_diagram_from_buffer(char* buffer,
       g_set_error_literal(error,
                           GDK_PIXBUF_ERROR,
                           GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
-                          "libdom error in svgtiny_parse()");
+                          "invalid XML DOM in svgtiny_parse()");
       break;
     case svgtiny_NOT_SVG:
       g_set_error_literal(error,
                           GDK_PIXBUF_ERROR,
                           GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
-                          "encountered svgtiny_NOT_SVG in svgtiny_parse()");
+                          "missing <svg> element in svgtiny_parse()");
       break;
     case svgtiny_SVG_ERROR:
       g_set_error(error,
                   GDK_PIXBUF_ERROR,
                   GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
-                  "SVG error in svgtiny_parse() on line %i: %s",
+                  "SVG format error in svgtiny_parse() on line %i: %s",
                   diagram->error_line,
                   diagram->error_message);
       break;
@@ -247,8 +247,8 @@ static cairo_t* cairo_context_from_diagram(diagram_t* diagram) {
   crs = cairo_status(cr);
   if (crs != CAIRO_STATUS_SUCCESS) {
     fprintf(stderr,
-           "cairo error: %s\n",
-           cairo_status_to_string(crs));
+            "cairo error: %s\n",
+            cairo_status_to_string(crs));
     cairo_destroy(cr);
     return NULL;
   }
@@ -272,18 +272,18 @@ static cairo_t* cairo_context_from_diagram(diagram_t* diagram) {
  *   returned; if not, @c NULL is returned and @c error is populated.
  */
 static GdkPixbuf* gdk_pixbuf_from_svg_buffer(char* buffer,
-                                            size_t bytecount,
-                                            GError** error) {
+                                             size_t bytecount,
+                                             GError** error) {
   diagram_t* diagram;
   cairo_t* cr = 0;
   GdkPixbuf* pb;
   GError* sub_error = NULL;
 
   diagram = svgtiny_diagram_from_buffer(buffer,
-                                       bytecount,
-                                       VIEWPORT_WIDTH,
-                                       VIEWPORT_HEIGHT,
-                                       &sub_error);
+                                        bytecount,
+                                        VIEWPORT_WIDTH,
+                                        VIEWPORT_HEIGHT,
+                                        &sub_error);
   if (!diagram) {
     g_propagate_error(error, sub_error);
     return NULL;
@@ -293,35 +293,33 @@ static GdkPixbuf* gdk_pixbuf_from_svg_buffer(char* buffer,
   if (!cr) {
     svgtiny_free(diagram);
     g_set_error_literal(error,
-                       GDK_PIXBUF_ERROR,
-                       GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
-                       "could not create Cairo surface from SVG diagram");
+                        GDK_PIXBUF_ERROR,
+                        GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                        "could not create Cairo surface from SVG diagram");
     return NULL;
   }
 
 
-  /* We're using the viewport width and height and not the diagram
-   * width/height for the image. The diagram can be of a different
-   * size and aspect ratio than the viewport, and our main use case is
-   * for icons that are generally square and reasonably sized. If the
-   * diagram is "small," then we want to scale it up until it fits
-   * nicely in the viewport before rendering it. That's as opposed to
-   * rendering the image small, and letting GDK scale it up. Of course
-   * this reasoning makes the assumption that the viewport is usually
-   * larger than the diagram.
-  */
+  /* I've gone back and forth on this about five times: we use the
+   * diagram width and height, and not the viewport width and height.
+   * This can ultimately render an image that's larger than the
+   * viewport size, but I think GDK will resize the final pixbuf
+   * anyway. More importantly, rendering small icons at a larger
+   * (viewport) size seems to make the whole thing go ape-shit.
+   * So for now I'm back in the diagram camp.
+   */
   pb = gdk_pixbuf_get_from_surface(cairo_get_target(cr),
-                                  0,
-                                  0,
-                                  VIEWPORT_WIDTH,
-                                  VIEWPORT_HEIGHT);
+                                   0,
+                                   0,
+                                   diagram->width,
+                                   diagram->height);
 
 
   if (!pb) {
     g_set_error_literal(error,
-                       GDK_PIXBUF_ERROR,
-                       GDK_PIXBUF_ERROR_FAILED,
-                       "failed to obtain a GdkPixbuf from Cairo surface");
+                        GDK_PIXBUF_ERROR,
+                        GDK_PIXBUF_ERROR_FAILED,
+                        "failed to obtain a GdkPixbuf from Cairo surface");
   }
 
   return pb;
@@ -329,10 +327,10 @@ static GdkPixbuf* gdk_pixbuf_from_svg_buffer(char* buffer,
 
 
 static gpointer gdk_pixbuf_begin_load(GdkPixbufModuleSizeFunc size_func,
-                                     GdkPixbufModulePreparedFunc prep_func,
-                                     GdkPixbufModuleUpdatedFunc updated_func,
-                                     gpointer user_data,
-                                     GError **error) {
+                                      GdkPixbufModulePreparedFunc prep_func,
+                                      GdkPixbufModuleUpdatedFunc updated_func,
+                                      gpointer user_data,
+                                      GError **error) {
 
   SvgTinyContext* context = g_new(SvgTinyContext, 1);
 
@@ -341,37 +339,24 @@ static gpointer gdk_pixbuf_begin_load(GdkPixbufModuleSizeFunc size_func,
   context->updated_func   = updated_func;
   context->user_data      = user_data;
 
-  /* YOLO, no error checking */
-  context->svg_data       = g_malloc(SVG_BUFFER_INCREMENT);
+  context->svg_data       = NULL;
   context->svg_data_size  = 0;
 
   return context;
 }
 
 static gboolean gdk_pixbuf_load_increment(gpointer data,
-                                         const guchar* buf,
-                                         guint size,
-                                         GError** error) {
+                                          const guchar* buf,
+                                          guint buf_size,
+                                          GError** error) {
   size_t increment = 0;
   SvgTinyContext* context = (SvgTinyContext*)data;
 
-  if (context->svg_data_size + size > context->svg_data_max) {
-    if (size > SVG_BUFFER_INCREMENT) {
-      increment = size;
-    }
-    else {
-      increment = SVG_BUFFER_INCREMENT;
-    }
-
-    /* YOLO, no error checking */
-    context->svg_data = g_realloc(context->svg_data,
-                                 context->svg_data_max + increment);
-
-    context->svg_data_max += increment;
-  }
-
-  memcpy(context->svg_data + context->svg_data_size, buf, size);
-  context->svg_data_size += size;
+  /* YOLO, no error checking */
+  context->svg_data = g_realloc(context->svg_data,
+                                context->svg_data_size + buf_size);
+  memcpy(context->svg_data + context->svg_data_size, buf, buf_size);
+  context->svg_data_size += buf_size;
 
   return TRUE;
 }
@@ -379,11 +364,11 @@ static gboolean gdk_pixbuf_load_increment(gpointer data,
 static void emit_updated(SvgTinyContext* context, GdkPixbuf* pixbuf) {
   if (context->updated_func != NULL) {
     (*context->updated_func)(pixbuf,
-                            0,
-                            0,
-                            gdk_pixbuf_get_width(pixbuf),
-                            gdk_pixbuf_get_height(pixbuf),
-                            context->user_data);
+                             0,
+                             0,
+                             gdk_pixbuf_get_width(pixbuf),
+                             gdk_pixbuf_get_height(pixbuf),
+                             context->user_data);
   }
 }
 
@@ -394,17 +379,6 @@ static void emit_prepared(SvgTinyContext* context, GdkPixbuf* pixbuf) {
 }
 
 
-/*
-static void emit_size(SvgTinyContext* context, GdkPixbuf* pixbuf) {
-  int w = gdk_pixbuf_get_width(pixbuf);
-  int h = gdk_pixbuf_get_height(pixbuf);
-  if (context->size_func != NULL) {
-    (*context->size_func)(&w, &h, context->user_data);
-  }
-}
-*/
-
-
 static gboolean gdk_pixbuf_stop_load(gpointer data, GError **error) {
   SvgTinyContext* context = (SvgTinyContext*)data;
   GdkPixbuf* pixbuf = NULL;
@@ -412,11 +386,10 @@ static gboolean gdk_pixbuf_stop_load(gpointer data, GError **error) {
   GError* sub_error = NULL;
 
   pixbuf = gdk_pixbuf_from_svg_buffer(context->svg_data,
-                                     context->svg_data_size,
-                                     &sub_error);
+                                      context->svg_data_size,
+                                      &sub_error);
 
   if (pixbuf != NULL) {
-    /*emit_size(context, pixbuf);*/
     emit_prepared(context, pixbuf);
     emit_updated(context, pixbuf);
     g_object_unref(pixbuf);
@@ -425,6 +398,7 @@ static gboolean gdk_pixbuf_stop_load(gpointer data, GError **error) {
     g_propagate_error(error, sub_error);
     result = FALSE;
   }
+  g_free(context->svg_data);
   g_free(context);
 
   return result;