-
-
Notifications
You must be signed in to change notification settings - Fork 646
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
spngsave: fix 8bpp palette save with transparency #2808
Conversation
Looking at this again, I was puzzled by the way the alpha was split off. Looking at spng.c: https://github.com/randy408/libspng/blob/master/spng/spng.c#L3887-L3890 It looks like the indexes of How about somethnig like: VipsPel *p = (VipsPel *) VIPS_IMAGE_ADDR( im_palette, 0, 0 );
struct spng_plte_entry *entry = &plte.entries[0];
uint8_t *type3_alpha = &trns.type3_alpha[0];
gboolean has_transparency = FALSE;
for( i = 0; i < palette_count; i++ ) {
entry[i].red = p[0];
entry[i].green = p[1];
entry[i].blue = p[2];
type3_alpha[i] = p[3];
if( p[3] != 255 )
has_transparency = TRUE;
p += 4;
}
plte.n_entries = palette_count;
if( has_transparency )
trns.n_type3_entries = palette_count; |
The code you linked is for loading PNG images ( Especially the |
Oh, I read your code wrong. The |
FWIW, there are no visual differences with both approaches from my tests. Details
Where #!/usr/bin/env python3
import sys
import pyvips
im = pyvips.Image.new_from_file(sys.argv[1])
hist = im[3].hist_find()
def band_stats(hist, val):
mask = (pyvips.Image.identity() > val) / 255
return (hist * mask).avg() * 256
total = im.width * im.height
alpha0 = band_stats(hist, 0)
alpha1 = band_stats(hist, 254)
# Metrics on how many pixels are opaque (no alpha),
# translucent (some alpha), and transparent (100% alpha)
print(f"opaque = {alpha1}")
print(f"translucent = {alpha0 - alpha1}")
print(f"transparent = {total - alpha0}") Only the file size can increase a bit if there are not so many transparency entries. See for example:
|
It was all mixed up. We don't need to call colourspace -- this is done for us by the SAVEABLE system. Fixes "vips identity x.png" error, see: #2808 (comment)
I fixed the "no route" error, spngsave was messed up. |
Ah I think I found it. I tried adding: printf( "%d) %d %d %d %d\n",
i, p[0], p[1], p[2], p[3] ); To the loop that copies the palette from libimagequant to spng, and it seems it always puts the transparent palette entries first. That's why PNG write works --- the indexes will always match in this case. I used this test prog:
The generated palette looks like this:
If you swap "more" for "less" on line 2 you can change the ordering of the transparent pixels in the file, but the generated palette keeps the transparent elements first. I tried with quantizr and it seems to have the same behaviour. Is this guaranteed to always be the case? I can't see it in the docs, but if it is, then you're right, we can save a smaller transparency table. @DarthSim can I ping you on this? Is this palette ordering a library feature? |
Quantizer sorts the palette by the alpha chanel. AFAIK LIQ does the same. |
OK, then let's merge, my worries are unfounded. |
When I investigated issue lovell/sharp#3395, I noticed this assertion failure:
(added in commit 2af2ca5) This can also reproduced with: vips identity bw.png
vips linear bw.png alpha.png 1 50
vips colourspace bw.png rgb.png srgb
vips bandjoin "rgb.png alpha.png" x3.png[palette] Thresholding the alpha channel seems to fix this. --- a/libvips/foreign/spngsave.c
+++ b/libvips/foreign/spngsave.c
@@ -387,7 +387,7 @@ vips_foreign_save_spng_write( VipsForeignSaveSpng *spng, VipsImage *in )
spng->Q,
spng->dither,
spng->effort,
- FALSE ) )
+ TRUE ) )
return( -1 );
/* PNG is 8-bit index only. And the output looks OK after that. But I'm not sure if this is the correct solution. Any ideas? |
I would not do this. Alpha threshold works when the resultant format supports binary transparency only (transparent/opaque). PNG on the other hand supports semi-transparent pixels, and thresholding the alpha channel may reduce transparency quality dramatically. It seems like I was wrong and LIQ doesn't sort the palette by alpha 😓 I prepared a PR that fixes the issue: #3074 (BTW, everything works fine with Quantizr 😉) |
Great, thanks! That would correspond to what is done in the libpng saver. libvips/libvips/foreign/vipspng.c Lines 1211 to 1213 in df9f5d3
|
Test case: