]> Dogcows Code - chaz/openbox/commitdiff
Add support for loading SVG icons using librsvg.
authorDana Jansens <danakj@orodu.net>
Sun, 11 Aug 2013 00:46:27 +0000 (20:46 -0400)
committerDana Jansens <danakj@orodu.net>
Sun, 11 Aug 2013 01:59:12 +0000 (21:59 -0400)
This adds a configure option --disable-librsvg, but defaults to
using the library if it is present during configure.

When enabled, Openbox will attempt to load svg image files using
the library, similar to how Imlib2 is used for other image
formats.

Since librsvg uses the libXml2 library, their errors end up in
the same global namespace as Openbox config file parsing. To
avoid this, we reset the libXml current error whenever we start
loading a file, and save the last error that occurred when we
are finished, by storing the error in the ObtXmlInst.

Makefile.am
configure.ac
obrender/image.c
obrender/obrender-3.5.pc.in
obrender/render.h
obt/xml.c
obt/xml.h
openbox/openbox.c
release/go

index f32591f571b3c4c53503092119015c8c3c57fb57..4d4a9097dc1de9a6eb569890771128d6c497adae 100644 (file)
@@ -75,6 +75,7 @@ obrender_libobrender_la_CPPFLAGS = \
        $(XML_CFLAGS) \
        $(PANGO_CFLAGS) \
        $(IMLIB2_CFLAGS) \
+       $(LIBRSVG_CFLAGS) \
        -DG_LOG_DOMAIN=\"ObRender\" \
        -DDEFAULT_THEME=\"$(theme)\"
 obrender_libobrender_la_LDFLAGS = \
@@ -85,6 +86,7 @@ obrender_libobrender_la_LIBADD = \
        $(PANGO_LIBS) \
        $(GLIB_LIBS) \
        $(IMLIB2_LIBS) \
+       $(LIBRSVG_LIBS) \
        $(XML_LIBS)
 obrender_libobrender_la_SOURCES = \
        gettext.h \
index a18330907ad6b61609d3742d5b08655a303556b6..49cfd93820de00a21ef4c7cd2790790a3af079d7 100644 (file)
@@ -195,6 +195,36 @@ fi
 
 AM_CONDITIONAL(USE_IMLIB2, [test $imlib2_found = yes])
 
+AC_ARG_ENABLE(librsvg,
+  AC_HELP_STRING(
+    [--disable-librsvg],
+    [disable use of SVG image files for loading icons. [default=enabled]]
+  ),
+  [enable_librsvg=$enableval],
+  [enable_librsvg=yes]
+)
+
+if test "$enable_librsvg" = yes; then
+PKG_CHECK_MODULES(LIBRSVG, [librsvg-2.0],
+  [
+    AC_DEFINE(USE_LIBRSVG, [1], [Use SVG image files])
+    AC_SUBST(LIBRSVG_CFLAGS)
+    AC_SUBST(LIBRSVG_LIBS)
+    # export it for the pkg-config file
+    PKG_CONFIG_LIBRSVG=librsvg-2.0
+    AC_SUBST(PKG_CONFIG_LIBRSVG)
+    librsvg_found=yes
+  ],
+  [
+    librsvg_found=no
+  ]
+)
+else
+  librsvg_found=no
+fi
+
+AM_CONDITIONAL(USE_LIBRSVG, [test $librsvg_found = yes])
+
 dnl Check for session management
 X11_SM
 
@@ -233,6 +263,7 @@ AC_MSG_RESULT([Compiling with these options:
                Startup Notification... $sn_found
                X Cursor Library... $xcursor_found
                Session Management... $SM
-               Imlib2 library... $imlib2_found
+               Imlib2 Library... $imlib2_found
+               SVG Support (librsvg)... $librsvg_found
                ])
 AC_MSG_RESULT([configure complete, now type "make"])
index 196d9d1ef419f8d68251a9dfbee39205a7ef461a..0164a8c8c3a5df8e42809e30999ea08e0d4107cb 100644 (file)
@@ -24,6 +24,9 @@
 #ifdef USE_IMLIB2
 #include <Imlib2.h>
 #endif
+#ifdef USE_LIBRSVG
+#include <librsvg/rsvg.h>
+#endif
 
 #include <glib.h>
 
