mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 09:00:22 +00:00
LibGfx/GIF: Add support for colors
To determine the palette of colors we use the median cut algorithm. While being a correct implementation, enhancements are obviously existing on both the median cut algorithm and the encoding side.
This commit is contained in:
parent
1ba8a6f80f
commit
a0401b0d86
Notes:
sideshowbarker
2024-07-17 05:05:51 +09:00
Author: https://github.com/LucasChollet Commit: https://github.com/SerenityOS/serenity/commit/a0401b0d86 Pull-request: https://github.com/SerenityOS/serenity/pull/24330 Reviewed-by: https://github.com/nico ✅
2 changed files with 13 additions and 16 deletions
|
@ -112,11 +112,8 @@ TEST_CASE(test_bmp)
|
|||
|
||||
TEST_CASE(test_gif)
|
||||
{
|
||||
auto bitmap = TRY_OR_FAIL(create_test_rgb_bitmap());
|
||||
|
||||
// We only support grayscale and non-animated images at the moment - convert bitmap to grayscale.
|
||||
for (auto& argb : *bitmap)
|
||||
argb = Color::from_argb(argb).to_grayscale().value();
|
||||
// Let's limit the size of the image so every color can fit in a color table of 256 elements.
|
||||
auto bitmap = TRY_OR_FAIL(TRY_OR_FAIL(create_test_rgb_bitmap())->cropped({ 0, 0, 16, 16 }));
|
||||
|
||||
auto encoded_bitmap = TRY_OR_FAIL((encode_bitmap<Gfx::GIFWriter>(bitmap)));
|
||||
auto decoder = TRY_OR_FAIL(Gfx::GIFImageDecoderPlugin::create(encoded_bitmap));
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <LibCompress/Lzw.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/ImageFormats/GIFWriter.h>
|
||||
#include <LibGfx/MedianCut.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
@ -49,29 +50,27 @@ ErrorOr<void> write_logical_descriptor(BigEndianOutputBitStream& stream, Bitmap
|
|||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> write_global_color_table(Stream& stream)
|
||||
ErrorOr<void> write_global_color_table(Stream& stream, ColorPalette const& palette)
|
||||
{
|
||||
// 19. Global Color Table
|
||||
|
||||
// FIXME: The color table should include color specific to the image
|
||||
for (u16 i = 0; i < 256; ++i) {
|
||||
TRY(stream.write_value<u8>(i));
|
||||
TRY(stream.write_value<u8>(i));
|
||||
TRY(stream.write_value<u8>(i));
|
||||
auto const color = i < palette.palette().size() ? palette.palette()[i] : Color::NamedColor::White;
|
||||
TRY(stream.write_value<u8>(color.red()));
|
||||
TRY(stream.write_value<u8>(color.green()));
|
||||
TRY(stream.write_value<u8>(color.blue()));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> write_image_data(Stream& stream, Bitmap const& bitmap)
|
||||
ErrorOr<void> write_image_data(Stream& stream, Bitmap const& bitmap, ColorPalette const& palette)
|
||||
{
|
||||
// 22. Table Based Image Data
|
||||
auto const pixel_number = static_cast<u32>(bitmap.width() * bitmap.height());
|
||||
auto indexes = TRY(ByteBuffer::create_uninitialized(pixel_number));
|
||||
for (u32 i = 0; i < pixel_number; ++i) {
|
||||
auto const color = Color::from_argb(*(bitmap.begin() + i));
|
||||
if (color.red() != color.green() || color.green() != color.blue())
|
||||
return Error::from_string_literal("Non grayscale images are unsupported.");
|
||||
indexes[i] = Color::from_argb(*(bitmap.begin() + i)).red(); // Any channel is correct
|
||||
indexes[i] = palette.index_of_closest_color(color);
|
||||
}
|
||||
|
||||
constexpr u8 lzw_minimum_code_size = 8;
|
||||
|
@ -132,15 +131,16 @@ ErrorOr<void> write_trailer(Stream& stream)
|
|||
|
||||
ErrorOr<void> GIFWriter::encode(Stream& stream, Bitmap const& bitmap)
|
||||
{
|
||||
auto const palette = TRY(median_cut(bitmap, 256));
|
||||
TRY(write_header(stream));
|
||||
|
||||
BigEndianOutputBitStream bit_stream { MaybeOwned<Stream> { stream } };
|
||||
TRY(write_logical_descriptor(bit_stream, bitmap));
|
||||
TRY(write_global_color_table(bit_stream));
|
||||
TRY(write_global_color_table(bit_stream, palette));
|
||||
|
||||
// Write a Table-Based Image
|
||||
TRY(write_image_descriptor(bit_stream, bitmap));
|
||||
TRY(write_image_data(stream, bitmap));
|
||||
TRY(write_image_data(stream, bitmap, palette));
|
||||
|
||||
TRY(write_trailer(bit_stream));
|
||||
|
||||
|
|
Loading…
Reference in a new issue