1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-16 10:07:38 +00:00
Commit graph

457 commits

Author SHA1 Message Date
Nico Weber
e269526020 LibGfx/PNM: Remove two fixmes
bab2113ec1 made read_whitespace() return ErrorOr, which makes this
easy to do.

(7cafd7d177, which added the fixmes, landed slightly after bab2113ec1,
so not quite sure why it wasn't like this immediately. Maybe commit
order got changed during review; both commits were in #17831.)

No behavior change.
2024-02-02 08:26:40 +00:00
Nico Weber
1dfd68c798 LibGfx/JPEGWriter: Make it possible to write CMYKBitmaps
We always store CMYK data as YCCK, for two reasons:

1. If we ever want to do subsampling, then doing 2111 or
   2112 makes sense with YCCK, while it doesn't make sense
   if we store CMYK directly.
2. It forces us to write a color transform header. With a color
   transform header, everyone agrees that the CMYK channels should
   be stored inverted, while without it behavior between decoders
   is inconsistent. (We could write an explicit  color transform header
   for CMYK too though, but with YCCK it's harder to forget since the
   output will look wrong everywhere without it.)

initialize_mcu() grows a full CMYKBitmap override. Some of the
macroblock traversal could probably shared with some kind of
for_all_macroblocks() type function in the future, but the color
conversion math is different enough that this should be a separate
function.

Other than that, we pass around a mode parameter and make a few fuctions
write 4 instead of 3 channels, and that's it.

We use the luminance quantization and huffman tables for the K
channel.
2024-02-02 07:19:18 +01:00
Nico Weber
e8788d4023 LibGfx/JPEGWriter: Move image data writing into new add_image() function
No behavior change.
2024-02-02 07:19:18 +01:00
Nico Weber
e449dba85b LibGfx/JPEGWriter: Move header writing into new add_headers() function
No behavior change.
2024-02-02 07:19:18 +01:00
Nico Weber
4e637fa1d2 LibGfx/JPEGWriter: Pass IntSize instead of Bitmap to add_frame_header()
No behavior change.
2024-02-02 07:19:18 +01:00
Nico Weber
38526414b0 LibGfx/JPEGWriter: Add a named constant in add_scan_header()
No behavior change.
2024-02-02 07:19:18 +01:00
Nico Weber
4a8e7f44dc LibGfx/JPEGWriter: Add a named constant in add_frame_header()
No behavior change.
2024-02-02 07:19:18 +01:00
Nico Weber
ad7d25f089 LibGfx/JPEGWriter: Make vertical_macroblocks a local
It's only used in one function.

No behavior change.
2024-02-02 07:19:18 +01:00
Nico Weber
8964a52fe0 LibGfx/JPEGWriter: Use ceil_div()
No behavior change.
2024-02-02 07:19:18 +01:00
Nico Weber
69964e10f4 LibGfx+Tests: Improve calculation of restart interval
JPEGs can store a `restart_interval`, which controls how many
minimum coded units (MCUs) apart the stream state resets.
This can be used for error correction, decoding parts of a jpeg
in parallel, etc.

We tried to use

    u32 i = vcursor * context.mblock_meta.hpadded_count + hcursor;
    i % (context.dc_restart_interval *
         context.sampling_factors.vertical *
         context.sampling_factors.horizontal) == 0

to check if we hit a multiple of an MCU.

`hcursor` is the horizontal offset into 8x8 blocks, vcursor the
vertical offset, and hpadded_count stores how many 8x8 blocks
we have per row, padded to a multiple of the sampling factor.

This isn't quite right if hcursor isn't divisible by both
the vertical and horizontal sampling factor. Tweak things so
that they work.

Also rename `i` to `number_of_mcus_decoded_so_far` since that
what it is, at least now.

For the test case, I converted an existing image to a ppm:

    Build/lagom/bin/image -o out.ppm \
        Tests/LibGfx/test-inputs/jpg/12-bit.jpg

Then I resized it to 102x77px in Photoshop and saved it again.
Then I turned it into a jpeg like so:

    path/to/cjpeg \
        -outfile Tests/LibGfx/test-inputs/jpg/odd-restart.jpg \
        -sample 2x2,1x1,1x1 -quality 5 -restart 3B out.ppm

The trick here is to:

a) Pick a size that's not divisible by the data size width (8),
   and that when rounded to a block size (13) still isn't divisible
   by the subsample factor -- done by picking a width of 102.