@@ -463,17 +466,164 @@ RrImage* RrImageNewFromData(RrImageCache *cache, RrPixel32 *data,
     return self;
 }
 
+#if defined(USE_IMLIB2)
+typedef struct _ImlibLoader ImlibLoader;
+
+struct _ImlibLoader
+{
+    Imlib_Image img;
+};
+
+void DestroyImlibLoader(ImlibLoader *loader)
+{
+    if (!loader)
+        return;
+
+    imlib_free_image();
+    g_slice_free(ImlibLoader, loader);
+}
+
+ImlibLoader* LoadWithImlib(gchar *path,
+                           RrPixel32 **pixel_data,
+                           gint *width,
+                           gint *height)
+{
+    ImlibLoader *loader = g_slice_new0(ImlibLoader);
+    if (!(loader->img = imlib_load_image(path))) {
+        DestroyImlibLoader(loader);
+        return NULL;
+    }
+
+    /* Get data and dimensions of the image.
+
+       WARNING: This stuff is NOT threadsafe !!
+    */
+    imlib_context_set_image(loader->img);
+    *pixel_data = imlib_image_get_data_for_reading_only();
+    *width = imlib_image_get_width();
+    *height = imlib_image_get_height();
+
+    return loader;
+}
+#endif  /* USE_IMLIB2 */
+
+#if defined(USE_LIBRSVG)
+typedef struct _RsvgLoader RsvgLoader;
+
+struct _RsvgLoader
+{
+    RsvgHandle *handle;
+    cairo_surface_t *surface;
+    RrPixel32 *pixel_data;
+};
+
+void DestroyRsvgLoader(RsvgLoader *loader)
+{
+    if (!loader)
+        return;
+
+    if (loader->pixel_data)
+        g_free(loader->pixel_data);
+    if (loader->surface)
+        cairo_surface_destroy(loader->surface);
+    if (loader->handle)
+        g_object_unref(loader->handle);
+    g_slice_free(RsvgLoader, loader);
+}
+
+RsvgLoader* LoadWithRsvg(gchar *path,
+                         RrPixel32 **pixel_data,
+                         gint *width,
+                         gint *height)
+{
+    RsvgLoader *loader = g_slice_new0(RsvgLoader);
+
+    if (!(loader->handle = rsvg_handle_new_from_file(path, NULL))) {
+        DestroyRsvgLoader(loader);
+        return NULL;
+    }
+
+    if (!rsvg_handle_close(loader->handle, NULL)) {
+        DestroyRsvgLoader(loader);
+        return NULL;
+    }
+
+    RsvgDimensionData dimension_data;
+    rsvg_handle_get_dimensions(loader->handle, &dimension_data);
+    *width = dimension_data.width;
+    *height = dimension_data.height;
+
+    loader->surface = cairo_image_surface_create(
+        CAIRO_FORMAT_ARGB32, *width, *height);
+
+    cairo_t* context = cairo_create(loader->surface);
+    gboolean success = rsvg_handle_render_cairo(loader->handle, context);
+    cairo_destroy(context);
+
+    if (!success) {
+        DestroyRsvgLoader(loader);
+        return NULL;
+    }
+
+    loader->pixel_data = g_new(guint32, *width * *height);
+
+    /*
+      Cairo has its data in ARGB with premultiplied alpha, but RrPixel32
+      non-premultipled, so convert that. Also, RrPixel32 doesn't allow
+      strides not equal to the width of the image.
+    */
+
+    /* Verify that RrPixel32 has the same ordering as cairo. */
+    g_assert(RrDefaultAlphaOffset == 24);
+    g_assert(RrDefaultRedOffset == 16);
+    g_assert(RrDefaultGreenOffset == 8);
+    g_assert(RrDefaultBlueOffset == 0);
+
+    guint32 *out_row = loader->pixel_data;
+
+    guint32 *in_row =
+        (guint32*)cairo_image_surface_get_data(loader->surface);
+    gint in_stride = cairo_image_surface_get_stride(loader->surface);
+
+    gint y;
+    for (y = 0; y < *height; ++y) {
+        gint x;
+        for (x = 0; x < *width; ++x) {
+            guchar a = in_row[x] >> 24;
+            guchar r = (in_row[x] >> 16) & 0xff;
+            guchar g = (in_row[x] >> 8) & 0xff;
+            guchar b = in_row[x] & 0xff;
+            out_row[x] =
+                ((r * 256 / (a + 1)) << RrDefaultRedOffset) +
+                ((g * 256 / (a + 1)) << RrDefaultGreenOffset) +
+                ((b * 256 / (a + 1)) << RrDefaultBlueOffset) +
+                (a << RrDefaultAlphaOffset);
+        }
+        in_row += in_stride / 4;
+        out_row += *width;
+    }
+
+    *pixel_data = loader->pixel_data;
+
+    return loader;
+}
+#endif  /* USE_LIBRSVG */
+
 RrImage* RrImageNewFromName(RrImageCache *cache, const gchar *name)
 {
-#ifndef USE_IMLIB2
-    return NULL;
-#else
     RrImage *self;
     RrImageSet *set;
-    Imlib_Image img;
     gint w, h;
     RrPixel32 *data;
     gchar *path;
+    gboolean loaded;
+
+#if defined(USE_IMLIB2)
+    ImlibLoader *imlib_loader = NULL;
+#endif
+#if defined(USE_LIBRSVG)
+    RsvgLoader *rsvg_loader = NULL;
+#endif
 
     g_return_val_if_fail(cache != NULL, NULL);
     g_return_val_if_fail(name != NULL, NULL);
@@ -488,21 +638,32 @@ RrImage* RrImageNewFromName(RrImageCache *cache, const gchar *name)
     /* XXX find the path via freedesktop icon spec (use obt) ! */
     path = g_strdup(name);
 
-    if (!(img = imlib_load_image(path)))
-        g_message("Cannot load image \"%s\" from file \"%s\"", name, path);
+    loaded = FALSE;
+#if defined(USE_LIBRSVG)
+    if (!loaded) {
+        rsvg_loader = LoadWithRsvg(path, &data, &w, &h);
+        loaded = !!rsvg_loader;
+    }
+#endif
+#if defined(USE_IMLIB2)
+    if (!loaded) {
+        imlib_loader = LoadWithImlib(path, &data, &w, &h);
+        loaded = !!imlib_loader;
+    }
+#endif
+
     g_free(path);
 
-    if (!img)
+    if (!loaded) {
+        g_message("Cannot load image \"%s\" from file \"%s\"", name, path);
+#if defined(USE_LIBRSVG)
+        DestroyRsvgLoader(rsvg_loader);
+#endif
+#if defined(USE_IMLIB2)
+        DestroyImlibLoader(imlib_loader);
+#endif
         return NULL;
-
-    /* Get data and dimensions of the image.
-
-       WARNING: This stuff is NOT threadsafe !!
-    */
-    imlib_context_set_image(img);
-    data = imlib_image_get_data_for_reading_only();
-    w = imlib_image_get_width();
-    h = imlib_image_get_height();
+    }
 
     /* get an RrImage that contains an RrImageSet with this picture in it.
        the RrImage might be new, or reused if the picture was already in the
@@ -517,9 +678,14 @@ RrImage* RrImageNewFromName(RrImageCache *cache, const gchar *name)
     self = RrImageNewFromData(cache, data, w, h);
     RrImageSetAddName(self->set, name);
 
-    imlib_free_image();
-    return self;
+#if defined(USE_LIBRSVG)
+    DestroyRsvgLoader(rsvg_loader);
+#endif
+#if defined(USE_IMLIB2)
+    DestroyImlibLoader(imlib_loader);
 #endif
+
+    return self;
 }
 
 /************************************************************************
index 2c8a4357b7e7cef3226d4c977a1bd934b6449883..8057a01789c7e19c6aba6e1efb4e31dc4a749f6a 100644 (file)
@@ -9,6 +9,6 @@ xlibs=@X_LIBS@
 Name: ObRender
 Description: Openbox Render Library
 Version: @RR_VERSION@
-Requires: obt-3.5 glib-2.0 xft pangoxft @PKG_CONFIG_IMLIB@
+Requires: obt-3.5 glib-2.0 xft pangoxft @PKG_CONFIG_IMLIB@ @PKG_CONFIG_LIBRSVG@
 Libs: -L${libdir} -lobrender ${xlibs}
 Cflags: -I${includedir}/openbox/@RR_VERSION@ ${xcflags}
index a5d6500ce5f768db32fc2fa90f65637f440b0667..59e77660cc1d237590c52938b2e926969339b217 100644 (file)
@@ -49,7 +49,7 @@ typedef struct _RrImagePic         RrImagePic;
 typedef struct _RrImageCache       RrImageCache;
 typedef struct _RrButton           RrButton;
 
-typedef guint32 RrPixel32;  /* RGBA format */
+typedef guint32 RrPixel32;  /* ARGB format, not premultiplied alpha */
 typedef guint16 RrPixel16;
 typedef guchar  RrPixel8;
 
index 5b7e77b5cb95c8b2ce94b5efca40cffed2593faa..223ad02b239df6dd0e173e7fb536e01b1027127a 100644 (file)
--- a/obt/xml.c
+++ b/obt/xml.c
@@ -48,8 +48,13 @@ struct _ObtXmlInst {
     xmlDocPtr doc;
     xmlNodePtr root;
     gchar *path;
+    gchar *last_error_file;
+    gint last_error_line;
+    gchar *last_error_message;
 };
 
+static void obt_xml_save_last_error(ObtXmlInst* inst);
+
 static void destfunc(struct Callback *c)
 {
     g_free(c->tag);
@@ -66,6 +71,9 @@ ObtXmlInst* obt_xml_instance_new(void)
     i->doc = NULL;
     i->root = NULL;
     i->path = NULL;
+    i->last_error_file = NULL;
+    i->last_error_line = -1;
+    i->last_error_message = NULL;
     return i;
 }
 
@@ -79,6 +87,8 @@ void obt_xml_instance_unref(ObtXmlInst *i)
     if (i && --i->ref == 0) {
         obt_paths_unref(i->xdg_paths);
         g_hash_table_destroy(i->callbacks);
+        g_free(i->last_error_file);
+        g_free(i->last_error_message);
         g_slice_free(ObtXmlInst, i);
     }
 }
