mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 17:32:44 +00:00 
			
		
		
		
	LibGfx/ILBM: Add an IFF-ILBM decoder :)
IFF was a generic container fileformat that was popular on the Amiga since it was the only file format supported by Deluxe Paint. ILBM is an image format popular in the late eighties/nineties that uses the IFF container. This is a very first version of the decoder that only supports (byterun) compressed files with bpp <= 8. Only the minimal chunks are decoded: CMAP, BODY, BMHD. I am planning to add support for the following variants: - EHB (32 colours + lighter 32 colours) - HAM6 / HAM8 (special mode that allowed to display the whole Amiga 4096 colours / 262 144 colours palette) - TrueColor (24bit) Things that could be fun to do: - Still images could be animated using color cycle information
This commit is contained in:
		
							parent
							
								
									66d6388b8a
								
							
						
					
					
						commit
						fda5590313
					
				
					 12 changed files with 379 additions and 2 deletions
				
			
		|  | @ -206,6 +206,10 @@ | |||
| #    cmakedefine01 ICO_DEBUG | ||||
| #endif | ||||
| 
 | ||||
| #ifndef ILBM_DEBUG | ||||
| #    cmakedefine01 ILBM_DEBUG | ||||
| #endif | ||||
| 
 | ||||
| #ifndef IMAGE_DECODER_DEBUG | ||||
| #    cmakedefine01 IMAGE_DECODER_DEBUG | ||||
| #endif | ||||
|  |  | |||
|  | @ -4,4 +4,4 @@ Executable=/bin/ImageViewer | |||
| Category=Graphics | ||||
| 
 | ||||
