emacs-devel
[Top][All Lists]
Advanced

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

Re: SVG and Emacs


From: Paul Pogonyshev
Subject: Re: SVG and Emacs
Date: Sat, 25 Sep 2004 18:31:08 -0200
User-agent: KMail/1.4.3

RMS wrote:

>     Actually, librsvg doesn't use full GTK+, only gdk-pixbuf,
>     GLib and Pango.  Maybe it could be made dynamically-loadable
>     (though I can't say I'm familiar with dynamic library loading).
>
> Maybe that is ok.

Here is the first shot.  It is extremely non-portable, hackish and
ugly, but it works! :)

Do we want to continue in this direction?  If yes, then I'm open for
suggestions, especially on how to improve `configure.in' and
`Makefile.in' bits.  Portability is also a major issue.  I only work
under GNU/Linux and I don't really know how portable `librsvg' is,
in the first place.

Also, do we want to make SVG rendering dynamically loadable
(optionally?)  Actually, if we go on this, we can as well make all
the rest of image renderers load their libraries with dlopen().
From what I understood by looking at the code, the libraries are
already loaded dynamically under WinNT, so there must not be any
principal show-stoppers here.

Note that many ``1 ||'' in `image.c' are because I didn't run
Autoheader and so `HAVE_RSVG' didn't make its way to `config.h'.

Two side note on `configure.in':

* Many modules (i.e. image-rendering modules) that are built by
  default are mentioned as `--with-xxx  use -lxxx ...' in the help
  output.  It seems more logical to write
  `--without-xxx  don't use -lxxx ...'

* Maybe it is better to use AC_HELP_STRING()?

Paul


--- configure.in        04 May 2004 19:24:09 -0200      1.367
+++ configure.in        25 Sep 2004 16:16:10 -0200      
@@ -109,10 +109,12 @@ AC_ARG_WITH(gif,
 [  --with-gif              use -lungif for displaying GIF images])
 AC_ARG_WITH(png,
 [  --with-png              use -lpng for displaying PNG images])