b) Pick a huffman table that doesn't happen to contain the bit
   pattern for a restart marker, so that reading a restart marker
   from the bitstream as data causes a failure (-quality 5 happens
   to do this)
c) Pick a restart interval where we fail to skip it if our calculation
   is off (-restart 3B)

Together with #22987, fixes #22780.
2024-01-30 14:50:43 +01:00
Nico Weber
1ed9e597a9 LibGfx/JPEGLoader: Use 1 / sqrt(8) instead of rsqrt(8)
At least on arm64, rsqrt(8) has noticeably worse precision:
https://github.com/SerenityOS/serenity/issues/22739#issuecomment-1912909835
2024-01-30 10:02:33 +01:00
Nico Weber
6971ba35d5 LibGfx+Tests: Support grayscale jpegs with 2x2 sampling and MCU reset
Non-interleaved files always have an MCU of one data unit.

(A "data unit" is an 8x8 tile of pixels, and an "MCU" is a
"minium coded unit", e.g. 2x2 data units for luminance and
1 data unit each for Cr and Cb for a YCrCb image with
4:2:0 subsampling.)

For the test case, I converted an existing image to a ppm:

    Build/lagom/bin/image -o out.ppm \
        Tests/LibGfx/test-inputs/jpg/12-bit.jpg

Then I converted it to grayscale and saved it as a pgm in Photoshop.
Then I turned it into a weird jpeg like so:

    path/to/cjpeg \
        -outfile Tests/LibGfx/test-inputs/jpg/grayscale_mcu.jpg \
        -sample 2x2 -restart 3 out.pgm

Makes 3 of the 5 jpegs failing to decode at #22780 go.
2024-01-30 05:35:22 +01:00
Nico Weber
002eb0ad03 LibGfx/JPEGLoader: Use ceil_div
No behavior change.
2024-01-29 13:33:10 -05:00
Nico Weber
393fb1f7b9 LibGfx/JPEGLoader: Pass SamplingFactors instead of Component to function
That's all this function reads from Component.

Also rename from validate_luma_and_modify_context() to
validate_sampling_factors_and_modify_context().

No behavior change.
2024-01-29 13:29:44 -05:00
Nico Weber
b37f3c86fd LibGfx/WebPLoaderLossless: Fix grammar-o in comment 2024-01-29 09:12:06 -05:00
Nico Weber
d89e42902e LibGfx/JPEGLoader: Extract inverse_dct_8x8() function
No behavior change.
2024-01-27 10:21:33 +00:00
Nico Weber
494fc1234e LibGfx/JPEGWriter: Rename y<=>x, u<=>v
Usually x and u go horizontally and y and v go vertically.

No behavior change.
2024-01-27 10:20:56 +00:00
Nico Weber
c3c7707de4 LibGfx/JPEGWriter: Don't throw away highest-frequency component in FDCT 2024-01-26 20:19:55 -05:00
Nico Weber
db4982bd29 LibGfx+image: Implement writing ICC profiles to jpeg files 2024-01-26 14:03:20 -05:00
Nico Weber
83ab9f7c2d LibGfx/PNM: Remove unnecessary line
This is already done at the caller decode() in
PortableImageLoaderCommon.h, as pointed out by @LucasChollet at
https://github.com/SerenityOS/serenity/pull/22935#discussion_r1467158789

No behavior change.
2024-01-26 14:53:33 +01:00
Nico Weber
556addc5cb LibGFX/PAM: Allow reading CMYK .pam files
These are written by `mutool extract` for CMYK images.

They don't contain color profiles so they're not super convenient.
But `image` can convert them (*) to sRGB as long as you use it with
`--assign-color-profile` pointing to some CMYK icc profile of your
choice.

