(image_background, image_background_transparent)
authorMiles Bader <miles@gnu.org>
Wed, 24 Oct 2001 17:57:28 +0000 (17:57 +0000)
committerMiles Bader <miles@gnu.org>
Wed, 24 Oct 2001 17:57:28 +0000 (17:57 +0000)
(four_corners_best): New functions.
(xpm_format, png_format, jpeg_format, tiff_format, gif_format)
(gs_format): Add `:background' entry.
(lookup_image): Set IMG's background color if specified.
(pbm_load, xbm_load_image, png_load): Set IMG's background field
when appropriate.
(x_clear_image_1): Reset `background_valid' and
`background_transparent_valid' fields.
(x_build_heuristic_mask): Use IMAGE_BACKGROUND instead of
calculating it here.  Set IMG's background_transparent field.
(enum xpm_keyword_index): Add XPM_BACKGROUND.
(enum png_keyword_index): Add PNG_BACKGROUND.
(enum jpeg_keyword_index): Add JPEG_BACKGROUND.
(enum tiff_keyword_index): Add TIFF_BACKGROUND.
(enum gif_keyword_index): Add GIF_BACKGROUND.
(enum gs_keyword_index): Add GS_BACKGROUND.
(pbm_load, png_load, jpeg_load, tiff_load, gif_load):
Pre-calculate image background color where necessary.

src/xfns.c

index e5256ec..ebe63d1 100644 (file)
@@ -5768,6 +5768,104 @@ image_ascent (img, face)
   return ascent;
 }
 
+\f
+/* Image background colors.  */
+
+static unsigned long
+four_corners_best (ximg, width, height)
+     XImage *ximg;
+     unsigned long width, height;
+{
+      unsigned long corners[4], best;
+      int i, best_count;
+
+      /* Get the colors at the corners of ximg.  */
+      corners[0] = XGetPixel (ximg, 0, 0);
+      corners[1] = XGetPixel (ximg, width - 1, 0);
+      corners[2] = XGetPixel (ximg, width - 1, height - 1);
+      corners[3] = XGetPixel (ximg, 0, height - 1);
+
+      /* Choose the most frequently found color as background.  */
+      for (i = best_count = 0; i < 4; ++i)
+       {
+         int j, n;
+         
+         for (j = n = 0; j < 4; ++j)
+           if (corners[i] == corners[j])
+             ++n;
+
+         if (n > best_count)
+           best = corners[i], best_count = n;
+       }
+
+      return best;
+}
+
+/* Return the `background' field of IMG.  If IMG doesn't have one yet,
+   it is guessed heuristically.  If non-zero, XIMG is an existing XImage
+   object to use for the heuristic.  */
+
+unsigned long
+image_background (img, f, ximg)
+     struct image *img;
+     struct frame *f;
+     XImage *ximg;
+{
+  if (! img->background_valid)
+    /* IMG doesn't have a background yet, try to guess a reasonable value.  */
+    {
+      int free_ximg = !ximg;
+
+      if (! ximg)
+       ximg = XGetImage (FRAME_X_DISPLAY (f), img->pixmap,
+                         0, 0, img->width, img->height, ~0, ZPixmap);
+
+      img->background = four_corners_best (ximg, img->width, img->height);
+
+      if (free_ximg)
+       XDestroyImage (ximg);
+
+      img->background_valid = 1;
+    }
+
+  return img->background;
+}
+
+/* Return the `background_transparent' field of IMG.  If IMG doesn't
+   have one yet, it is guessed heuristically.  If non-zero, MASK is an
+   existing XImage object to use for the heuristic.  */
+
+int
+image_background_transparent (img, f, mask)
+     struct image *img;
+     struct frame *f;
+     XImage *mask;
+{
+  if (! img->background_transparent_valid)
+    /* IMG doesn't have a background yet, try to guess a reasonable value.  */
+    {
+      if (img->mask)
+       {
+         int free_mask = !mask;
+
+         if (! mask)
+           mask = XGetImage (FRAME_X_DISPLAY (f), img->mask,
+                             0, 0, img->width, img->height, ~0, ZPixmap);
+
+         img->background_transparent
+           = !four_corners_best (mask, img->width, img->height);
+
+         if (free_mask)
+           XDestroyImage (mask);
+       }
+      else
+       img->background_transparent = 0;
+
+      img->background_transparent_valid = 1;
+    }
+
+  return img->background_transparent;
+}
 
 \f
 /***********************************************************************
@@ -5798,12 +5896,14 @@ x_clear_image_1 (f, img, pixmap_p, mask_p, colors_p)
     {
       XFreePixmap (FRAME_X_DISPLAY (f), img->pixmap);
       img->pixmap = None;
+      img->background_valid = 0;
     }
 
   if (mask_p && img->mask)
     {
       XFreePixmap (FRAME_X_DISPLAY (f), img->mask);
       img->mask = None;
+      img->background_transparent_valid = 0;
     }
       
   if (colors_p && img->ncolors)
@@ -6133,8 +6233,9 @@ lookup_image (f, spec)
       else
        {
          /* Handle image type independent image attributes
-            `:ascent ASCENT', `:margin MARGIN', `:relief RELIEF'.  */
-         Lisp_Object ascent, margin, relief;
+            `:ascent ASCENT', `:margin MARGIN', `:relief RELIEF',
+            `:background COLOR'.  */
+         Lisp_Object ascent, margin, relief, bg;
 
          ascent = image_spec_value (spec, QCascent, NULL);
          if (INTEGERP (ascent))
@@ -6162,6 +6263,18 @@ lookup_image (f, spec)
              img->vmargin += abs (img->relief);
            }
 
+         if (! img->background_valid)
+           {
+             bg = image_spec_value (img->spec, QCbackground, NULL);
+             if (!NILP (bg))
+               {
+                 img->background
+                   = x_alloc_image_color (f, img, bg,
+                                          FRAME_BACKGROUND_PIXEL (f));
+                 img->background_valid = 1;
+               }
+           }
+
          /* Do image transformations and compute masks, unless we
             don't have the image yet.  */
          if (!EQ (*img->type->type, Qpostscript))
@@ -6875,10 +6988,13 @@ xbm_load_image (f, img, contents, end)
       value = image_spec_value (img->spec, QCforeground, NULL);
       if (!NILP (value))
        foreground = x_alloc_image_color (f, img, value, foreground);
-      
       value = image_spec_value (img->spec, QCbackground, NULL);
       if (!NILP (value))
-       background = x_alloc_image_color (f, img, value, background);
+       {
+         background = x_alloc_image_color (f, img, value, background);
+         img->background = background;
+         img->background_valid = 1;
+       }
 
       img->pixmap
        = XCreatePixmapFromBitmapData (FRAME_X_DISPLAY (f),
@@ -7081,6 +7197,7 @@ enum xpm_keyword_index
   XPM_HEURISTIC_MASK,
   XPM_MASK,
   XPM_COLOR_SYMBOLS,
+  XPM_BACKGROUND,
   XPM_LAST
 };
 
@@ -7098,7 +7215,8 @@ static struct image_keyword xpm_format[XPM_LAST] =
   {":conversion",      IMAGE_DONT_CHECK_VALUE_TYPE,            0},
   {":heuristic-mask",  IMAGE_DONT_CHECK_VALUE_TYPE,            0},
   {":mask",            IMAGE_DONT_CHECK_VALUE_TYPE,            0},
-  {":color-symbols",   IMAGE_DONT_CHECK_VALUE_TYPE,            0}
+  {":color-symbols",   IMAGE_DONT_CHECK_VALUE_TYPE,            0},
+  {":background",      IMAGE_STRING_OR_NIL_VALUE,              0}
 };
 
 /* Structure describing the image type XBM.  */
@@ -8041,13 +8159,14 @@ x_build_heuristic_mask (f, img, how)
 {
   Display *dpy = FRAME_X_DISPLAY (f);
   XImage *ximg, *mask_img;
-  int x, y, rc, look_at_corners_p;
+  int x, y, rc, use_img_background;
   unsigned long bg = 0;
 
   if (img->mask)
     {
       XFreePixmap (FRAME_X_DISPLAY (f), img->mask);
       img->mask = None;
+      img->background_transparent_valid = 0;
     }
 
   /* Create an image and pixmap serving as mask.  */
@@ -8061,9 +8180,8 @@ x_build_heuristic_mask (f, img, how)
                    ~0, ZPixmap);
 
   /* Determine the background color of ximg.  If HOW is `(R G B)'