+AC_ARG_WITH(rsvg,
+[  --with-rsvg             use -lrsvg-2 for displaying SVG images])
 AC_ARG_WITH(gtk,
 [  --with-gtk              use GTK (same as --with-x-toolkit=gtk)])
 AC_ARG_WITH(pkg-config-prog,
-[  --with-pkg-config-prog  Path to pkg-config to use for finding GTK])
+[  --with-pkg-config-prog  path to pkg-config to use for finding GTK and 
librsvg])
 AC_ARG_WITH(toolkit-scroll-bars,
 [  --without-toolkit-scroll-bars
                           don't use Motif or Xaw3d scroll bars])
@@ -1926,6 +1928,31 @@ AC_DEFUN([PKG_CHECK_MODULES], [
   fi
 ])
 
+### Use -lrsvg-2 if available, unless `--with-rsvg=no' is specified.
+HAVE_RSVG=no
+if test "${HAVE_X11}" = "yes"; then
+  if test "${with_rsvg}" != "no"; then
+    dnl Check if `--with-pkg-config-prog' has been given.
+    if test "X${with_pkg_config_prog}" != X; then
+      PKG_CONFIG="${with_pkg_config_prog}"
+    fi
+
+    RSVG_REQUIRED=2.0.0
+    RSVG_MODULE="librsvg-2.0 >= $RSVG_REQUIRED"
+
+    PKG_CHECK_MODULES(RSVG, $RSVG_MODULE, :, :)
+    AC_SUBST(RSVG_CFLAGS)
+    AC_SUBST(RSVG_LIBS)
+
+    if test ".${RSVG_CFLAGS}" != "."; then
+      HAVE_RSVG=yes
+      AC_DEFINE(HAVE_RSVG, 1, [Define to 1 if using librsvg.])
+      CFLAGS="$CFLAGS $RSVG_CFLAGS"
+      LIBS="$RSVG_LIBS $LIBS"
+    fi
+  fi
+fi
+
 HAVE_GTK=no
 if test "${with_gtk}" = "yes" || test "$USE_X_TOOLKIT" = "gtk"; then
   if test "$USE_X_TOOLKIT" != "none" && test "$USE_X_TOOLKIT" != "maybe"; then
@@ -3035,6 +3062,7 @@ echo "  Does Emacs use -ljpeg?          
 echo "  Does Emacs use -ltiff?                                  ${HAVE_TIFF}"
 echo "  Does Emacs use -lungif?                                 ${HAVE_GIF}"
 echo "  Does Emacs use -lpng?                                   ${HAVE_PNG}"
+echo "  Does Emacs use -lrsvg-2?                                ${HAVE_RSVG}"
 echo "  Does Emacs use X toolkit scroll bars?                   
${USE_TOOLKIT_SCROLL_BARS}"
 echo
 
--- lisp/image-file.el  01 Sep 2003 13:45:13 -0200      1.19
+++ lisp/image-file.el  25 Sep 2004 17:54:33 -0200      
@@ -38,7 +38,7 @@
 
 ;;;###autoload
 (defcustom image-file-name-extensions
-  '("png" "jpeg" "jpg" "gif" "tiff" "tif" "xbm" "xpm" "pbm" "pgm" "ppm" "pnm")
+  '("png" "jpeg" "jpg" "gif" "tiff" "tif" "xbm" "xpm" "pbm" "pgm" "ppm" "pnm" 
"svg")
   "*A list of image-file filename extensions.
 Filenames having one of these extensions are considered image files,
 in addition to those matching `image-file-name-regexps'.
--- lisp/image.el       21 Jun 2004 19:52:28 -0200      1.42
+++ lisp/image.el       25 Sep 2004 17:58:38 -0200      
@@ -40,7 +40,8 @@
     ("\\`[\t\n\r ]*#define" . xbm)
     ("\\`\\(MM\0\\*\\|II\\*\0\\)" . tiff)
     ("\\`[\t\n\r ]*%!PS" . postscript)
-    ("\\`\xff\xd8" . (image-jpeg-p . jpeg)))
+    ("\\`\xff\xd8" . (image-jpeg-p . jpeg))
+    ("\\`<\\?xml " . svg))
   "Alist of (REGEXP . IMAGE-TYPE) pairs used to auto-detect image types.
 When the first bytes of an image file match REGEXP, it is assumed to
 be of image type IMAGE-TYPE if IMAGE-TYPE is a symbol.  If not a symbol,
--- src/image.c 30 Jun 2004 11:11:36 -0200      1.16
+++ src/image.c 25 Sep 2004 18:16:31 -0200      
@@ -7589,6 +7589,277 @@ gif_load (f, img)
 
 #endif /* HAVE_GIF */
 
