diff options
| author | Glenn Morris | 2007-08-22 04:01:54 +0000 |
|---|---|---|
| committer | Glenn Morris | 2007-08-22 04:01:54 +0000 |
| commit | 4ab27a430b045dbfede1f8c00901eb7e09c7dae1 (patch) | |
| tree | b800fb791b816e9755a04137a83cbd21d51003db /src/image.c | |
| parent | 8fe63ec714e20a48fdb3a124beb0387a679e0f5e (diff) | |
| download | emacs-4ab27a430b045dbfede1f8c00901eb7e09c7dae1.tar.gz emacs-4ab27a430b045dbfede1f8c00901eb7e09c7dae1.zip | |
Paul Pogonyshev <pogonyshev at gmx.net>
Add support for SVG images.
Some additional comments by Joakim Verona <joakim@verona.se>.
Diffstat (limited to 'src/image.c')
| -rw-r--r-- | src/image.c | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/src/image.c b/src/image.c index a3cd3195217..2f2fbf3a596 100644 --- a/src/image.c +++ b/src/image.c | |||
| @@ -8199,6 +8199,329 @@ gif_load (f, img) | |||
| 8199 | #endif /* HAVE_GIF */ | 8199 | #endif /* HAVE_GIF */ |
| 8200 | 8200 | ||
| 8201 | 8201 | ||
| 8202 | |||
| 8203 | /*********************************************************************** | ||
| 8204 | SVG | ||
| 8205 | ***********************************************************************/ | ||
| 8206 | |||
| 8207 | #if defined (HAVE_RSVG) | ||
| 8208 | |||
| 8209 | /* Function prototypes. */ | ||
| 8210 | |||
| 8211 | static int svg_image_p P_ ((Lisp_Object object)); | ||
| 8212 | static int svg_load P_ ((struct frame *f, struct image *img)); | ||
| 8213 | |||
| 8214 | static int svg_load_image P_ ((struct frame *, struct image *, | ||
| 8215 | unsigned char *, unsigned int)); | ||
| 8216 | |||
| 8217 | /* The symbol `svg' identifying images of this type. */ | ||
| 8218 | |||
| 8219 | Lisp_Object Qsvg; | ||
| 8220 | |||
| 8221 | /* Indices of image specification fields in svg_format, below. */ | ||
| 8222 | |||
| 8223 | enum svg_keyword_index | ||
| 8224 | { | ||
| 8225 | SVG_TYPE, | ||
| 8226 | SVG_DATA, | ||
| 8227 | SVG_FILE, | ||
| 8228 | SVG_ASCENT, | ||
| 8229 | SVG_MARGIN, | ||
| 8230 | SVG_RELIEF, | ||
| 8231 | SVG_ALGORITHM, | ||
| 8232 | SVG_HEURISTIC_MASK, | ||
| 8233 | SVG_MASK, | ||
| 8234 | SVG_BACKGROUND, | ||
| 8235 | SVG_LAST | ||
| 8236 | }; | ||
| 8237 | |||
| 8238 | /* Vector of image_keyword structures describing the format | ||
| 8239 | of valid user-defined image specifications. */ | ||
| 8240 | |||
| 8241 | static struct image_keyword svg_format[SVG_LAST] = | ||
| 8242 | { | ||
| 8243 | {":type", IMAGE_SYMBOL_VALUE, 1}, | ||
| 8244 | {":data", IMAGE_STRING_VALUE, 0}, | ||
| 8245 | {":file", IMAGE_STRING_VALUE, 0}, | ||
| 8246 | {":ascent", IMAGE_ASCENT_VALUE, 0}, | ||
| 8247 | {":margin", IMAGE_POSITIVE_INTEGER_VALUE_OR_PAIR, 0}, | ||
| 8248 | {":relief", IMAGE_INTEGER_VALUE, 0}, | ||
| 8249 | {":conversion", IMAGE_DONT_CHECK_VALUE_TYPE, 0}, | ||
| 8250 | {":heuristic-mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0}, | ||
| 8251 | {":mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0}, | ||
| 8252 | {":background", IMAGE_STRING_OR_NIL_VALUE, 0} | ||
| 8253 | }; | ||
| 8254 | |||
| 8255 | /* Structure describing the image type `svg'. Its the same type of | ||
| 8256 | structure defined for all image formats, handled by emacs image | ||
| 8257 | functions. See struct image_type in dispextern.h. */ | ||
| 8258 | |||
| 8259 | static struct image_type svg_type = | ||
| 8260 | { | ||
| 8261 | /* An identifier showing that this is an image structure for the SVG format. */ | ||
| 8262 | &Qsvg, | ||
| 8263 | /* Handle to a function that can be used to identify a SVG file. */ | ||
| 8264 | svg_image_p, | ||
| 8265 | /* Handle to function used to load a SVG file. */ | ||
| 8266 | svg_load, | ||
| 8267 | /* Handle to function to free sresources for SVG. */ | ||
| 8268 | x_clear_image, | ||
| 8269 | /* An internal field to link to the next image type in a list of | ||
| 8270 | image types, will be filled in when registering the format. */ | ||
| 8271 | NULL | ||
| 8272 | }; | ||
| 8273 | |||
| 8274 | |||
| 8275 | /* Return non-zero if OBJECT is a valid SVG image specification. Do | ||
| 8276 | this by calling parse_image_spec and supplying the keywords that | ||
| 8277 | identify the SVG format. */ | ||
| 8278 | |||
| 8279 | static int | ||
| 8280 | svg_image_p (object) | ||
| 8281 | Lisp_Object object; | ||
| 8282 | { | ||
| 8283 | struct image_keyword fmt[SVG_LAST]; | ||
| 8284 | bcopy (svg_format, fmt, sizeof fmt); | ||
| 8285 | |||
| 8286 | if (!parse_image_spec (object, fmt, SVG_LAST, Qsvg)) | ||
| 8287 | return 0; | ||
| 8288 | |||
| 8289 | /* Must specify either the :data or :file keyword. */ | ||
| 8290 | return fmt[SVG_FILE].count + fmt[SVG_DATA].count == 1; | ||
| 8291 | } | ||
| 8292 | |||
| 8293 | #include <librsvg/rsvg.h> | ||
| 8294 | |||
| 8295 | /* TO DO: define DEF_IMGLIB_FN here. This macro is used to handle | ||
| 8296 | loading of dynamic link library functions for various OS:es. | ||
| 8297 | Currently only librsvg2 is supported, which is only available for X, | ||
| 8298 | so its not strictly necessary yet. The current code is thought to be | ||
| 8299 | compatible with this scheme because of the defines below, should | ||
| 8300 | librsvg2 become available on more plattforms. */ | ||
| 8301 | |||
| 8302 | #define fn_rsvg_handle_new rsvg_handle_new | ||
| 8303 | #define fn_rsvg_handle_set_size_callback rsvg_handle_set_size_callback | ||
| 8304 | #define fn_rsvg_handle_write rsvg_handle_write | ||
| 8305 | #define fn_rsvg_handle_close rsvg_handle_close | ||
| 8306 | #define fn_rsvg_handle_get_pixbuf rsvg_handle_get_pixbuf | ||
| 8307 | #define fn_rsvg_handle_free rsvg_handle_free | ||
| 8308 | |||
| 8309 | #define fn_gdk_pixbuf_get_width gdk_pixbuf_get_width | ||
| 8310 | #define fn_gdk_pixbuf_get_height gdk_pixbuf_get_height | ||
| 8311 | #define fn_gdk_pixbuf_get_pixels gdk_pixbuf_get_pixels | ||
| 8312 | #define fn_gdk_pixbuf_get_rowstride gdk_pixbuf_get_rowstride | ||
| 8313 | #define fn_gdk_pixbuf_get_colorspace gdk_pixbuf_get_colorspace | ||
| 8314 | #define fn_gdk_pixbuf_get_n_channels gdk_pixbuf_get_n_channels | ||
| 8315 | #define fn_gdk_pixbuf_get_has_alpha gdk_pixbuf_get_has_alpha | ||
| 8316 | #define fn_gdk_pixbuf_get_bits_per_sample gdk_pixbuf_get_bits_per_sample | ||
| 8317 | |||
| 8318 | |||
| 8319 | /* Load SVG image IMG for use on frame F. Value is non-zero if | ||
| 8320 | successful. this function will go into the svg_type structure, and | ||
| 8321 | the prototype thus needs to be compatible with that structure. */ | ||
| 8322 | |||
| 8323 | static int | ||
| 8324 | svg_load (f, img) | ||
| 8325 | struct frame *f; | ||
| 8326 | struct image *img; | ||
| 8327 | { | ||
| 8328 | int success_p = 0; | ||
| 8329 | Lisp_Object file_name; | ||
| 8330 | |||
| 8331 | /* If IMG->spec specifies a file name, create a non-file spec from it. */ | ||
| 8332 | file_name = image_spec_value (img->spec, QCfile, NULL); | ||
| 8333 | if (STRINGP (file_name)) | ||
| 8334 | { | ||
| 8335 | Lisp_Object file; | ||
| 8336 | unsigned char *contents; | ||
| 8337 | int size; | ||
| 8338 | struct gcpro gcpro1; | ||
| 8339 | |||
| 8340 | file = x_find_image_file (file_name); | ||
| 8341 | GCPRO1 (file); | ||
| 8342 | if (!STRINGP (file)) | ||
| 8343 | { | ||
| 8344 | image_error ("Cannot find image file `%s'", file_name, Qnil); | ||
| 8345 | UNGCPRO; | ||
| 8346 | return 0; | ||
| 8347 | } | ||
| 8348 | |||
| 8349 | /* Read the entire file into memory. */ | ||
| 8350 | contents = slurp_file (SDATA (file), &size); | ||
| 8351 | if (contents == NULL) | ||
| 8352 | { | ||
| 8353 | image_error ("Error loading SVG image `%s'", img->spec, Qnil); | ||
| 8354 | UNGCPRO; | ||
| 8355 | return 0; | ||
| 8356 | } | ||
| 8357 | /* If the file was slurped into memory properly, parse it. */ | ||
| 8358 | success_p = svg_load_image (f, img, contents, size); | ||
| 8359 | xfree (contents); | ||
| 8360 | UNGCPRO; | ||
| 8361 | } | ||
| 8362 | /* Else its not a file, its a lisp object. Load the image from a | ||
| 8363 | lisp object rather than a file. */ | ||
| 8364 | else | ||
| 8365 | { | ||
| 8366 | Lisp_Object data; | ||
| 8367 | |||
| 8368 | data = image_spec_value (img->spec, QCdata, NULL); | ||
| 8369 | success_p = svg_load_image (f, img, SDATA (data), SBYTES (data)); | ||
| 8370 | } | ||
| 8371 | |||
| 8372 | return success_p; | ||
| 8373 | } | ||
| 8374 | |||
| 8375 | /* svg_load_image is a helper function for svg_load, which does the actual | ||
| 8376 | loading given contents and size, apart from frame and image | ||
| 8377 | structures, passed from svg_load. | ||
| 8378 | |||
| 8379 | Uses librsvg to do most of the image processing. | ||
| 8380 | |||
| 8381 | Returns non-zero when sucessful. */ | ||
| 8382 | static int | ||
| 8383 | svg_load_image (f, img, contents, size) | ||
| 8384 | /* Pointer to emacs frame sturcture. */ | ||
| 8385 | struct frame *f; | ||
| 8386 | /* Pointer to emacs image structure. */ | ||
| 8387 | struct image *img; | ||
| 8388 | /* String containing the SVG XML data to be parsed. */ | ||
| 8389 | unsigned char *contents; | ||
| 8390 | /* Size of data in bytes. */ | ||
| 8391 | unsigned int size; | ||
| 8392 | { | ||
| 8393 | RsvgHandle *rsvg_handle; | ||
| 8394 | GError *error = NULL; | ||
| 8395 | GdkPixbuf *pixbuf; | ||
| 8396 | int width; | ||
| 8397 | int height; | ||
| 8398 | const guint8 *pixels; | ||
| 8399 | int rowstride; | ||
| 8400 | XImagePtr ximg; | ||
| 8401 | XColor background; | ||
| 8402 | int x; | ||
| 8403 | int y; | ||
| 8404 | |||
| 8405 | /* g_type_init is a glib function that must be called prior to using | ||
| 8406 | gnome type library functions. */ | ||
| 8407 | g_type_init (); | ||
| 8408 | /* Make a handle to a new rsvg object. */ | ||
| 8409 | rsvg_handle = fn_rsvg_handle_new (); | ||
| 8410 | |||
| 8411 | /* Parse the contents argument and fill in the rsvg_handle. */ | ||
| 8412 | fn_rsvg_handle_write (rsvg_handle, contents, size, &error); | ||
| 8413 | if (error) | ||
| 8414 | goto rsvg_error; | ||
| 8415 | |||
| 8416 | /* The parsing is complete, rsvg_handle is ready to used, close it | ||
| 8417 | for further writes. */ | ||
| 8418 | fn_rsvg_handle_close (rsvg_handle, &error); | ||
| 8419 | if (error) | ||
| 8420 | goto rsvg_error; | ||
| 8421 | /* We can now get a valid pixel buffer from the svg file, if all | ||
| 8422 | went ok. */ | ||
| 8423 | pixbuf = fn_rsvg_handle_get_pixbuf (rsvg_handle); | ||
| 8424 | eassert (pixbuf); | ||
| 8425 | |||
| 8426 | /* Extract some meta data from the svg handle. */ | ||
| 8427 | width = fn_gdk_pixbuf_get_width (pixbuf); | ||
| 8428 | height = fn_gdk_pixbuf_get_height (pixbuf); | ||
| 8429 | pixels = fn_gdk_pixbuf_get_pixels (pixbuf); | ||
| 8430 | rowstride = fn_gdk_pixbuf_get_rowstride (pixbuf); | ||
| 8431 | |||
| 8432 | /* Validate the svg meta data. */ | ||
| 8433 | eassert (fn_gdk_pixbuf_get_colorspace (pixbuf) == GDK_COLORSPACE_RGB); | ||
| 8434 | eassert (fn_gdk_pixbuf_get_n_channels (pixbuf) == 4); | ||
| 8435 | eassert (fn_gdk_pixbuf_get_has_alpha (pixbuf)); | ||
| 8436 | eassert (fn_gdk_pixbuf_get_bits_per_sample (pixbuf) == 8); | ||
| 8437 | |||
| 8438 | /* Try to create a x pixmap to hold the svg pixmap. */ | ||
| 8439 | if (!x_create_x_image_and_pixmap (f, width, height, 0, &ximg, &img->pixmap)) { | ||
| 8440 | g_object_unref (pixbuf); | ||
| 8441 | return 0; | ||
| 8442 | } | ||
| 8443 | |||
| 8444 | init_color_table (); | ||
| 8445 | |||
| 8446 | /* TODO: The code is somewhat prepared for other environments than | ||
| 8447 | X, but is far from done. */ | ||
| 8448 | #ifdef HAVE_X_WINDOWS | ||
| 8449 | |||
| 8450 | background.pixel = FRAME_BACKGROUND_PIXEL (f); | ||
| 8451 | x_query_color (f, &background); | ||
| 8452 | |||
| 8453 | /* SVG pixmaps specify transparency in the last byte, so right shift | ||
| 8454 | 8 bits to get rid of it, since emacs doesnt support | ||
| 8455 | transparency. */ | ||
| 8456 | background.red >>= 8; | ||
| 8457 | background.green >>= 8; | ||
| 8458 | background.blue >>= 8; | ||
| 8459 | |||
| 8460 | #else /* not HAVE_X_WINDOWS */ | ||
| 8461 | #error FIXME | ||
| 8462 | #endif | ||
| 8463 | |||
| 8464 | /* This loop handles opacity values, since Emacs assumes | ||
| 8465 | non-transparent images. Each pixel must be "flattened" by | ||
| 8466 | calculating he resulting color, given the transparency of the | ||
| 8467 | pixel, and the image background color. */ | ||
| 8468 | for (y = 0; y < height; ++y) | ||
| 8469 | { | ||
| 8470 | for (x = 0; x < width; ++x) | ||
| 8471 | { | ||
| 8472 | unsigned red; | ||
| 8473 | unsigned green; | ||
| 8474 | unsigned blue; | ||
| 8475 | unsigned opacity; | ||
| 8476 | |||
| 8477 | red = *pixels++; | ||
| 8478 | green = *pixels++; | ||
| 8479 | blue = *pixels++; | ||
| 8480 | opacity = *pixels++; | ||
| 8481 | |||
| 8482 | red = ((red * opacity) | ||
| 8483 | + (background.red * ((1 << 8) - opacity))); | ||
| 8484 | green = ((green * opacity) | ||
| 8485 | + (background.green * ((1 << 8) - opacity))); | ||
| 8486 | blue = ((blue * opacity) | ||
| 8487 | + (background.blue * ((1 << 8) - opacity))); | ||
| 8488 | |||
| 8489 | XPutPixel (ximg, x, y, lookup_rgb_color (f, red, green, blue)); | ||
| 8490 | } | ||
| 8491 | |||
| 8492 | pixels += rowstride - 4 * width; | ||
| 8493 | } | ||
| 8494 | |||
| 8495 | #ifdef COLOR_TABLE_SUPPORT | ||
| 8496 | /* Remember colors allocated for this image. */ | ||
| 8497 | img->colors = colors_in_color_table (&img->ncolors); | ||
| 8498 | free_color_table (); | ||
| 8499 | #endif /* COLOR_TABLE_SUPPORT */ | ||
| 8500 | |||
| 8501 | g_object_unref (pixbuf); | ||
| 8502 | |||
| 8503 | /* Put the image into the pixmap, then free the X image and its | ||
| 8504 | buffer. */ | ||
| 8505 | x_put_x_image (f, ximg, img->pixmap, width, height); | ||
| 8506 | x_destroy_x_image (ximg); | ||
| 8507 | |||
| 8508 | img->width = width; | ||
| 8509 | img->height = height; | ||
| 8510 | |||
| 8511 | return 1; | ||
| 8512 | |||
| 8513 | rsvg_error: | ||
| 8514 | /* FIXME: Use error->message so the user knows what is the actual | ||
| 8515 | problem with the image. */ | ||
| 8516 | image_error ("Error parsing SVG image `%s'", img->spec, Qnil); | ||
| 8517 | g_error_free (error); | ||
| 8518 | return 0; | ||
| 8519 | } | ||
| 8520 | |||
| 8521 | #endif /* defined (HAVE_RSVG) */ | ||
| 8522 | |||
| 8523 | |||
| 8524 | |||
| 8202 | 8525 | ||
| 8203 | /*********************************************************************** | 8526 | /*********************************************************************** |
| 8204 | Ghostscript | 8527 | Ghostscript |
| @@ -8591,6 +8914,11 @@ of `image-library-alist', which see). */) | |||
| 8591 | return CHECK_LIB_AVAILABLE (&png_type, init_png_functions, libraries); | 8914 | return CHECK_LIB_AVAILABLE (&png_type, init_png_functions, libraries); |
| 8592 | #endif | 8915 | #endif |
| 8593 | 8916 | ||
| 8917 | #if defined (HAVE_RSVG) | ||
| 8918 | if (EQ (type, Qsvg)) | ||
| 8919 | return CHECK_LIB_AVAILABLE (&svg_type, init_svg_functions, libraries); | ||
| 8920 | #endif | ||
| 8921 | |||
| 8594 | #ifdef HAVE_GHOSTSCRIPT | 8922 | #ifdef HAVE_GHOSTSCRIPT |
| 8595 | if (EQ (type, Qpostscript)) | 8923 | if (EQ (type, Qpostscript)) |
| 8596 | return CHECK_LIB_AVAILABLE (&gs_type, init_gs_functions, libraries); | 8924 | return CHECK_LIB_AVAILABLE (&gs_type, init_gs_functions, libraries); |
| @@ -8733,6 +9061,13 @@ non-numeric, there is no explicit limit on the size of images. */); | |||
| 8733 | ADD_IMAGE_TYPE(Qpng); | 9061 | ADD_IMAGE_TYPE(Qpng); |
| 8734 | #endif | 9062 | #endif |
| 8735 | 9063 | ||
| 9064 | #if defined (HAVE_RSVG) | ||
| 9065 | Qsvg = intern ("svg"); | ||
| 9066 | staticpro (&Qsvg); | ||
| 9067 | ADD_IMAGE_TYPE(Qsvg); | ||
| 9068 | #endif | ||
| 9069 | |||
| 9070 | |||
| 8736 | defsubr (&Sinit_image_library); | 9071 | defsubr (&Sinit_image_library); |
| 8737 | defsubr (&Sclear_image_cache); | 9072 | defsubr (&Sclear_image_cache); |
| 8738 | defsubr (&Simage_refresh); | 9073 | defsubr (&Simage_refresh); |