#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;
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;
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:
* 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) {
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;
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;
}
* 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;
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;
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);
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;
}
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);
}
}
}
-/*
-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;
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);
g_propagate_error(error, sub_error);
result = FALSE;
}
+ g_free(context->svg_data);
g_free(context);
return result;