unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* [PATCH] A small patch to make emacs support transparent image (png, webp, svg)
@ 2023-12-22  7:18 Qiutum
  2023-12-22  8:25 ` Manuel Giraud via Emacs development discussions.
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Qiutum @ 2023-12-22  7:18 UTC (permalink / raw)
  To: emacs-devel


[-- Attachment #1.1: Type: text/plain, Size: 819 bytes --]

Hi everyone,

Recently, I've been reading the code of image.c and wondering how to make
emacs support transparent image. Then I find that a small change on the
src/image.c can make it worked (and wonder why this have not been solved
since the solution is so simple).

This patch can support the transparent png, webp and svg. The core of the
patch is to change the code,

 PUT_PIXEL (mask_img, x, y, *p > 0 ? PIX_MASK_DRAW : PIX_MASK_RETAIN);

to

 PUT_PIXEL (mask_img, x, y, *p > 0 ? *p : PIX_MASK_RETAIN);

Then all worked like a magic.

I have checked my patch in linux by default configure option "../configure"
for dwm and pgtk build for hyprland, both are fine. I don't know whether it
could support other platforms since I don't have them.

Please check my patch and happy to hear comments.

Best regards,
Zhang

[-- Attachment #1.2: Type: text/html, Size: 1099 bytes --]

[-- Attachment #2: support-image-transparent.patch --]
[-- Type: text/x-patch, Size: 9393 bytes --]

diff --git a/src/image.c b/src/image.c
index 84db9bfb3b8..d59352b33ce 100644
--- a/src/image.c
+++ b/src/image.c
@@ -8054,7 +8054,6 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c)
   int bit_depth, color_type, interlace_type;
   png_byte channels;
   png_uint_32 row_bytes;
-  bool transparent_p;
   struct png_memory_storage tbr;  /* Data to be read */
   ptrdiff_t nbytes;
   Emacs_Pix_Container ximg, mask_img = NULL;
@@ -8183,25 +8182,6 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c)
   if (!image_create_x_image_and_pixmap (f, img, width, height, 0, &ximg, 0))
     goto error;
 
-  /* If image contains simply transparency data, we prefer to
-     construct a clipping mask.  */
-  transparent_p = false;
-# ifdef PNG_tRNS_SUPPORTED
-  png_bytep trans_alpha;
-  int num_trans;
-  if (png_get_tRNS (png_ptr, info_ptr, &trans_alpha, &num_trans, NULL))
-    {
-      transparent_p = true;
-      if (trans_alpha)
-	for (int i = 0; i < num_trans; i++)
-	  if (0 < trans_alpha[i] && trans_alpha[i] < 255)
-	    {
-	      transparent_p = false;
-	      break;
-	    }
-    }
-# endif
-
   /* This function is easier to write if we only have to handle
      one data format: RGB or RGBA with 8 bits per channel.  Let's
      transform other formats into that format.  */
@@ -8219,39 +8199,6 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c)
       || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
     png_set_gray_to_rgb (png_ptr);
 
-  /* Handle alpha channel by combining the image with a background
-     color.  Do this only if a real alpha channel is supplied.  For
-     simple transparency, we prefer a clipping mask.  */
-  if (!transparent_p)
-    {
-      Lisp_Object specified_bg
-	= image_spec_value (img->spec, QCbackground, NULL);
-      Emacs_Color color;
-
-      /* If the user specified a color, try to use it; if not, use the
-	 current frame background, ignoring any default background
-	 color set by the image.  */
-      if (STRINGP (specified_bg)
-	  ? FRAME_TERMINAL (f)->defined_color_hook (f,
-                                                    SSDATA (specified_bg),
-                                                    &color,
-                                                    false,
-                                                    false)
-	  : (FRAME_TERMINAL (f)->query_frame_background_color (f, &color),
-             true))
-	/* The user specified `:background', use that.  */
-	{
-	  int shift = bit_depth == 16 ? 0 : 8;
-	  png_color_16 bg = { 0 };
-	  bg.red = color.red >> shift;
-	  bg.green = color.green >> shift;
-	  bg.blue = color.blue >> shift;
-
-	  png_set_background (png_ptr, &bg,
-			      PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
-	}
-    }
-
   png_set_interlace_handling (png_ptr);
   png_read_update_info (png_ptr, info_ptr);
 
@@ -8287,7 +8234,6 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c)
   /* Create an image and pixmap serving as mask if the PNG image
      contains an alpha channel.  */
   if (channels == 4
-      && transparent_p
       && !image_create_x_image_and_pixmap (f, img, width, height, 1,
 					   &mask_img, 1))
     {
@@ -8330,7 +8276,7 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c)
 	  if (channels == 4)
 	    {
 	      if (mask_img)
-		PUT_PIXEL (mask_img, x, y, *p > 0 ? PIX_MASK_DRAW : PIX_MASK_RETAIN);
+		PUT_PIXEL (mask_img, x, y, *p > 0 ? *p : PIX_MASK_RETAIN);
 	      ++p;
 	    }
 	}
