Commit graph

12 commits

Author SHA1 Message Date
Nico Weber
cf4bad31f0 LibGfx/WebPWriter: Add debug logging for ANMF chunks 2024-05-14 13:43:03 -04:00
Nico Weber
f506818052 LibGfx/WebPWriter: Support animations with transparent pixels
Once we see a frame with transparent pixels, we now toggle the
"has alpha" bit in the header.

To not require a SeekableStream opened for reading, we now pass the
unmodified original flag bit to WebPAnimationWriter.
2024-05-11 15:43:02 -04:00
Nico Weber
3a4e0c2804 LibGfx+Utilities: Add animation utility, make it write animated webps
The high-level design is that we have a static method on WebPWriter that
returns an AnimationWriter object. AnimationWriter has a virtual method
for writing individual frames. This allows streaming animations to disk,
without having to buffer up the entire animation in memory first.
The semantics of this function, add_frame(), are that data is flushed
to disk every time the function is called, so that no explicit `close()`
method is needed.

For some formats that store animation length at the start of the file,
including WebP, this means that this needs to write to a SeekableStream,
so that add_frame() can seek to the start and update the size when a
frame is written.

This design should work for GIF and APNG writing as well. We can move
AnimationWriter to a new header if we add writers for these.

Currently, `animation` can read any animated image format we can read
(apng, gif, webp) and convert it to an animated webp file.

The written animated webp file is not compressed whatsoever, so this
creates large output files at the moment.
2024-05-11 15:43:02 -04:00
Nico Weber
27cc9e7386 LibGfx/WebPWriter: Write VP8X chunk header and bytes at once
For VP8L it arguably makes sense to separate the two, but for
fixed-size chunk like VP8X it doesn't.

No behavior change.
2024-05-11 15:43:02 -04:00
Nico Weber
d988b6facc LibGfx/WebPWriter+TestImageWriter: Fix bugs writing VP8X header
Two bugs:

1. Correctly set bits in VP8X header.
   Turns out these were set in the wrong order.

2. Correctly set the `has_alpha` flag.

Also add a test for writing webp files with icc data. With the
additional checks in other commits in this PR, this test catches
the bug in WebPWriter.

Rearrange some existing functions to make it easier to write this test:

* Extract encode_bitmap() from get_roundtrip_bitmap().
  encode_bitmap() allows passing extra_args that the test uses to pass
  in ICC data.
* Extract expect_bitmaps_equal() from test_roundtrip()
2024-05-08 16:34:11 +02:00
Nico Weber
25be8f8ae7 LibGfx/WebPWriter: Add some debug logging 2024-05-08 16:34:11 +02:00
Nico Weber
51fc51d0b5 LibGfx/WebPWriter: Use one-element huffman tree for opaque images
That way, we can write 0 instead of 8 bits for every alpha byte.
Reduces the size of sunset-retro.png when saved as a webp file
from 3 MiB to 2.25 MiB, without affecting encode speed.

Once we use CanonicalCodes we'll get this for free for all channels,
but opaque images are common enough that it feels worth it to do this
before then.
2024-05-07 11:13:01 -04:00
Nico Weber
7d93f7fc99 LibGfx/WebPWriter: Correctly fill in alpha_is_used hint 2024-05-07 11:13:01 -04:00
Nico Weber
0e2e7cd1be LibGfx/WebPWriter: Rename alpha_hint to alpha_is_used_hint
No behavior change.
2024-05-07 11:13:01 -04:00
Nico Weber
5b335d51e1 LibGfx/WebPWriter: Implement ICC color profile data writing
Most of this code (VP8X chunk writing) will be used for writing
animated webp files too.
2024-05-07 11:11:41 -04:00
Nico Weber
d0a2cf2dce LibGfx/WebPWriter: Implement basic lossless webp writing
This doesn't use any transforms yet (in particular not the predictor
transform), and doesn't do anything else that actually compresses the
data.

It also give all 256 values code length 8 unconditionally. This means
the huffman trees are not data-dependent at all and provide no
compression. It also means we can just write out the image data
unmodified.

So the output is fairly large. But it _is_ a valid lossless webp file.

Possible follow-ups, to improve compression later:
1. Use actual byte distributions to create huffman trees, to get
   huffman compression.
2. If the distribution has just 1 element, write a simple code length
   code (that way, images that have alpha 0xff everywhere need to store
   no data for alpha).
3. Add backref support, to get full deflate(ish) compression of pixels.
4. Add predictor transform.
5. Consider writing different sets of prefix codes for every 16x16 tile
   (by writing a meta prefix code image).

(It might be nice to make the follow-ups optional, so that this can also
be used as a webp example file generator.)
2024-05-06 17:32:19 +02:00
Nico Weber
d04f9cf3c8 LibGfx+image: Add scaffolding for writing webp files 2024-05-06 17:32:19 +02:00