google / wuffs

Wrangling Untrusted File Formats Safely
Other
4.06k stars 129 forks source link

Decode PNG with gray+alpha as 2 channels (i.e. YA not BGRA) #143

Closed b-stage closed 3 months ago

b-stage commented 3 months ago

Is there a way to decode a PNG with gray+alpha channels (png color type 4) as a format that also has just 2 channels?

Currently for me, with such a PNG file, the pixel format returned by decode_image_config is 0x81008888 (WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL) but I would expect 0x21000008 (WUFFS_BASE__PIXEL_FORMAT__YA_NONPREMUL). Trying to enforce such a YA pixel format makes the decoder return "#base: unsupported pixel swizzler option". I think this was mentioned before here before but I don't see it being addressed.

Here's an example PNG: https://raw.githubusercontent.com/libharu/libharu/master/demo/pngsuite/basn4a08.png

nigeltao commented 3 months ago

I think this was mentioned before here before but I don't see it being addressed.

Earlier in that same discussion, I talked about "3x16 RGB" but the same applies to "2x8 YA":

Wuffs' pixel swizzler supports converting from pixel format S to pixel format D for a variety of S and D pixel formats, but it's not the full set of all possible pairs of pixel formats... the former [3x16 RGB] seems rare enough in practice that it wasn't worth dedicated code paths.

There's a super-linear (multiplicative in number-of-supported-S-formats and number-of-supported-D-formats) amount of code for pixel format conversion. There's a cost to Wuffs presenting PNG color type 4 as WUFFS_BASE__PIXEL_FORMAT__YA_NONPREMUL instead of WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL.

On the other hand, it's been maybe 15 years since I wrote my first PNG decoder, and I can't remember ever seeing a YA (color type 4) PNG image in the wild, other than these files in the pngsuite:

pngsuite/basi4a08.png
pngsuite/basi4a16.png
pngsuite/basn4a08.png
pngsuite/basn4a16.png
pngsuite/bgai4a08.png
pngsuite/bgai4a16.png
pngsuite/bgbn4a08.png
pngsuite/bggn4a16.png

Do you have examples of real-world YA PNG images, outside of pngsuite?

But also, can you elaborate on why you want to decode to YA instead of to BGRA? What do you do with the pixel buffer after you've decoded the PNG?

b-stage commented 3 months ago

I'm using an old tool called Paint Shop Pro 7.04 to edit PNG files and that tool naturally writes a color type 4 type PNG when setting the edited image to gray scale and having alpha transparency. Another case I found are png optimizer tools. Both zopflipng and pngout will choose color type 4 when the image has only grayscale colors and alpha transparency (and the total number of unique color/alpha pixels is more than 256, otherwise they switch to palette indexing).

For us we use PNGs as texture files for a video game where we'd like to treat a 2 color channel file as a 2 color channel texture loaded onto the GPU to match the shader materials we make and to save memory. We used stb_image until now whose stbi_info_from_memory function returned 2 as the number of color components for a YA PNG file which worked great. Performance of wuffs is extremely compelling so I'd really like to switch :-)

Until just now I thought there's no way for me with wuffs to know the underlying color type of the PNG but I just realized I can read it via png_decoder.private_impl.f_color_type (knowing it might break in a future update). So maybe for now I can just manually convert to 2 channels after decoding but before passing it onto the GPU when I see color type 4.

nigeltao commented 3 months ago

You shoud now be able to decode to WUFFS_BASE__PIXEL_FORMAT__YA_NONPREMUL, if you're using release/c/wuffs-unsupported-snapshot.c.

If you could confirm that that works for you, I'm happy to roll a new release/c/wuffs-v0.4.c alpha-version.

Out of curiosity... Do your shaders actually interpret the 2-channel texture as (gray, alpha) or is it something else - I don't know, (something, specularity)?? - and it's just that the only way to get a 2-channel PNG is to use PNG color type 4?

b-stage commented 3 months ago

Thank you so much for the quick update! I can confirm decoding to WUFFS_BASE__PIXEL_FORMAT__YA_NONPREMUL works perfectly now, while requesting WUFFS_BASE__PIXEL_FORMAT__RGBA_NONPREMUL still works just like it did before.

We have a somewhat flexible system for 3D meshes where images with 1, 2, 3 or 4 channels can be assigned to as many monochromatic texture parameters as there are channels (i.e. roughness, metallic, ambience occlusion, ...) via data definitions that are set along the mesh and the texture paths. It's part of a game that supports modding (user generated content) so it tries to suit as many workflows and tools as possible. So a gray+alpha PNG doesn't necessarily end up as that on screen.

nigeltao commented 3 months ago

I have rolled a new release/c/wuffs-v0.4.c version.