+ 
+/***********************************************************************
+                                SVG
+ ***********************************************************************/
+
+#if 1 || defined (HAVE_RSVG)
+
+/* Function prototypes.  */
+
+static int svg_image_p P_ ((Lisp_Object object));
+static int svg_load P_ ((struct frame *f, struct image *img));
+
+static int svg_load_image P_ ((struct frame *, struct image *,
+                              unsigned char *, unsigned int));
+
+/* The symbol `svg' identifying images of this type. */
+
+Lisp_Object Qsvg;
+
+/* Indices of image specification fields in svg_format, below.  */
+
+enum svg_keyword_index
+{
+  SVG_TYPE,
+  SVG_DATA,
+  SVG_FILE,
+  SVG_ASCENT,
+  SVG_MARGIN,
+  SVG_RELIEF,
+  SVG_ALGORITHM,
+  SVG_HEURISTIC_MASK,
+  SVG_MASK,
+  SVG_BACKGROUND,
+  SVG_LAST
+};
+
+/* Vector of image_keyword structures describing the format
+   of valid user-defined image specifications.  */
+
+static struct image_keyword svg_format[SVG_LAST] =
+{
+  {":type",            IMAGE_SYMBOL_VALUE,                     1},
+  {":data",            IMAGE_STRING_VALUE,                     0},
+  {":file",            IMAGE_STRING_VALUE,                     0},
+  {":ascent",          IMAGE_ASCENT_VALUE,                     0},
+  {":margin",          IMAGE_POSITIVE_INTEGER_VALUE_OR_PAIR,   0},
+  {":relief",          IMAGE_INTEGER_VALUE,                    0},
+  {":conversion",      IMAGE_DONT_CHECK_VALUE_TYPE,            0},
+  {":heuristic-mask",  IMAGE_DONT_CHECK_VALUE_TYPE,            0},
+  {":mask",            IMAGE_DONT_CHECK_VALUE_TYPE,            0},
+  {":background",      IMAGE_STRING_OR_NIL_VALUE,              0}
+};
+
+/* Structure describing the image type `svg'.  */
+
+static struct image_type svg_type =
+{
+  &Qsvg,
+  svg_image_p,
+  svg_load,
+  x_clear_image,
+  NULL
+};
+
+
+/* Return non-zero if OBJECT is a valid SVG image specification.  */
+
+static int
+svg_image_p (object)
+     Lisp_Object object;
+{
+  struct image_keyword fmt[SVG_LAST];
+  bcopy (svg_format, fmt, sizeof fmt);
+
+  if (!parse_image_spec (object, fmt, SVG_LAST, Qsvg))
+    return 0;
+
+  /* Must specify either the :data or :file keyword.  */
+  return fmt[SVG_FILE].count + fmt[SVG_DATA].count == 1;
+}
+
+#include <librsvg/rsvg.h>
+
+/* DEF_IMGLIB_FN() here? */
+
+#define fn_rsvg_handle_new             rsvg_handle_new
+#define fn_rsvg_handle_set_size_callback rsvg_handle_set_size_callback
+#define fn_rsvg_handle_write           rsvg_handle_write
+#define fn_rsvg_handle_close           rsvg_handle_close
+#define fn_rsvg_handle_get_pixbuf      rsvg_handle_get_pixbuf
+#define fn_rsvg_handle_free            rsvg_handle_free
+
+#define fn_gdk_pixbuf_get_width                gdk_pixbuf_get_width
+#define fn_gdk_pixbuf_get_height       gdk_pixbuf_get_height
+#define fn_gdk_pixbuf_get_pixels       gdk_pixbuf_get_pixels
+#define fn_gdk_pixbuf_get_rowstride    gdk_pixbuf_get_rowstride
+#define fn_gdk_pixbuf_get_colorspace   gdk_pixbuf_get_colorspace
+#define fn_gdk_pixbuf_get_n_channels   gdk_pixbuf_get_n_channels
+#define fn_gdk_pixbuf_get_has_alpha    gdk_pixbuf_get_has_alpha
+#define fn_gdk_pixbuf_get_bits_per_sample gdk_pixbuf_get_bits_per_sample
+
+
+/* Load SVG image IMG for use on frame F.  Value is non-zero if
+   successful.  */
+
+static int
+svg_load (f, img)
+     struct frame *f;
+     struct image *img;
+{
+  int success_p = 0;
+  Lisp_Object file_name;
+
+  /* If IMG->spec specifies a file name, create a non-file spec from it.  */
+  file_name = image_spec_value (img->spec, QCfile, NULL);
+  if (STRINGP (file_name))
+    {
+      Lisp_Object file;
+      unsigned char *contents;
+      int size;
+      struct gcpro gcpro1;
+
+      file = x_find_image_file (file_name);
+      GCPRO1 (file);
+      if (!STRINGP (file))
+      {
+        image_error ("Cannot find image file `%s'", file_name, Qnil);
+        UNGCPRO;
+        return 0;
+      }
+
+      contents = slurp_file (SDATA (file), &size);
+      if (contents == NULL)
+      {
+        image_error ("Error loading SVG image `%s'", img->spec, Qnil);
+        UNGCPRO;
+        return 0;
+      }
+
+      success_p = svg_load_image (f, img, contents, size);
+      xfree (contents);
+      UNGCPRO;
+    }
+  else
+    {
+      Lisp_Object data;
+
+      data = image_spec_value (img->spec, QCdata, NULL);
+      success_p = svg_load_image (f, img, SDATA (data), SBYTES (data));
+    }
+
+  return success_p;
+}
+
+
+static int
+svg_load_image (f, img, contents, size)
+     struct frame *f;
+     struct image *img;
+     unsigned char *contents;
+     unsigned int size;
+{
+  RsvgHandle *rsvg_handle;
+  GError *error = NULL;
+  GdkPixbuf *pixbuf;
+  int width;
+  int height;
+  const guint8 *pixels;
+  int rowstride;
+  XImagePtr ximg;
+  XColor background;
+  int x;
+  int y;
+
+  g_type_init ();
+  rsvg_handle = fn_rsvg_handle_new ();
+
+  fn_rsvg_handle_write (rsvg_handle, contents, size, &error);
+  if (error)
+    goto rsvg_error;
+
+  fn_rsvg_handle_close (rsvg_handle, &error);
+  if (error)
+    goto rsvg_error;
+
+  pixbuf = fn_rsvg_handle_get_pixbuf (rsvg_handle);
+  eassert (pixbuf);
+
+  width     = fn_gdk_pixbuf_get_width (pixbuf);
+  height    = fn_gdk_pixbuf_get_height (pixbuf);
+  pixels    = fn_gdk_pixbuf_get_pixels (pixbuf);
+  rowstride = fn_gdk_pixbuf_get_rowstride (pixbuf);
+
+  eassert (fn_gdk_pixbuf_get_colorspace (pixbuf) == GDK_COLORSPACE_RGB);
+  eassert (fn_gdk_pixbuf_get_n_channels (pixbuf) == 4);
+  eassert (fn_gdk_pixbuf_get_has_alpha (pixbuf));
+  eassert (fn_gdk_pixbuf_get_bits_per_sample (pixbuf) == 8);
+
+  if (!x_create_x_image_and_pixmap (f, width, height, 0, &ximg, &img->pixmap)) 
{
+    g_object_unref (pixbuf);
+    return 0;
+  }
+
+  init_color_table ();
+
+#ifdef HAVE_X_WINDOWS
+
+  background.pixel = FRAME_BACKGROUND_PIXEL (f);
+  x_query_color (f, &background);
+
+  background.red   >>= 8;
+  background.green >>= 8;
+  background.blue  >>= 8;
+
+#else /* not HAVE_X_WINDOWS */
+#error FIXME
+#endif
+
+  for (y = 0; y < height; ++y)
+    {
+      for (x = 0; x < width; ++x)
+       {
+         unsigned red;
+         unsigned green;
+         unsigned blue;
+         unsigned opacity;
+
+         red     = *pixels++;
+         green   = *pixels++;
+         blue    = *pixels++;
+         opacity = *pixels++;
+
+         red   = ((red * opacity)
+                  + (background.red * ((1 << 8) - opacity)));
+         green = ((green * opacity)
+                  + (background.green * ((1 << 8) - opacity)));
+         blue  = ((blue * opacity)
+                  + (background.blue * ((1 << 8) - opacity)));
+
+         XPutPixel (ximg, x, y, lookup_rgb_color (f, red, green, blue));
+       }
+
+      pixels += rowstride - 4 * width;
+    }
+
+#ifdef COLOR_TABLE_SUPPORT
+  /* Remember colors allocated for this image.  */
+  img->colors = colors_in_color_table (&img->ncolors);
+  free_color_table ();
+#endif /* COLOR_TABLE_SUPPORT */
+
+  g_object_unref (pixbuf);
+
+  /* Put the image into the pixmap, then free the X image and its buffer. */
+  x_put_x_image (f, ximg, img->pixmap, width, height);
+  x_destroy_x_image (ximg);
+
+  img->width  = width;
+  img->height = height;
+
+  return 1;
+
+ rsvg_error:
+  /* FIXME: Use error->message. */
+  image_error ("Error parsing SVG image `%s'", img->spec, Qnil);
+  g_error_free (error);
+  return 0;
+}
+
+#endif /* defined (HAVE_RSVG) */
+
 
  
 /***********************************************************************
@@ -7976,6 +8247,11 @@ of `image-library-alist', which see).  *
     return CHECK_LIB_AVAILABLE (&png_type, init_png_functions, libraries);
 #endif
 
+#if 1 || defined (HAVE_RSVG)
+  if (EQ (type, Qsvg))
+    return CHECK_LIB_AVAILABLE (&svg_type, init_svg_functions, libraries);
+#endif
+
 #ifdef HAVE_GHOSTSCRIPT
   if (EQ (type, Qpostscript))
     return CHECK_LIB_AVAILABLE (&gs_type, init_gs_functions, libraries);
@@ -8082,6 +8358,12 @@ To check whether it is really supported,
   ADD_IMAGE_TYPE(Qpng);
 #endif
 
+#if 1 || defined (HAVE_RSVG)
+  Qsvg = intern ("svg");
+  staticpro (&Qsvg);
+  ADD_IMAGE_TYPE(Qsvg);
+#endif
+
   defsubr (&Sinit_image_library);
   defsubr (&Sclear_image_cache);
   defsubr (&Simage_size);
--- src/Makefile.in     10 Sep 2004 21:26:00 -0200      1.300
+++ src/Makefile.in     25 Sep 2004 16:25:50 -0200      
@@ -280,7 +280,7 @@ TOOLKIT_DEFINES =
 
 /* C_SWITCH_X_SITE must come before C_SWITCH_X_MACHINE and C_SWITCH_X_SYSTEM
    since it may have -I options that should override those two.  */