| [Launcher] | ||||
| FileTypes=bmp,dds,gif,ico,jpeg,jpg,jxl,pbm,pgm,png,ppm,qoi,tga,tvg | ||||
| FileTypes=bmp,dds,gif,ico,iff,jpeg,jpg,jxl,pbm,pgm,png,ppm,qoi,tga,tvg | ||||
|  |  | |||
|  | @ -72,6 +72,7 @@ set(HTTPJOB_DEBUG ON) | |||
| set(HUNKS_DEBUG ON) | ||||
| set(ICMP_DEBUG ON) | ||||
| set(ICO_DEBUG ON) | ||||
| set(ILBM_DEBUG ON) | ||||
| set(IMAGE_DECODER_DEBUG ON) | ||||
| set(IMAGE_LOADER_DEBUG ON) | ||||
| set(IMAP_PARSER_DEBUG ON) | ||||
|  |  | |||
|  | @ -283,6 +283,7 @@ write_cmake_config("ak_debug_gen") { | |||
|     "HTTPJOB_DEBUG=", | ||||
|     "HUNKS_DEBUG=", | ||||
|     "ICO_DEBUG=", | ||||
|     "ILBM_DEBUG=", | ||||
|     "IMAGE_DECODER_DEBUG=", | ||||
|     "IMAGE_LOADER_DEBUG=", | ||||
|     "ITEM_RECTS_DEBUG=", | ||||
|  |  | |||
|  | @ -41,6 +41,7 @@ shared_library("LibGfx") { | |||
|     "ImageFormats/DDSLoader.cpp", | ||||
|     "ImageFormats/GIFLoader.cpp", | ||||
|     "ImageFormats/ICOLoader.cpp", | ||||
|     "ImageFormats/ILBMLoader.cpp", | ||||
|     "ImageFormats/ImageDecoder.cpp", | ||||
|     "ImageFormats/JPEGLoader.cpp", | ||||
|     "ImageFormats/JPEGWriter.cpp", | ||||
|  |  | |||
|  | @ -132,6 +132,7 @@ static Array const s_registered_mime_type = { | |||
|     MimeType { .name = "image/vnd.ms-dds"sv, .common_extensions = { ".dds"sv }, .description = "DDS image data"sv, .magic_bytes = Vector<u8> { 'D', 'D', 'S', ' ' } }, | ||||
|     MimeType { .name = "image/webp"sv, .common_extensions = { ".webp"sv }, .description = "WebP image data"sv, .magic_bytes = Vector<u8> { 'W', 'E', 'B', 'P' }, .offset = 8 }, | ||||
|     MimeType { .name = "image/x-icon"sv, .common_extensions = { ".ico"sv }, .description = "ICO image data"sv }, | ||||
|     MimeType { .name = "image/x-ilbm"sv, .common_extensions = { ".iff"sv }, .description = "Interleaved bitmap image data"sv, .magic_bytes = Vector<u8> { 0x46, 0x4F, 0x52, 0x4F } }, | ||||
|     MimeType { .name = "image/x-portable-bitmap"sv, .common_extensions = { ".pbm"sv }, .description = "PBM image data"sv, .magic_bytes = Vector<u8> { 0x50, 0x31, 0x0A } }, | ||||
|     MimeType { .name = "image/x-portable-graymap"sv, .common_extensions = { ".pgm"sv }, .description = "PGM image data"sv, .magic_bytes = Vector<u8> { 0x50, 0x32, 0x0A } }, | ||||
|     MimeType { .name = "image/x-portable-pixmap"sv, .common_extensions = { ".ppm"sv }, .description = "PPM image data"sv, .magic_bytes = Vector<u8> { 0x50, 0x33, 0x0A } }, | ||||
|  |  | |||
|  | @ -25,7 +25,7 @@ struct FileTypeFilter { | |||
| 
 | ||||
|     static FileTypeFilter image_files() | ||||
|     { | ||||
|         return FileTypeFilter { "Image Files", Vector<DeprecatedString> { "png", "gif", "bmp", "dip", "pbm", "pgm", "ppm", "ico", "jpeg", "jpg", "jxl", "dds", "qoi", "webp", "tvg" } }; | ||||
|         return FileTypeFilter { "Image Files", Vector<DeprecatedString> { "png", "gif", "bmp", "dip", "pbm", "pgm", "ppm", "ico", "iff", "jpeg", "jpg", "jxl", "dds", "qoi", "webp", "tvg" } }; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -24,6 +24,7 @@ | |||
|     __ENUMERATE_IMAGE_FORMAT(gif, ".gif")   \ | ||||
|     __ENUMERATE_IMAGE_FORMAT(bmp, ".bmp")   \ | ||||
|     __ENUMERATE_IMAGE_FORMAT(ico, ".ico")   \ | ||||
|     __ENUMERATE_IMAGE_FORMAT(iff, ".iff")   \ | ||||
|     __ENUMERATE_IMAGE_FORMAT(jpeg, ".jpg")  \ | ||||
|     __ENUMERATE_IMAGE_FORMAT(jpeg, ".jpeg") \ | ||||
|     __ENUMERATE_IMAGE_FORMAT(jxl, ".jxl")   \ | ||||
|  |  | |||
|  | @ -37,6 +37,7 @@ set(SOURCES | |||
|     ImageFormats/DDSLoader.cpp | ||||
|     ImageFormats/GIFLoader.cpp | ||||
|     ImageFormats/ICOLoader.cpp | ||||
|     ImageFormats/ILBMLoader.cpp | ||||
|     ImageFormats/ImageDecoder.cpp | ||||
|     ImageFormats/ISOBMFF/Boxes.cpp | ||||
|     ImageFormats/ISOBMFF/Reader.cpp | ||||
|  |  | |||
							
								
								
									
										333
									
								
								Userland/Libraries/LibGfx/ImageFormats/ILBMLoader.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										333
									
								
								Userland/Libraries/LibGfx/ImageFormats/ILBMLoader.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,333 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2023, Nicolas Ramz <nicolas.ramz@gmail.com> | ||||
|  * | ||||
|  * SPDX-License-Identifier: BSD-2-Clause | ||||
|  */ | ||||
| 
 | ||||
| #include <AK/Debug.h> | ||||
| #include <AK/Endian.h> | ||||
| #include <AK/FixedArray.h> | ||||
| #include <LibGfx/ImageFormats/ILBMLoader.h> | ||||
| 
 | ||||
| namespace Gfx { | ||||
| 
 | ||||
| struct IFFHeader { | ||||
|     FourCC form; | ||||
|     BigEndian<u32> file_size; | ||||
|     FourCC format; | ||||
| }; | ||||
| 
 | ||||
| static_assert(AssertSize<IFFHeader, 12>()); | ||||
| 
 | ||||
| struct Chunk { | ||||
|     FourCC type; | ||||
|     ReadonlyBytes data; | ||||
| }; | ||||
| 
 | ||||
| enum class CompressionType : u8 { | ||||
|     None = 0, | ||||
|     ByteRun = 1 | ||||
| }; | ||||
| 
 | ||||
| enum class MaskType : u8 { | ||||
|     None = 0, | ||||
|     HasMask = 1, | ||||
|     HasTransparentColor = 2, | ||||
|     HasLasso = 3 | ||||
| }; | ||||
| 
 | ||||
| struct ChunkHeader { | ||||
|     FourCC chunk_type; | ||||
|     BigEndian<u32> chunk_size; | ||||
| }; | ||||
| 
 | ||||
| struct BMHDHeader { | ||||
|     BigEndian<u16> width; | ||||
|     BigEndian<u16> height; | ||||
|     BigEndian<i16> x; | ||||
|     BigEndian<i16> y; | ||||
|     u8 planes; | ||||
|     MaskType mask; | ||||
|     CompressionType compression; | ||||
|     u8 pad; | ||||
|     BigEndian<u16> transparent_color; | ||||
|     u8 x_aspect; | ||||
|     u8 y_aspect; | ||||
|     BigEndian<u16> page_width; | ||||
|     BigEndian<u16> page_height; | ||||
| }; | ||||
| 
 | ||||
| static_assert(sizeof(BMHDHeader) == 20); | ||||
| 
 | ||||
| struct ILBMLoadingContext { | ||||
|     enum class State { | ||||
|         NotDecoded = 0, | ||||
|         HeaderDecoded, | ||||
|         BitmapDecoded | ||||
|     }; | ||||
|     State state { State::NotDecoded }; | ||||
|     ReadonlyBytes data; | ||||
| 
 | ||||
|     // points to current chunk
 | ||||
|     ReadonlyBytes chunks_cursor; | ||||
| 
 | ||||
|     // max number of bytes per plane row
 | ||||
|     u16 pitch; | ||||
| 
 | ||||
|     FixedArray<Color> color_table; | ||||
| 
 | ||||
|     RefPtr<Gfx::Bitmap> bitmap; | ||||
| 
 | ||||
|     BMHDHeader bm_header; | ||||
| }; | ||||
| 
 | ||||
| static ErrorOr<void> decode_iff_ilbm_header(ILBMLoadingContext& context) | ||||
| { | ||||
|     if (context.state >= ILBMLoadingContext::State::HeaderDecoded) | ||||
|         return {}; | ||||
| 
 | ||||
|     if (context.data.size() < sizeof(IFFHeader)) | ||||
|         return Error::from_string_literal("Missing IFF header"); | ||||
| 
 | ||||
|     auto& header = *bit_cast<IFFHeader const*>(context.data.data()); | ||||
|     if (header.form != FourCC("FORM") || header.format != FourCC("ILBM")) | ||||
|         return Error::from_string_literal("Invalid IFF-ILBM header"); | ||||
| 
 | ||||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| static ErrorOr<FixedArray<Color>> decode_cmap_chunk(Chunk cmap_chunk) | ||||
| { | ||||
|     size_t const size = cmap_chunk.data.size() / 3; | ||||
|     FixedArray<Color> color_table = TRY(FixedArray<Color>::create(size)); | ||||
| 
 | ||||
|     for (size_t i = 0; i < size; ++i) { | ||||
|         color_table[i] = Color(cmap_chunk.data[i * 3], cmap_chunk.data[(i * 3) + 1], cmap_chunk.data[(i * 3) + 2]); | ||||
|     } | ||||
| 
 | ||||
|     return color_table; | ||||
| } | ||||
| 
 | ||||
| static ErrorOr<RefPtr<Gfx::Bitmap>> chunky_to_bitmap(ILBMLoadingContext& context, ByteBuffer const& chunky) | ||||
| { | ||||
|     auto const width = context.bm_header.width; | ||||
|     auto const height = context.bm_header.height; | ||||
| 
 | ||||
|     RefPtr<Gfx::Bitmap> bitmap = TRY(Bitmap::create(BitmapFormat::BGRA8888, { width, height })); | ||||
| 
 | ||||
|     dbgln_if(ILBM_DEBUG, "created Bitmap {}x{}", width, height); | ||||
| 
 | ||||
|     for (int row = 0; row < height; ++row) { | ||||
|         for (int col = 0; col < width; ++col) { | ||||
|             u8 index = chunky[(width * row) + col]; | ||||
|             bitmap->set_pixel(col, row, context.color_table[index]); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     dbgln_if(ILBM_DEBUG, "filled Bitmap"); | ||||
| 
 | ||||
|     return bitmap; | ||||
| } | ||||
| 
 | ||||
| static ErrorOr<ByteBuffer> planar_to_chunky(ReadonlyBytes bitplanes, ILBMLoadingContext& context) | ||||
| { | ||||
|     dbgln_if(ILBM_DEBUG, "planar_to_chunky"); | ||||
|     u16 pitch = context.pitch; | ||||
|     u16 width = context.bm_header.width; | ||||
|     u16 height = context.bm_header.height; | ||||
|     u8 planes = context.bm_header.planes; | ||||
|     auto chunky = TRY(ByteBuffer::create_zeroed(width * height)); | ||||
| 
 | ||||
|     for (u16 y = 0; y < height; y++) { | ||||
|         for (u8 p = 0; p < planes; p++) { | ||||
|             u8 const plane_mask = 1 << p; | ||||
|             for (u16 i = 0; i < pitch; i++) { | ||||
|                 u16 offset = (pitch * planes * y) + (p * pitch) + i; | ||||
|                 u8 bit = bitplanes[offset]; | ||||
| 
 | ||||
|                 for (u8 b = 0; b < 8; b++) { | ||||
|                     u8 mask = 1 << (7 - b); | ||||
|                     // get current plane
 | ||||
|                     if (bit & mask) { | ||||
|                         u16 x = (i * 8) + b; | ||||
|                         chunky[(y * width) + x] |= plane_mask; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return chunky; | ||||
| } | ||||
| 
 | ||||
| static ErrorOr<ByteBuffer> uncompress_byte_run(ReadonlyBytes data, ILBMLoadingContext& context) | ||||
| { | ||||
|     auto length = data.size(); | ||||
|     dbgln_if(ILBM_DEBUG, "uncompress_byte_run pitch={} size={}", context.pitch, data.size()); | ||||
| 
 | ||||
|     auto plane_data = TRY(ByteBuffer::create_uninitialized(context.pitch * context.bm_header.height * context.bm_header.planes)); | ||||
| 
 | ||||
|     u32 index = 0; | ||||
|     u32 read_bytes = 0; | ||||
|     while (read_bytes < length) { | ||||
|         auto const byte = static_cast<i8>(data[read_bytes++]); | ||||
|         if (byte >= -127 && byte <= -1) { | ||||
|             // read next byte
 | ||||
|             u8 next_byte = data[read_bytes++]; | ||||
|             for (u16 i = 0; i < -byte + 1; ++i) { | ||||
|                 plane_data[index++] = next_byte; | ||||
|             } | ||||
|         } else if (byte >= 0) { | ||||
|             for (u16 i = 0; i < byte + 1; ++i) { | ||||
|                 plane_data[index] = data[read_bytes]; | ||||
|                 read_bytes++; | ||||
|                 index++; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return plane_data; | ||||
| } | ||||
| 
 | ||||
| static ErrorOr<void> decode_body_chunk(Chunk body_chunk, ILBMLoadingContext& context) | ||||
| { | ||||
|     dbgln_if(ILBM_DEBUG, "decode_body_chunk {}", body_chunk.data.size()); | ||||
| 
 | ||||
|     if (context.bm_header.compression == CompressionType::ByteRun) { | ||||
|         // these are the uncompressed interleaved bitmap planes
 | ||||
|         auto plane_data = TRY(uncompress_byte_run(body_chunk.data, context)); | ||||
|         // that we need to convert to chunky pixel data
 | ||||
|         auto pixel_data = TRY(planar_to_chunky(plane_data, context)); | ||||
| 
 | ||||
|         context.bitmap = TRY(chunky_to_bitmap(context, pixel_data)); | ||||
|     } else { | ||||
|         return Error::from_string_literal("Uncompress body not supported yet"); | ||||
|     } | ||||
| 
 | ||||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| static ErrorOr<Chunk> decode_iff_chunk_header(ReadonlyBytes chunks) | ||||
| { | ||||
|     if (chunks.size() < sizeof(ChunkHeader)) | ||||
|         return Error::from_string_literal("Not enough data for IFF chunk header"); | ||||
| 
 | ||||
|     auto const& header = *bit_cast<ChunkHeader const*>(chunks.data()); | ||||
| 
 | ||||
|     if (chunks.size() < sizeof(ChunkHeader) + header.chunk_size) | ||||
|         return Error::from_string_literal("Not enough data for IFF chunk"); | ||||
| 
 | ||||
|     return Chunk { header.chunk_type, { chunks.data() + sizeof(ChunkHeader), header.chunk_size } }; | ||||
| } | ||||
| 
 | ||||
| static ErrorOr<Chunk> decode_iff_advance_chunk(ReadonlyBytes& chunks) | ||||
| { | ||||
|     auto chunk = TRY(decode_iff_chunk_header(chunks)); | ||||
| 
 | ||||
|     chunks = chunks.slice(sizeof(ChunkHeader) + chunk.data.size()); | ||||
| 
 | ||||
|     // add padding if needed
 | ||||
|     if (chunk.data.size() % 2 != 0) { | ||||
|         if (chunks.is_empty()) | ||||
|             return Error::from_string_literal("Missing data for padding byte"); | ||||
|         if (*chunks.data() != 0) | ||||
|             return Error::from_string_literal("Padding byte is not 0"); | ||||
|         chunks = chunks.slice(1); | ||||
|     } | ||||
| 
 | ||||
|     return chunk; | ||||
| } | ||||
| 
 | ||||
| static ErrorOr<void> decode_iff_chunks(ILBMLoadingContext& context) | ||||
| { | ||||
|     auto& chunks = context.chunks_cursor; | ||||
| 
 | ||||
|     dbgln_if(ILBM_DEBUG, "decode_iff_chunks"); | ||||
| 
 | ||||
|     while (!chunks.is_empty()) { | ||||
|         auto chunk = TRY(decode_iff_advance_chunk(chunks)); | ||||
|         if (chunk.type == FourCC("CMAP")) { | ||||
|             context.color_table = TRY(decode_cmap_chunk(chunk)); | ||||
|         } else if (chunk.type == FourCC("BODY")) { | ||||
|             TRY(decode_body_chunk(chunk, context)); | ||||
|             context.state = ILBMLoadingContext::State::BitmapDecoded; | ||||
|         } else if (chunk.type == FourCC("CRNG")) { | ||||
|             dbgln_if(ILBM_DEBUG, "Chunk:CRNG"); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| static ErrorOr<void> decode_bmhd_chunk(ILBMLoadingContext& context) | ||||
| { | ||||
|     context.chunks_cursor = context.data.slice(sizeof(IFFHeader)); | ||||
|     auto first_chunk = TRY(decode_iff_advance_chunk(context.chunks_cursor)); | ||||
| 
 | ||||
|     if (first_chunk.type != FourCC("BMHD")) | ||||
|         return Error::from_string_literal("IFFImageDecoderPlugin: Invalid chunk type, expected BMHD"); | ||||
| 
 | ||||
|     context.bm_header = *bit_cast<BMHDHeader const*>(first_chunk.data.data()); | ||||
|     context.pitch = ceil_div((u16)context.bm_header.width, (u16)16) * 2; | ||||
| 
 | ||||
|     context.state = ILBMLoadingContext::State::HeaderDecoded; | ||||
| 
 | ||||
|     dbgln_if(ILBM_DEBUG, "IFFImageDecoderPlugin: BMHD: {}x{} ({},{}), p={}, m={}, c={}", | ||||
|         context.bm_header.width, | ||||
|         context.bm_header.height, | ||||
|         context.bm_header.x, | ||||
|         context.bm_header.y, | ||||
|         context.bm_header.planes, | ||||
|         to_underlying(context.bm_header.mask), | ||||
|         to_underlying(context.bm_header.compression)); | ||||
| 
 | ||||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| ILBMImageDecoderPlugin::ILBMImageDecoderPlugin(ReadonlyBytes data, NonnullOwnPtr<ILBMLoadingContext> context) | ||||
|     : m_context(move(context)) | ||||
| { | ||||
|     m_context->data = data; | ||||
| } | ||||
| 
 | ||||
| ILBMImageDecoderPlugin::~ILBMImageDecoderPlugin() = default; | ||||
| 
 | ||||
| IntSize ILBMImageDecoderPlugin::size() | ||||
| { | ||||
|     return IntSize { m_context->bm_header.width, m_context->bm_header.height }; | ||||
| } | ||||
| 
 | ||||
| bool ILBMImageDecoderPlugin::sniff(ReadonlyBytes data) | ||||
| { | ||||
|     ILBMLoadingContext context; | ||||
|     context.data = data; | ||||
| 
 | ||||
|     return !decode_iff_ilbm_header(context).is_error(); | ||||
| } | ||||
| 
 | ||||
| ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> ILBMImageDecoderPlugin::create(ReadonlyBytes data) | ||||
| { | ||||
|     auto context = TRY(try_make<ILBMLoadingContext>()); | ||||
|     auto plugin = TRY(adopt_nonnull_own_or_enomem(new (nothrow) ILBMImageDecoderPlugin(data, move(context)))); | ||||
|     TRY(decode_iff_ilbm_header(*plugin->m_context)); | ||||
|     TRY(decode_bmhd_chunk(*plugin->m_context)); | ||||
|     return plugin; | ||||
| } | ||||
| 
 | ||||
| ErrorOr<ImageFrameDescriptor> ILBMImageDecoderPlugin::frame(size_t index, Optional<IntSize>) | ||||
| { | ||||
|     if (index > 0) | ||||
|         return Error::from_string_literal("ILBMImageDecoderPlugin: frame index must be 0"); | ||||
| 
 | ||||
|     if (m_context->state < ILBMLoadingContext::State::BitmapDecoded) | ||||
|         TRY(decode_iff_chunks(*m_context)); | ||||
| 
 | ||||
|     VERIFY(m_context->bitmap); | ||||
|     return ImageFrameDescriptor { m_context->bitmap, 0 }; | ||||
| } | ||||
| 
 | ||||
| ErrorOr<Optional<ReadonlyBytes>> ILBMImageDecoderPlugin::icc_data() | ||||
| { | ||||
|     return OptionalNone {}; | ||||
| } | ||||
| } | ||||
							
								
								
									
										32
									
								
								Userland/Libraries/LibGfx/ImageFormats/ILBMLoader.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								Userland/Libraries/LibGfx/ImageFormats/ILBMLoader.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2023, Nicolas Ramz <nicolas.ramz@gmail.com> | ||||
|  * | ||||
|  * SPDX-License-Identifier: BSD-2-Clause | ||||
|  */ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <LibGfx/ImageFormats/ImageDecoder.h> | ||||
| 
 | ||||
| namespace Gfx { | ||||
| 
 | ||||
| struct ILBMLoadingContext; | ||||
| 
 | ||||
| class ILBMImageDecoderPlugin final : public ImageDecoderPlugin { | ||||
| public: | ||||
|     static bool sniff(ReadonlyBytes); | ||||
|     static ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> create(ReadonlyBytes); | ||||
| 
 | ||||
|     virtual ~ILBMImageDecoderPlugin() override; | ||||
| 
 | ||||
|     virtual IntSize size() override; | ||||
|     virtual ErrorOr<Optional<ReadonlyBytes>> icc_data() override; | ||||
| 
 | ||||
|     virtual ErrorOr<ImageFrameDescriptor> frame(size_t index, Optional<IntSize> ideal_size = {}) override; | ||||
| 
 | ||||
| private: | ||||
|     ILBMImageDecoderPlugin(ReadonlyBytes, NonnullOwnPtr<ILBMLoadingContext>); | ||||
|     NonnullOwnPtr<ILBMLoadingContext> m_context; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  | @ -9,6 +9,7 @@ | |||
| #include <LibGfx/ImageFormats/DDSLoader.h> | ||||
| #include <LibGfx/ImageFormats/GIFLoader.h> | ||||
| #include <LibGfx/ImageFormats/ICOLoader.h> | ||||
| #include <LibGfx/ImageFormats/ILBMLoader.h> | ||||
| #include <LibGfx/ImageFormats/ImageDecoder.h> | ||||
| #include <LibGfx/ImageFormats/JPEGLoader.h> | ||||
| #include <LibGfx/ImageFormats/JPEGXLLoader.h> | ||||
|  | @ -38,6 +39,7 @@ static OwnPtr<ImageDecoderPlugin> probe_and_sniff_for_appropriate_plugin(Readonl | |||
|         { PGMImageDecoderPlugin::sniff, PGMImageDecoderPlugin::create }, | ||||
|         { PPMImageDecoderPlugin::sniff, PPMImageDecoderPlugin::create }, | ||||
|         { ICOImageDecoderPlugin::sniff, ICOImageDecoderPlugin::create }, | ||||
|         { ILBMImageDecoderPlugin::sniff, ILBMImageDecoderPlugin::create }, | ||||
|         { JPEGImageDecoderPlugin::sniff, JPEGImageDecoderPlugin::create }, | ||||
|         { JPEGXLImageDecoderPlugin::sniff, JPEGXLImageDecoderPlugin::create }, | ||||
|         { DDSImageDecoderPlugin::sniff, DDSImageDecoderPlugin::create }, | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Nicolas Ramz
						Nicolas Ramz