mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-12 19:19:30 +00:00
LibGfx/WebPWriter: Add a toggle for disabling individual transforms
This commit is contained in:
parent
4a327d98a4
commit
533b29dde7
Notes:
sideshowbarker
2024-07-17 20:58:35 +09:00
Author: https://github.com/nico
Commit: 533b29dde7
Pull-request: https://github.com/SerenityOS/serenity/pull/24483
Reviewed-by: https://github.com/gmta ✅
5 changed files with 22 additions and 11 deletions
|
@ -213,7 +213,9 @@ TEST_CASE(test_webp_icc)
|
||||||
auto sRGB_icc_data = MUST(Gfx::ICC::encode(sRGB_icc_profile));
|
auto sRGB_icc_data = MUST(Gfx::ICC::encode(sRGB_icc_profile));
|
||||||
|
|
||||||
auto rgba_bitmap = TRY_OR_FAIL(create_test_rgba_bitmap());
|
auto rgba_bitmap = TRY_OR_FAIL(create_test_rgba_bitmap());
|
||||||
auto encoded_rgba_bitmap = TRY_OR_FAIL((encode_bitmap<Gfx::WebPWriter>(rgba_bitmap, Gfx::WebPEncoderOptions { .icc_data = sRGB_icc_data })));
|
Gfx::WebPEncoderOptions options;
|
||||||
|
options.icc_data = sRGB_icc_data;
|
||||||
|
auto encoded_rgba_bitmap = TRY_OR_FAIL((encode_bitmap<Gfx::WebPWriter>(rgba_bitmap, options)));
|
||||||
|
|
||||||
auto decoded_rgba_plugin = TRY_OR_FAIL(Gfx::WebPImageDecoderPlugin::create(encoded_rgba_bitmap));
|
auto decoded_rgba_plugin = TRY_OR_FAIL(Gfx::WebPImageDecoderPlugin::create(encoded_rgba_bitmap));
|
||||||
expect_bitmaps_equal(*TRY_OR_FAIL(expect_single_frame_of_size(*decoded_rgba_plugin, rgba_bitmap->size())), rgba_bitmap);
|
expect_bitmaps_equal(*TRY_OR_FAIL(expect_single_frame_of_size(*decoded_rgba_plugin, rgba_bitmap->size())), rgba_bitmap);
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
#include <LibGfx/ImageFormats/AnimationWriter.h>
|
#include <LibGfx/ImageFormats/AnimationWriter.h>
|
||||||
#include <LibGfx/ImageFormats/WebPShared.h>
|
#include <LibGfx/ImageFormats/WebPShared.h>
|
||||||
#include <LibGfx/ImageFormats/WebPWriter.h>
|
#include <LibGfx/ImageFormats/WebPWriter.h>
|
||||||
#include <LibGfx/ImageFormats/WebPWriterLossless.h>
|
|
||||||
|
|
||||||
namespace Gfx {
|
namespace Gfx {
|
||||||
|
|
||||||
|
@ -183,7 +182,7 @@ ErrorOr<void> WebPWriter::encode(Stream& stream, Bitmap const& bitmap, Options c
|
||||||
{
|
{
|
||||||
// The chunk headers need to know their size, so we either need a SeekableStream or need to buffer the data. We're doing the latter.
|
// The chunk headers need to know their size, so we either need a SeekableStream or need to buffer the data. We're doing the latter.
|
||||||
bool is_fully_opaque;
|
bool is_fully_opaque;
|
||||||
auto vp8l_data_bytes = TRY(compress_VP8L_image_data(bitmap, is_fully_opaque));
|
auto vp8l_data_bytes = TRY(compress_VP8L_image_data(bitmap, options.vp8l_options, is_fully_opaque));
|
||||||
bool alpha_is_used_hint = !is_fully_opaque;
|
bool alpha_is_used_hint = !is_fully_opaque;
|
||||||
dbgln_if(WEBP_DEBUG, "Writing WebP of size {} with alpha hint: {}", bitmap.size(), alpha_is_used_hint);
|
dbgln_if(WEBP_DEBUG, "Writing WebP of size {} with alpha hint: {}", bitmap.size(), alpha_is_used_hint);
|
||||||
|
|
||||||
|
@ -216,10 +215,11 @@ ErrorOr<void> WebPWriter::encode(Stream& stream, Bitmap const& bitmap, Options c
|
||||||
|
|
||||||
class WebPAnimationWriter : public AnimationWriter {
|
class WebPAnimationWriter : public AnimationWriter {
|
||||||
public:
|
public:
|
||||||
WebPAnimationWriter(SeekableStream& stream, IntSize dimensions, u8 original_vp8x_flags)
|
WebPAnimationWriter(SeekableStream& stream, IntSize dimensions, u8 original_vp8x_flags, VP8LEncoderOptions vp8l_options)
|
||||||
: m_stream(stream)
|
: m_stream(stream)
|
||||||
, m_dimensions(dimensions)
|
, m_dimensions(dimensions)
|
||||||
, m_vp8x_flags(original_vp8x_flags)
|
, m_vp8x_flags(original_vp8x_flags)
|
||||||
|
, m_vp8l_options(vp8l_options)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,6 +232,7 @@ private:
|
||||||
SeekableStream& m_stream;
|
SeekableStream& m_stream;
|
||||||
IntSize m_dimensions;
|
IntSize m_dimensions;
|
||||||
u8 m_vp8x_flags { 0 };
|
u8 m_vp8x_flags { 0 };
|
||||||
|
VP8LEncoderOptions m_vp8l_options;
|
||||||
};
|
};
|
||||||
|
|
||||||
static ErrorOr<void> align_to_two(SeekableStream& stream)
|
static ErrorOr<void> align_to_two(SeekableStream& stream)
|
||||||
|
@ -307,7 +308,7 @@ ErrorOr<void> WebPAnimationWriter::add_frame(Bitmap& bitmap, int duration_ms, In
|
||||||
// compress the frame data directly to the stream, and then go back and update the two sizes.
|
// compress the frame data directly to the stream, and then go back and update the two sizes.
|
||||||
// That's pretty messy though, and the compressed image data is smaller than the uncompressed bitmap passed in. So we'll buffer it.
|
// That's pretty messy though, and the compressed image data is smaller than the uncompressed bitmap passed in. So we'll buffer it.
|
||||||
bool is_fully_opaque;
|
bool is_fully_opaque;
|
||||||
auto vp8l_data_bytes = TRY(compress_VP8L_image_data(bitmap, is_fully_opaque));
|
auto vp8l_data_bytes = TRY(compress_VP8L_image_data(bitmap, m_vp8l_options, is_fully_opaque));
|
||||||
|
|
||||||
ANMFChunkHeader chunk;
|
ANMFChunkHeader chunk;
|
||||||
chunk.frame_x = static_cast<u32>(at.x());
|
chunk.frame_x = static_cast<u32>(at.x());
|
||||||
|
@ -387,7 +388,7 @@ ErrorOr<NonnullOwnPtr<AnimationWriter>> WebPWriter::start_encoding_animation(See
|
||||||
|
|
||||||
TRY(write_ANIM_chunk(stream, { .background_color = background_color.value(), .loop_count = static_cast<u16>(loop_count) }));
|
TRY(write_ANIM_chunk(stream, { .background_color = background_color.value(), .loop_count = static_cast<u16>(loop_count) }));
|
||||||
|
|
||||||
auto writer = make<WebPAnimationWriter>(stream, dimensions, vp8x_flags_from_header(vp8x_header));
|
auto writer = make<WebPAnimationWriter>(stream, dimensions, vp8x_flags_from_header(vp8x_header), options.vp8l_options);
|
||||||
TRY(writer->update_size_in_header());
|
TRY(writer->update_size_in_header());
|
||||||
return writer;
|
return writer;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <AK/Error.h>
|
#include <AK/Error.h>
|
||||||
#include <LibGfx/Color.h>
|
#include <LibGfx/Color.h>
|
||||||
#include <LibGfx/Forward.h>
|
#include <LibGfx/Forward.h>
|
||||||
|
#include <LibGfx/ImageFormats/WebPWriterLossless.h>
|
||||||
#include <LibGfx/Point.h>
|
#include <LibGfx/Point.h>
|
||||||
|
|
||||||
namespace Gfx {
|
namespace Gfx {
|
||||||
|
@ -16,6 +17,7 @@ namespace Gfx {
|
||||||
class AnimationWriter;
|
class AnimationWriter;
|
||||||
|
|
||||||
struct WebPEncoderOptions {
|
struct WebPEncoderOptions {
|
||||||
|
VP8LEncoderOptions vp8l_options;
|
||||||
Optional<ReadonlyBytes> icc_data;
|
Optional<ReadonlyBytes> icc_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -411,13 +411,14 @@ static ErrorOr<NonnullRefPtr<Bitmap>> maybe_write_color_indexing_transform(Littl
|
||||||
return new_bitmap;
|
return new_bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ErrorOr<void> write_VP8L_image_data(Stream& stream, NonnullRefPtr<Bitmap> bitmap, IsOpaque& is_fully_opaque)
|
static ErrorOr<void> write_VP8L_image_data(Stream& stream, NonnullRefPtr<Bitmap> bitmap, VP8LEncoderOptions const& options, IsOpaque& is_fully_opaque)
|
||||||
{
|
{
|
||||||
LittleEndianOutputBitStream bit_stream { MaybeOwned<Stream>(stream) };
|
LittleEndianOutputBitStream bit_stream { MaybeOwned<Stream>(stream) };
|
||||||
|
|
||||||
// image-stream = optional-transform spatially-coded-image
|
// image-stream = optional-transform spatially-coded-image
|
||||||
// optional-transform = (%b1 transform optional-transform) / %b0
|
// optional-transform = (%b1 transform optional-transform) / %b0
|
||||||
bitmap = TRY(maybe_write_color_indexing_transform(bit_stream, bitmap, is_fully_opaque));
|
if (options.allowed_transforms & (1u << COLOR_INDEXING_TRANSFORM))
|
||||||
|
bitmap = TRY(maybe_write_color_indexing_transform(bit_stream, bitmap, is_fully_opaque));
|
||||||
TRY(bit_stream.write_bits(0u, 1u)); // No further transforms for now.
|
TRY(bit_stream.write_bits(0u, 1u)); // No further transforms for now.
|
||||||
|
|
||||||
TRY(write_VP8L_coded_image(ImageKind::SpatiallyCoded, bit_stream, *bitmap, is_fully_opaque));
|
TRY(write_VP8L_coded_image(ImageKind::SpatiallyCoded, bit_stream, *bitmap, is_fully_opaque));
|
||||||
|
@ -429,11 +430,11 @@ static ErrorOr<void> write_VP8L_image_data(Stream& stream, NonnullRefPtr<Bitmap>
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<ByteBuffer> compress_VP8L_image_data(Bitmap const& bitmap, bool& is_fully_opaque)
|
ErrorOr<ByteBuffer> compress_VP8L_image_data(Bitmap const& bitmap, VP8LEncoderOptions const& options, bool& is_fully_opaque)
|
||||||
{
|
{
|
||||||
AllocatingMemoryStream vp8l_data_stream;
|
AllocatingMemoryStream vp8l_data_stream;
|
||||||
IsOpaque is_opaque_struct;
|
IsOpaque is_opaque_struct;
|
||||||
TRY(write_VP8L_image_data(vp8l_data_stream, bitmap, is_opaque_struct));
|
TRY(write_VP8L_image_data(vp8l_data_stream, bitmap, options, is_opaque_struct));
|
||||||
VERIFY(is_opaque_struct.is_opacity_known);
|
VERIFY(is_opaque_struct.is_opacity_known);
|
||||||
is_fully_opaque = is_opaque_struct.is_fully_opaque;
|
is_fully_opaque = is_opaque_struct.is_fully_opaque;
|
||||||
return vp8l_data_stream.read_until_eof();
|
return vp8l_data_stream.read_until_eof();
|
||||||
|
|
|
@ -11,6 +11,11 @@
|
||||||
|
|
||||||
namespace Gfx {
|
namespace Gfx {
|
||||||
|
|
||||||
ErrorOr<ByteBuffer> compress_VP8L_image_data(Bitmap const&, bool& is_fully_opaque);
|
struct VP8LEncoderOptions {
|
||||||
|
// For each TransformType, set bit `1 << transform_type` if that transform type is allowed.
|
||||||
|
unsigned allowed_transforms { 0xf };
|
||||||
|
};
|
||||||
|
|
||||||
|
ErrorOr<ByteBuffer> compress_VP8L_image_data(Bitmap const&, VP8LEncoderOptions const&, bool& is_fully_opaque);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue