emacs-devel
[Top][All Lists]
Advanced

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

new image-type: composite, or not


From: Andreas Politz
Subject: new image-type: composite, or not
Date: Sat, 26 Jan 2013 18:30:52 +0100

Hi,

lately I started to develop an isearch mode for PDF files, since being
able to search a document effectively is one of the few things that
lets me start-up xpdf, while writing LaTeX.  I came across a library
called poppler, a fork of xpdf, that makes query these kinds of
documents fairly easy.

The idea was to annotate doc-views png pages with ,,colored
rectangles'', you know like isearch does.  I started out doing this
with imagemagicks convert tool, but I soon realized, that creating and
loading a new image (with Millions of pixel) is far too costly
time-wise, in order for this to become a pleasurable experience.  (It
takes around 750ms for reasonable sized images on my labtop.)

So the idea of doing this all in Emacs sprang to my mind; not creating
and loading new images, but reusing already loaded ones and compose
them in new ways. That way a new image type came to live, which I
ingeniously called `composite'.  A patch against the latest bzr
version is at the end of this message.

It looks a bit uglier than the convert version, because of non-fuzzy
masking of the background, but is totally usable, regarding
performance.

Alas, all this made me realize, that maybe a simpler approach would
suffice.  Something like a single function

(mark-image foreground background &rest edges-list)

,which does not have the side-effect of reloading the image.  This
would also allow for other things, like having a pseudo point and mark
in such buffers.  E.g. since I have already the positions of all the
text in the document, it'll be easy to implement some basic selection
and copying functions.  Though I don't know if this would be useful
for anything else, besides doc-view.  Maybe selecting a slice of some
image...

What do you think ?

Andreas

Oh, here is an application of this patch, actually it should work with
or without it.

http://www.fh-trier.de/~politza/emacs/pdf-isearch_v0.2.tar.gz

=== modified file 'src/image.c'
*** src/image.c 2013-01-02 16:13:04 +0000
--- src/image.c 2013-01-26 16:21:44 +0000
***************
*** 8717,8722 ****
--- 8717,9162 ----

  
  /***********************************************************************
+                               Composite
+  ***********************************************************************/
+
+
+ /* composite-image-spec ::=
+  * (image :type 'composite
+  *        [:background color]
+  *        [:width int]
+  *        [:height int]
+  *        [:images ((image-spec
+  *                   [:slice (x y w h)]
+  *                   [:offset (x . y)]
+  *                   [:mask t | (r g b)])* )])
+  */
+
+ #ifdef HAVE_X_WINDOWS
+ static bool comp_image_p (Lisp_Object object);
+ static bool comp_load (struct frame *f, struct image *img);
+ static bool
+ comp_determine_size (struct frame *f, Lisp_Object images, Lisp_Object 
spec_width,
+                      Lisp_Object spec_height, int *width, int *height);
+
+ static void
+ image_comp_get_slice(Lisp_Object slice,int *x,int *y,int *w,int *h);
+ static Pixmap
+ comp_build_mask (struct frame *f, struct image *img,
+                  int x, int y, int width, int height, Lisp_Object how);
+
+ /* The symbol `composite' identifying images of this type.  */
+
+ static Lisp_Object Qcomposite;
+
+ /* Keyword symbols.  */
+
+ static Lisp_Object QCimages, QCslice, QCoffset;
+
+ /* Indices of image specification fields in comp_format, below.  */
+
+ enum comp_keyword_index
+ {
+   COMP_TYPE,
+   COMP_BACKGROUND,
+   COMP_WIDTH,
+   COMP_HEIGHT,
+   COMP_IMAGES,
+   COMP_ASCENT,
+   COMP_MARGIN,
+   COMP_RELIEF,
+   COMP_ALGORITHM,
+   COMP_HEURISTIC_MASK,
+   COMP_MASK,
+   COMP_LAST
+ };
+
+ /* Vector of image_keyword structures describing the format
+    of valid user-defined image specifications.  */
+
+ static const struct image_keyword comp_format[COMP_LAST] =
+ {
+   {":type",           IMAGE_SYMBOL_VALUE,                     1},
+   {":background",     IMAGE_STRING_OR_NIL_VALUE,              0},
+   {":width",          IMAGE_POSITIVE_INTEGER_VALUE,           0},
+   {":height",                 IMAGE_POSITIVE_INTEGER_VALUE,           0},
+   {":images",                 IMAGE_DONT_CHECK_VALUE_TYPE,            0},
+   {":ascent",         IMAGE_ASCENT_VALUE,                     0},
+   {":margin",         IMAGE_NON_NEGATIVE_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}
+ };
+
+ /* Indices of image specification fields in comp_images_format, below.  */
+
+ enum comp_images_keyword_index
+ {
+   COMP_IMAGES_SLICE,
+   COMP_IMAGES_OFFSET,
+   COMP_IMAGES_MASK,
+   COMP_IMAGES_LAST
+ };
+
+ /* Vector of image_keyword structures describing the :images format of
+    valid user-defined subimage specifications.  */
+
+ static const struct image_keyword comp_images_format[COMP_IMAGES_LAST] =
+ {
+   {":slice",  IMAGE_DONT_CHECK_VALUE_TYPE,                    0},
+   {":offset", IMAGE_NON_NEGATIVE_INTEGER_VALUE_OR_PAIR,       0},
+   {":mask",   IMAGE_DONT_CHECK_VALUE_TYPE,                    0}
+ };
+
+ /* Structure describing the image type `composite'.  */
+
+ static struct image_type comp_type =
+ {
+   &Qcomposite,
+   comp_image_p,
+   comp_load,
+   x_clear_image,
+   NULL,
+   NULL
+ };
+
+
+ /* Return true if OBJECT is a valid composite image
+    specification.  */
+
+ static bool
+ comp_image_p (Lisp_Object object)
+ {
+   struct image_keyword fmt[COMP_LAST];
+   Lisp_Object images, offset, slice, slice0, mask;
+
+   memcpy (fmt, comp_format, sizeof fmt);
+
+   if (!parse_image_spec (object, fmt, COMP_LAST, Qcomposite))
+     return 0;
+
+   /* Check that the :images are valid. */
+   images = fmt[COMP_IMAGES].value;
+   if (images)
+     {
+       if (! CONSP (images) && ! NILP (images))
+         return 0;
+       for (; ! NILP (images); images = XCDR (images))
+         {
+           if (! CONSP (XCAR (images)))
+             return 0;
+           if (! valid_image_p (XCAR (XCAR (images))))
+             return 0;
+
+           /* Can't use image_spec_value, because of type checking.  */
+           offset = Fplist_get (XCDR (XCAR (images)), QCoffset);
+           if (! NILP (offset))
+             {
+               if (! (RANGED_INTEGERP (0, offset, INT_MAX) ||
+                      (CONSP (offset)
+                       && RANGED_INTEGERP (0, XCAR (offset), INT_MAX)
+                       && RANGED_INTEGERP (0, XCDR (offset), INT_MAX))))
+                 return 0;
+             }
+           slice = Fplist_get (XCDR (XCAR (images)), QCslice);
+           if (! NILP (slice))
+             {
+               if (! CONSP(slice) || 4 != XFASTINT (Flength (slice)))
+                 return 0;
+               for (; ! NILP (slice); slice = XCDR (slice))
+                 if (! RANGED_INTEGERP (0, XCAR (slice), INT_MAX))
+                   return 0;
+             }
+           mask = Fplist_get (XCDR (XCAR (images)), QCmask);
+           if (CONSP (mask))
+             {
+               if (3 != XFASTINT (Flength (mask)))
+                 return 0;
+               for (; ! NILP (slice); slice = XCDR (slice))
+                 if (! RANGED_INTEGERP (0, XCAR (slice), 0xffff))
+                   return 0;
+             }
+         }
+     }
+   return 1;
+ }
+
+ /* Create a composite image IMG for use on frame F.  Value is true if
+    successful.  */
+
+ static bool
+ comp_load (struct frame *f, struct image *img)
+ {
+   Lisp_Object images, bg_spec, width_spec, height_spec;
+
+   struct image *src;
+   Lisp_Object slice, offset, mask;
+   int src_x, src_y, dest_x, dest_y, width, height;
+
+   Pixmap dest, mask_pixmap;
+   GC gc;
+   XGCValues gcv;
+   Display *display = FRAME_X_DISPLAY (f);
+   Window window = FRAME_X_WINDOW (f);
+   Screen *screen = FRAME_X_SCREEN (f);
+   int depth = DefaultDepthOfScreen (screen);
+   bool success = 1;
+
+   images = image_spec_value (img->spec, QCimages, NULL);
+
+   /* Determine the resulting image size */
+   width_spec = image_spec_value (img->spec, QCwidth, NULL);
+   height_spec = image_spec_value (img->spec, QCheight, NULL);
+
+   if (! comp_determine_size (f, images, width_spec,
+                              height_spec, &img->width, &img->height))
+     image_error ("Invalid image size (see `max-image-size')", Qnil, Qnil);
+
+   /* Maybe setup up a background */
+   bg_spec = image_spec_value (img->spec, QCbackground, NULL);
+   if (! NILP (bg_spec))
+     {
+       img->background = x_alloc_image_color (f, img, bg_spec,
+                                              FRAME_BACKGROUND_PIXEL (f));
+       img->background_valid = 1;
+     }
+
+
+   block_input ();               /* FIXME: Do I need to do this ? */
+   dest = XCreatePixmap (display, window, img->width, img->height, depth);
+   if (dest == NO_PIXMAP)
+     {
+       image_error ("Unable to create X pixmap", Qnil, Qnil);
+       unblock_input ();
+       return 0;
+     }
+
+   gc = XCreateGC (display, dest, 0, NULL);
+   /* Fill the background. */
+   if (img->background_valid)
+     {
+       gcv.foreground = img->background;
+       gcv.fill_style = FillSolid;
+       XChangeGC (display, gc, GCForeground|GCFillStyle, &gcv);
+       XFillRectangle (display, dest, gc, 0, 0, img->width, img->height);
+     }
+   /* Now draw the images on top of it. */
+   for (; ! NILP (images); images = XCDR (images))
+     {
+       slice = Fplist_get (XCDR (XCAR (images)), QCslice);
+       offset = Fplist_get (XCDR (XCAR (images)), QCoffset);
+       mask = Fplist_get (XCDR (XCAR (images)), QCmask);
+
+       src_x = src_y = dest_x = dest_y = 0;
+       width = img->width;
+       height = img->height;
+       src = IMAGE_FROM_ID (f, lookup_image (f, XCAR (XCAR (images))));
+
+       if (! src || src->pixmap == NO_PIXMAP)
+         {
+           success = 0;
+           break;
+         }
+
+       if (! NILP (slice))
+         image_comp_get_slice(slice, &src_x, &src_y, &width, &height);
+
+       if (! NILP (offset))
+         {
+           if (CONSP (offset))
+             {
+               dest_x = XFASTINT (XCAR (offset));
+               dest_y = XFASTINT (XCDR (offset));
+             }
+           else
+             {
+               dest_x = dest_y = XFASTINT (offset);
+             }
+         }
+
+       if (! NILP (mask))
+         {
+           mask_pixmap = comp_build_mask(f, src, src_x, src_y,
+                                         width, height, mask);
+           if (mask_pixmap == NO_PIXMAP)
+             {
+               image_error ("Unable to create mask pixmap", Qnil, Qnil);
+               success = 0;
+               break;
+             }
+
+           gcv.clip_mask = mask_pixmap;
+           gcv.clip_x_origin = dest_x;
+           gcv.clip_y_origin = dest_y;
+           XChangeGC (display, gc, GCClipMask|GCClipYOrigin|GCClipXOrigin, 
&gcv);
+         }
+
+       XCopyArea (display, src->pixmap, dest, gc, src_x,  src_y,
+                  width,  height, dest_x,  dest_y);
+
+       if (! NILP (mask) && mask_pixmap != NO_PIXMAP)
+         {
+           gcv.clip_mask = NO_PIXMAP;
+           XChangeGC (display, gc, GCClipMask, &gcv);
+           XFreePixmap (display, mask_pixmap);
+         }
+     }
+
+   XFreeGC(display, gc);
+   if (success)
+     img->pixmap = dest;
+   else
+     XFreePixmap (display, dest);
+
+   unblock_input ();
+   return success;
+ }
+
+ static bool
+ comp_determine_size (struct frame *f, Lisp_Object images, Lisp_Object 
spec_width,
+                      Lisp_Object spec_height, int *width, int *height)
+ {
+   Lisp_Object slice, offset;
+   int w, h, this_width, this_height;
+   struct image *img;
+
+   w = ! NILP (spec_width) ? XFASTINT (spec_width) : 0;
+   h = ! NILP (spec_height) ? XFASTINT (spec_height) : 0;
+
+
+   if (NILP (spec_width) || NILP (spec_height))
+     {
+       for (; ! NILP (images); images = XCDR (images))
+         {
+           img = IMAGE_FROM_ID (f, lookup_image (f, XCAR (XCAR (images))));
+
+           if (! img || img->pixmap == NO_PIXMAP)
+             continue;
+
+           slice = Fplist_get (XCDR (XCAR (images)), QCslice);
+           if (! NILP (slice))
+             {
+               int sw, sh;
+               image_comp_get_slice(slice, NULL, NULL, &sw, &sh);
+
+               this_width = min (img->width, sw);
+               this_height = min (img->height, sh);
+             }
+           else
+             {
+               this_width = img->width;
+               this_height = img->height;
+             }
+
+           offset = Fplist_get (XCDR (XCAR (images)), QCoffset);
+           if (! NILP (offset))
+             {
+               if (CONSP (offset))
+                 {
+                   this_width += XFASTINT (XCAR (offset));
+                   this_height += XFASTINT (XCDR (offset));
+                 }
+               else
+                 {
+                   this_width += XFASTINT (offset);
+                   this_height += XFASTINT (offset);
+                 }
+             }
+
+           if (NILP (spec_width))
+             w = max (w, this_width);
+           if (NILP (spec_height))
+             h = max (h, this_height);
+         }
+     }
+
+   if (check_image_size (f, w, h))
+     {
+       *width = w;
+       *height = h;
+       return 1;
+     }
+   return 0;
+ }
+
+ static void
+ image_comp_get_slice(Lisp_Object slice,int *x,int *y,int *w,int *h)
+ {
+   if (x)
+     *x = XFASTINT (XCAR (slice));
+   if (y)
+     *y = XFASTINT (XCAR (XCDR (slice)));
+   if (w)
+     *w = XFASTINT (XCAR (XCDR (XCDR (slice))));
+   if (h)
+     *h = XFASTINT (XCAR (XCDR (XCDR (XCDR (slice)))));
+ }
+
+ /* This function is almost identical to x_build_heuristic_mask, but
+    allows for slices. */
+ static Pixmap
+ comp_build_mask (struct frame *f, struct image *img,
+                  int x, int y, int width, int height, Lisp_Object how)
+ {
+   XImagePtr ximg;
+   XImagePtr mask_img;
+   Pixmap mask;
+   bool rc, use_img_background;
+   unsigned long bg = 0;
+
+   /* Create an image and pixmap serving as mask.  */
+   rc = x_create_x_image_and_pixmap (f, width, height, 1, &mask_img, &mask);
+   if (!rc)
+     return NO_PIXMAP;
+
+   /* Get the X image of IMG->pixmap.  */
+   ximg = XGetImage (FRAME_X_DISPLAY (f), img->pixmap, x, y,
+                     width, height, ~0, ZPixmap);
+
+   /* Determine the background color of ximg.  If HOW is `(R G B)'
+      take that as color.  Otherwise, use the image's background color. */
+   use_img_background = 1;
+
+   if (CONSP (how))
+     {
+       int rgb[3], i;
+
+       for (i = 0; i < 3 && CONSP (how) && NATNUMP (XCAR (how)); ++i)
+       {
+         rgb[i] = XINT (XCAR (how)) & 0xffff;
+         how = XCDR (how);
+       }
+
+       if (i == 3 && NILP (how))
+       {
+         char color_name[30];
+         sprintf (color_name, "#%04x%04x%04x", rgb[0], rgb[1], rgb[2]);
+         bg = x_alloc_image_color (f, img, build_string (color_name), 0);
+         use_img_background = 0;
+       }
+     }
+
+   if (use_img_background)
+     bg = four_corners_best (ximg, NULL, width, height);
+
+   /* Set all bits in mask_img to 1 whose color in ximg is different
+      from the background color bg.  */
+   for (y = 0; y < height; ++y)
+     for (x = 0; x < width; ++x)
+       XPutPixel (mask_img, x, y, (XGetPixel (ximg, x, y) != bg
+                                 ? PIX_MASK_DRAW : PIX_MASK_RETAIN));
+
+   /* Put mask_img into img->mask.  */
+   x_put_x_image (f, mask_img, mask, width, height);
+   x_destroy_x_image (mask_img);
+
+   return mask;
+ }
+ #endif  /* HAVE_X_WINDOWS */
+
+ 
+ /***********************************************************************
                                Tests
   ***********************************************************************/

***************
*** 8814,8819 ****
--- 9254,9264 ----
      return define_image_type (&gs_type);
  #endif

+ #ifdef HAVE_X_WINDOWS
+   if (EQ (type, Qcomposite))
+     return define_image_type (&comp_type);
+ #endif  /* HAVE_X_WINDOWS */
+
    return NULL;
  }

***************
*** 8947,8952 ****
--- 9392,9405 ----
  #endif /* HAVE_NTGUI  */
  #endif /* HAVE_RSVG  */

+ #ifdef HAVE_X_WINDOWS
+   DEFSYM (Qcomposite, "composite");
+   DEFSYM (QCslice, ":slice");
+   DEFSYM (QCoffset, ":offset");
+   DEFSYM (QCimages, ":images");
+   ADD_IMAGE_TYPE (Qcomposite);
+ #endif  /* HAVE_X_WINDOWS */
+
    defsubr (&Sinit_image_library);
  #ifdef HAVE_IMAGEMAGICK
    defsubr (&Simagemagick_types);



reply via email to

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