return NULL;
}
+#ifdef HAVE_SVG
+/*
+ * Renders a SVG into a Cairo context at the given dimensions keeping
+ * the aspect ratio.
+ *
+ * Adapted from https://developer.gnome.org/rsvg/2.40/RsvgHandle.html
+ * #rsvg-handle-set-size-callback
+ */
+void render_scaled_proportionally(RsvgHandle *handle, cairo_t *cr, int width, int height)
+{
+ RsvgDimensionData dimensions;
+ double x_factor, y_factor;
+ double scale_factor;
+
+ rsvg_handle_get_dimensions(handle, &dimensions);
+
+ x_factor = (double) width / dimensions.width;
+ y_factor = (double) height / dimensions.height;
+
+ scale_factor = MIN(x_factor, y_factor);
+
+ cairo_scale(cr, scale_factor, scale_factor);
+
+ rsvg_handle_render_cairo(handle, cr);
+}
+
+/*
+ * Generates a new Pixbuf from a Cairo context of the given dimensions.
+ *
+ * Adapted from https://gist.github.com/bert/985903
+ */
+GdkPixbuf *pixbuf_from_cairo(cairo_t *cr, gboolean alpha, int width, int height)
+{
+ gint p_stride, /* Pixbuf stride value */
+ p_n_channels, /* RGB -> 3, RGBA -> 4 */
+ s_stride; /* Surface stride value */
+ guchar *p_pixels, /* Pixbuf's pixel data */
+ *s_pixels; /* Surface's pixel data */
+ cairo_surface_t *surface; /* Temporary image surface */
+ GdkPixbuf *pixbuf; /* Returned pixbuf */
+
+ /* Create pixbuf */
+ pixbuf = gdk_pixbuf_new
+ (GDK_COLORSPACE_RGB, alpha, 8, width, height);
+ if (pixbuf == NULL) {
+ g_warning("failed to create a new %d x %d pixbuf", width, height);
+ return NULL;
+ }
+ /* Obtain surface from where pixel values will be copied */
+ surface = cairo_get_target(cr);
+ if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
+ g_warning("invalid cairo surface for copying");
+ return NULL;
+ }
+ /* Inspect pixbuf */
+ g_object_get(G_OBJECT(pixbuf),
+ "rowstride", &p_stride,
+ "n-channels", &p_n_channels,
+ "pixels", &p_pixels,
+ NULL);
+ /* and surface */
+ s_stride = cairo_image_surface_get_stride(surface);
+ s_pixels = cairo_image_surface_get_data(surface);
+
+ /* Copy pixel data from surface to pixbuf */
+ while (height--) {
+ gint i;
+ guchar *p_iter = p_pixels, *s_iter = s_pixels;
+ for (i = 0; i < width; i++) {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ /* Pixbuf: RGB(A) - Surface: BGRA */
+ gdouble alpha_factor = (gdouble)0xff / s_iter[3];
+ p_iter[0] = (guchar)( s_iter[2] * alpha_factor + .5 );
+ p_iter[1] = (guchar)( s_iter[1] * alpha_factor + .5 );
+ p_iter[2] = (guchar)( s_iter[0] * alpha_factor + .5 );
+ if (p_n_channels == 4)
+ p_iter[3] = s_iter[3];
+#elif G_BYTE_ORDER == G_BIG_ENDIAN
+ /* Pixbuf: RGB(A) - Surface: ARGB */
+ gdouble alpha_factor = (gdouble)0xff / s_iter[0];
+ p_iter[0] = (guchar)( s_iter[1] * alpha_factor + .5 );
+ p_iter[1] = (guchar)( s_iter[2] * alpha_factor + .5 );
+ p_iter[2] = (guchar)( s_iter[3] * alpha_factor + .5 );
+ if (p_n_channels == 4)
+ p_iter[3] = s_iter[0];
+#else /* PDP endianness */
+ /* Pixbuf: RGB(A) - Surface: RABG */
+ gdouble alpha_factor = (gdouble)0xff / s_iter[1];
+ p_iter[0] = (guchar)( s_iter[0] * alpha_factor + .5 );
+ p_iter[1] = (guchar)( s_iter[3] * alpha_factor + .5 );
+ p_iter[2] = (guchar)( s_iter[2] * alpha_factor + .5 );
+ if (p_n_channels == 4)
+ p_iter[3] = s_iter[1];
+#endif
+ s_iter += 4;
+ p_iter += p_n_channels;
+ }
+ s_pixels += s_stride;
+ p_pixels += p_stride;
+ }
+ /* Destroy context */
+ cairo_destroy(cr);
+
+ return pixbuf;
+}
+
+/*
+ * Renders a SVG file into a pixbuf with the dimensions of the
+ * given pixmap data (optionally with alpha channel).
+ */
+GdkPixbuf *pixbuf_from_svg_like_icon(char *filename, GError **error, StockPixmapData *icondata, gboolean alpha)
+{
+ int width, height;
+ cairo_surface_t *surface;
+ cairo_t *context;
+ RsvgHandle *handle;
+
+ cm_return_val_if_fail(filename != NULL, NULL);
+ cm_return_val_if_fail(icondata != NULL, NULL);
+
+ if (sscanf((icondata->data)[0], "%d %d ", &width, &height) != 2) {
+ g_warning("failed reading icondata width and height");
+ return NULL;
+ }
+
+ /* load SVG file */
+ handle = rsvg_handle_new_from_file(filename, error);
+ if (handle == NULL) {
+ g_warning("failed loading SVG '%s': %s (%d)", filename,
+ (*error)->message, (*error)->code);
+ return NULL;
+ }
+ /* create drawing context */
+ surface = cairo_image_surface_create(
+ alpha? CAIRO_FORMAT_ARGB32: CAIRO_FORMAT_RGB24,
+ width, height);
+ if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
+ g_warning("failed to create a cairo surface: %s",
+ cairo_status_to_string(cairo_surface_status(surface)));
+ g_object_unref(handle);
+ return NULL;
+ }
+ context = cairo_create(surface);
+ cairo_surface_destroy(surface);
+ if (cairo_status(context) != CAIRO_STATUS_SUCCESS) {
+ g_warning("failed to create a cairo context: %s",
+ cairo_status_to_string(cairo_status(context)));
+ cairo_destroy(context);
+ return NULL;
+ }
+ /* render SVG */
+ render_scaled_proportionally(handle, context, width, height);
+ /* build result and destroy context */
+ return pixbuf_from_cairo(context, alpha, width, height);
+}
+#endif
+
/*!
*\brief
*/
GError *err = NULL;
#ifdef HAVE_SVG
if (!strncmp(extension[i], ".svg", 4)) {
- pix = rsvg_pixbuf_from_file(icon_file_name, &err);
+ pix = pixbuf_from_svg_like_icon(icon_file_name, &err, pix_d, TRUE);
} else {
pix = gdk_pixbuf_new_from_file(icon_file_name, &err);
}