*: Once #22922 is merged.
2024-01-26 07:36:53 +01:00
Nico Weber
187862ebe0 LibGfx: Add a .pam loader
.pam is a "portrable arbitrarymap" as documented at
https://netpbm.sourceforge.net/doc/pam.html

It's very similar to .pbm, .pgm, and .ppm, so this uses the
PortableImageMapLoader framework. The header is slightly different,
so this has a custom header parsing function.

Also, .pam only exixts in binary form, so the ascii form support
becomes optional.
2024-01-26 07:36:53 +01:00
Nico Weber
c6eac16d00 LibGfx: Remove unused parameter from cmyk_frame() 2024-01-25 15:53:44 +01:00
Lucas CHOLLET
a17041fe7f LibGfx/TIFF: Add support for CMYK
The test case has been generated with Krita.
2024-01-24 22:16:22 -07:00
Lucas CHOLLET
984272d83e LibGfx/TIFF: Put manage_extra_channels in its own function 2024-01-24 22:16:22 -07:00
Lucas CHOLLET
3f4bf7a0c7 LibGfx/ExifOrientedBitmap: Add support for CMYKBitmap 2024-01-24 22:16:22 -07:00
Lucas CHOLLET
c80b2cf782 LibGfx/ExifOrientedBitmap: Use scanline instead of set_pixel()
The former has the advantage to be available in CMYKBitmap too.

No behavior change.
2024-01-24 22:16:22 -07:00
Lucas CHOLLET
8229a19081 LibGfx/ExifOrientedBitmap: Reorganize create parameters
No behavior change.
2024-01-24 22:16:22 -07:00
Lucas CHOLLET
09b2b3539b LibGfx/JPEG+CMYKBitmap: Extract the CMYK to RGB conversion code
Right now, the JPEG decoder is the only one supporting CMYK, but that
won't last for long. So, let's move the conversion to CMYKBitmap.
2024-01-24 22:16:22 -07:00
Lucas CHOLLET
0462858247 LibGfx/JPEG: Keep the original CMYK data in memory
When decoding a CMYK image and asked for a normal `frame()`, the decoder
would convert the CMYK bitmap into an RGB bitmap. Calling `cmyk_frame()`
after that point will provoke a null-dereference.
2024-01-24 22:16:22 -07:00
Lucas CHOLLET
5dfa660a94 LibGfx/TIFF: Add support for Float and Double types
We previously were considering Float and Doubles as non-supported types.
But this was done in a sneaky way, by letting them hit the default case
in the `read_type` method. So, when I ported this function to the
generator we started to make this types flow into the system without a
proper support there. Since 3124c161, we would have crashes on images
containing tags with a floating point value.
2024-01-22 20:50:06 -07:00
Lucas CHOLLET
6eb574a2b6 LibGfx/JPEG: Expose the Exif metadata
The Exif metadata is contained in the APP1 segment. We only need to
call the TIFF decoder to get the metadata back :^).
2024-01-22 20:16:32 -07:00
Lucas CHOLLET
f6f647bf13 LibGfx/TIFF: Add an alternative entry point to only request metadata
A lot of images format use Exif to store there metadata. As Exif is
based on the TIFF structure, the TIFF decoder can, without modification
be able to decode the metadata. We only need a new API to explicitly
mention that we only need the metadata.
2024-01-22 20:16:32 -07:00
Lucas CHOLLET
12c38035db LibGfx/TIFF: Rename JPEG to OldJPEG and introduce a new JPEG tag
Support for JPEGs embedded in TIFF images was introduced with TIFF 6.0.
However, this implementation had major issues. It was so problematic
that they decided to reimplement it from scratch in 1995, three years
later. The two incarnations are obviously incompatible.

For more details see:
https://www.awaresystems.be/imaging/tiff/specification/TIFFTechNote2.txt
2024-01-22 20:13:53 -07:00
Lucas CHOLLET
1faf9bb44f LibGfx/TIFF: Apply the HorizontalDifferencing on the alpha channel
When present, the alpha channel is also affected by the horizontal
differencing predictor.