@@ -10462,36 +10408,20 @@ webp_load (struct frame *f, struct image *img)
     }
 
   /* Create the x image and pixmap.  */
-  Emacs_Pix_Container ximg;
+  Emacs_Pix_Container ximg, mask_img = NULL;
   if (!image_create_x_image_and_pixmap (f, img, width, height, 0, &ximg, false))
     goto webp_error2;
 
-  /* Find the background to use if the WebP image contains an alpha
-     channel.  */
-  Emacs_Color bg_color;
-  if (features.has_alpha)
-    {
-      Lisp_Object specified_bg
-	= image_spec_value (img->spec, QCbackground, NULL);
-
-      /* If the user specified a color, try to use it; if not, use the
-	 current frame background, ignoring any default background
-	 color set by the image.  */
-      if (STRINGP (specified_bg))
-	FRAME_TERMINAL (f)->defined_color_hook (f,
-						SSDATA (specified_bg),
-						&bg_color,
-						false,
-						false);
-      else
-	FRAME_TERMINAL (f)->query_frame_background_color (f, &bg_color);
-      bg_color.red   >>= 8;
-      bg_color.green >>= 8;
-      bg_color.blue  >>= 8;
+  if (features.has_alpha
+      && !image_create_x_image_and_pixmap (f, img, width, height, 1,
+					   &mask_img, true))
+    {
+      image_destroy_x_image (ximg);
+      image_clear_image_1 (f, img, CLEAR_IMAGE_PIXMAP);
+      goto webp_error2;
     }
 
-  /* Fill the X image from WebP data.  */
-
+  /* Fill the X image and mask from WebP data.  */
   init_color_table ();
 
   img->corners[TOP_CORNER] = 0;
@@ -10506,24 +10436,16 @@ webp_load (struct frame *f, struct image *img)
     {
       for (int x = 0; x < width; ++x)
 	{
-	  int r, g, b;
-	  /* The WebP alpha channel allows 256 levels of partial
-	     transparency.  Blend it with the background manually.  */
+	  int r = *p++ << 8;
+	  int g = *p++ << 8;
+	  int b = *p++ << 8;
+	  PUT_PIXEL (ximg, x, y, lookup_rgb_color (f, r, g, b));
 	  if (features.has_alpha || anim)
 	    {
-	      float a = (float) p[3] / UINT8_MAX;
-	      r = (int)(a * p[0] + (1 - a) * bg_color.red)   << 8;
-	      g = (int)(a * p[1] + (1 - a) * bg_color.green) << 8;
-	      b = (int)(a * p[2] + (1 - a) * bg_color.blue)  << 8;
-	      p += 4;
-	    }
-	  else
-	    {
-	      r = *p++ << 8;
-	      g = *p++ << 8;
-	      b = *p++ << 8;
+	      if (mask_img)
+		PUT_PIXEL (mask_img, x, y, *p > 0 ? *p : PIX_MASK_RETAIN);
+	      ++p;
 	    }
-	  PUT_PIXEL (ximg, x, y, lookup_rgb_color (f, r, g, b));
 	}
     }
 
@@ -10536,6 +10458,16 @@ webp_load (struct frame *f, struct image *img)
   /* Put ximg into the image.  */
   image_put_x_image (f, img, ximg, 0);
 
+    /* Same for the mask.  */
+  if (mask_img)
+    {
+      /* Fill in the background_transparent field while we have the
+	 mask handy.  Casting avoids a GCC warning.  */
+      image_background_transparent (img, f, (Emacs_Pix_Context)mask_img);
+
+      image_put_x_image (f, img, mask_img, 1);
+    }
+
   img->width = width;
   img->height = height;
 
@@ -12047,16 +11979,13 @@ svg_load_image (struct frame *f, struct image *img, char *contents,
     /* The wrapper sets the foreground color, width and height, and
        viewBox must contain the dimensions of the original image.  It
        also draws a rectangle over the whole space, set to the
-       background color, before including the original image.  This
-       acts to set the background color, instead of leaving it
-       transparent.  */
+       background color, before including the original image. */
     const char *wrapper =
       "<svg xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
       "xmlns:xi=\"http://www.w3.org/2001/XInclude\" "
       "style=\"color: #%06X; fill: currentColor;\" "
       "width=\"%d\" height=\"%d\" preserveAspectRatio=\"none\" "
       "viewBox=\"0 0 %f %f\">"
-      "<rect width=\"100%%\" height=\"100%%\" fill=\"#%06X\"/>"
       "<xi:include href=\"data:image/svg+xml;base64,%s\"></xi:include>"
       "</svg>";
 
@@ -12092,7 +12021,6 @@ svg_load_image (struct frame *f, struct image *img, char *contents,
     if (buffer_size <= snprintf (wrapped_contents, buffer_size, wrapper,
 				 foreground & 0xFFFFFF, width, height,
 				 viewbox_width, viewbox_height,
-				 background & 0xFFFFFF,
 				 SSDATA (encoded_contents)))
       goto rsvg_error;
 
@@ -12173,13 +12101,21 @@ svg_load_image (struct frame *f, struct image *img, char *contents,
 
   {
     /* Try to create a x pixmap to hold the svg pixmap.  */
-    Emacs_Pix_Container ximg;
+    Emacs_Pix_Container ximg, mask_img = NULL;
     if (!image_create_x_image_and_pixmap (f, img, width, height, 0, &ximg, 0))
       {
 	g_object_unref (pixbuf);
 	return false;
       }
 
+  if (!image_create_x_image_and_pixmap (f, img, width, height, 1,
+					   &mask_img, true))
+    {
+      image_destroy_x_image (ximg);
+      image_clear_image_1 (f, img, CLEAR_IMAGE_PIXMAP);
+      return false;
+    }
+
     init_color_table ();
 
     /* This loop handles opacity values, since Emacs assumes
@@ -12195,9 +12131,13 @@ svg_load_image (struct frame *f, struct image *img, char *contents,
 	    int blue    = *pixels++;
 
             /* Skip opacity.  */
-	    pixels++;
+	    int opacity = *pixels++;
 
 	    PUT_PIXEL (ximg, x, y, lookup_rgb_color (f, red << 8, green << 8, blue << 8));
+
+		if (mask_img)
+			PUT_PIXEL (mask_img, x, y, opacity > 0 ? opacity : PIX_MASK_RETAIN);
+
 	  }
 
 	pixels += rowstride - 4 * width;
@@ -12220,6 +12160,16 @@ svg_load_image (struct frame *f, struct image *img, char *contents,
 
     /* Put ximg into the image.  */
     image_put_x_image (f, img, ximg, 0);
+
+    /* Same for the mask.  */
+    if (mask_img)
+      {
+	/* Fill in the background_transparent field while we have the
+	   mask handy.  Casting avoids a GCC warning.  */
+	image_background_transparent (img, f, (Emacs_Pix_Context)mask_img);
+
+	image_put_x_image (f, img, mask_img, 1);
+      }
   }
 
   eassume (err == NULL);

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH] A small patch to make emacs support transparent image (png, webp, svg)
  2023-12-22  7:18 [PATCH] A small patch to make emacs support transparent image (png, webp, svg) Qiutum
@ 2023-12-22  8:25 ` Manuel Giraud via Emacs development discussions.
  2023-12-22  8:59   ` Qiutum
  2023-12-22  9:49 ` Stefan Kangas
  2023-12-22 12:13 ` Po Lu
  2 siblings, 1 reply; 6+ messages in thread
From: Manuel Giraud via Emacs development discussions. @ 2023-12-22  8:25 UTC (permalink / raw)
  To: Qiutum; +Cc: emacs-devel

[-- Attachment #1: Type: text/plain, Size: 175 bytes --]

Hi,

Thanks for your patch.  I have lightly tested it: it does seems to work
for PNG transparency but it completely breaks SVG rendering for me.
Here is before/after example:

[-- Attachment #2: before.png --]
[-- Type: image/png, Size: 42987 bytes --]

[-- Attachment #3: after.png --]
[-- Type: image/png, Size: 38586 bytes --]

[-- Attachment #4: Type: text/plain, Size: 18 bytes --]

-- 
Manuel Giraud

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] A small patch to make emacs support transparent image (png, webp, svg)
  2023-12-22  8:25 ` Manuel Giraud via Emacs development discussions.
@ 2023-12-22  8:59   ` Qiutum
  2023-12-22  9:43     ` Manuel Giraud via Emacs development discussions.
  0 siblings, 1 reply; 6+ messages in thread
From: Qiutum @ 2023-12-22  8:59 UTC (permalink / raw)
  To: Manuel Giraud; +Cc: emacs-devel


[-- Attachment #1.1: Type: text/plain, Size: 809 bytes --]

Hi,

Thanks for your check, may you send me the svg file, since the svg files I
checked don't show this problem.
If you read the code of src/image.c, you will find that the code do a lot
of things on the svg. So more check on the svg is necessory and welcome.

Here is a simple svg file that I have checked and the other image is the
screenshot on my computer (left is picture shown on kitty, right is the
picture shown on emacs). Maybe you could also help check this image.

Best regards,
Zhang

Manuel Giraud <manuel@ledu-giraud.fr> 于2023年12月22日周五 16:25写道:

> Hi,
>
> Thanks for your patch.  I have lightly tested it: it does seems to work
> for PNG transparency but it completely breaks SVG rendering for me.
> Here is before/after example:
> --
> Manuel Giraud
>

[-- Attachment #1.2: Type: text/html, Size: 1205 bytes --]

[-- Attachment #2: screenshots_171.png --]
[-- Type: image/png, Size: 354054 bytes --]

[-- Attachment #3: Example.svg --]
[-- Type: image/svg+xml, Size: 10009 bytes --]

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] A small patch to make emacs support transparent image (png, webp, svg)
  2023-12-22  8:59   ` Qiutum
@ 2023-12-22  9:43     ` Manuel Giraud via Emacs development discussions.
  0 siblings, 0 replies; 6+ messages in thread
From: Manuel Giraud via Emacs development discussions. @ 2023-12-22  9:43 UTC (permalink / raw)
  To: Qiutum; +Cc: emacs-devel

Qiutum <zh4710jj@gmail.com> writes:

> Hi,
>
> Thanks for your check, may you send me the svg file, since the svg files I
> checked don't show this problem.

It is a file in the Emacs source tree:
"etc/images/icons/hicolor/scalable/mimetypes/emacs-document.svg"

> If you read the code of src/image.c, you will find that the code do a lot
> of things on the svg. So more check on the svg is necessory and welcome.
>
> Here is a simple svg file that I have checked and the other image is the
> screenshot on my computer (left is picture shown on kitty, right is the
> picture shown on emacs). Maybe you could also help check this image.

Ok, maybe I'll check this one too.

Best regards,
-- 
Manuel Giraud



^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] A small patch to make emacs support transparent image (png, webp, svg)
  2023-12-22  7:18 [PATCH] A small patch to make emacs support transparent image (png, webp, svg) Qiutum
  2023-12-22  8:25 ` Manuel Giraud via Emacs development discussions.
@ 2023-12-22  9:49 ` Stefan Kangas
  2023-12-22 12:13 ` Po Lu
  2 siblings, 0 replies; 6+ messages in thread
From: Stefan Kangas @ 2023-12-22  9:49 UTC (permalink / raw)
  To: Qiutum, emacs-devel

Thanks, but please send this exact same email to bug-gnu-emacs@gnu.org
instead, so that we don't lose track of your patch.



^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] A small patch to make emacs support transparent image (png, webp, svg)
  2023-12-22  7:18 [PATCH] A small patch to make emacs support transparent image (png, webp, svg) Qiutum
  2023-12-22  8:25 ` Manuel Giraud via Emacs development discussions.
  2023-12-22  9:49 ` Stefan Kangas
@ 2023-12-22 12:13 ` Po Lu
  2 siblings, 0 replies; 6+ messages in thread
From: Po Lu @ 2023-12-22 12:13 UTC (permalink / raw)
  To: Qiutum; +Cc: emacs-devel

Qiutum <zh4710jj@gmail.com> writes:

> Recently, I've been reading the code of image.c and wondering how to
> make emacs support transparent image. Then I find that a small change
> on the src/image.c can make it worked (and wonder why this have not
> been solved since the solution is so simple).

Because the change is _not_ this simple, just deceptively so: the depth
of the mask image is not guaranteed to exceed 1, storing complete pixel
values into which is undefined behavior, but on your system saves the
alpha channel alone into the 8-bit pixmap created there.  In combination
with the deletion of the code blending the remainder of the image with
the frame's background and an X server configured such that the picture
format chosen by Emacs for the mask picture coincides with that used by
libpng, this results in the image being composited onto the frame by the
X server.  Such a house of cards is liable to collapse without a
moment's notice, so with good reason we have not settled on such a
solution.

At the minimum, it would be required for both mechanisms to be preserved
with the original used on systems incapable of alpha compositing in the
manner of XRender, and to make provisions for converting between the
image formats used by these image libraries and those of the X server in
the event they differ.  However, image.c is long overdue for a rewrite
to eliminate the crud that has accumulated over the course of decades,
particularly the subtle variations in behavior across different systems
which are addressed at many levels with unnavigable OS and window system
conditionals, and will probably stand in the way of correctly
implementing that feature in any case.



^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2023-12-22 12:13 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-12-22  7:18 [PATCH] A small patch to make emacs support transparent image (png, webp, svg) Qiutum
2023-12-22  8:25 ` Manuel Giraud via Emacs development discussions.
2023-12-22  8:59   ` Qiutum
2023-12-22  9:43     ` Manuel Giraud via Emacs development discussions.
2023-12-22  9:49 ` Stefan Kangas
2023-12-22 12:13 ` Po Lu

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).