| 
									
										
										
										
											2020-01-18 09:38:21 +01:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |  * Copyright (c) 2018-2024, Andreas Kling <andreas@ladybird.org> | 
					
						
							| 
									
										
										
										
											2022-03-13 16:09:41 -06:00
										 |  |  |  * Copyright (c) 2022, the SerenityOS developers. | 
					
						
							| 
									
										
										
										
											2020-01-18 09:38:21 +01:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2021-04-22 01:24:48 -07:00
										 |  |  |  * SPDX-License-Identifier: BSD-2-Clause | 
					
						
							| 
									
										
										
										
											2020-01-18 09:38:21 +01:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-11 07:30:37 -05:00
										 |  |  | #include <AK/Vector.h>
 | 
					
						
							| 
									
										
										
										
											2023-03-21 14:58:06 -04:00
										 |  |  | #include <LibGfx/ImageFormats/PNGLoader.h>
 | 
					
						
							| 
									
										
										
										
											2024-05-22 22:45:22 -04:00
										 |  |  | #include <LibGfx/ImageFormats/TIFFLoader.h>
 | 
					
						
							|  |  |  | #include <LibGfx/ImageFormats/TIFFMetadata.h>
 | 
					
						
							| 
									
										
										
										
											2023-04-08 23:40:55 -06:00
										 |  |  | #include <LibGfx/Painter.h>
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  | #include <png.h>
 | 
					
						
							| 
									
										
										
										
											2019-03-21 03:57:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-06 11:56:38 +01:00
										 |  |  | namespace Gfx { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-08 23:40:55 -06:00
										 |  |  | struct AnimationFrame { | 
					
						
							|  |  |  |     RefPtr<Bitmap> bitmap; | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     int x_offset { 0 }; | 
					
						
							|  |  |  |     int y_offset { 0 }; | 
					
						
							|  |  |  |     int width { 0 }; | 
					
						
							|  |  |  |     int height { 0 }; | 
					
						
							|  |  |  |     int delay_den { 0 }; | 
					
						
							|  |  |  |     int delay_num { 0 }; | 
					
						
							|  |  |  |     u8 blend_op { 0 }; | 
					
						
							|  |  |  |     u8 dispose_op { 0 }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     AnimationFrame(RefPtr<Bitmap> bitmap, int x_offset, int y_offset, int width, int height, int delay_den, int delay_num, u8 blend_op, u8 dispose_op) | 
					
						
							|  |  |  |         : bitmap(move(bitmap)) | 
					
						
							|  |  |  |         , x_offset(x_offset) | 
					
						
							|  |  |  |         , y_offset(y_offset) | 
					
						
							|  |  |  |         , width(width) | 
					
						
							|  |  |  |         , height(height) | 
					
						
							|  |  |  |         , delay_den(delay_den) | 
					
						
							|  |  |  |         , delay_num(delay_num) | 
					
						
							|  |  |  |         , blend_op(blend_op) | 
					
						
							|  |  |  |         , dispose_op(dispose_op) | 
					
						
							| 
									
										
										
										
											2023-04-08 23:40:55 -06:00
										 |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     [[nodiscard]] int duration_ms() const | 
					
						
							| 
									
										
										
										
											2023-04-08 23:40:55 -06:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |         if (delay_num == 0) | 
					
						
							| 
									
										
										
										
											2023-04-08 23:40:55 -06:00
										 |  |  |             return 1; | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |         u32 const denominator = delay_den != 0 ? static_cast<u32>(delay_den) : 100u; | 
					
						
							|  |  |  |         auto unsigned_duration_ms = (delay_num * 1000) / denominator; | 
					
						
							|  |  |  |         if (unsigned_duration_ms > INT_MAX) | 
					
						
							|  |  |  |             return INT_MAX; | 
					
						
							|  |  |  |         return static_cast<int>(unsigned_duration_ms); | 
					
						
							| 
									
										
										
										
											2023-04-08 23:40:55 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     [[nodiscard]] IntRect rect() const { return { x_offset, y_offset, width, height }; } | 
					
						
							| 
									
										
										
										
											2023-04-08 23:40:55 -06:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-21 03:57:42 +01:00
										 |  |  | struct PNGLoadingContext { | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     ReadonlyBytes data; | 
					
						
							|  |  |  |     IntSize size; | 
					
						
							|  |  |  |     u32 frame_count { 0 }; | 
					
						
							|  |  |  |     u32 loop_count { 0 }; | 
					
						
							|  |  |  |     Vector<ImageFrameDescriptor> frame_descriptors; | 
					
						
							|  |  |  |     Optional<ByteBuffer> icc_profile; | 
					
						
							| 
									
										
										
										
											2024-05-22 22:45:22 -04:00
										 |  |  |     OwnPtr<ExifMetadata> exif_metadata; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     Vector<AnimationFrame> animation_frames; | 
					
						
							|  |  |  |     Vector<u8*> row_pointers; | 
					
						
							|  |  |  |     Vector<u8> image_data; | 
					
						
							|  |  |  |     RefPtr<Gfx::Bitmap> decoded_frame_bitmap; | 
					
						
							| 
									
										
										
										
											2019-03-21 03:57:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     ErrorOr<size_t> read_frames(png_structp, png_infop); | 
					
						
							| 
									
										
										
										
											2019-03-21 16:19:11 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  | ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> PNGImageDecoderPlugin::create(ReadonlyBytes bytes) | 
					
						
							| 
									
										
										
										
											2019-03-21 16:40:40 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     auto decoder = adopt_own(*new PNGImageDecoderPlugin(bytes)); | 
					
						
							|  |  |  |     if (!TRY(decoder->initialize())) | 
					
						
							|  |  |  |         return Error::from_string_literal("PNG load error"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return decoder; | 
					
						
							| 
									
										
										
										
											2019-03-21 16:40:40 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  | PNGImageDecoderPlugin::PNGImageDecoderPlugin(ReadonlyBytes data) | 
					
						
							|  |  |  |     : m_context(adopt_own(*new PNGLoadingContext)) | 
					
						
							| 
									
										
										
										
											2020-06-02 17:34:56 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     m_context->data = data; | 
					
						
							| 
									
										
										
										
											2020-06-02 17:34:56 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  | size_t PNGImageDecoderPlugin::first_animated_frame_index() | 
					
						
							| 
									
										
										
										
											2020-06-02 17:34:56 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     return 0; | 
					
						
							| 
									
										
										
										
											2020-06-02 17:34:56 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  | IntSize PNGImageDecoderPlugin::size() | 
					
						
							| 
									
										
										
										
											2020-06-02 17:34:56 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     return m_context->size; | 
					
						
							| 
									
										
										
										
											2020-06-02 17:34:56 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  | bool PNGImageDecoderPlugin::is_animated() | 
					
						
							| 
									
										
										
										
											2022-02-14 17:55:35 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     return m_context->frame_count > 1; | 
					
						
							| 
									
										
										
										
											2022-02-14 17:55:35 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  | size_t PNGImageDecoderPlugin::loop_count() | 
					
						
							| 
									
										
										
										
											2019-03-21 16:19:11 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     return m_context->loop_count; | 
					
						
							| 
									
										
										
										
											2019-03-21 16:19:11 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  | size_t PNGImageDecoderPlugin::frame_count() | 
					
						
							| 
									
										
										
										
											2019-03-21 03:57:42 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     return m_context->frame_count; | 
					
						
							| 
									
										
										
										
											2019-10-16 20:01:11 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-03-21 14:08:14 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  | ErrorOr<ImageFrameDescriptor> PNGImageDecoderPlugin::frame(size_t index, Optional<IntSize>) | 
					
						
							| 
									
										
										
										
											2019-10-16 20:01:11 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     if (index >= m_context->frame_descriptors.size()) | 
					
						
							|  |  |  |         return Error::from_errno(EINVAL); | 
					
						
							| 
									
										
										
										
											2023-04-08 23:40:55 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     return m_context->frame_descriptors[index]; | 
					
						
							| 
									
										
										
										
											2019-10-16 20:01:11 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  | ErrorOr<Optional<ReadonlyBytes>> PNGImageDecoderPlugin::icc_data() | 
					
						
							| 
									
										
										
										
											2023-04-08 23:40:55 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     if (m_context->icc_profile.has_value()) | 
					
						
							|  |  |  |         return Optional<ReadonlyBytes>(*m_context->icc_profile); | 
					
						
							|  |  |  |     return OptionalNone {}; | 
					
						
							| 
									
										
										
										
											2023-04-08 23:40:55 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  | ErrorOr<bool> PNGImageDecoderPlugin::initialize() | 
					
						
							| 
									
										
										
										
											2023-04-08 23:40:55 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); | 
					
						
							|  |  |  |     if (!png_ptr) | 
					
						
							| 
									
										
										
										
											2023-04-08 23:40:55 -06:00
										 |  |  |         return false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     png_infop info_ptr = png_create_info_struct(png_ptr); | 
					
						
							|  |  |  |     if (!info_ptr) { | 
					
						
							|  |  |  |         png_destroy_read_struct(&png_ptr, nullptr, nullptr); | 
					
						
							|  |  |  |         return false; | 
					
						
							| 
									
										
										
										
											2023-04-08 23:40:55 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     if (setjmp(png_jmpbuf(png_ptr))) { | 
					
						
							|  |  |  |         png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); | 
					
						
							| 
									
										
										
										
											2023-04-08 23:40:55 -06:00
										 |  |  |         return false; | 
					
						
							| 
									
										
										
										
											2019-03-21 03:57:42 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     png_set_read_fn(png_ptr, &m_context->data, [](png_structp png_ptr, png_bytep data, png_size_t length) { | 
					
						
							|  |  |  |         auto* read_data = reinterpret_cast<ReadonlyBytes*>(png_get_io_ptr(png_ptr)); | 
					
						
							|  |  |  |         if (read_data->size() < length) { | 
					
						
							|  |  |  |             png_error(png_ptr, "Read error"); | 
					
						
							|  |  |  |             return; | 
					
						
							| 
									
										
										
										
											2020-06-11 09:32:17 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |         memcpy(data, read_data->data(), length); | 
					
						
							|  |  |  |         *read_data = read_data->slice(length); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2020-06-11 09:32:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     png_read_info(png_ptr, info_ptr); | 
					
						
							| 
									
										
										
										
											2021-05-21 10:30:21 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     u32 width = 0; | 
					
						
							|  |  |  |     u32 height = 0; | 
					
						
							|  |  |  |     int bit_depth = 0; | 
					
						
							|  |  |  |     int color_type = 0; | 
					
						
							|  |  |  |     png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, nullptr, nullptr, nullptr); | 
					
						
							|  |  |  |     m_context->size = { static_cast<int>(width), static_cast<int>(height) }; | 
					
						
							| 
									
										
										
										
											2020-06-13 13:17:43 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     if (color_type == PNG_COLOR_TYPE_PALETTE) | 
					
						
							|  |  |  |         png_set_palette_to_rgb(png_ptr); | 
					
						
							| 
									
										
										
										
											2020-06-13 13:17:43 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) | 
					
						
							|  |  |  |         png_set_expand_gray_1_2_4_to_8(png_ptr); | 
					
						
							| 
									
										
										
										
											2020-06-13 13:17:43 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) | 
					
						
							|  |  |  |         png_set_tRNS_to_alpha(png_ptr); | 
					
						
							| 
									
										
										
										
											2020-06-13 13:17:43 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     if (bit_depth == 16) | 
					
						
							|  |  |  |         png_set_strip_16(png_ptr); | 
					
						
							| 
									
										
										
										
											2020-12-23 19:04:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) | 
					
						
							|  |  |  |         png_set_gray_to_rgb(png_ptr); | 
					
						
							| 
									
										
										
										
											2020-06-13 13:17:43 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER); | 
					
						
							|  |  |  |     png_set_bgr(png_ptr); | 
					
						
							| 
									
										
										
										
											2020-06-13 13:17:43 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     char* profile_name = nullptr; | 
					
						
							|  |  |  |     int compression_type = 0; | 
					
						
							|  |  |  |     u8* profile_data = nullptr; | 
					
						
							|  |  |  |     u32 profile_len = 0; | 
					
						
							|  |  |  |     if (png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &profile_len)) { | 
					
						
							|  |  |  |         m_context->icc_profile = TRY(ByteBuffer::copy(profile_data, profile_len)); | 
					
						
							| 
									
										
										
										
											2020-06-13 13:17:43 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     png_read_update_info(png_ptr, info_ptr); | 
					
						
							|  |  |  |     m_context->frame_count = TRY(m_context->read_frames(png_ptr, info_ptr)); | 
					
						
							| 
									
										
										
										
											2020-06-13 13:17:43 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     u8* exif_data = nullptr; | 
					
						
							|  |  |  |     u32 exif_length = 0; | 
					
						
							|  |  |  |     int const num_exif_chunks = png_get_eXIf_1(png_ptr, info_ptr, &exif_length, &exif_data); | 
					
						
							|  |  |  |     if (num_exif_chunks > 0) { | 
					
						
							|  |  |  |         m_context->exif_metadata = TRY(TIFFImageDecoderPlugin::read_exif_metadata({ exif_data, exif_length })); | 
					
						
							| 
									
										
										
										
											2020-06-13 13:17:43 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); | 
					
						
							|  |  |  |     return true; | 
					
						
							| 
									
										
										
										
											2019-10-15 21:48:08 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  | static ErrorOr<NonnullRefPtr<Bitmap>> render_animation_frame(AnimationFrame const& prev_animation_frame, AnimationFrame const& animation_frame, Bitmap const& decoded_frame_bitmap) | 
					
						
							| 
									
										
										
										
											2023-04-08 23:40:55 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     auto rendered_bitmap = TRY(prev_animation_frame.bitmap->clone()); | 
					
						
							|  |  |  |     Painter painter(rendered_bitmap); | 
					
						
							| 
									
										
										
										
											2023-04-08 23:40:55 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     auto frame_rect = animation_frame.rect(); | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     switch (prev_animation_frame.dispose_op) { | 
					
						
							|  |  |  |     case PNG_DISPOSE_OP_BACKGROUND: | 
					
						
							|  |  |  |         painter.clear_rect(rendered_bitmap->rect(), Color::NamedColor::Transparent); | 
					
						
							| 
									
										
										
										
											2023-04-08 23:40:55 -06:00
										 |  |  |         break; | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     case PNG_DISPOSE_OP_PREVIOUS: | 
					
						
							|  |  |  |         painter.blit(frame_rect.location(), decoded_frame_bitmap, frame_rect, 1.0f, false); | 
					
						
							| 
									
										
										
										
											2023-04-08 23:40:55 -06:00
										 |  |  |         break; | 
					
						
							|  |  |  |     default: | 
					
						
							| 
									
										
										
										
											2019-09-29 02:55:28 -05:00
										 |  |  |         break; | 
					
						
							| 
									
										
										
										
											2019-03-21 05:24:42 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     switch (animation_frame.blend_op) { | 
					
						
							|  |  |  |     case PNG_BLEND_OP_SOURCE: | 
					
						
							|  |  |  |         painter.blit(frame_rect.location(), decoded_frame_bitmap, decoded_frame_bitmap.rect(), 1.0f, false); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case PNG_BLEND_OP_OVER: | 
					
						
							|  |  |  |         painter.blit(frame_rect.location(), decoded_frame_bitmap, decoded_frame_bitmap.rect(), 1.0f, true); | 
					
						
							| 
									
										
										
										
											2019-09-29 02:55:28 -05:00
										 |  |  |         break; | 
					
						
							| 
									
										
										
										
											2022-07-10 00:08:32 +02:00
										 |  |  |     default: | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2019-09-29 02:55:28 -05:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     return rendered_bitmap; | 
					
						
							| 
									
										
										
										
											2023-04-08 23:40:55 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  | ErrorOr<size_t> PNGLoadingContext::read_frames(png_structp png_ptr, png_infop info_ptr) | 
					
						
							| 
									
										
										
										
											2023-04-08 23:40:55 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     if (png_get_acTL(png_ptr, info_ptr, &frame_count, &loop_count)) { | 
					
						
							|  |  |  |         // acTL chunk present: This is an APNG.
 | 
					
						
							| 
									
										
										
										
											2023-04-08 23:40:55 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |         png_set_acTL(png_ptr, info_ptr, frame_count, loop_count); | 
					
						
							| 
									
										
										
										
											2023-04-08 23:40:55 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |         for (size_t frame_index = 0; frame_index < frame_count; ++frame_index) { | 
					
						
							|  |  |  |             png_read_frame_head(png_ptr, info_ptr); | 
					
						
							|  |  |  |             u32 width = 0; | 
					
						
							|  |  |  |             u32 height = 0; | 
					
						
							|  |  |  |             u32 x = 0; | 
					
						
							|  |  |  |             u32 y = 0; | 
					
						
							|  |  |  |             u16 delay_num = 0; | 
					
						
							|  |  |  |             u16 delay_den = 0; | 
					
						
							| 
									
										
										
										
											2024-08-19 15:49:11 +02:00
										 |  |  |             u8 dispose_op = PNG_DISPOSE_OP_NONE; | 
					
						
							|  |  |  |             u8 blend_op = PNG_BLEND_OP_SOURCE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (png_get_valid(png_ptr, info_ptr, PNG_INFO_fcTL)) { | 
					
						
							|  |  |  |                 png_get_next_frame_fcTL(png_ptr, info_ptr, &width, &height, &x, &y, &delay_num, &delay_den, &dispose_op, &blend_op); | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 width = png_get_image_width(png_ptr, info_ptr); | 
					
						
							|  |  |  |                 height = png_get_image_height(png_ptr, info_ptr); | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-06-11 19:14:59 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-02 13:02:36 +02:00
										 |  |  |             decoded_frame_bitmap = TRY(Bitmap::create(BitmapFormat::BGRA8888, AlphaType::Unpremultiplied, IntSize { static_cast<int>(width), static_cast<int>(height) })); | 
					
						
							| 
									
										
										
										
											2023-04-08 23:40:55 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |             row_pointers.resize(height); | 
					
						
							|  |  |  |             for (u32 i = 0; i < height; ++i) { | 
					
						
							|  |  |  |                 row_pointers[i] = decoded_frame_bitmap->scanline_u8(i); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2024-05-22 22:45:22 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |             png_read_image(png_ptr, row_pointers.data()); | 
					
						
							| 
									
										
										
										
											2023-04-08 23:40:55 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |             auto animation_frame = AnimationFrame(nullptr, x, y, width, height, delay_den, delay_num, blend_op, dispose_op); | 
					
						
							| 
									
										
										
										
											2023-04-08 23:40:55 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |             if (frame_index == 0) { | 
					
						
							|  |  |  |                 animation_frame.bitmap = decoded_frame_bitmap; | 
					
						
							|  |  |  |                 frame_descriptors.append({ decoded_frame_bitmap, animation_frame.duration_ms() }); | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 animation_frame.bitmap = TRY(render_animation_frame(animation_frames.last(), animation_frame, *decoded_frame_bitmap)); | 
					
						
							|  |  |  |                 frame_descriptors.append({ animation_frame.bitmap, animation_frame.duration_ms() }); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             animation_frames.append(move(animation_frame)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         // This is a single-frame PNG.
 | 
					
						
							| 
									
										
										
										
											2023-06-11 16:20:14 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |         frame_count = 1; | 
					
						
							|  |  |  |         loop_count = 0; | 
					
						
							| 
									
										
										
										
											2019-03-21 03:57:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-02 13:02:36 +02:00
										 |  |  |         decoded_frame_bitmap = TRY(Bitmap::create(BitmapFormat::BGRA8888, AlphaType::Unpremultiplied, size)); | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |         row_pointers.resize(size.height()); | 
					
						
							|  |  |  |         for (int i = 0; i < size.height(); ++i) | 
					
						
							|  |  |  |             row_pointers[i] = decoded_frame_bitmap->scanline_u8(i); | 
					
						
							| 
									
										
										
										
											2023-10-07 10:02:32 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |         png_read_image(png_ptr, row_pointers.data()); | 
					
						
							|  |  |  |         frame_descriptors.append({ move(decoded_frame_bitmap), 0 }); | 
					
						
							| 
									
										
										
										
											2023-10-07 10:02:32 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     return this->frame_count; | 
					
						
							| 
									
										
										
										
											2019-10-15 21:48:08 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-14 13:26:37 -06:00
										 |  |  | PNGImageDecoderPlugin::~PNGImageDecoderPlugin() = default; | 
					
						
							| 
									
										
										
										
											2019-10-15 21:48:08 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-26 18:02:50 +00:00
										 |  |  | bool PNGImageDecoderPlugin::sniff(ReadonlyBytes data) | 
					
						
							| 
									
										
										
										
											2023-01-20 10:13:14 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     Array<u8, 8> png_signature { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a }; | 
					
						
							|  |  |  |     if (data.size() < png_signature.size()) | 
					
						
							| 
									
										
										
										
											2023-04-08 23:40:55 -06:00
										 |  |  |         return false; | 
					
						
							| 
									
										
										
										
											2024-06-18 21:06:15 +02:00
										 |  |  |     return data.slice(0, png_signature.size()) == ReadonlyBytes(png_signature.data(), png_signature.size()); | 
					
						
							| 
									
										
										
										
											2020-05-03 17:12:54 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-22 22:45:22 -04:00
										 |  |  | Optional<Metadata const&> PNGImageDecoderPlugin::metadata() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (m_context->exif_metadata) | 
					
						
							|  |  |  |         return *m_context->exif_metadata; | 
					
						
							|  |  |  |     return OptionalNone {}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-06 11:56:38 +01:00
										 |  |  | } |