The test case was generated with GIMP with the following steps:
 - Open an RGB image
 - Add a transparency layer
 - Export as TIFF with the LZW compression scheme
2024-01-22 20:10:48 -07:00
Lucas CHOLLET
c2c7365494 LibGfx/TIFF: Accept images with a single strip and no RowsPerStrip tag
This tag is required by the specification, but some encoders (at least
Krita) don't write it for images with a single strip.

The test file was generated by opening deflate.tiff in Krita and saving
it with the DEFLATE compression.
2024-01-19 14:13:44 +01:00
Lucas CHOLLET
284e785053 LibGfx/TIFF: Accept the PixarDeflate compression tag
I would have liked to avoid adding a deprecated tag but this is the one
currently in use in Krita.
2024-01-19 14:13:44 +01:00
Lucas CHOLLET
75d87ccf5f LibGfx/TIFF+CCITT: Start to decode CCITT Group 3 images
We currently only support 1D Group 3, but that's a start.

The test case was generated with GIMP (it happens to be 1D by chance).
2024-01-18 14:00:56 +01:00
Lucas CHOLLET
1cc10a6245 LibGfx/CCITT: Extract the code to decode a single CCITT3 1D line
This will be handy for the pure CCITT Group 3 decoder too :^)
2024-01-18 14:00:56 +01:00
Lucas CHOLLET
6a94b09029 LibGfx/TIFF: Make strip decoders take the strip height
While most decoders do not require it, it is necessary for the CCITT
Group 3 decoder.
2024-01-18 14:00:56 +01:00
Lucas CHOLLET
26494600c4 LibGfx/TIFF: Parse the T4Options CCITT field
This field is described in: Section 11: CCITT Bilevel Encodings.
2024-01-18 14:00:56 +01:00
Lucas CHOLLET
edffdc35a9 LibGfx/TIFF+CCITT: Clarify naming of compression type 2
Type 2 <=> One-dimensional Group3, customized for TIFF
Type 3 <=> Two-dimensional Group3, uses the original 1D internally
Type 4 <=> Two-dimensional Group4

So let's clarify that this is not Group3 1D but the TIFF variant, which
is called `CCITTRLE` in libtiff. So let's stick with this name to avoid
confusion.
2024-01-18 14:00:56 +01:00
Lucas CHOLLET
9b50b5793b LibGfx/TIFF: Factorize code to verify that CCITT images are correct 2024-01-18 14:00:56 +01:00
Nicolas Ramz
534eeb6c4b LibGfx/ILBMLoader: Properly display images with a bitplane mask
Images with a display mask ("stencil" as it's called in DPaint) add
an extra bitplane which acts as a mask. For now, at least skip it
properly. Later we should render masked pixels as transparent, but
this requires some refactoring.
2024-01-18 13:59:17 +01:00
kleines Filmröllchen
eb305c6974 LibGfx: Simplify ISOBMFF enums with RIFF ChunkID
RIFF ChunkID can (and should) be used as "just" a FourCC type, which
simplifies this code greatly.
2024-01-15 23:23:26 -07:00
kleines Filmröllchen
19a1369a70 LibGfx: Use LibRIFF for WebP loading 2024-01-15 23:23:26 -07:00
kleines Filmröllchen
60c3a1a77b LibGfx: Use LibRIFF for IFF parsing 2024-01-15 23:23:26 -07:00
Nico Weber
cf95910ae2 LibGfx/JPEG: Simplify loops walking all pixels in all macroblocks
When we want to walk everything, we can just do a linear walk.

No behavior change.
2024-01-15 23:04:56 -07:00
Nico Weber
5efe38ccd7 LibGfx/JPEG: Remove a silly initializer
SamplingFactors already has default initializers for its field,
so no need to have an explicit one for the first of the two fields.

No behavior change.
2024-01-15 23:04:56 -07:00
Nico Weber
6713ed483b LibGfx/PNG: Spec comment for PNGImageDecoderPlugin::unfilter_scanline()
Every time I read this, I'm like "wait, this does the wrong thing for
images with bpp != 8". It doesn't, though.
2024-01-15 23:42:45 +01:00