-     take that as color.  Otherwise, try to determine the color
-     heuristically. */
-  look_at_corners_p = 1;
+     take that as color.  Otherwise, use the image's background color. */
+  use_img_background = 1;
   
   if (CONSP (how))
     {
@@ -8089,35 +8207,13 @@ x_build_heuristic_mask (f, img, how)
          if (XLookupColor (dpy, cmap, color_name, &exact, &color))
            {
              bg = color.pixel;
-             look_at_corners_p = 0;
+             use_img_background = 0;
            }
        }
     }
   
-  if (look_at_corners_p)
-    {
-      unsigned long corners[4];
-      int i, best_count;
-
-      /* Get the colors at the corners of ximg.  */
-      corners[0] = XGetPixel (ximg, 0, 0);
-      corners[1] = XGetPixel (ximg, img->width - 1, 0);
-      corners[2] = XGetPixel (ximg, img->width - 1, img->height - 1);
-      corners[3] = XGetPixel (ximg, 0, img->height - 1);
-
-      /* Choose the most frequently found color as background.  */
-      for (i = best_count = 0; i < 4; ++i)
-       {
-         int j, n;
-         
-         for (j = n = 0; j < 4; ++j)
-           if (corners[i] == corners[j])
-             ++n;
-
-         if (n > best_count)
-           bg = corners[i], best_count = n;
-       }
-    }
+  if (use_img_background)
+    bg = IMAGE_BACKGROUND (img, f, ximg);
 
   /* Set all bits in mask_img to 1 whose color in ximg is different
      from the background color bg.  */
