2021-02-01 09:03:04 +00:00
/*
* Copyright ( c ) 2021 , the SerenityOS developers .
2021-04-29 01:38:16 +00:00
* Copyright ( c ) 2021 , Brian Gianforcaro < bgianf @ serenityos . org >
2021-02-01 09:03:04 +00:00
*
2021-04-22 08:24:48 +00:00
* SPDX - License - Identifier : BSD - 2 - Clause
2021-02-01 09:03:04 +00:00
*/
2023-12-16 14:19:34 +00:00
# include <AK/ByteString.h>
2021-11-23 10:32:25 +00:00
# include <LibCore/MappedFile.h>
2024-07-06 07:33:38 +00:00
# include <LibGfx/ImageFormats/AVIFLoader.h>
2023-03-21 18:58:06 +00:00
# include <LibGfx/ImageFormats/BMPLoader.h>
# include <LibGfx/ImageFormats/GIFLoader.h>
# include <LibGfx/ImageFormats/ICOLoader.h>
# include <LibGfx/ImageFormats/ImageDecoder.h>
# include <LibGfx/ImageFormats/JPEGLoader.h>
2023-07-22 03:04:29 +00:00
# include <LibGfx/ImageFormats/JPEGXLLoader.h>
2023-03-21 18:58:06 +00:00
# include <LibGfx/ImageFormats/PNGLoader.h>
2023-10-29 04:12:05 +00:00
# include <LibGfx/ImageFormats/TIFFLoader.h>
2024-05-23 02:45:22 +00:00
# include <LibGfx/ImageFormats/TIFFMetadata.h>
2023-07-02 22:09:27 +00:00
# include <LibGfx/ImageFormats/TinyVGLoader.h>
2023-03-21 18:58:06 +00:00
# include <LibGfx/ImageFormats/WebPLoader.h>
2021-04-29 01:38:16 +00:00
# include <LibTest/TestCase.h>
2021-02-01 09:03:04 +00:00
# include <stdio.h>
# include <string.h>
2024-07-04 21:39:15 +00:00
# define TEST_INPUT(x) ("test-inputs / " x)
2023-01-31 19:00:33 +00:00
2023-12-09 01:17:15 +00:00
static ErrorOr < Gfx : : ImageFrameDescriptor > expect_single_frame ( Gfx : : ImageDecoderPlugin & plugin_decoder )
2023-06-20 00:46:43 +00:00
{
EXPECT_EQ ( plugin_decoder . frame_count ( ) , 1u ) ;
EXPECT ( ! plugin_decoder . is_animated ( ) ) ;
EXPECT ( ! plugin_decoder . loop_count ( ) ) ;
2023-12-09 01:17:15 +00:00
auto frame = TRY ( plugin_decoder . frame ( 0 ) ) ;
2023-06-20 00:46:43 +00:00
EXPECT_EQ ( frame . duration , 0 ) ;
return frame ;
}
2023-12-09 01:17:15 +00:00
static ErrorOr < Gfx : : ImageFrameDescriptor > expect_single_frame_of_size ( Gfx : : ImageDecoderPlugin & plugin_decoder , Gfx : : IntSize size )
2023-06-20 00:46:43 +00:00
{
EXPECT_EQ ( plugin_decoder . size ( ) , size ) ;
2023-12-09 01:17:15 +00:00
auto frame = TRY ( expect_single_frame ( plugin_decoder ) ) ;
2023-06-20 00:46:43 +00:00
EXPECT_EQ ( frame . image - > size ( ) , size ) ;
return frame ;
}
2021-04-29 01:38:16 +00:00
TEST_CASE ( test_bmp )
2021-02-01 09:03:04 +00:00
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " bmp/rgba32-1.bmp " sv ) ) ) ;
2023-10-18 18:37:35 +00:00
EXPECT ( Gfx : : BMPImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : BMPImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-10-18 18:37:35 +00:00
2023-12-09 01:17:15 +00:00
TRY_OR_FAIL ( expect_single_frame ( * plugin_decoder ) ) ;
2023-10-18 18:37:35 +00:00
}
TEST_CASE ( test_bmp_top_down )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " bmp/top-down.bmp " sv ) ) ) ;
2023-02-26 18:02:50 +00:00
EXPECT ( Gfx : : BMPImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : BMPImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2021-02-01 09:03:04 +00:00
2023-12-09 01:17:15 +00:00
TRY_OR_FAIL ( expect_single_frame ( * plugin_decoder ) ) ;
2021-02-01 09:03:04 +00:00
}
2024-03-05 00:56:13 +00:00
TEST_CASE ( test_bmp_1bpp )
{
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " bmp/bitmap.bmp " sv ) ) ) ;
EXPECT ( Gfx : : BMPImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : BMPImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 399 , 400 } ) ) ;
EXPECT_EQ ( frame . image - > begin ( ) [ 0 ] , 0xff'ff'ff'ff ) ;
}
2023-10-21 22:22:32 +00:00
TEST_CASE ( test_ico_malformed_frame )
{
Array test_inputs = {
TEST_INPUT ( " ico/oss-fuzz-testcase-62541.ico " sv ) ,
2023-10-19 21:10:53 +00:00
TEST_INPUT ( " ico/oss-fuzz-testcase-63177.ico " sv ) ,
TEST_INPUT ( " ico/oss-fuzz-testcase-63357.ico " sv )
2023-10-21 22:22:32 +00:00
} ;
for ( auto test_input : test_inputs ) {
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( test_input ) ) ;
2023-10-21 22:22:32 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : ICOImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame_or_error = plugin_decoder - > frame ( 0 ) ;
EXPECT ( frame_or_error . is_error ( ) ) ;
}
}
2021-04-29 01:38:16 +00:00
TEST_CASE ( test_gif )
2021-02-01 09:03:04 +00:00
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " download-animation.gif " sv ) ) ) ;
2023-02-26 18:02:50 +00:00
EXPECT ( Gfx : : GIFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : GIFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2021-02-01 09:03:04 +00:00
2023-01-20 08:13:14 +00:00
EXPECT ( plugin_decoder - > frame_count ( ) ) ;
2023-01-20 16:41:21 +00:00
EXPECT ( plugin_decoder - > is_animated ( ) ) ;
2023-01-20 08:13:14 +00:00
EXPECT ( ! plugin_decoder - > loop_count ( ) ) ;
2021-02-01 09:03:04 +00:00
2024-01-16 00:04:11 +00:00
auto frame = TRY_OR_FAIL ( plugin_decoder - > frame ( 1 ) ) ;
2021-11-11 10:20:58 +00:00
EXPECT ( frame . duration = = 400 ) ;
2021-02-01 09:03:04 +00:00
}
2024-01-04 20:19:53 +00:00
TEST_CASE ( test_gif_without_global_color_table )
{
Array < u8 , 35 > gif_data {
// Header (6 bytes): "GIF89a"
0x47 ,
0x49 ,
0x46 ,
0x38 ,
0x39 ,
0x61 ,
// Logical Screen Descriptor (7 bytes)
0x01 ,
0x00 , // Width (1)
0x01 ,
0x00 , // Height (1)
0x00 , // Packed fields (NOTE: the MSB here is the Global Color Table flag!)
0x00 , // Background Color Index
0x00 , // Pixel Aspect Ratio
// Image Descriptor (10 bytes)
0x2C ,
0x00 ,
0x00 ,
0x00 ,
0x00 ,
0x01 ,
0x00 ,
0x01 ,
0x00 ,
0x80 ,
// Local Color Table (6 bytes: 2 colors, 3 bytes per color)
0x00 ,
0x00 ,
0x00 , // Color 1: Black (RGB: 0, 0, 0)
0xff ,
0x00 ,
0x00 , // Color 2: Red (RGB: 255, 0, 0)
// Image Data (8 bytes)
0x02 , // LZW Minimum Code Size
0x02 , // Data Sub-block size (2 bytes)
0x4C ,
0x01 , // Image Data
0x00 , // Data Sub-block Terminator
// Trailer (1 byte)
0x3B ,
} ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : GIFImageDecoderPlugin : : create ( gif_data ) ) ;
EXPECT_EQ ( plugin_decoder - > frame_count ( ) , 1u ) ;
auto frame = TRY_OR_FAIL ( plugin_decoder - > frame ( 0 ) ) ;
EXPECT ( frame . image ) ;
EXPECT_EQ ( frame . image - > size ( ) , Gfx : : IntSize ( 1 , 1 ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) , Gfx : : Color : : NamedColor : : Red ) ;
}
2022-12-18 18:13:19 +00:00
TEST_CASE ( test_not_ico )
2021-02-01 09:03:04 +00:00
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " png/buggie.png " sv ) ) ) ;
2023-02-26 18:02:50 +00:00
EXPECT ( ! Gfx : : ICOImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-07-17 16:24:16 +00:00
EXPECT ( Gfx : : ICOImageDecoderPlugin : : create ( file - > bytes ( ) ) . is_error ( ) ) ;
2021-02-01 09:03:04 +00:00
}
2022-12-18 18:13:19 +00:00
TEST_CASE ( test_bmp_embedded_in_ico )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " ico/serenity.ico " sv ) ) ) ;
2023-02-26 18:02:50 +00:00
EXPECT ( Gfx : : ICOImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : ICOImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2022-12-18 18:13:19 +00:00
2024-01-07 00:40:53 +00:00
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 16 , 16 } ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) , Gfx : : Color : : NamedColor : : Transparent ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 7 , 4 ) , Gfx : : Color ( 161 , 0 , 0 ) ) ;
2022-12-18 18:13:19 +00:00
}
2024-01-07 00:24:41 +00:00
TEST_CASE ( test_malformed_maskless_ico )
{
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " ico/malformed_maskless.ico " sv ) ) ) ;
EXPECT ( Gfx : : ICOImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : ICOImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 16 , 16 } ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) , Gfx : : Color : : NamedColor : : Transparent ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 7 , 4 ) , Gfx : : Color ( 161 , 0 , 0 ) ) ;
}
2023-02-26 22:02:07 +00:00
TEST_CASE ( test_jpeg_sof0_one_scan )
2021-02-01 09:03:04 +00:00
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " jpg/rgb24.jpg " sv ) ) ) ;
2023-02-26 18:02:50 +00:00
EXPECT ( Gfx : : JPEGImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : JPEGImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2021-02-01 09:03:04 +00:00
2023-12-09 01:17:15 +00:00
TRY_OR_FAIL ( expect_single_frame ( * plugin_decoder ) ) ;
2021-02-01 09:03:04 +00:00
}
2023-02-26 22:07:14 +00:00
TEST_CASE ( test_jpeg_sof0_several_scans )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " jpg/several_scans.jpg " sv ) ) ) ;
2023-02-26 22:07:14 +00:00
EXPECT ( Gfx : : JPEGImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : JPEGImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-02-26 22:07:14 +00:00
2023-12-09 01:17:15 +00:00
TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 592 , 800 } ) ) ;
2023-02-26 22:07:14 +00:00
}
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-29 17:49:22 +00:00
TEST_CASE ( test_odd_mcu_restart_interval )
{
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " jpg/odd-restart.jpg " sv ) ) ) ;
EXPECT ( Gfx : : JPEGImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : JPEGImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 102 , 77 } ) ) ;
}
2023-03-05 03:05:02 +00:00
TEST_CASE ( test_jpeg_rgb_components )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " jpg/rgb_components.jpg " sv ) ) ) ;
2023-03-05 03:05:02 +00:00
EXPECT ( Gfx : : JPEGImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : JPEGImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-03-05 03:05:02 +00:00
2023-12-09 01:17:15 +00:00
TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 592 , 800 } ) ) ;
2023-03-05 03:05:02 +00:00
}
2023-12-29 01:51:44 +00:00
TEST_CASE ( test_jpeg_ycck )
{
Array test_inputs = {
TEST_INPUT ( " jpg/ycck-1111.jpg " sv ) ,
2024-01-14 23:48:43 +00:00
TEST_INPUT ( " jpg/ycck-2111.jpg " sv ) ,
2023-12-29 01:58:35 +00:00
TEST_INPUT ( " jpg/ycck-2112.jpg " sv ) ,
2023-12-29 01:51:44 +00:00
} ;
for ( auto test_input : test_inputs ) {
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( test_input ) ) ;
2023-12-29 01:51:44 +00:00
EXPECT ( Gfx : : JPEGImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : JPEGImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 592 , 800 } ) ) ;
// Compare difference between pixels so we don't depend on exact CMYK->RGB conversion behavior.
// These two pixels are currently off by one in R.
// FIXME: For 2111, they're off by way more.
EXPECT ( frame . image - > get_pixel ( 6 , 319 ) . distance_squared_to ( frame . image - > get_pixel ( 6 , 320 ) ) < 1.0f / 255.0f ) ;
}
}
2023-02-27 19:32:41 +00:00
TEST_CASE ( test_jpeg_sof2_spectral_selection )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " jpg/spectral_selection.jpg " sv ) ) ) ;
2023-02-27 19:32:41 +00:00
EXPECT ( Gfx : : JPEGImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : JPEGImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-02-27 19:32:41 +00:00
2023-12-09 01:17:15 +00:00
TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 592 , 800 } ) ) ;
2023-02-27 19:32:41 +00:00
}
2023-03-04 04:45:47 +00:00
TEST_CASE ( test_jpeg_sof0_several_scans_odd_number_mcu )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " jpg/several_scans_odd_number_mcu.jpg " sv ) ) ) ;
2023-03-04 04:45:47 +00:00
EXPECT ( Gfx : : JPEGImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : JPEGImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-03-04 04:45:47 +00:00
2023-12-09 01:17:15 +00:00
TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 600 , 600 } ) ) ;
2023-03-04 04:45:47 +00:00
}
2023-03-18 03:39:30 +00:00
TEST_CASE ( test_jpeg_sof2_successive_aproximation )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " jpg/successive_approximation.jpg " sv ) ) ) ;
2023-03-18 03:39:30 +00:00
EXPECT ( Gfx : : JPEGImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : JPEGImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-03-18 03:39:30 +00:00
2023-12-09 01:17:15 +00:00
TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 600 , 800 } ) ) ;
2023-03-18 03:39:30 +00:00
}
2023-07-05 15:08:35 +00:00
TEST_CASE ( test_jpeg_empty_icc )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " jpg/gradient_empty_icc.jpg " sv ) ) ) ;
2023-07-05 15:08:35 +00:00
EXPECT ( Gfx : : JPEGImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : JPEGImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-07-05 15:19:43 +00:00
2023-12-09 01:17:15 +00:00
TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 80 , 80 } ) ) ;
2023-07-05 15:19:43 +00:00
}
TEST_CASE ( test_jpeg_grayscale_with_app14 )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " jpg/grayscale_app14.jpg " sv ) ) ) ;
2023-07-05 15:19:43 +00:00
EXPECT ( Gfx : : JPEGImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : JPEGImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-07-05 15:08:35 +00:00
2023-12-09 01:17:15 +00:00
TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 80 , 80 } ) ) ;
2023-07-05 15:08:35 +00:00
}
2024-01-29 15:56:22 +00:00
TEST_CASE ( test_jpeg_grayscale_with_weird_mcu_and_reset_marker )
{
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " jpg/grayscale_mcu.jpg " sv ) ) ) ;
EXPECT ( Gfx : : JPEGImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : JPEGImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 320 , 240 } ) ) ;
}
2023-10-21 22:22:32 +00:00
TEST_CASE ( test_jpeg_malformed_header )
{
Array test_inputs = {
TEST_INPUT ( " jpg/oss-fuzz-testcase-59785.jpg " sv )
} ;
for ( auto test_input : test_inputs ) {
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( test_input ) ) ;
2024-06-16 14:08:31 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : JPEGImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame_or_error = plugin_decoder - > frame ( 0 ) ;
EXPECT ( frame_or_error . is_error ( ) ) ;
2023-10-21 22:22:32 +00:00
}
}
TEST_CASE ( test_jpeg_malformed_frame )
{
Array test_inputs = {
2023-11-03 20:29:31 +00:00
TEST_INPUT ( " jpg/oss-fuzz-testcase-62584.jpg " sv ) ,
TEST_INPUT ( " jpg/oss-fuzz-testcase-63815.jpg " sv )
2023-10-21 22:22:32 +00:00
} ;
for ( auto test_input : test_inputs ) {
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( test_input ) ) ;
2023-10-21 22:22:32 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : JPEGImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame_or_error = plugin_decoder - > frame ( 0 ) ;
EXPECT ( frame_or_error . is_error ( ) ) ;
}
}
2021-04-29 01:38:16 +00:00
TEST_CASE ( test_png )
2021-02-01 09:03:04 +00:00
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " png/buggie.png " sv ) ) ) ;
2023-02-26 18:02:50 +00:00
EXPECT ( Gfx : : PNGImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : PNGImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2021-02-01 09:03:04 +00:00
2023-12-09 01:17:15 +00:00
TRY_OR_FAIL ( expect_single_frame ( * plugin_decoder ) ) ;
2021-02-01 09:03:04 +00:00
}
2024-05-23 02:45:22 +00:00
TEST_CASE ( test_exif )
{
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " png/exif.png " sv ) ) ) ;
EXPECT ( Gfx : : PNGImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : PNGImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 100 , 200 } ) ) ;
EXPECT ( plugin_decoder - > metadata ( ) . has_value ( ) ) ;
auto const & exif_metadata = static_cast < Gfx : : ExifMetadata const & > ( plugin_decoder - > metadata ( ) . value ( ) ) ;
EXPECT_EQ ( * exif_metadata . orientation ( ) , Gfx : : TIFF : : Orientation : : Rotate90Clockwise ) ;
}
2023-10-21 22:22:32 +00:00
TEST_CASE ( test_png_malformed_frame )
{
Array test_inputs = {
TEST_INPUT ( " png/oss-fuzz-testcase-62371.png " sv ) ,
TEST_INPUT ( " png/oss-fuzz-testcase-63052.png " sv )
} ;
for ( auto test_input : test_inputs ) {
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( test_input ) ) ;
2024-06-18 19:06:15 +00:00
auto plugin_decoder_or_error = Gfx : : PNGImageDecoderPlugin : : create ( file - > bytes ( ) ) ;
if ( plugin_decoder_or_error . is_error ( ) )
continue ;
auto plugin_decoder = plugin_decoder_or_error . release_value ( ) ;
2023-10-21 22:22:32 +00:00
auto frame_or_error = plugin_decoder - > frame ( 0 ) ;
EXPECT ( frame_or_error . is_error ( ) ) ;
}
}
2023-10-29 04:12:05 +00:00
TEST_CASE ( test_tiff_uncompressed )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " tiff/uncompressed.tiff " sv ) ) ) ;
2023-10-29 04:12:05 +00:00
EXPECT ( Gfx : : TIFFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : TIFFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-10-29 04:12:05 +00:00
2023-12-09 01:17:15 +00:00
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 400 , 300 } ) ) ;
2023-10-29 04:12:05 +00:00
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) , Gfx : : Color : : NamedColor : : White ) ;
2023-11-04 21:22:54 +00:00
EXPECT_EQ ( frame . image - > get_pixel ( 60 , 75 ) , Gfx : : Color : : NamedColor : : Red ) ;
}
2024-01-15 05:46:10 +00:00
TEST_CASE ( test_tiff_ccitt_rle )
LibGfx/TIFF: Add support for images with CCITT3 1D compression
This compression (tag Compression=2) is not very popular on its own, but
a base to implement CCITT3 2D and CCITT4 compressions.
As the format has no real benefits, it is quite hard to find an app that
accepts tho encode that for you. So I used the following program that
calls `libtiff` directly:
```cpp
#include <vector>
#include <cstdlib>
#include <iostream>
#include <tiffio.h>
// An array containing 0 and 1 of length width * height.
extern std::vector<uint8_t> array;
int main() {
// From: https://stackoverflow.com/a/34257789
TIFF *image = TIFFOpen("input.tif", "w");
int const width = 400;
int const height = 300;
TIFFSetField(image, TIFFTAG_IMAGEWIDTH, width);
TIFFSetField(image, TIFFTAG_IMAGELENGTH, height);
TIFFSetField(image, TIFFTAG_PHOTOMETRIC, 0);
TIFFSetField(image, TIFFTAG_COMPRESSION, COMPRESSION_CCITTRLE);
TIFFSetField(image, TIFFTAG_BITSPERSAMPLE, 1);
TIFFSetField(image, TIFFTAG_SAMPLESPERPIXEL, 1);
TIFFSetField(image, TIFFTAG_ROWSPERSTRIP, 1);
std::vector<uint8_t> scan_line(width / 8 + 8, 0);
int count = 0;
for (int i = 0; i < height; i++) {
std::fill(scan_line.begin(), scan_line.end(), 0);
for (int x = 0; x < width; ++x) {
uint8_t eight_pixels = scan_line.at(x / 8);
eight_pixels = eight_pixels << 1;
eight_pixels |= !array.at(i * width + x);
scan_line.at(x / 8) = eight_pixels;
}
int bytes = int(width / 8.0 + 0.5);
if (TIFFWriteScanline(image, scan_line.data(), i, bytes) != 1)
std::cerr << "Something went wrong\n";
}
TIFFClose(image);
}
```
2023-11-25 21:34:56 +00:00
{
2024-01-15 05:46:10 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " tiff/ccitt_rle.tiff " sv ) ) ) ;
LibGfx/TIFF: Add support for images with CCITT3 1D compression
This compression (tag Compression=2) is not very popular on its own, but
a base to implement CCITT3 2D and CCITT4 compressions.
As the format has no real benefits, it is quite hard to find an app that
accepts tho encode that for you. So I used the following program that
calls `libtiff` directly:
```cpp
#include <vector>
#include <cstdlib>
#include <iostream>
#include <tiffio.h>
// An array containing 0 and 1 of length width * height.
extern std::vector<uint8_t> array;
int main() {
// From: https://stackoverflow.com/a/34257789
TIFF *image = TIFFOpen("input.tif", "w");
int const width = 400;
int const height = 300;
TIFFSetField(image, TIFFTAG_IMAGEWIDTH, width);
TIFFSetField(image, TIFFTAG_IMAGELENGTH, height);
TIFFSetField(image, TIFFTAG_PHOTOMETRIC, 0);
TIFFSetField(image, TIFFTAG_COMPRESSION, COMPRESSION_CCITTRLE);
TIFFSetField(image, TIFFTAG_BITSPERSAMPLE, 1);
TIFFSetField(image, TIFFTAG_SAMPLESPERPIXEL, 1);
TIFFSetField(image, TIFFTAG_ROWSPERSTRIP, 1);
std::vector<uint8_t> scan_line(width / 8 + 8, 0);
int count = 0;
for (int i = 0; i < height; i++) {
std::fill(scan_line.begin(), scan_line.end(), 0);
for (int x = 0; x < width; ++x) {
uint8_t eight_pixels = scan_line.at(x / 8);
eight_pixels = eight_pixels << 1;
eight_pixels |= !array.at(i * width + x);
scan_line.at(x / 8) = eight_pixels;
}
int bytes = int(width / 8.0 + 0.5);
if (TIFFWriteScanline(image, scan_line.data(), i, bytes) != 1)
std::cerr << "Something went wrong\n";
}
TIFFClose(image);
}
```
2023-11-25 21:34:56 +00:00
EXPECT ( Gfx : : TIFFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : TIFFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 400 , 300 } ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) , Gfx : : Color : : NamedColor : : White ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 60 , 75 ) , Gfx : : Color : : NamedColor : : Black ) ;
}
2024-01-15 05:56:21 +00:00
TEST_CASE ( test_tiff_ccitt3 )
{
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " tiff/ccitt3.tiff " sv ) ) ) ;
EXPECT ( Gfx : : TIFFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : TIFFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 400 , 300 } ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) , Gfx : : Color : : NamedColor : : White ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 60 , 75 ) , Gfx : : Color : : NamedColor : : Black ) ;
}
2024-05-07 12:20:18 +00:00
TEST_CASE ( test_tiff_ccitt3_no_tags )
{
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " tiff/ccitt3_no_tags.tiff " sv ) ) ) ;
EXPECT ( Gfx : : TIFFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : TIFFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 6 , 4 } ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) , Gfx : : Color : : NamedColor : : White ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 3 , 0 ) , Gfx : : Color : : NamedColor : : Black ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 2 , 2 ) , Gfx : : Color : : NamedColor : : White ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 5 , 3 ) , Gfx : : Color : : NamedColor : : White ) ;
}
2024-01-18 05:29:46 +00:00
TEST_CASE ( test_tiff_ccitt3_fill )
{
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " tiff/ccitt3_1d_fill.tiff " sv ) ) ) ;
EXPECT ( Gfx : : TIFFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : TIFFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 6 , 4 } ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) , Gfx : : Color : : NamedColor : : White ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 3 , 0 ) , Gfx : : Color : : NamedColor : : Black ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 2 , 2 ) , Gfx : : Color : : NamedColor : : White ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 5 , 3 ) , Gfx : : Color : : NamedColor : : White ) ;
}
2024-02-12 14:36:46 +00:00
TEST_CASE ( test_tiff_ccitt3_2d )
{
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " tiff/ccitt3_2d.tiff " sv ) ) ) ;
EXPECT ( Gfx : : TIFFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : TIFFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 400 , 300 } ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) , Gfx : : Color : : NamedColor : : White ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 60 , 75 ) , Gfx : : Color : : NamedColor : : Black ) ;
}
TEST_CASE ( test_tiff_ccitt3_2d_fill )
{
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " tiff/ccitt3_2d_fill.tiff " sv ) ) ) ;
EXPECT ( Gfx : : TIFFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2024-02-16 00:47:44 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : TIFFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 400 , 300 } ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) , Gfx : : Color : : NamedColor : : White ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 60 , 75 ) , Gfx : : Color : : NamedColor : : Black ) ;
}
TEST_CASE ( test_tiff_ccitt4 )
{
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " tiff/ccitt4.tiff " sv ) ) ) ;
EXPECT ( Gfx : : TIFFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2024-02-12 14:36:46 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : TIFFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 400 , 300 } ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) , Gfx : : Color : : NamedColor : : White ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 60 , 75 ) , Gfx : : Color : : NamedColor : : Black ) ;
}
2023-11-04 21:22:54 +00:00
TEST_CASE ( test_tiff_lzw )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " tiff/lzw.tiff " sv ) ) ) ;
2023-11-04 21:22:54 +00:00
EXPECT ( Gfx : : TIFFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : TIFFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-11-04 21:22:54 +00:00
2023-12-09 01:17:15 +00:00
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 400 , 300 } ) ) ;
2023-11-04 21:22:54 +00:00
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) , Gfx : : Color : : NamedColor : : White ) ;
2023-10-29 04:12:05 +00:00
EXPECT_EQ ( frame . image - > get_pixel ( 60 , 75 ) , Gfx : : Color : : NamedColor : : Red ) ;
}
2024-01-07 06:03:52 +00:00
TEST_CASE ( test_tiff_deflate )
2023-12-23 05:09:26 +00:00
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " tiff/deflate.tiff " sv ) ) ) ;
2023-12-23 05:09:26 +00:00
EXPECT ( Gfx : : TIFFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : TIFFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 400 , 300 } ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) , Gfx : : Color : : NamedColor : : White ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 60 , 75 ) , Gfx : : Color : : NamedColor : : Red ) ;
}
2024-01-16 05:27:52 +00:00
TEST_CASE ( test_tiff_krita )
{
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " tiff/krita.tif " sv ) ) ) ;
EXPECT ( Gfx : : TIFFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : TIFFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 400 , 300 } ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) , Gfx : : Color : : NamedColor : : White ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 60 , 75 ) , Gfx : : Color : : NamedColor : : Red ) ;
}
2024-01-07 05:20:40 +00:00
TEST_CASE ( test_tiff_orientation )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " tiff/orientation.tiff " sv ) ) ) ;
2024-01-07 05:20:40 +00:00
EXPECT ( Gfx : : TIFFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : TIFFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 300 , 400 } ) ) ;
// Orientation is Rotate90Clockwise
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) , Gfx : : Color : : NamedColor : : White ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 300 - 75 , 60 ) , Gfx : : Color : : NamedColor : : Red ) ;
}
2023-10-29 21:02:45 +00:00
TEST_CASE ( test_tiff_packed_bits )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " tiff/packed_bits.tiff " sv ) ) ) ;
2023-10-29 21:02:45 +00:00
EXPECT ( Gfx : : TIFFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : TIFFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-10-29 21:02:45 +00:00
2023-12-09 01:17:15 +00:00
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 400 , 300 } ) ) ;
2023-10-29 21:02:45 +00:00
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) , Gfx : : Color : : NamedColor : : White ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 60 , 75 ) , Gfx : : Color : : NamedColor : : Red ) ;
}
2023-12-02 00:28:06 +00:00
TEST_CASE ( test_tiff_grayscale )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " tiff/grayscale.tiff " sv ) ) ) ;
2023-12-02 00:28:06 +00:00
EXPECT ( Gfx : : TIFFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : TIFFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-12-02 00:28:06 +00:00
2023-12-09 01:17:15 +00:00
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 400 , 300 } ) ) ;
2023-12-02 00:28:06 +00:00
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) , Gfx : : Color : : NamedColor : : White ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 60 , 75 ) , Gfx : : Color ( 130 , 130 , 130 ) ) ;
}
2023-12-17 04:05:41 +00:00
TEST_CASE ( test_tiff_grayscale_alpha )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " tiff/grayscale_alpha.tiff " sv ) ) ) ;
2023-12-17 04:05:41 +00:00
EXPECT ( Gfx : : TIFFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : TIFFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 400 , 300 } ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) . alpha ( ) , 0 ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 60 , 75 ) , Gfx : : Color ( 130 , 130 , 130 ) ) ;
}
TEST_CASE ( test_tiff_rgb_alpha )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " tiff/rgb_alpha.tiff " sv ) ) ) ;
2023-12-17 04:05:41 +00:00
EXPECT ( Gfx : : TIFFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : TIFFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-12-18 04:35:21 +00:00
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 400 , 300 } ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) . alpha ( ) , 0 ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 60 , 75 ) , Gfx : : Color : : NamedColor : : Red ) ;
}
TEST_CASE ( test_tiff_palette_alpha )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " tiff/rgb_palette_alpha.tiff " sv ) ) ) ;
2023-12-18 04:35:21 +00:00
EXPECT ( Gfx : : TIFFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : TIFFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-12-17 04:05:41 +00:00
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 400 , 300 } ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) . alpha ( ) , 0 ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 60 , 75 ) , Gfx : : Color : : NamedColor : : Red ) ;
}
2024-01-23 02:19:42 +00:00
TEST_CASE ( test_tiff_alpha_predictor )
{
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " tiff/alpha_predictor.tiff " sv ) ) ) ;
EXPECT ( Gfx : : TIFFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : TIFFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 400 , 300 } ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) . alpha ( ) , 255 ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 60 , 75 ) , Gfx : : Color : : NamedColor : : Red ) ;
}
2023-12-09 04:02:22 +00:00
TEST_CASE ( test_tiff_16_bits )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " tiff/16_bits.tiff " sv ) ) ) ;
2023-12-09 04:02:22 +00:00
EXPECT ( Gfx : : TIFFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : TIFFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 400 , 300 } ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) , Gfx : : Color : : NamedColor : : White ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 60 , 75 ) , Gfx : : Color : : NamedColor : : Red ) ;
}
2024-01-19 00:57:05 +00:00
TEST_CASE ( test_tiff_cmyk )
{
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " tiff/cmyk.tiff " sv ) ) ) ;
EXPECT ( Gfx : : TIFFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : TIFFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 400 , 300 } ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) , Gfx : : Color : : NamedColor : : White ) ;
// I stripped the ICC profile from the image, so we can't test for equality with Red here.
EXPECT_NE ( frame . image - > get_pixel ( 60 , 75 ) , Gfx : : Color : : NamedColor : : White ) ;
}
2024-02-10 02:35:33 +00:00
TEST_CASE ( test_tiff_tiled )
{
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " tiff/tiled.tiff " sv ) ) ) ;
EXPECT ( Gfx : : TIFFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : TIFFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 400 , 300 } ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) , Gfx : : Color : : NamedColor : : White ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 60 , 75 ) , Gfx : : Color : : NamedColor : : Red ) ;
}
2023-12-30 04:54:47 +00:00
TEST_CASE ( test_tiff_invalid_tag )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " tiff/invalid_tag.tiff " sv ) ) ) ;
2023-12-30 04:54:47 +00:00
EXPECT ( Gfx : : TIFFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : TIFFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 10 , 10 } ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) , Gfx : : Color : : NamedColor : : Black ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 9 ) , Gfx : : Color : : NamedColor : : White ) ;
}
2023-02-26 00:27:09 +00:00
TEST_CASE ( test_webp_simple_lossy )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " webp/simple-vp8.webp " sv ) ) ) ;
2023-02-26 18:02:50 +00:00
EXPECT ( Gfx : : WebPImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : WebPImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-02-26 00:27:09 +00:00
2023-12-09 01:17:15 +00:00
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 240 , 240 } ) ) ;
2023-05-29 17:01:45 +00:00
// While VP8 YUV contents are defined bit-exact, the YUV->RGB conversion isn't.
// So pixels changing by 1 or so below is fine if you change code.
2024-07-12 08:08:39 +00:00
EXPECT_EQ ( frame . image - > get_pixel ( 120 , 232 ) , Gfx : : Color ( 0xf1 , 0xef , 0xf0 , 255 ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 198 , 202 ) , Gfx : : Color ( 0x7a , 0xaa , 0xd5 , 255 ) ) ;
2023-02-26 00:27:09 +00:00
}
TEST_CASE ( test_webp_simple_lossless )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " webp/simple-vp8l.webp " sv ) ) ) ;
2023-02-26 18:02:50 +00:00
EXPECT ( Gfx : : WebPImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : WebPImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-02-26 00:27:09 +00:00
2023-04-06 17:33:43 +00:00
// Ironically, simple-vp8l.webp is a much more complex file than extended-lossless.webp tested below.
// extended-lossless.webp tests the decoding basics.
// This here tests the predictor, color, and subtract green transforms,
// as well as meta prefix images, one-element canonical code handling,
// and handling of canonical codes with more than 288 elements.
// This image uses all 13 predictor modes of the predictor transform.
2023-12-09 01:17:15 +00:00
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 386 , 395 } ) ) ;
WebP/Lossless: Set alpha to 0xff if is_alpha_used is false in header
simple-vp8l-alpha-used-false.webp is a copy of simple-vp8l.webp,
with the byte at offset 0x18 changed from 0x10 to 0x00 -- that
is, the bit in the VP8L header that stores `is_alpha_used` is cleared.
We would already allocated a BGRx8888 instead of a BGRA8888 bitmap,
but keep actual alpha data in the `x` channel.
That lead to at least `image` still writing a PNG with an alpha channel.
So explicitly set the alpha channel to 0xff when is_alpha_used is false,
to make sure all consumers of decoded lossless webp data have behavior
consistent with other webp readers.
In practice, webp encoders usually don't write files that have
`is_alpha_used` set to false and then write actual alpha data to their
output. So this is rarely observable. However, for example for
lossy+ALPH webp files, the lossless webp used to store the ALPH channel
has `is_alpha_used` set to false and all channels but green are 0
(since the lossless green channel stores the alpha channel of a
lossy+ALPH webp). So if we dump such a bitmap to a standalone webp
file (e.g. with the temporary debugging code in fc3249a1ca8f42e72),
then without this commit here, `image` would convert that webp to
a fully transparent webp, while other webp software would correctly
display the green image with opaque alpha.
2023-06-18 14:47:05 +00:00
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) , Gfx : : Color ( 0 , 0 , 0 , 0 ) ) ;
2023-04-06 17:33:43 +00:00
// This pixel tests all predictor modes except 5, 7, 8, 9, and 13.
EXPECT_EQ ( frame . image - > get_pixel ( 289 , 332 ) , Gfx : : Color ( 0xf2 , 0xee , 0xd3 , 255 ) ) ;
2023-02-26 00:27:09 +00:00
}
WebP/Lossless: Set alpha to 0xff if is_alpha_used is false in header
simple-vp8l-alpha-used-false.webp is a copy of simple-vp8l.webp,
with the byte at offset 0x18 changed from 0x10 to 0x00 -- that
is, the bit in the VP8L header that stores `is_alpha_used` is cleared.
We would already allocated a BGRx8888 instead of a BGRA8888 bitmap,
but keep actual alpha data in the `x` channel.
That lead to at least `image` still writing a PNG with an alpha channel.
So explicitly set the alpha channel to 0xff when is_alpha_used is false,
to make sure all consumers of decoded lossless webp data have behavior
consistent with other webp readers.
In practice, webp encoders usually don't write files that have
`is_alpha_used` set to false and then write actual alpha data to their
output. So this is rarely observable. However, for example for
lossy+ALPH webp files, the lossless webp used to store the ALPH channel
has `is_alpha_used` set to false and all channels but green are 0
(since the lossless green channel stores the alpha channel of a
lossy+ALPH webp). So if we dump such a bitmap to a standalone webp
file (e.g. with the temporary debugging code in fc3249a1ca8f42e72),
then without this commit here, `image` would convert that webp to
a fully transparent webp, while other webp software would correctly
display the green image with opaque alpha.
2023-06-18 14:47:05 +00:00
TEST_CASE ( test_webp_simple_lossless_alpha_used_false )
{
// This file is identical to simple-vp8l.webp, but the `is_alpha_used` used bit is false.
// The file still contains alpha data. This tests that the decoder replaces the stored alpha data with 0xff if `is_alpha_used` is false.
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " webp/simple-vp8l-alpha-used-false.webp " sv ) ) ) ;
WebP/Lossless: Set alpha to 0xff if is_alpha_used is false in header
simple-vp8l-alpha-used-false.webp is a copy of simple-vp8l.webp,
with the byte at offset 0x18 changed from 0x10 to 0x00 -- that
is, the bit in the VP8L header that stores `is_alpha_used` is cleared.
We would already allocated a BGRx8888 instead of a BGRA8888 bitmap,
but keep actual alpha data in the `x` channel.
That lead to at least `image` still writing a PNG with an alpha channel.
So explicitly set the alpha channel to 0xff when is_alpha_used is false,
to make sure all consumers of decoded lossless webp data have behavior
consistent with other webp readers.
In practice, webp encoders usually don't write files that have
`is_alpha_used` set to false and then write actual alpha data to their
output. So this is rarely observable. However, for example for
lossy+ALPH webp files, the lossless webp used to store the ALPH channel
has `is_alpha_used` set to false and all channels but green are 0
(since the lossless green channel stores the alpha channel of a
lossy+ALPH webp). So if we dump such a bitmap to a standalone webp
file (e.g. with the temporary debugging code in fc3249a1ca8f42e72),
then without this commit here, `image` would convert that webp to
a fully transparent webp, while other webp software would correctly
display the green image with opaque alpha.
2023-06-18 14:47:05 +00:00
EXPECT ( Gfx : : WebPImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : WebPImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
WebP/Lossless: Set alpha to 0xff if is_alpha_used is false in header
simple-vp8l-alpha-used-false.webp is a copy of simple-vp8l.webp,
with the byte at offset 0x18 changed from 0x10 to 0x00 -- that
is, the bit in the VP8L header that stores `is_alpha_used` is cleared.
We would already allocated a BGRx8888 instead of a BGRA8888 bitmap,
but keep actual alpha data in the `x` channel.
That lead to at least `image` still writing a PNG with an alpha channel.
So explicitly set the alpha channel to 0xff when is_alpha_used is false,
to make sure all consumers of decoded lossless webp data have behavior
consistent with other webp readers.
In practice, webp encoders usually don't write files that have
`is_alpha_used` set to false and then write actual alpha data to their
output. So this is rarely observable. However, for example for
lossy+ALPH webp files, the lossless webp used to store the ALPH channel
has `is_alpha_used` set to false and all channels but green are 0
(since the lossless green channel stores the alpha channel of a
lossy+ALPH webp). So if we dump such a bitmap to a standalone webp
file (e.g. with the temporary debugging code in fc3249a1ca8f42e72),
then without this commit here, `image` would convert that webp to
a fully transparent webp, while other webp software would correctly
display the green image with opaque alpha.
2023-06-18 14:47:05 +00:00
2023-12-09 01:17:15 +00:00
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 386 , 395 } ) ) ;
WebP/Lossless: Set alpha to 0xff if is_alpha_used is false in header
simple-vp8l-alpha-used-false.webp is a copy of simple-vp8l.webp,
with the byte at offset 0x18 changed from 0x10 to 0x00 -- that
is, the bit in the VP8L header that stores `is_alpha_used` is cleared.
We would already allocated a BGRx8888 instead of a BGRA8888 bitmap,
but keep actual alpha data in the `x` channel.
That lead to at least `image` still writing a PNG with an alpha channel.
So explicitly set the alpha channel to 0xff when is_alpha_used is false,
to make sure all consumers of decoded lossless webp data have behavior
consistent with other webp readers.
In practice, webp encoders usually don't write files that have
`is_alpha_used` set to false and then write actual alpha data to their
output. So this is rarely observable. However, for example for
lossy+ALPH webp files, the lossless webp used to store the ALPH channel
has `is_alpha_used` set to false and all channels but green are 0
(since the lossless green channel stores the alpha channel of a
lossy+ALPH webp). So if we dump such a bitmap to a standalone webp
file (e.g. with the temporary debugging code in fc3249a1ca8f42e72),
then without this commit here, `image` would convert that webp to
a fully transparent webp, while other webp software would correctly
display the green image with opaque alpha.
2023-06-18 14:47:05 +00:00
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) , Gfx : : Color ( 0 , 0 , 0 , 0xff ) ) ;
}
2023-02-26 00:27:09 +00:00
TEST_CASE ( test_webp_extended_lossy )
{
2023-06-08 21:28:59 +00:00
// This extended lossy image has an ALPH chunk for (losslessly compressed) alpha data.
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " webp/extended-lossy.webp " sv ) ) ) ;
2023-02-26 18:02:50 +00:00
EXPECT ( Gfx : : WebPImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : WebPImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-02-26 00:27:09 +00:00
2023-12-09 01:17:15 +00:00
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 417 , 223 } ) ) ;
2023-05-29 17:01:45 +00:00
// While VP8 YUV contents are defined bit-exact, the YUV->RGB conversion isn't.
// So pixels changing by 1 or so below is fine if you change code.
EXPECT_EQ ( frame . image - > get_pixel ( 89 , 72 ) , Gfx : : Color ( 255 , 1 , 0 , 255 ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 174 , 69 ) , Gfx : : Color ( 0 , 255 , 0 , 255 ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 245 , 84 ) , Gfx : : Color ( 0 , 0 , 255 , 255 ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 352 , 125 ) , Gfx : : Color ( 0 , 0 , 0 , 128 ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 355 , 106 ) , Gfx : : Color ( 0 , 0 , 0 , 0 ) ) ;
// Check same basic pixels as in test_webp_extended_lossless too.
// (The top-left pixel in the lossy version is fully transparent white, compared to fully transparent black in the lossless version).
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) , Gfx : : Color ( 255 , 255 , 255 , 0 ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 43 , 75 ) , Gfx : : Color ( 255 , 0 , 2 , 255 ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 141 , 75 ) , Gfx : : Color ( 0 , 255 , 3 , 255 ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 235 , 75 ) , Gfx : : Color ( 0 , 0 , 255 , 255 ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 341 , 75 ) , Gfx : : Color ( 0 , 0 , 0 , 128 ) ) ;
2023-02-26 00:27:09 +00:00
}
2023-06-08 21:28:59 +00:00
TEST_CASE ( test_webp_extended_lossy_alpha_horizontal_filter )
{
// Also lossy rgb + lossless alpha, but with a horizontal alpha filtering method.
// The image should look like smolkling.webp, but with a horizontal alpha gradient.
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " webp/smolkling-horizontal-alpha.webp " sv ) ) ) ;
2023-06-08 21:28:59 +00:00
EXPECT ( Gfx : : WebPImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : WebPImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-06-08 21:28:59 +00:00
2023-12-09 01:17:15 +00:00
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 264 , 264 } ) ) ;
2023-06-08 21:28:59 +00:00
// While VP8 YUV contents are defined bit-exact, the YUV->RGB conversion isn't.
// So pixels changing by 1 or so below is fine if you change code.
// The important component in this test is alpha, and that shouldn't change even by 1 as it's losslessly compressed and doesn't use YUV.
2024-07-12 08:08:39 +00:00
EXPECT_EQ ( frame . image - > get_pixel ( 131 , 131 ) , Gfx : : Color ( 0x8f , 0x50 , 0x33 , 0x4b ) ) ;
2023-06-08 21:28:59 +00:00
}
2023-06-08 22:28:11 +00:00
TEST_CASE ( test_webp_extended_lossy_alpha_vertical_filter )
{
// Also lossy rgb + lossless alpha, but with a vertical alpha filtering method.
// The image should look like smolkling.webp, but with a vertical alpha gradient, and with a fully transparent first column.
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " webp/smolkling-vertical-alpha.webp " sv ) ) ) ;
2023-06-08 22:28:11 +00:00
EXPECT ( Gfx : : WebPImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : WebPImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-06-08 22:28:11 +00:00
2023-12-09 01:17:15 +00:00
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 264 , 264 } ) ) ;
2023-06-08 22:28:11 +00:00
// While VP8 YUV contents are defined bit-exact, the YUV->RGB conversion isn't.
// So pixels changing by 1 or so below is fine if you change code.
// The important component in this test is alpha, and that shouldn't change even by 1 as it's losslessly compressed and doesn't use YUV.
2024-07-12 08:08:39 +00:00
EXPECT_EQ ( frame . image - > get_pixel ( 131 , 131 ) , Gfx : : Color ( 0x92 , 0x50 , 0x32 , 0x4c ) ) ;
2023-06-08 22:28:11 +00:00
}
2023-06-08 21:55:39 +00:00
TEST_CASE ( test_webp_extended_lossy_alpha_gradient_filter )
{
// Also lossy rgb + lossless alpha, but with a gradient alpha filtering method.
// The image should look like smolkling.webp, but with a few transparent pixels in the shape of a C on it. Most of the image should not be transparent.
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " webp/smolkling-gradient-alpha.webp " sv ) ) ) ;
2023-06-08 21:55:39 +00:00
EXPECT ( Gfx : : WebPImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : WebPImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-06-08 21:55:39 +00:00
2023-12-09 01:17:15 +00:00
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 264 , 264 } ) ) ;
2023-06-08 21:55:39 +00:00
// While VP8 YUV contents are defined bit-exact, the YUV->RGB conversion isn't.
// So pixels changing by 1 or so below is fine if you change code.
// The important component in this test is alpha, and that shouldn't change even by 1 as it's losslessly compressed and doesn't use YUV.
// In particular, the center of the image should be fully opaque, not fully transparent.
2024-07-12 08:08:39 +00:00
EXPECT_EQ ( frame . image - > get_pixel ( 131 , 131 ) , Gfx : : Color ( 0x8a , 0x48 , 0x2e , 255 ) ) ;
2023-06-08 21:55:39 +00:00
}
2023-05-29 23:19:29 +00:00
TEST_CASE ( test_webp_extended_lossy_uncompressed_alpha )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " webp/extended-lossy-uncompressed-alpha.webp " sv ) ) ) ;
2023-05-29 23:19:29 +00:00
EXPECT ( Gfx : : WebPImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : WebPImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-05-29 23:19:29 +00:00
2023-12-09 01:17:15 +00:00
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 417 , 223 } ) ) ;
2023-05-29 23:19:29 +00:00
// While VP8 YUV contents are defined bit-exact, the YUV->RGB conversion isn't.
// So pixels changing by 1 or so below is fine if you change code.
2024-07-12 08:08:39 +00:00
EXPECT_EQ ( frame . image - > get_pixel ( 89 , 72 ) , Gfx : : Color ( 254 , 0 , 6 , 255 ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 174 , 69 ) , Gfx : : Color ( 0 , 255 , 0 , 255 ) ) ;
2023-05-29 23:19:29 +00:00
EXPECT_EQ ( frame . image - > get_pixel ( 245 , 84 ) , Gfx : : Color ( 0 , 0 , 255 , 255 ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 352 , 125 ) , Gfx : : Color ( 0 , 0 , 0 , 128 ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 355 , 106 ) , Gfx : : Color ( 0 , 0 , 0 , 0 ) ) ;
}
2023-06-01 13:09:24 +00:00
TEST_CASE ( test_webp_extended_lossy_negative_quantization_offset )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " webp/smolkling.webp " sv ) ) ) ;
2023-06-01 13:09:24 +00:00
EXPECT ( Gfx : : WebPImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : WebPImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-06-01 13:09:24 +00:00
2023-12-09 01:17:15 +00:00
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 264 , 264 } ) ) ;
2023-06-01 13:09:24 +00:00
// While VP8 YUV contents are defined bit-exact, the YUV->RGB conversion isn't.
// So pixels changing by 1 or so below is fine if you change code.
2024-07-12 08:08:39 +00:00
EXPECT_EQ ( frame . image - > get_pixel ( 16 , 16 ) , Gfx : : Color ( 0x3b , 0x25 , 0x18 , 255 ) ) ;
2023-06-01 13:09:24 +00:00
}
2023-05-30 14:21:58 +00:00
TEST_CASE ( test_webp_lossy_4 )
2023-05-29 19:23:33 +00:00
{
// This is https://commons.wikimedia.org/wiki/File:Fr%C3%BChling_bl%C3%BChender_Kirschenbaum.jpg,
// under the Creative Commons Attribution-Share Alike 3.0 Unported license. The image was re-encoded
// as webp at https://developers.google.com/speed/webp/gallery1 and the webp version is from there.
// No other changes have been made.
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " webp/4.webp " sv ) ) ) ;
2023-05-29 19:23:33 +00:00
EXPECT ( Gfx : : WebPImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : WebPImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-05-29 19:23:33 +00:00
2023-12-09 01:17:15 +00:00
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 1024 , 772 } ) ) ;
2023-05-29 19:23:33 +00:00
// This image tests macroblocks that have `skip_coefficients` set to true, and it test a boolean entropy decoder edge case.
2023-05-30 14:21:58 +00:00
EXPECT_EQ ( frame . image - > get_pixel ( 780 , 570 ) , Gfx : : Color ( 0x72 , 0xc8 , 0xf6 , 255 ) ) ;
2023-05-29 19:23:33 +00:00
}
WebP/Lossy: Add support for images with more than one partition
Each secondary partition has an independent BooleanDecoder.
Their bitstreams interleave per macroblock row, that is the first
macroblock row is read from the first decoder, the second from the
second, ..., until it wraps around again.
All partitions share a single prediction state though: The second
macroblock row (which reads coefficients off the second decoder) is
predicted using the result of decoding the frist macroblock row (which
reads coefficients off the first decoder).
So if I understand things right, in theory the coefficient reading could
be parallelized, but prediction can't be. (IDCT can also be
parallelized, but that's true with just a single partition too.)
I created the test image by running
examples/cwebp -low_memory -partitions 3 -o foo.webp \
~/src/serenity/Tests/LibGfx/test-inputs/4.webp
using a cwebp hacked up as described in #19149. Since creating
multi-partition lossy webps requires hacking up `cwebp`, they're likely
very rare in practice. (But maybe other programs using the libwebp API
create them.)
Fixes #19149.
With this, webp lossy support is complete (*) :^)
And with that, webp support is complete: Lossless, lossy, lossy with
alpha, animated lossless, animated lossy, animated lossy with alpha all
work.
(*: Loop filtering isn't implemented yet, which has a minor visual
effect on the output. But it's only visible when carefully comparing
a webp decoded without loop filtering to the same decoded with it.
But it's technically a part of the spec that's still missing.
The upsampling of UV in the YUV->RGB code is also low-quality. This
produces somewhat visible banding in practice in some images (e.g.
in the fire breather's face in 5.webp), so we should probably improve
that at some point. Our JPG decoder has the same issue.)
2023-05-30 14:10:14 +00:00
TEST_CASE ( test_webp_lossy_4_with_partitions )
{
// Same input file as in the previous test, but re-encoded to use 8 secondary partitions.
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " webp/4-with-8-partitions.webp " sv ) ) ) ;
WebP/Lossy: Add support for images with more than one partition
Each secondary partition has an independent BooleanDecoder.
Their bitstreams interleave per macroblock row, that is the first
macroblock row is read from the first decoder, the second from the
second, ..., until it wraps around again.
All partitions share a single prediction state though: The second
macroblock row (which reads coefficients off the second decoder) is
predicted using the result of decoding the frist macroblock row (which
reads coefficients off the first decoder).
So if I understand things right, in theory the coefficient reading could
be parallelized, but prediction can't be. (IDCT can also be
parallelized, but that's true with just a single partition too.)
I created the test image by running
examples/cwebp -low_memory -partitions 3 -o foo.webp \
~/src/serenity/Tests/LibGfx/test-inputs/4.webp
using a cwebp hacked up as described in #19149. Since creating
multi-partition lossy webps requires hacking up `cwebp`, they're likely
very rare in practice. (But maybe other programs using the libwebp API
create them.)
Fixes #19149.
With this, webp lossy support is complete (*) :^)
And with that, webp support is complete: Lossless, lossy, lossy with
alpha, animated lossless, animated lossy, animated lossy with alpha all
work.
(*: Loop filtering isn't implemented yet, which has a minor visual
effect on the output. But it's only visible when carefully comparing
a webp decoded without loop filtering to the same decoded with it.
But it's technically a part of the spec that's still missing.
The upsampling of UV in the YUV->RGB code is also low-quality. This
produces somewhat visible banding in practice in some images (e.g.
in the fire breather's face in 5.webp), so we should probably improve
that at some point. Our JPG decoder has the same issue.)
2023-05-30 14:10:14 +00:00
EXPECT ( Gfx : : WebPImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : WebPImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
WebP/Lossy: Add support for images with more than one partition
Each secondary partition has an independent BooleanDecoder.
Their bitstreams interleave per macroblock row, that is the first
macroblock row is read from the first decoder, the second from the
second, ..., until it wraps around again.
All partitions share a single prediction state though: The second
macroblock row (which reads coefficients off the second decoder) is
predicted using the result of decoding the frist macroblock row (which
reads coefficients off the first decoder).
So if I understand things right, in theory the coefficient reading could
be parallelized, but prediction can't be. (IDCT can also be
parallelized, but that's true with just a single partition too.)
I created the test image by running
examples/cwebp -low_memory -partitions 3 -o foo.webp \
~/src/serenity/Tests/LibGfx/test-inputs/4.webp
using a cwebp hacked up as described in #19149. Since creating
multi-partition lossy webps requires hacking up `cwebp`, they're likely
very rare in practice. (But maybe other programs using the libwebp API
create them.)
Fixes #19149.
With this, webp lossy support is complete (*) :^)
And with that, webp support is complete: Lossless, lossy, lossy with
alpha, animated lossless, animated lossy, animated lossy with alpha all
work.
(*: Loop filtering isn't implemented yet, which has a minor visual
effect on the output. But it's only visible when carefully comparing
a webp decoded without loop filtering to the same decoded with it.
But it's technically a part of the spec that's still missing.
The upsampling of UV in the YUV->RGB code is also low-quality. This
produces somewhat visible banding in practice in some images (e.g.
in the fire breather's face in 5.webp), so we should probably improve
that at some point. Our JPG decoder has the same issue.)
2023-05-30 14:10:14 +00:00
2023-12-09 01:17:15 +00:00
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 1024 , 772 } ) ) ;
2024-07-12 08:08:39 +00:00
EXPECT_EQ ( frame . image - > get_pixel ( 780 , 570 ) , Gfx : : Color ( 0x72 , 0xc7 , 0xf8 , 255 ) ) ;
WebP/Lossy: Add support for images with more than one partition
Each secondary partition has an independent BooleanDecoder.
Their bitstreams interleave per macroblock row, that is the first
macroblock row is read from the first decoder, the second from the
second, ..., until it wraps around again.
All partitions share a single prediction state though: The second
macroblock row (which reads coefficients off the second decoder) is
predicted using the result of decoding the frist macroblock row (which
reads coefficients off the first decoder).
So if I understand things right, in theory the coefficient reading could
be parallelized, but prediction can't be. (IDCT can also be
parallelized, but that's true with just a single partition too.)
I created the test image by running
examples/cwebp -low_memory -partitions 3 -o foo.webp \
~/src/serenity/Tests/LibGfx/test-inputs/4.webp
using a cwebp hacked up as described in #19149. Since creating
multi-partition lossy webps requires hacking up `cwebp`, they're likely
very rare in practice. (But maybe other programs using the libwebp API
create them.)
Fixes #19149.
With this, webp lossy support is complete (*) :^)
And with that, webp support is complete: Lossless, lossy, lossy with
alpha, animated lossless, animated lossy, animated lossy with alpha all
work.
(*: Loop filtering isn't implemented yet, which has a minor visual
effect on the output. But it's only visible when carefully comparing
a webp decoded without loop filtering to the same decoded with it.
But it's technically a part of the spec that's still missing.
The upsampling of UV in the YUV->RGB code is also low-quality. This
produces somewhat visible banding in practice in some images (e.g.
in the fire breather's face in 5.webp), so we should probably improve
that at some point. Our JPG decoder has the same issue.)
2023-05-30 14:10:14 +00:00
}
2023-02-26 00:27:09 +00:00
TEST_CASE ( test_webp_extended_lossless )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " webp/extended-lossless.webp " sv ) ) ) ;
2023-02-26 18:02:50 +00:00
EXPECT ( Gfx : : WebPImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : WebPImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-02-26 00:27:09 +00:00
2023-12-09 01:17:15 +00:00
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 417 , 223 } ) ) ;
2023-04-03 18:54:05 +00:00
// Check some basic pixels.
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) , Gfx : : Color ( 0 , 0 , 0 , 0 ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 43 , 75 ) , Gfx : : Color ( 255 , 0 , 0 , 255 ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 141 , 75 ) , Gfx : : Color ( 0 , 255 , 0 , 255 ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 235 , 75 ) , Gfx : : Color ( 0 , 0 , 255 , 255 ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 341 , 75 ) , Gfx : : Color ( 0 , 0 , 0 , 128 ) ) ;
// Check pixels using the color cache.
EXPECT_EQ ( frame . image - > get_pixel ( 94 , 73 ) , Gfx : : Color ( 255 , 0 , 0 , 255 ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 176 , 115 ) , Gfx : : Color ( 0 , 255 , 0 , 255 ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 290 , 89 ) , Gfx : : Color ( 0 , 0 , 255 , 255 ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 359 , 73 ) , Gfx : : Color ( 0 , 0 , 0 , 128 ) ) ;
2023-02-26 00:27:09 +00:00
}
2023-04-07 19:21:57 +00:00
TEST_CASE ( test_webp_simple_lossless_color_index_transform )
{
// In addition to testing the index transform, this file also tests handling of explicity setting max_symbol.
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " webp/Qpalette.webp " sv ) ) ) ;
2023-04-07 19:21:57 +00:00
EXPECT ( Gfx : : WebPImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : WebPImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-04-07 19:21:57 +00:00
2023-12-09 01:17:15 +00:00
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 256 , 256 } ) ) ;
2023-04-07 19:21:57 +00:00
EXPECT_EQ ( frame . image - > get_pixel ( 100 , 100 ) , Gfx : : Color ( 0x73 , 0x37 , 0x23 , 0xff ) ) ;
}
2023-04-07 22:40:28 +00:00
TEST_CASE ( test_webp_simple_lossless_color_index_transform_pixel_bundling )
{
struct TestCase {
StringView file_name ;
Gfx : : Color line_color ;
Gfx : : Color background_color ;
} ;
// The number after the dash is the number of colors in each file's color index bitmap.
// catdog-alert-2 tests the 1-bit-per-pixel case,
// catdog-alert-3 tests the 2-bit-per-pixel case,
// catdog-alert-8 and catdog-alert-13 both test the 4-bits-per-pixel case.
2023-06-20 01:07:17 +00:00
// catdog-alert-13-alpha-used-false is like catdog-alert-13, but with is_alpha_used set to false in the header
// (which has the effect of ignoring the alpha information in the palette and instead always setting alpha to 0xff).
2023-04-07 22:40:28 +00:00
TestCase test_cases [ ] = {
2023-06-18 22:34:25 +00:00
{ " webp/catdog-alert-2.webp " sv , Gfx : : Color ( 0x35 , 0x12 , 0x0a , 0xff ) , Gfx : : Color ( 0xf3 , 0xe6 , 0xd8 , 0xff ) } ,
{ " webp/catdog-alert-3.webp " sv , Gfx : : Color ( 0x35 , 0x12 , 0x0a , 0xff ) , Gfx : : Color ( 0 , 0 , 0 , 0 ) } ,
{ " webp/catdog-alert-8.webp " sv , Gfx : : Color ( 0 , 0 , 0 , 255 ) , Gfx : : Color ( 0 , 0 , 0 , 0 ) } ,
{ " webp/catdog-alert-13.webp " sv , Gfx : : Color ( 0 , 0 , 0 , 255 ) , Gfx : : Color ( 0 , 0 , 0 , 0 ) } ,
2023-06-20 01:07:17 +00:00
{ " webp/catdog-alert-13-alpha-used-false.webp " sv , Gfx : : Color ( 0 , 0 , 0 , 255 ) , Gfx : : Color ( 0 , 0 , 0 , 255 ) } ,
2023-04-07 22:40:28 +00:00
} ;
for ( auto test_case : test_cases ) {
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( MUST ( String : : formatted ( " {}{} " , TEST_INPUT ( " " ) , test_case . file_name ) ) ) ) ;
2023-04-07 22:40:28 +00:00
EXPECT ( Gfx : : WebPImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : WebPImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-04-07 22:40:28 +00:00
2023-12-09 01:17:15 +00:00
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 32 , 32 } ) ) ;
2023-04-07 22:40:28 +00:00
EXPECT_EQ ( frame . image - > get_pixel ( 4 , 0 ) , test_case . background_color ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 5 , 0 ) , test_case . line_color ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 9 , 5 ) , test_case . background_color ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 10 , 5 ) , test_case . line_color ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 11 , 5 ) , test_case . background_color ) ;
}
}
2023-04-08 21:50:54 +00:00
TEST_CASE ( test_webp_simple_lossless_color_index_transform_pixel_bundling_odd_width )
{
StringView file_names [ ] = {
2023-06-18 22:34:25 +00:00
" webp/width11-height11-colors2.webp " sv ,
" webp/width11-height11-colors3.webp " sv ,
" webp/width11-height11-colors15.webp " sv ,
2023-04-08 21:50:54 +00:00
} ;
for ( auto file_name : file_names ) {
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( MUST ( String : : formatted ( " {}{} " , TEST_INPUT ( " " ) , file_name ) ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : WebPImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 11 , 11 } ) ) ;
2023-04-08 21:50:54 +00:00
}
}
2023-02-26 00:27:09 +00:00
TEST_CASE ( test_webp_extended_lossless_animated )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " webp/extended-lossless-animated.webp " sv ) ) ) ;
2023-02-26 18:02:50 +00:00
EXPECT ( Gfx : : WebPImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : WebPImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-02-26 00:27:09 +00:00
2024-06-03 00:02:16 +00:00
EXPECT_EQ ( plugin_decoder - > loop_count ( ) , 42u ) ;
2023-02-26 03:15:34 +00:00
EXPECT_EQ ( plugin_decoder - > frame_count ( ) , 8u ) ;
EXPECT ( plugin_decoder - > is_animated ( ) ) ;
2023-02-26 00:27:09 +00:00
EXPECT_EQ ( plugin_decoder - > size ( ) , Gfx : : IntSize ( 990 , 1050 ) ) ;
2023-05-06 20:28:26 +00:00
for ( size_t frame_index = 0 ; frame_index < plugin_decoder - > frame_count ( ) ; + + frame_index ) {
2024-01-16 00:04:11 +00:00
auto frame = TRY_OR_FAIL ( plugin_decoder - > frame ( frame_index ) ) ;
2023-05-06 20:28:26 +00:00
EXPECT_EQ ( frame . image - > size ( ) , Gfx : : IntSize ( 990 , 1050 ) ) ;
// This pixel happens to be the same color in all frames.
EXPECT_EQ ( frame . image - > get_pixel ( 500 , 700 ) , Gfx : : Color : : Yellow ) ;
// This one isn't the same in all frames.
2024-07-12 20:40:23 +00:00
EXPECT_EQ ( frame . image - > get_pixel ( 500 , 0 ) , ( frame_index = = 2 | | frame_index = = 6 ) ? Gfx : : Color : : Black : Gfx : : Color ( 0 , 0 , 0 , 0 ) ) ;
2023-05-06 20:28:26 +00:00
}
2023-02-26 00:27:09 +00:00
}
2023-07-02 22:09:27 +00:00
TEST_CASE ( test_tvg )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " tvg/yak.tvg " sv ) ) ) ;
2023-07-02 22:09:27 +00:00
EXPECT ( Gfx : : TinyVGImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : TinyVGImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-07-02 22:09:27 +00:00
2023-12-09 01:17:15 +00:00
TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 1024 , 1024 } ) ) ;
2023-07-02 22:09:27 +00:00
}
2023-07-14 20:35:26 +00:00
TEST_CASE ( test_everything_tvg )
{
Array file_names {
TEST_INPUT ( " tvg/everything.tvg " sv ) ,
TEST_INPUT ( " tvg/everything-32.tvg " sv )
} ;
for ( auto file_name : file_names ) {
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( file_name ) ) ;
2023-07-14 20:35:26 +00:00
EXPECT ( Gfx : : TinyVGImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : TinyVGImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-07-14 20:35:26 +00:00
2023-12-09 01:17:15 +00:00
TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 400 , 768 } ) ) ;
2023-07-14 20:35:26 +00:00
}
}
2023-07-22 03:04:29 +00:00
2023-10-31 18:16:27 +00:00
TEST_CASE ( test_tvg_malformed )
{
Array test_inputs = {
TEST_INPUT ( " tvg/bogus-color-table-size.tvg " sv )
} ;
for ( auto test_input : test_inputs ) {
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( test_input ) ) ;
2023-10-31 18:16:27 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : TinyVGImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame_or_error = plugin_decoder - > frame ( 0 ) ;
EXPECT ( frame_or_error . is_error ( ) ) ;
}
}
2024-03-12 19:31:17 +00:00
TEST_CASE ( test_tvg_rgb565 )
{
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " tvg/green-rgb565.tvg " sv ) ) ) ;
EXPECT ( Gfx : : TinyVGImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : TinyVGImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 100 , 100 } ) ) ;
// Should be a solid dark green:
EXPECT_EQ ( frame . image - > get_pixel ( 50 , 50 ) , Gfx : : Color ( 0 , 130 , 0 ) ) ;
}
2023-07-22 03:04:29 +00:00
TEST_CASE ( test_jxl_modular_simple_tree_upsample2_10bits )
{
2024-01-16 00:04:11 +00:00
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " jxl/modular_simple_tree_upsample2_10bits_rct.jxl " sv ) ) ) ;
2023-07-22 03:04:29 +00:00
EXPECT ( Gfx : : JPEGXLImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
2023-12-09 01:17:15 +00:00
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : JPEGXLImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
2023-07-22 03:04:29 +00:00
2023-12-09 01:17:15 +00:00
TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 128 , 128 } ) ) ;
2023-07-22 17:20:48 +00:00
2024-01-16 00:04:11 +00:00
auto frame = TRY_OR_FAIL ( plugin_decoder - > frame ( 0 ) ) ;
2023-07-31 20:56:12 +00:00
}
2024-07-06 07:33:38 +00:00
TEST_CASE ( test_avif_simple_lossy )
{
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " avif/simple-lossy.avif " sv ) ) ) ;
EXPECT ( Gfx : : AVIFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : AVIFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 240 , 240 } ) ) ;
// While AVIF YUV contents are defined bit-exact, the YUV->RGB conversion isn't.
// So pixels changing by 1 or so below is fine if you change code.
EXPECT_EQ ( frame . image - > get_pixel ( 120 , 232 ) , Gfx : : Color ( 0xf1 , 0xef , 0xf0 , 255 ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 198 , 202 ) , Gfx : : Color ( 0x7b , 0xaa , 0xd6 , 255 ) ) ;
}
TEST_CASE ( test_avif_simple_lossless )
{
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " avif/simple-lossless.avif " sv ) ) ) ;
EXPECT ( Gfx : : AVIFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : AVIFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 386 , 395 } ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 0 , 0 ) , Gfx : : Color ( 0 , 0 , 0 , 0 ) ) ;
EXPECT_EQ ( frame . image - > get_pixel ( 289 , 332 ) , Gfx : : Color ( 0xf2 , 0xee , 0xd3 , 255 ) ) ;
}
TEST_CASE ( test_avif_simple_lossy_bitdepth10 )
{
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " avif/simple-bitdepth10.avif " sv ) ) ) ;
EXPECT ( ! Gfx : : AVIFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
}
TEST_CASE ( test_avif_icc_profile )
{
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " avif/icc_profile.avif " sv ) ) ) ;
EXPECT ( Gfx : : AVIFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : AVIFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 240 , 240 } ) ) ;
EXPECT ( TRY_OR_FAIL ( plugin_decoder - > icc_data ( ) ) . has_value ( ) ) ;
}
TEST_CASE ( test_avif_no_icc_profile )
{
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " avif/simple-lossy.avif " sv ) ) ) ;
EXPECT ( Gfx : : AVIFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : AVIFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame = TRY_OR_FAIL ( expect_single_frame_of_size ( * plugin_decoder , { 240 , 240 } ) ) ;
EXPECT ( ! TRY_OR_FAIL ( plugin_decoder - > icc_data ( ) ) . has_value ( ) ) ;
}
TEST_CASE ( test_avif_frame_out_of_bounds )
{
auto file = TRY_OR_FAIL ( Core : : MappedFile : : map ( TEST_INPUT ( " avif/simple-lossy.avif " sv ) ) ) ;
EXPECT ( Gfx : : AVIFImageDecoderPlugin : : sniff ( file - > bytes ( ) ) ) ;
auto plugin_decoder = TRY_OR_FAIL ( Gfx : : AVIFImageDecoderPlugin : : create ( file - > bytes ( ) ) ) ;
auto frame1 = TRY_OR_FAIL ( plugin_decoder - > frame ( 0 ) ) ;
EXPECT ( plugin_decoder - > frame ( 1 ) . is_error ( ) ) ;
}