The idea is that we'll have one type for each tag type.
For now, this treats all tag types as unknown, but it puts most
of the infrastructure for reading tags in place.
This happened because the reader was incrementing the byte index of it
after each read of a byte from the Span, so by the end of the frame
method, we could be at the end of the mapped file, so the next call
on the same decoder will just resume from that point and will be quickly
out of boundary.
To ensure this doesn't happen we only set the bitmap to m_context member
at the end of the method, and call to that method again will just give
the already-generated bitmap.
In case of setting the bitmap as volatile, we test for that case and
re-generate a reader to read the frame again correctly.
When trying to figure out the correct implementation, we now have a very
strong distinction on plugins that are well suited for sniffing, and
plugins that need a MIME type to be chosen.
Instead of having multiple calls to non-static virtual sniff methods for
each Image decoding plugin, we have 2 static methods for each
implementation:
1. The sniff method, which in contrast to the old method, gets a
ReadonlyBytes parameter and ensures we can figure out the result
with zero heap allocations for most implementations.
2. The create method, which just creates a new instance so we don't
expose the constructor to everyone anymore.
In addition to that, we have a new virtual method called initialize,
which has a per-implementation initialization pattern to actually ensure
each implementation can construct a decoder object, and then have a
correct context being applied to it for the actual decoding.
Originally I simply thought that passing file paths is quite OK, but as
Linus pointed to, it turned out that passing file paths to ensure some
files are able to be decoded is awkward because it does not work with
images being served over HTTP.
Therefore, ideally we should just use the MIME type as an optional
argument to ensure that we can always fallback to use that in case
sniffing for the correct image type has failed so we can still detect
files like with the TGA format, which has no magic bytes.
These are currently being implicitly including by FixedPoint.h by way of
Format.h. The former will soon be removed from the latter, which would
otherwise cause a compile error in these files.
Because TGA images don't have magic bytes as a signature to be detected,
instead assume a sequence of ReadonlyBytes is a possible TGA image only
if we are given a path so we could check the extension of the file and
see if it's a TGA image.
When we know the path of the file being loaded, we will try to first
check its extension, and only if there's no match to a known decoder,
based on simple extension lookup, then we would probe for other formats
as usual with the normal sniffing method.
RLE is an old technique being used for decades, as is known as
Run-Length-Encoding, which means that for repeating sequence of bytes,
we keep an indicator for the length of the sequence and only one sample
of it, to save storage space.
GIMP can generate lossless-compressed TGA images, with RLE compression
being used. It means that for a compressed image, the data is no longer
arranged in sequence of pixels, but a sequence of pixel packets.
There are two possible pixel packets:
- RLE packets, which are encoded with one byte for indicating the
run-length and another one pixel (3 bytes for TrueColor pixel), so
essentially in runtime, the TGA decoder will use the length to plot
the same pixel in multiple pixels of the output pixel bitmap.
- Raw packets, which are encoded with one byte as indicator for the
length of the whole pixel sequence and N-length pixel sequence
afterwards.
This is not used for any sort of compression by the TGA format, but
still needed to be supported for full compatibility with TGA images
that uses the RLE compression.
GIMP allows a user to export a TGA image with either of these possible
orientations, so we can easily support it by looking at the X origin and
Y origin values to determine where to put the pixels in the bitmap.
This defines all the OpenType opcodes/instructions from the
specification:
https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions
Each instructions has mnemonic and a range of possible opcodes (as some
of the bits are pretty much immediate value flags).
There's a little helper Instruction struct for accessing the flags and
any associated data (in the case of PUSH instructions).
Then the InstructionStream provides a way of iterating over all the
instructions in some bytes.
This moves the CSS gradient painting to the painter creating:
- Painter::fill_rect_with_linear_gradient()
- Painter::fill_rect_with_conic_gradient()
- Painter::fill_rect_with_radial_gradient()
This has a few benefits:
- The gradients can now easily respect the painter scale
- The Painter::fill_pixels() escape hatch can be removed
- We can remove the old fixed color stop gradient code
- The old functions are now just a shim
- Anywhere can now easily use this gradient painting code!
This only leaves the color stop resolution in LibWeb (which is fine).
Just means in LibGfx you have to actually specify color stop positions.
(Also while here add a small optimization to avoid generating
excessively long gradient lines)
DeprecatedFlyString relies heavily on DeprecatedString's StringImpl, so
let's rename it to A) match the name of DeprecatedString, B) write a new
FlyString class that is tied to String.
While this subtable ID is supposed to be deprecated, it is used heavily
in PDF files.
It supports mapping one or two-byte values, with quite a large list of
encodings to tell you which one to expect.
For our use case, we ignore this encoding ID and just pick the first
subtable with this platform ID. Unsupported encodings will get caught
by Subtable::glyph_id_for_code_point() anyway.
This trims the input bytes to the profile size stored in the file.
Alternatively, we could reject files where the stored size doesn't
match the handed in size. But ICC profiles can be embedded in other
files, and those could conceivably pad the ICC profile data some.
There's a small, old-timey list of platforms in the spec, but as far
as I can tell nobody is using additional platforms on Linux or Android
or what. So let's try going with an enum class instead of the FourCC
machinery for now.
Namely:
- preferred CMM type
- device manufacturer
- device model
- profile creator
These all have in common that they can take arbitrary values, so I added
a FourCC class to deal with them, instead of using an enum class.
I made distinct types for each of them, so that they aren't accidentally
mixed up.
These flags are always 0 in practice in all profiles I've seen so far,
but hey, probably nice to dump them anyways.
And hey, it's just 86 lines to print 4 bits.
Always computing computing the md5 takes some time, but most
icc profiles are small. So that's probably fine.
If this ends up being a perf problem in the future, or if it ends up
rejecting tons of embedded proiles from images, we can row it back.
But let's see if we can get away with this first.
I checked that they are zero for all profiles in Compact-ICC-Profiles
and for all .icc files in /Library/ColorSync and
/System/Library/ColorSync on my Mac (running macOS 12.6.2).