diff src/image.c @ 109849:28a8c62b8ee7

imagemagick patch5 + minor mods
author joakim <joakim@exodia>
date Fri, 02 Apr 2010 23:09:13 +0200
parents 9c0ed2d6731a
children fe07c47cf7a7
line wrap: on
line diff
--- a/src/image.c	Thu Apr 01 18:29:04 2010 +0300
+++ b/src/image.c	Fri Apr 02 23:09:13 2010 +0200
@@ -606,7 +606,7 @@
 extern Lisp_Object Qcenter;
 Lisp_Object QCascent, QCmargin, QCrelief, Qcount, Qextension_data;
 Lisp_Object QCconversion, QCcolor_symbols, QCheuristic_mask;
-Lisp_Object QCindex, QCmatrix, QCcolor_adjustment, QCmask;
+Lisp_Object QCindex, QCmatrix, QCcolor_adjustment, QCmask, QCgeometry, QCcrop, QCrotation;
 
 /* Other symbols.  */
 
@@ -7538,6 +7538,449 @@
 #endif /* HAVE_GIF */
 
 
+/***********************************************************************
+				 imagemagick
+ ***********************************************************************/
+#if defined (HAVE_IMAGEMAGICK)
+Lisp_Object Vimagemagick_render_type;
+/* Function prototypes.  */
+
+static int imagemagick_image_p (Lisp_Object object);
+static int imagemagick_load (struct frame *f, struct image *img);
+
+static int imagemagick_load_image (struct frame *, struct image *,
+                                       unsigned char *, unsigned int, unsigned char *);
+
+/* The symbol `imagemagick' identifying images of this type.  */
+
+Lisp_Object Qimagemagick;
+Lisp_Object Vimagemagick_render_type;
+
+/* Indices of image specification fields in imagemagick_format, below.  */
+
+enum imagemagick_keyword_index
+{
+  IMAGEMAGICK_TYPE,
+  IMAGEMAGICK_DATA,
+  IMAGEMAGICK_FILE,
+  IMAGEMAGICK_ASCENT,
+  IMAGEMAGICK_MARGIN,
+  IMAGEMAGICK_RELIEF,
+  IMAGEMAGICK_ALGORITHM,
+  IMAGEMAGICK_HEURISTIC_MASK,
+  IMAGEMAGICK_MASK,
+  IMAGEMAGICK_BACKGROUND,
+  IMAGEMAGICK_LAST
+};
+
+/* Vector of image_keyword structures describing the format
+   of valid user-defined image specifications.  */
+
+static struct image_keyword imagemagick_format[IMAGEMAGICK_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}
+};
+/* Free X resources of imagemagick image IMG which is used on frame F.  */
+
+static void
+imagemagick_clear_image (f, img)
+     struct frame *f;
+     struct image *img;
+{
+  printf("clearing imagemagick image\n");
+  x_clear_image (f, img);
+}
+
+/* Structure describing the image type `imagemagick'.  Its the same type of
+   structure defined for all image formats, handled by Emacs image
+   functions.  See struct image_type in dispextern.h.  */
+
+static struct image_type imagemagick_type =
+{
+  /* An identifier showing that this is an image structure for the IMAGEMAGICK format.  */
+  &Qimagemagick,
+  /* Handle to a function that can be used to identify a IMAGEMAGICK file.  */
+  imagemagick_image_p,
+  /* Handle to function used to load a IMAGEMAGICK file.  */
+  imagemagick_load,
+  /* Handle to function to free resources for IMAGEMAGICK.  */
+  imagemagick_clear_image,
+  /* An internal field to link to the next image type in a list of
+     image types, will be filled in when registering the format.  */
+  NULL
+};
+
+
+/* Return non-zero if OBJECT is a valid IMAGEMAGICK image specification.  Do
+   this by calling parse_image_spec and supplying the keywords that
+   identify the IMAGEMAGICK format.   */
+
+static int
+imagemagick_image_p (object)
+     Lisp_Object object;
+{
+  struct image_keyword fmt[IMAGEMAGICK_LAST];
+  bcopy (imagemagick_format, fmt, sizeof fmt);
+
+  if (!parse_image_spec (object, fmt, IMAGEMAGICK_LAST, Qimagemagick))
+    return 0;
+
+  /* Must specify either the :data or :file keyword.  */
+  return fmt[IMAGEMAGICK_FILE].count + fmt[IMAGEMAGICK_DATA].count == 1;
+}
+
+/* The GIF library also defines DrawRectangle, but its never used in Emacs.
+   Therefore rename the function so it doesnt collide with ImageMagick.  */
+#define DrawRectangle DrawRectangleGif
+#include <wand/MagickWand.h>
+
+/* Load IMAGEMAGICK image IMG for use on frame F.  Value is non-zero if
+   successful. this function will go into the imagemagick_type structure, and
+   the prototype thus needs to be compatible with that structure.  */
+
+static int
+imagemagick_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;
+	}
+
+      /* Read the entire file into memory.  */
+      /* contents = slurp_file (SDATA (file), &size); */
+      /* if (contents == NULL) */
+      /*   { */
+      /*     image_error ("Error loading IMAGEMAGICK image `%s'", img->spec, Qnil); */
+      /*     UNGCPRO; */
+      /*     return 0; */
+      /*   } */
+      /* If the file was slurped into memory properly, parse it.  */
+      success_p = imagemagick_load_image (f, img, 0, 0, SDATA(file_name));
+      UNGCPRO;
+    }
+  /* Else its not a file, its a lisp object.  Load the image from a
+     lisp object rather than a file.  */
+  else
+    {
+      Lisp_Object data;
+
+      data = image_spec_value (img->spec, QCdata, NULL);
+      success_p = imagemagick_load_image (f, img, SDATA (data), SBYTES (data),NULL);
+    }
+
+  return success_p;
+}
+
+/* imagemagick_load_image is a helper function for imagemagick_load, which does the
+   actual loading given contents and size, apart from frame and image
+   structures, passed from imagemagick_load.
+
+   Uses librimagemagick to do most of the image processing.
+
+   Returns non-zero when successful.
+*/
+
+static int
+imagemagick_load_image (f, img, contents, size, filename)
+    /* Pointer to emacs frame structure.  */
+     struct frame *f;
+     /* Pointer to emacs image structure.  */
+     struct image *img;
+     /* String containing the IMAGEMAGICK  data to be parsed.  */
+     unsigned char *contents;
+     /* Size of data in bytes.  */
+     unsigned int size;
+     /* Filename, either pass filename or contents/size.  */
+     unsigned char *filename;
+{
+  long unsigned int width;
+  long unsigned int height;
+
+  MagickBooleanType
+    status;
+
+  XImagePtr ximg;
+  Lisp_Object specified_bg;
+  XColor background;
+  int x;
+  int y;
+
+  MagickWand  *image_wand;
+  PixelIterator *iterator;
+  PixelWand  **pixels;
+  MagickPixelPacket  pixel;
+
+  
+  /* image_wand will contain the image.  */
+  image_wand = NewMagickWand();  
+
+  /* Parse the contents argument and initialize image_wand.  */
+  if(filename!=NULL)
+    status=MagickReadImage(image_wand, filename);
+  else
+    status=MagickReadImageBlob(image_wand, contents, size);
+  image_error ("im read failed", Qnil, Qnil);
+  if (status == MagickFalse) goto imagemagick_error;
+
+  /* Handle image index for image types who can contain more than one image.
+   Interface :index is same as for GIF.  */
+  Lisp_Object image;
+  long ino;
+  image = image_spec_value (img->spec, QCindex, NULL);
+  ino = INTEGERP (image) ? XFASTINT (image) : 0;
+  
+  /* if (ino >= ) */
+  /*   { */
+  /*     image_error ("Invalid image number `%s' in image `%s'", */
+  /*       	   image, img->spec); */
+  /*     UNGCPRO; */
+  /*     return 0; */
+  /*   } */
+
+  if(ino==0)
+    MagickSetFirstIterator(image_wand);
+  else
+    MagickSetIteratorIndex(image_wand, ino);
+
+  /*
+    If width and/or height is set in the display spec
+    assume we want to scale to those.  */
+
+  int desired_width, desired_height;
+  Lisp_Object value;  
+  value = image_spec_value (img->spec, QCwidth, NULL);
+  desired_width = (INTEGERP (value)  ? XFASTINT (value) : -1);
+  value = image_spec_value (img->spec, QCheight, NULL);
+  desired_height = (INTEGERP (value) ? XFASTINT (value) : -1);
+  if(desired_width != -1 && desired_height != -1){
+    printf("MagickScaleImage %d %d\n",desired_width, desired_height);
+    status=MagickScaleImage(image_wand, desired_width, desired_height);
+    if (status == MagickFalse) {
+      image_error ("Imagemagick scale failed", Qnil, Qnil);
+      goto imagemagick_error;
+    }
+    
+  }
+
+  /* Also support :geometry and :crop which are imagemagick specific descriptors.  */
+
+  Lisp_Object crop, geometry;
+  crop     = image_spec_value (img->spec, QCcrop, NULL);
+  geometry = image_spec_value (img->spec, QCgeometry, NULL);
+  if (STRINGP (crop) && STRINGP (geometry)){
+    printf("MagickTransformImage %s %s\n",SDATA(crop), SDATA(geometry));
+    image_wand = MagickTransformImage (image_wand, SDATA (crop), SDATA (geometry));
+    /* TODO differ between image_wand and transform_wand. */
+  }
+
+  /* Furthermore :rotation. we need background color and angle for rotation.  */
+  /*
+    TODO background handling for rotation
+    specified_bg = image_spec_value (img->spec, QCbackground, NULL);
+  if (!STRINGP (specified_bg)
+  */
+    double rotation;
+    value = image_spec_value (img->spec, QCrotation, NULL);
+    if (FLOATP (value)){
+      PixelWand* background = NewPixelWand();
+      PixelSetColor (background, "#ffffff");/*TODO remove hardcode*/
+
+      rotation = extract_float (value);
+      printf ("MagickRotateImage %f\n",rotation);
+      
+      status=MagickRotateImage (image_wand, background,rotation);
+      DestroyPixelWand (background);
+      if (status == MagickFalse) {
+        image_error ("Imagemagick image rotate failed", Qnil, Qnil);
+        goto imagemagick_error;
+      }
+    }
+  
+  /* Finaly we are done manipulating the image,
+     figure out resulting width, height, and then transfer ownerwship to Emacs.
+   */
+  height=MagickGetImageHeight (image_wand);
+  width=MagickGetImageWidth (image_wand);
+  if (status == MagickFalse) {
+      image_error ("Imagemagick image get size failed", Qnil, Qnil);  
+      goto imagemagick_error;
+  }
+    
+  if (! check_image_size (f, width, height))
+    {
+      image_error ("Invalid image size (see `max-image-size')", Qnil, Qnil);
+      goto imagemagick_error;
+    }
+  
+  /* We can now get a valid pixel buffer from the imagemagick file, if all
+     went ok.  */
+  
+
+  init_color_table ();
+  int imagemagick_rendermethod=(INTEGERP (Vimagemagick_render_type) ? XFASTINT (Vimagemagick_render_type) : 0);
+  if (imagemagick_rendermethod==0){
+    /* Try to create a x pixmap to hold the imagemagick pixmap.  */
+    if (!x_create_x_image_and_pixmap (f, width, height, 0, &ximg, &img->pixmap)){
+      image_error("Imagemagick X bitmap allocation failure",Qnil,Qnil);
+      goto imagemagick_error;
+    }
+    
+    /* Copy imagegmagick image to x with primitive yet robust pixel
+       pusher loop.  This has been tested a lot with many different
+       images, it doesnt work too well with image archive formats though!
+
+       Also seems slow.
+    */
+  
+    /* Copy pixels from the imagemagick image structure to the x image map. */
+    iterator = NewPixelIterator (image_wand);
+    if ((iterator == (PixelIterator *) NULL)) {
+          image_error ("Imagemagick pixel iterator creation failed", Qnil, Qnil);
+          goto imagemagick_error;
+    }
+
+    for (y=0; y < (long) MagickGetImageHeight(image_wand); y++)
+      {
+        pixels = PixelGetNextIteratorRow (iterator, &width);
+        if ((pixels == (PixelWand **) NULL))
+          break;
+        for (x=0; x < (long) width; x++)
+          {
+            PixelGetMagickColor (pixels[x], &pixel);
+            XPutPixel (ximg, x, y, lookup_rgb_color (f, pixel.red, pixel.green, pixel.blue));      
+          }
+      }
+    DestroyPixelIterator (iterator);
+  }
+
+  if (imagemagick_rendermethod==1){
+    /* Try if magicexportimage is any faster than pixelpushing. */
+    /* printf("ximg: bitmap_unit:%d format:%d byte_order:%d depth:%d bits_per_pixel:%d\n", */
+    /*        ximg->bitmap_unit,ximg->format,ximg->byte_order,ximg->depth,ximg->bits_per_pixel); */
+    int imagedepth=24;/*MagickGetImageDepth(image_wand);*/
+    char* exportdepth= imagedepth <= 8 ? "I" : "BGRP";/*"RGBP";*/
+    /* Try to create a x pixmap to hold the imagemagick pixmap.  */
+    printf("imagedepth:%d exportdepth:%s\n", imagedepth, exportdepth);
+    if (!x_create_x_image_and_pixmap (f, width, height, imagedepth, &ximg, &img->pixmap)){
+      image_error("Imagemagick X bitmap allocation failure",Qnil,Qnil);
+      goto imagemagick_error;
+    }
+
+    
+    /* Oddly, the below code doesnt seem to work:*/
+    int pixelwidth; 
+    /* switch(ximg->bitmap_unit){ */
+    /* case 8: */
+    /*   pixelwidth=CharPixel; */
+    /*   break; */
+    /* case   16: */
+    /*   pixelwidth=ShortPixel; */
+    /*   break; */
+    /* case   32: */
+    /*   pixelwidth=LongPixel; */
+    /*   break; */
+    /* } */
+    /*
+      Here im just guessing the format of the bitmap.
+      happens to work fine for:
+      - bw djvu images
+      on rgb display.
+      seems about 3 times as fast as pixel pushing(not carefully measured)
+      with color djvu, the bitplanes are mapped to wrong color(seems fixed).
+
+    */
+    pixelwidth=CharPixel;/*??? TODO figure out*/
+    MagickExportImagePixels(image_wand,
+                            0,0,
+                            width,height,
+                            exportdepth,
+                            pixelwidth, 
+                            /*&(img->pixmap));*/
+                            ximg->data);
+  }
+  
+#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 */
+
+
+  img->width  = width;
+  img->height = height;
+
+  /* Maybe fill in the background field while we have ximg handy.
+     Casting avoids a GCC warning.  */
+  /*  IMAGE_BACKGROUND (img, f, (XImagePtr_or_DC)ximg);*/
+
+  /* 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);
+
+
+  /* JAVE TODO more cleanup.  */
+  DestroyMagickWand (image_wand);
+
+  return 1;
+
+ imagemagick_error:
+  /* TODO more cleanup.  */
+  image_error ("Error parsing IMAGEMAGICK image `%s'", img->spec, Qnil);
+  printf("Imagemagick error, see *Messages*\n");
+  return 0;
+}
+
+DEFUN ("imagemagick-types", Fimagemagick_types, Simagemagick_types, 0,0,0,
+       doc: /* Return image file types supported by ImageMagick.
+             Since ImageMagic recognizes a lot of file-types that clash with Emacs,
+            such as .c, we want to be able to alter the list at the lisp level.  */)
+  ()
+{
+  Lisp_Object typelist = Qnil;
+  unsigned long numf;
+  ExceptionInfo ex;
+  char** imtypes = GetMagickList ("*", &numf, &ex);
+  int i;
+  Lisp_Object Qimagemagicktype;
+  for (i = 0; i < numf; i++) {
+    Qimagemagicktype = intern (*( imtypes + i));
+    typelist = Fcons (Qimagemagicktype, typelist);
+  }
+  return typelist;
+}
+  
+#endif	/* defined (HAVE_IMAGEMAGICK) */
+
+
 
 /***********************************************************************
 				 SVG
@@ -8335,6 +8778,14 @@
     return CHECK_LIB_AVAILABLE (&svg_type, init_svg_functions, libraries);
 #endif
 
+#if defined (HAVE_IMAGEMAGICK)
+  if (EQ (type, Qimagemagick)){
+    /* MagickWandGenesis() initalizes the imagemagick library.  */
+    MagickWandGenesis(); 
+    return CHECK_LIB_AVAILABLE (&imagemagick_type, init_imagemagick_functions, libraries);
+  }
+#endif
+
 #ifdef HAVE_GHOSTSCRIPT
   if (EQ (type, Qpostscript))
     return CHECK_LIB_AVAILABLE (&gs_type, init_gs_functions, libraries);