@@ -128,6 +138,8 @@ static gboolean load_file(ObtXmlInst *i,
 
     g_assert(i->doc == NULL); /* another doc isn't open already? */
 
+    xmlResetLastError();
+
     for (it = paths; !r && it; it = g_slist_next(it)) {
         gchar *path;
         struct stat s;
@@ -169,6 +181,8 @@ static gboolean load_file(ObtXmlInst *i,
         g_free(path);
     }
 
+    obt_xml_save_last_error(i);
+
     return r;
 }
 
@@ -264,6 +278,8 @@ gboolean obt_xml_load_mem(ObtXmlInst *i,
 
     g_assert(i->doc == NULL); /* another doc isn't open already? */
 
+    xmlResetLastError();
+
     i->doc = xmlParseMemory(data, len);
     if (i) {
         i->root = xmlDocGetRootElement(i->doc);
@@ -282,9 +298,51 @@ gboolean obt_xml_load_mem(ObtXmlInst *i,
         else
             r = TRUE; /* ok ! */
     }
+
+    obt_xml_save_last_error(i);
+
     return r;
 }
 
+static void obt_xml_save_last_error(ObtXmlInst* inst)
+{
+    xmlErrorPtr error = xmlGetLastError();
+    if (error) {
+        inst->last_error_file = g_strdup(error->file);
+        inst->last_error_line = error->line;
+        inst->last_error_message = g_strdup(error->message);
+        xmlResetError(error);
+    }
+}
+
+gboolean obt_xml_last_error(ObtXmlInst *inst)
+{
+    return inst->last_error_file &&
+        inst->last_error_line >= 0 &&
+        inst->last_error_message;
+}
+
+gchar* obt_xml_last_error_file(ObtXmlInst *inst)
+{
+    if (!obt_xml_last_error(inst))
+        return NULL;
+    return inst->last_error_file;
+}
+
+gint obt_xml_last_error_line(ObtXmlInst *inst)
+{
+    if (!obt_xml_last_error(inst))
+        return -1;
+    return inst->last_error_line;
+}
+
+gchar* obt_xml_last_error_message(ObtXmlInst *inst)
+{
+    if (!obt_xml_last_error(inst))
+        return NULL;
+    return inst->last_error_message;
+}
+
 gboolean obt_xml_save_file(ObtXmlInst *inst,
                            const gchar *path,
                            gboolean pretty)
index 831aba63714a5cf5e5f69d8a77c599ae30ae5793..f6b5dc23b87748ff4a9365547930fa8019f8210a 100644 (file)
--- a/obt/xml.h
+++ b/obt/xml.h
@@ -51,6 +51,12 @@ gboolean obt_xml_load_theme_file(ObtXmlInst *inst,
 gboolean obt_xml_load_mem(ObtXmlInst *inst,
                           gpointer data, guint len, const gchar *root_node);
 
+/* Returns true if an error is present. */
+gboolean obt_xml_last_error(ObtXmlInst *inst);
+gchar* obt_xml_last_error_file(ObtXmlInst *inst);
+gint obt_xml_last_error_line(ObtXmlInst *inst);
+gchar* obt_xml_last_error_message(ObtXmlInst *inst);
+
 gboolean obt_xml_save_file(ObtXmlInst *inst,
                            const gchar *path,
                            gboolean pretty);
index 4ac09cd6665f02cb47870a40acbb61e057542691..cba0499533573ddfc777497e79cd5bc6d8c0e41c 100644 (file)
@@ -224,6 +224,7 @@ gint main(gint argc, gchar **argv)
         event_reset_time();
 
         do {
+            gchar *xml_error_string = NULL;
             ObPrompt *xmlprompt = NULL;
 
             if (reconfigure) obt_keyboard_reload();
@@ -264,6 +265,14 @@ gint main(gint argc, gchar **argv)
                 else
                     OBT_PROP_ERASE(obt_root(ob_screen), OB_CONFIG_FILE);
 
+                if (obt_xml_last_error(i)) {
+                    xml_error_string = g_strdup_printf(
+                        _("One or more XML syntax errors were found while parsing the Openbox configuration files.  See stdout for more information.  The last error seen was in file \"%s\" line %d, with message: %s"),
+                        obt_xml_last_error_file(i),
+                        obt_xml_last_error_line(i),
+                        obt_xml_last_error_message(i));
+                }
+
                 /* we're done with parsing now, kill it */
                 obt_xml_instance_unref(i);
             }
@@ -362,17 +371,12 @@ gint main(gint argc, gchar **argv)
             reconfigure = FALSE;
 
             /* look for parsing errors */
-            {
-                xmlErrorPtr e = xmlGetLastError();
-                if (e) {
-                    gchar *m;
-
-                    m = g_strdup_printf(_("One or more XML syntax errors were found while parsing the Openbox configuration files.  See stdout for more information.  The last error seen was in file \"%s\" line %d, with message: %s"), e->file, e->line, e->message);
-                    xmlprompt =
-                        prompt_show_message(m, _("Openbox Syntax Error"), _("Close"));
-                    g_free(m);
-                    xmlResetError(e);
-                }
+            if (xml_error_string) {
+                xmlprompt = prompt_show_message(xml_error_string,
+                                                _("Openbox Syntax Error"),
+                                                _("Close"));
+                g_free(xml_error_string);
+                xml_error_string = NULL;
             }
 
             g_main_loop_run(ob_main_loop);
index 3158ac21c7349b56858613c67877cd23bf9e581a..f54e80148d4f543b06cab395134ac988ada6e681 100755 (executable)
@@ -78,6 +78,13 @@ make >/dev/null 2>/dev/null || \
   error "make (with --disable-imlib2) failed"
 make clean >/dev/null || error "make clean failed"
 
+echo Check compile with librsvg disabled
+./configure -C --disable-imlib2 >/dev/null || \
+  error "configure failed"
+make >/dev/null 2>/dev/null || \
+  error "make (with --disable-librsvg) failed"
+make clean >/dev/null || error "make clean failed"
+
 echo Check compile with session management disabled
 ./configure -C --disable-session-management >/dev/null || \
   error "configure failed"
This page took 0.036222 seconds and 4 git commands to generate.