@@ -8125,6 +8221,9 @@ x_build_heuristic_mask (f, img, how)
     for (x = 0; x < img->width; ++x)
       XPutPixel (mask_img, x, y, XGetPixel (ximg, x, y) != bg);
 
+  /* Fill in the background_transparent field while we have the mask handy. */
+  image_background_transparent (img, f, mask_img);
+
   /* Put mask_img into img->mask.  */
   x_put_x_image (f, mask_img, img->mask, img->width, img->height);
   x_destroy_x_image (mask_img);
@@ -8383,7 +8482,11 @@ pbm_load (f, img)
        fg = x_alloc_image_color (f, img, fmt[PBM_FOREGROUND].value, fg);
       if (fmt[PBM_BACKGROUND].count
          && STRINGP (fmt[PBM_BACKGROUND].value))
-       bg = x_alloc_image_color (f, img, fmt[PBM_BACKGROUND].value, bg);
+       {
+         bg = x_alloc_image_color (f, img, fmt[PBM_BACKGROUND].value, bg);
+         img->background = bg;
+         img->background_valid = 1;
+       }
       
       for (y = 0; y < height; ++y)
        for (x = 0; x < width; ++x)
@@ -8446,6 +8549,10 @@ pbm_load (f, img)
      free the color table.  */
   img->colors = colors_in_color_table (&img->ncolors);
   free_color_table ();
+
+  /* Maybe fill in the background field while we have ximg handy. */
+  if (NILP (image_spec_value (img->spec, QCbackground, NULL)))
+    IMAGE_BACKGROUND (img, f, ximg);
   
   /* Put the image into a pixmap.  */
   x_put_x_image (f, ximg, img->pixmap, width, height);
@@ -8491,6 +8598,7 @@ enum png_keyword_index
   PNG_ALGORITHM,
   PNG_HEURISTIC_MASK,
   PNG_MASK,
+  PNG_BACKGROUND,
   PNG_LAST
 };
 