@@ -8345,6 +8796,7 @@
   return Qnil;
 }
 
+
 void
 syms_of_image ()
 {
@@ -8422,7 +8874,14 @@
   staticpro (&QCheuristic_mask);
   QCindex = intern_c_string (":index");
   staticpro (&QCindex);
+  QCgeometry = intern (":geometry");
+  staticpro (&QCgeometry);
+  QCcrop = intern (":crop");
+  staticpro (&QCcrop);
+  QCrotation = intern (":rotation");
+  staticpro (&QCrotation);
   QCmatrix = intern_c_string (":matrix");
+
   staticpro (&QCmatrix);
   QCcolor_adjustment = intern_c_string (":color-adjustment");
   staticpro (&QCcolor_adjustment);
@@ -8482,6 +8941,12 @@
   ADD_IMAGE_TYPE (Qpng);
 #endif
 
+#if defined (HAVE_IMAGEMAGICK)
+  Qimagemagick = intern ("imagemagick");
+  staticpro (&Qimagemagick);
+  ADD_IMAGE_TYPE (Qimagemagick);
+#endif
+  
 #if defined (HAVE_RSVG)
   Qsvg = intern_c_string ("svg");
   staticpro (&Qsvg);
@@ -8498,6 +8963,9 @@
 #endif /* HAVE_RSVG  */
 
   defsubr (&Sinit_image_library);
+#ifdef HAVE_IMAGEMAGICK  
+  defsubr (&Simagemagick_types);
+#endif  
   defsubr (&Sclear_image_cache);
   defsubr (&Simage_refresh);
   defsubr (&Simage_size);
@@ -8525,12 +8993,20 @@
 from the image cache.  Value must be an integer or nil with nil
 meaning don't clear the cache.  */);
   Vimage_cache_eviction_delay = make_number (30 * 60);
+
+#ifdef HAVE_IMAGEMAGICK  
+  DEFVAR_LISP ("imagemagick-render-type", &Vimagemagick_render_type,
+    doc: /*   */);
+#endif    
 }
 
 void
 init_image ()
 {
-}
+
+}
+
+
 
 /* arch-tag: 123c2a5e-14a8-4c53-ab95-af47d7db49b9
    (do not change this comment) */