The ideal size is the size the user will display the image. Raster
formats should ignore this parameter, but vector formats can use
it to generate a bitmap of the ideal size.
The spec says "Decoders are not required to use this information in any
specified way" about this field, but that's probably a typo and belongs
in the previous section. At least, images in the wild look wrong
without this, for example
https://fjord.dropboxstatic.com/warp/conversion/dropbox/warp/en-us/dropbox/Integrations_4@2x.png?id=ce8269af-ef1a-460a-8199-65af3dd978a3&output_type=webp
Implementation-wise, this now copies both uncompressed and compressed
data to yet another buffer for processing the alpha, then does
filtering on that buffer, and then copies the filtered alpha data
into the final image. (The RGB data comes from a lossy webp.)
This is a bit wasteful and we could probably manage without that
local copy, but that'd make the code more convoluted, so this is
good enough for now, at least until we've added tests for this case.
No test, because I haven't yet found out how to create images in this
format.
Else, WebP files with a broken header just return "Decoding failed"
without any more details. This way, there's some debug logging with
more details.
Maybe we'll want to remove this again since it might lead to duplicate
error messages for files that have their error not in the header.
We'll see how this feels. (But most files don't have any errors, so
it's probably fine.)
It's up to callers of the ImageDecoderPlugin to honor loop_count().
The ImageDecoderPlugin doesn't have to look at it when decoding frames.
No behavior change.
Pixels will leave the lossy decoder with alpha set to 255.
The old code would be a no-op in that case.
No observable behavior change yet, since there still is no
decoder for lossy webp.
In practice, it looks like e.g. the animaged webp file on
https://mathiasbynens.be/demo/animated-webp has the header flag set,
because 2 of the frames have alpha, but they're composited on top of
the final bitmap, but the final bitmap isn't transparent there. So
that image still gets a useless alpha channel. Oh well.
Pure code move (except of removing `static` on the two public functions
in the new header), not behavior change.
There isn't a lot of lossy decoder yet, but it'll make implementing it
more convenient.
No behavior change.
Namely:
* Store compressed data in VP8Header
* Make the functions just take ReadonlyBytes instead of a Chunk
Having a function that takes a header and does decoding of the data
after the header isn't really necessary for VP8. For VP8L, it's needed
because the ALPH chunk stores VP8L data without the VP8L header.
But it's nice to make the functions consistent, and it's kind of a
nice structure.
No behavior change.
decode_webp_chunk_VP8() itself will only ever decode RGB data from a
lossy webp stream, but a separate ALPH chunk could add alpha data
later on. Let the function know if that will happen, so that it can
return a bitmap with an alpha channel if appropriate.
Since lossy decoding isn't implemented yet, no behavior change. But it
makes it a bit easier to implement lossy decoding in the future.
The one caller checked the chunk type, so the VERIFY() for that did
nothing.
The VERIFY() for vp8l data only being in files that start with
VP8L or VP8X chunks wasn't completely useless, but also not very
useful.
Remove the now-unused context parameter of decode_webp_image_data().
Most places used to call `context.error()` to report an error,
which would set the context's state to `Error` and then return an
`Error::from_string_literal()`.
This is somewhat elegant, but it doesn't work: Some functions this
code calls returns ErrorOr<>s that aren't created by `context.error()`,
and for these we wouldn't enter the error state.
Instead, manually check error-ness at the leaf entry functions of the
class:
1. Add a set_error() helper for functions returning bool
2. In the two functions returning ErrorOr<>, awkwardly check the error
manually. If this becomes a very common pattern, maybe we can add
a `TRY_WITH_HANDLER(expr, error_lambda)` which would invoke a
lambda on error. We could use that here to set the error code.
No real behavior change (except we enter the error state more often
when something goes wrong).
Instead of ImageData having an Optional<Chunk> for the image data,
have it have a Chunk, and give the context an Optional<ImageData>.
This allows sharing a single code path for checking that either the
main image or an animation frame has a main image data chunk, and
that an alpha chunk is only present with a lossy main image data
chunk.
No behavior change.
A compressed ALPH chunk is a lossless webp bitstream, but without
the 5 byte "header" that stores width, height, is-alpha-channel-used
(it never is for an ALPH chunk since the ALPH chunk gets the alpha
data out of the lossless webp's green channel), and version fields.
For that reason, this cuts decode_webp_chunk_VP8L() into the
header-reading part and the remaining part, so that the remaining
part can be called by the ALPH reading routine.
Lossy webp files with a (losslessly) compressed alpha channel can
be found in the wild. Since we can't decode lossy webp data yet,
change the `#if 0` in decode_webp_chunk_VP8() to `#if 1` to test this.
ALPH chunks are only used to give lossy webp frames an alpha channel,
and lossy decompression isn't implemented yet. So this can currently
never be hit in practice -- but for debugging and testing, I put in
some code behind `#if 0` for now that fake-decompresses a lossy webp
frame by returning an empty bitmap.
But this also doesn't implement compressed ALPH chunks yet, and I
couldn't find any lossy-webp-with-alpha files that use uncompressed
alpha channels. So the code here isn't really tested.
If someone comes along who wants to implement lossy webp decoding,
they now only need to implement decode_webp_chunk_VP8() and everything
might Just Work.
It also makes it possible to implement alpha chunk decoding before
implementing lossy decoding (by making decode_webp_chunk_VP8()
return an empty black bitmap for testing).
That way, animated and non-animated webp files use the same code path
to decode images. That will make it easier to add handling for lossy
decompression and for alpha chunk handling.
No behavior change.
With this, lossless animated webp files work :^)
(Missing: Loop count handling is not yet implemented, and alpha blending
between frames isn't done in linear space.)
read_webp_first_chunk() sensibly assumes that if decode_webp_header()
succeeds, there are at least sizeof(WebPFileHeader) bytes available.
But if the file size in the header was less than the size of the header,
decode_webp_header() would truncate the data to less than that and
happily report success. Now it no longer does that.
Found by clusterfuzz:
https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=57843&sort=-opened&can=1&q=proj%3Aserenity
Prefix code decoding seems to work fairly well and produces a ton of
log output with `#define WEBP_DEBUG 1`, so remove the log lines.
(If needed it's always possible to just locally revert this commit.)
No behavior change, since WEBP_DEBUG isn't usually defined.
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.)
See the lengthy comment added in this commit for details.
With this, the webp lossless decoder is feature complete :^)
(...except for bug fixes and performance improvements, as always.)