Previously, the regression tests for OSS-Fuzz issues 62033 and 63296
used test case files directly from OSS-Fuzz. These files are invalid
in multiple ways because they have been generated by a fuzzer. This
commit replaces these files with ones that only expose the issue being
tested.
The first implementation of this property was just plain wrong. Looks
like this property isn't used a lot as I found the issue by reviewing
the code and not because of a specific image.
The test image is a 32x32 mosaic of alternating black and yellow pixels,
it was generated using this code:
Bitdepth 8
RCT 1
Width 32
Height 32
if W-WW-NW+NWW > -300
- Set -1000
- Set 900
This image is exactly the same as the previous one, excepted the RCT
transformation. It has been generated with:
Width 64
Height 64
RCT 29
Upsample 2
Bitdepth 10
if N > 300
- NE -6
- W 6
This image uses the modular encoding with a very simple prediction tree.
It also makes use of two features: upsampling (x2 factor) and a
non-standard bit depth (10 bits). The file has been generated on
https://jxl-art.surma.technology/ , with the following input:
Width 64
Height 64
Upsample 2
Bitdepth 10
if N > 300
- NE -6
- W 6
Reordering these calls allow us to ensure that all encoders are able to
return the size of the image before they are requested to decode the
whole bitmap.
This tests that we can successfully parse the "everything" TVG files,
which make use of every feature in TinyVG.
Test files taken from https://github.com/TinyVG/examples (MIT).
This just works at the moment after e19892a099, but if we ever do
the FIXME in ColorIndexingTransform::transform(), this test will
remind us to think of this case there too.
catdog-alert-13-alpha-used-false.webp is identical to
catdog-alert-13.web but with the byte at offset 0x2a changed from
0x10 to 0x00 -- that is, the bit in the VP8L header that stores
`is_alpha_used` is cleared.
See the commit message of e19892a099 for more information.
simple-vp8l-alpha-used-false.webp is a copy of simple-vp8l.webp,
with the byte at offset 0x18 changed from 0x10 to 0x00 -- that
is, the bit in the VP8L header that stores `is_alpha_used` is cleared.
We would already allocated a BGRx8888 instead of a BGRA8888 bitmap,
but keep actual alpha data in the `x` channel.
That lead to at least `image` still writing a PNG with an alpha channel.
So explicitly set the alpha channel to 0xff when is_alpha_used is false,
to make sure all consumers of decoded lossless webp data have behavior
consistent with other webp readers.
In practice, webp encoders usually don't write files that have
`is_alpha_used` set to false and then write actual alpha data to their
output. So this is rarely observable. However, for example for
lossy+ALPH webp files, the lossless webp used to store the ALPH channel
has `is_alpha_used` set to false and all channels but green are 0
(since the lossless green channel stores the alpha channel of a
lossy+ALPH webp). So if we dump such a bitmap to a standalone webp
file (e.g. with the temporary debugging code in fc3249a1ca),
then without this commit here, `image` would convert that webp to
a fully transparent webp, while other webp software would correctly
display the green image with opaque alpha.
Each secondary partition has an independent BooleanDecoder.
Their bitstreams interleave per macroblock row, that is the first
macroblock row is read from the first decoder, the second from the
second, ..., until it wraps around again.
All partitions share a single prediction state though: The second
macroblock row (which reads coefficients off the second decoder) is
predicted using the result of decoding the frist macroblock row (which
reads coefficients off the first decoder).
So if I understand things right, in theory the coefficient reading could
be parallelized, but prediction can't be. (IDCT can also be
parallelized, but that's true with just a single partition too.)
I created the test image by running
examples/cwebp -low_memory -partitions 3 -o foo.webp \
~/src/serenity/Tests/LibGfx/test-inputs/4.webp
using a cwebp hacked up as described in #19149. Since creating
multi-partition lossy webps requires hacking up `cwebp`, they're likely
very rare in practice. (But maybe other programs using the libwebp API
create them.)
Fixes#19149.
With this, webp lossy support is complete (*) :^)
And with that, webp support is complete: Lossless, lossy, lossy with
alpha, animated lossless, animated lossy, animated lossy with alpha all
work.
(*: Loop filtering isn't implemented yet, which has a minor visual
effect on the output. But it's only visible when carefully comparing
a webp decoded without loop filtering to the same decoded with it.
But it's technically a part of the spec that's still missing.
The upsampling of UV in the YUV->RGB code is also low-quality. This
produces somewhat visible banding in practice in some images (e.g.
in the fire breather's face in 5.webp), so we should probably improve
that at some point. Our JPG decoder has the same issue.)
I somehow added the wrong image here. 4.webp is the one described
by the comment in the test. Now test actually uses the image it
claims to use.
No behavior change.
The alpha channel of a lossy webp is always stored separately from
the (lossy) RGB data. Alpha is either compressed in a lossless webp
that stores just the alpha data, or it's stored completely
uncompressed. (But again, even if it's compressed, it's losslessly
compressed.)
This adds a test for uncompressed alpha, which I hadn't tested before.
It seems to work correctly, though :^)
I generated the test image by running:
~/Downloads/libwebp-1.3.0-mac-arm64/bin/cwebp \
-alpha_method 0 \
Tests/LibGfx/test-inputs/extended-lossless.webp \
-o Tests/LibGfx/test-inputs/extended-lossy-uncompressed-alpha.webp
This image covers two things that aren't covered by the existing
tests, and I found it useful for testing locally. The image's license
allows redistributing it, so add it as a test case.
WebP lossless files that use a color indexing transform with <= 16
colors use pixel bundling to pack 2, 4, or 8 pixels into a single pixel.
If the image's width doesn't happen to be an exact multiple of the
bundling factor, we need to:
1. Use ceil_div() instead of just dividing the width by the bundling
factor
2. Remember the original width and use it instead of computing
reduced width times bundling factor
This does these changes, and adds a simple test for it -- it at least
checks that the decoded images have the right size.
(I created these images myself in Photoshop, and used the same
technique as for Tests/LibGfx/test-inputs/catdog-alert-*.webp
to create images with a certain number of colors.)
For the test files, I opened Base/res/icons/catdog/alert.png in Adobe
Photoshop 2023, used Image->Mode->Index Color...->
Palette: Local (Perceptive) to reduce the number of colors to 13, 8, and
3 with transparency, and 2 without transparency, then converted it back
to Image->Mode->RGB Color (else it can't be saved as webp), then
File->Save a Copy... to save a WebP (mode lossless) for every palette
size.
The image is https://quakewiki.org/wiki/File:Qpalette.png in lossless
webp format with a color indexing transform.
I've created Qpalette.webp by running
examples/cwebp -z 0 ~/src/serenity/tmp.ppm -o Qpalette.webp
built at libwebp webmproject/libwebp@0825faa4c1 (without
png support, so I first ran
Build/lagom/image ~/Downloads/Qpalette.png -o tmp.ppm
to convert it from png to a format my cwebp binary could read).
This file also happens to explicitly set max_symbol, so it serves
as a test for that code path as well.