-ALL_CFLAGS=-Demacs -DHAVE_CONFIG_H $(TOOLKIT_DEFINES) $(MYCPPFLAGS) -I. 
-I${srcdir} C_SWITCH_MACHINE C_SWITCH_SYSTEM C_SWITCH_SITE C_SWITCH_X_SITE 
C_SWITCH_X_MACHINE C_SWITCH_X_SYSTEM C_SWITCH_SYSTEM_TEMACS ${CFLAGS}
+ALL_CFLAGS=-Demacs -DHAVE_CONFIG_H $(TOOLKIT_DEFINES) $(MYCPPFLAGS) -I. 
-I${srcdir} C_SWITCH_MACHINE C_SWITCH_SYSTEM C_SWITCH_SITE @RSVG_CFLAGS@ 
C_SWITCH_X_SITE C_SWITCH_X_MACHINE C_SWITCH_X_SYSTEM C_SWITCH_SYSTEM_TEMACS 
${CFLAGS}
 .c.o:
        $(CC) -c $(CPPFLAGS) $(ALL_CFLAGS) $<
 
@@ -444,7 +444,7 @@ LIBXT=$(LIBW)
 /* LD_SWITCH_X_DEFAULT comes after everything else that specifies
    options for where to find X libraries, but before those libraries.  */
 X11_LDFLAGS = LD_SWITCH_X_SITE LD_SWITCH_X_DEFAULT
-LIBX= $(LIBXMENU) $(X11_LDFLAGS) $(LIBXT) LIBTIFF LIBJPEG LIBPNG LIBGIF LIBXPM 
LIB_X11_LIB LIBX11_MACHINE LIBX11_SYSTEM
+LIBX= $(LIBXMENU) $(X11_LDFLAGS) $(LIBXT) LIBTIFF LIBJPEG LIBPNG LIBGIF LIBXPM 
@RSVG_LIBS@ LIB_X11_LIB LIBX11_MACHINE LIBX11_SYSTEM
 #else /* not HAVE_X11 */
 LIBX= $(LIBXMENU) LD_SWITCH_X_SITE -lX10 LIBX10_MACHINE LIBX10_SYSTEM
 #endif /* not HAVE_X11 */





reply via email to

[Prev in Thread] Current Thread [Next in Thread]