@@ -8508,6 +8616,7 @@ static struct image_keyword png_format[PNG_LAST] =
   {":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 `png'.  */
@@ -8778,12 +8887,31 @@ png_load (f, img)
      simple transparency, we prefer a clipping mask.  */
   if (!transparent_p)
     {
-      png_color_16 *image_background;
+      png_color_16 *image_bg;
+      Lisp_Object specified_bg
+       = image_spec_value (img->spec, QCbackground, NULL);
+
+      if (! NILP (specified_bg))
+       /* The user specified `:background', use that.  */
+       {
+         XColor color;
+         if (x_defined_color (f, specified_bg, &color, 0))
+           {
+             png_color_16 user_bg;
+
+             bzero (&user_bg, sizeof user_bg);
+             user_bg.red = color.red;
+             user_bg.green = color.green;
+             user_bg.blue = color.blue;
 
-      if (png_get_bKGD (png_ptr, info_ptr, &image_background))
+             png_set_background (png_ptr, &user_bg,
+                                 PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
+           }
+       }
+      else if (png_get_bKGD (png_ptr, info_ptr, &image_bg))
        /* Image contains a background color with which to 
           combine the image.  */
-       png_set_background (png_ptr, image_background,
+       png_set_background (png_ptr, image_bg,
                            PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
       else
        {
@@ -8896,6 +9024,18 @@ png_load (f, img)
        }
     }
 
+  if (NILP (image_spec_value (img->spec, QCbackground, NULL)))
+    /* Set IMG's background color from the PNG image, unless the user
+       overrode it.  */
+    {
+      png_color_16 *bg;
+      if (png_get_bKGD (png_ptr, info_ptr, &bg))
+       {
+         img->background = lookup_rgb_color (f, bg.red, bg.green, bg.blue);
+         img->background_valid = 1;
+       }
+    }
+
   /* Remember colors allocated for this image.  */
   img->colors = colors_in_color_table (&img->ncolors);
   free_color_table ();
@@ -8908,6 +9048,9 @@ png_load (f, img)
   img->width = width;
   img->height = height;
 
+  /* Maybe fill in the background field while we have ximg handy. */
+  IMAGE_BACKGROUND (img, f, 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);
@@ -8915,6 +9058,10 @@ png_load (f, img)
   /* Same for the mask.  */
   if (mask_img)
     {
+      /* Fill in the background_transparent field while we have the mask
+        handy. */
+      image_background_transparent (img, f, mask_img);
+
       x_put_x_image (f, mask_img, img->mask, img->width, img->height);
       x_destroy_x_image (mask_img);
     }
@@ -8968,6 +9115,7 @@ enum jpeg_keyword_index
   JPEG_ALGORITHM,
   JPEG_HEURISTIC_MASK,
   JPEG_MASK,
+  JPEG_BACKGROUND,
   JPEG_LAST
 };
 
@@ -8984,7 +9132,8 @@ static struct image_keyword jpeg_format[JPEG_LAST] =
   {":relief",          IMAGE_INTEGER_VALUE,                    0},
   {":conversions",     IMAGE_DONT_CHECK_VALUE_TYPE,            0},
   {":heuristic-mask",  IMAGE_DONT_CHECK_VALUE_TYPE,            0},
-  {":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 `jpeg'.  */
@@ -9283,6 +9432,10 @@ jpeg_load (f, img)
   jpeg_destroy_decompress (&cinfo);
   if (fp)
     fclose ((FILE *) fp);
+
+  /* Maybe fill in the background field while we have ximg handy. */
+  if (NILP (image_spec_value (img->spec, QCbackground, NULL)))
+    IMAGE_BACKGROUND (img, f, ximg);
   
   /* Put the image into the pixmap.  */
   x_put_x_image (f, ximg, img->pixmap, width, height);
@@ -9323,6 +9476,7 @@ enum tiff_keyword_index
   TIFF_ALGORITHM,
   TIFF_HEURISTIC_MASK,
   TIFF_MASK,
+  TIFF_BACKGROUND,
   TIFF_LAST
 };
 
@@ -9339,7 +9493,8 @@ static struct image_keyword tiff_format[TIFF_LAST] =
   {":relief",          IMAGE_INTEGER_VALUE,                    0},
   {":conversions",     IMAGE_DONT_CHECK_VALUE_TYPE,            0},
   {":heuristic-mask",  IMAGE_DONT_CHECK_VALUE_TYPE,            0},
-  {":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 `tiff'.  */
@@ -9631,14 +9786,18 @@ tiff_load (f, img)
   /* Remember the colors allocated for the image.  Free the color table.  */
   img->colors = colors_in_color_table (&img->ncolors);
   free_color_table ();
+      
+  img->width = width;
+  img->height = height;
+
+  /* Maybe fill in the background field while we have ximg handy. */
+  if (NILP (image_spec_value (img->spec, QCbackground, NULL)))
+    IMAGE_BACKGROUND (img, f, 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);
   xfree (buf);
-      
-  img->width = width;
-  img->height = height;
 
   UNGCPRO;
   return 1;
@@ -9677,6 +9836,7 @@ enum gif_keyword_index
   GIF_HEURISTIC_MASK,
   GIF_MASK,
   GIF_IMAGE,
+  GIF_BACKGROUND,
   GIF_LAST
 };
 
@@ -9695,6 +9855,7 @@ static struct image_keyword gif_format[GIF_LAST] =
   {":heuristic-mask",  IMAGE_DONT_CHECK_VALUE_TYPE,            0},
   {":mask",            IMAGE_DONT_CHECK_VALUE_TYPE,            0},
   {":image",           IMAGE_NON_NEGATIVE_INTEGER_VALUE,       0}
+  {":background",      IMAGE_STRING_OR_NIL_VALUE,              0}
 };
 
 /* Structure describing the image type `gif'.  */
@@ -9942,6 +10103,10 @@ gif_load (f, img)
     }
   
   DGifCloseFile (gif);
+
+  /* Maybe fill in the background field while we have ximg handy. */
+  if (NILP (image_spec_value (img->spec, QCbackground, NULL)))
+    IMAGE_BACKGROUND (img, f, 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);
@@ -9987,6 +10152,7 @@ enum gs_keyword_index
   GS_ALGORITHM,
   GS_HEURISTIC_MASK,
   GS_MASK,
+  GS_BACKGROUND,
   GS_LAST
 };
 
@@ -10006,7 +10172,8 @@ static struct image_keyword gs_format[GS_LAST] =
   {":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}
+  {":mask",            IMAGE_DONT_CHECK_VALUE_TYPE,            0},
+  {":background",      IMAGE_STRING_OR_NIL_VALUE,              0}
 };
 
 /* Structure describing the image type `ghostscript'.  */