-
-
Notifications
You must be signed in to change notification settings - Fork 3.2k
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
LibGfx/WebP+image: Add support for writing color index transforms, and fix an encoding bug #24483
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
In an early version of the huffman writing code, we always used 8 bits here, and the comments still reflected that. Since we're now always writing only as many bits as we need (in practice, still almost always 8), the comments are misleading.
This way, it can be used to write entropy-coded-images too, which will be used when writing transforms.
No behavior change, but this makes it easy to correctly set this flag when adding an indexing transform: Opacity then needs to be determined based on if colors in the color table have opacity, not if the indexes into the color table do. With this struct, only the first time something sets opacity is honored, giving us those semantics.
If an image has 256 or fewer colors, WebP/Lossless allows storing the colors in a helper image, and then storing just indexes into that helper image in the main image's green channel, while setting r, b, and a of the main image to 0. Since constant-color channels need to space to store in WebP, this reduces storage needed to 1/4th (if alpha is used) or 1/3rd (if alpha is constant across the image). If an image has <= 16 colors, WebP lossless files pack multiple color table indexes into a single pixel's green channel, further reducing file size. This pixel packing is not yet implemented in this commit. GIFs can store at most 256 colors per frame, so animated gifs often have 256 or fewer colors, making this effective when transcoding gifs. (WebP also has a "subtract green" transform, which can be used to need to store just a single channel for grayscale images, without having to store a color table. That's not yet implemented -- for now, we'll now store grayscale images using this color indexing transform instead, which wastes to storage for the color table.) (If an image has <= 256 colors but all these colors use only a single channel, then storing a color table for these colors is also wasteful, at least if the image has > 16 colors too. That's rare in practice, but maybe we can add code for it later on.) (WebP also has a "color cache" feature where the last few used colors can be referenced using very few bits. This is what the webp spec says is similar to palettes as well. We don't implement color cache writing support yet either; maybe it's better than using a color indexing transform for some inputs.) Some numbers on my test files: sunset-retro.png: No performance or binary size impact. The input quickly uses more than 256 colors. giphy.gif (184k): 4.1M -> 3.9M, 95.5 ms ± 4.9 ms -> 106.4 ms ± 5.3 ms Most frames use more than 256 colors, but just barely. So fairly expensive runtime wise, with just a small win. (See comment on SerenityOS#24454 for the previous 4.9 MiB -> 4.1 MiB drop.) 7z7c.gif (11K): 118K -> 40K Every frame has less than 256 colors (but more than 16, so no packing), and so we can cut filesize roughly to 1/3rd: We only need to store an index per channel. From 10.7x as large as the input to 3.6x as large.
github-actions
bot
added
the
👀 pr-needs-review
PR needs review from a maintainer or community member
label
May 29, 2024
If an image has <= 16 colors, WebP lossless files pack multiple color table indexes into a single pixel's green channel, further reducing file size. This adds support for that. My current test files all have more than 16 colors. For a 16x16 black-and-white bitmap that contains a little smiley face in the middle, this reduces the output size from 128B to 54B.
nico
force-pushed
the
webp-color-index
branch
2 times, most recently
from
May 29, 2024 22:50
72d54f4
to
ca5de72
Compare
gmta
reviewed
May 31, 2024
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
nico
force-pushed
the
webp-color-index
branch
2 times, most recently
from
May 31, 2024 19:38
a0ea09d
to
6e04463
Compare
Storing a number n needs floor(log2(n) + 1) bits, not ceil(log2(n)). (The two expressions are identical except for when n is a power of 2.) Serendipitously covered by the indexed color transform tests in this PR.
Else UBSan complains about the color indexing test in this PR.
This is mainly useful for testing.
gmta
approved these changes
May 31, 2024
gmta
added
✅ pr-maintainer-approved-but-awaiting-ci
PR has been approved by a maintainer and can be merged after CI has passed
and removed
👀 pr-needs-review
PR needs review from a maintainer or community member
labels
May 31, 2024
github-actions
bot
removed
the
✅ pr-maintainer-approved-but-awaiting-ci
PR has been approved by a maintainer and can be merged after CI has passed
label
May 31, 2024
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
If an image has 256 or fewer colors, WebP/Lossless allows storing
the colors in a helper image, and then storing just indexes into that
helper image in the main image's green channel, while setting
r, b, and a of the main image to 0.
Since constant-color channels need to space to store in WebP,
this reduces storage needed to 1/4th (if alpha is used) or 1/3rd
(if alpha is constant across the image).
If an image has <= 16 colors, WebP lossless files pack multiple
color table indexes into a single pixel's green channel, further
reducing file size.
GIFs can store at most 256 colors per frame, so animated gifs
often have 256 or fewer colors, making this effective when
transcoding gifs.
This adds support for writing that color index transform.
It also fixes a general encoding bug that the tests for this transform
exposed.
Some numbers on my test files:
sunset-retro.png: No performance or binary size impact. The input
quickly uses more than 256 colors.
giphy.gif (184k): 4.1M -> 3.9M, 95.5 ms ± 4.9 ms -> 106.4 ms ± 5.3 ms
Most frames use more than 256 colors, but just barely. So fairly
expensive runtime wise, with just a small win.
(See comment on #24454 for the previous 4.9 MiB -> 4.1 MiB drop.)
7z7c.gif (11K): 118K -> 40K
Every frame has less than 256 colors (but more than 16, so no packing),
and so we can cut filesize roughly to 1/3rd: We only need to store an
index per channel. From 10.7x as large as the input to 3.6x as large.
Also add an option to
image
to choose which transforms are used.(Even though only one is supported so far, that flag supports all four
already.)
animation
doesn't have this flag yet; maybe I'll add that later.