diff options
author | Nico Weber <thakis@chromium.org> | 2023-05-08 00:48:23 -0400 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2023-05-09 06:35:56 +0200 |
commit | bc207fd0a05fd50924082e0e92e8f91581d55772 (patch) | |
tree | cde67cb561df759db41f4605c95c084f4e9fe111 | |
parent | 83da7ad875d8b0b5e09d8f6661a4ff7d708e07d6 (diff) | |
download | serenity-bc207fd0a05fd50924082e0e92e8f91581d55772.zip |
LibGfx/WebP: Move lossy decoder to its own file
Pure code move (except of removing `static` on the two public functions
in the new header), not behavior change.
There isn't a lot of lossy decoder yet, but it'll make implementing it
more convenient.
No behavior change.
4 files changed, 120 insertions, 81 deletions
diff --git a/Userland/Libraries/LibGfx/CMakeLists.txt b/Userland/Libraries/LibGfx/CMakeLists.txt index 7af66dafa7..4e88d5f65f 100644 --- a/Userland/Libraries/LibGfx/CMakeLists.txt +++ b/Userland/Libraries/LibGfx/CMakeLists.txt @@ -49,6 +49,7 @@ set(SOURCES ImageFormats/TGALoader.cpp ImageFormats/WebPLoader.cpp ImageFormats/WebPLoaderLossless.cpp + ImageFormats/WebPLoaderLossy.cpp Painter.cpp Palette.cpp Path.cpp diff --git a/Userland/Libraries/LibGfx/ImageFormats/WebPLoader.cpp b/Userland/Libraries/LibGfx/ImageFormats/WebPLoader.cpp index b5cc00e55b..e5b9715d47 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/WebPLoader.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/WebPLoader.cpp @@ -11,11 +11,11 @@ #include <AK/Vector.h> #include <LibGfx/ImageFormats/WebPLoader.h> #include <LibGfx/ImageFormats/WebPLoaderLossless.h> +#include <LibGfx/ImageFormats/WebPLoaderLossy.h> #include <LibGfx/Painter.h> // Overview: https://developers.google.com/speed/webp/docs/compression // Container: https://developers.google.com/speed/webp/docs/riff_container -// Lossy format: https://datatracker.ietf.org/doc/html/rfc6386 namespace Gfx { @@ -55,17 +55,6 @@ struct Chunk { ReadonlyBytes data; }; -struct VP8Header { - u8 version; - bool show_frame; - u32 size_of_first_partition; - u32 width; - u8 horizontal_scale; - u32 height; - u8 vertical_scale; - ReadonlyBytes lossy_data; -}; - struct VP8XHeader { bool has_icc; bool has_alpha; @@ -228,75 +217,6 @@ static ErrorOr<Chunk> decode_webp_advance_chunk(ReadonlyBytes& chunks) return chunk; } -// https://developers.google.com/speed/webp/docs/riff_container#simple_file_format_lossy -// https://datatracker.ietf.org/doc/html/rfc6386#section-19 "Annex A: Bitstream Syntax" -static ErrorOr<VP8Header> decode_webp_chunk_VP8_header(ReadonlyBytes vp8_data) -{ - if (vp8_data.size() < 10) - return Error::from_string_literal("WebPImageDecoderPlugin: 'VP8 ' chunk too small"); - - // FIXME: Eventually, this should probably call into LibVideo/VP8, - // and image decoders should move into LibImageDecoders which depends on both LibGfx and LibVideo. - // (LibVideo depends on LibGfx, so LibGfx can't depend on LibVideo itself.) - - // https://datatracker.ietf.org/doc/html/rfc6386#section-4 "Overview of Compressed Data Format" - // "The decoder is simply presented with a sequence of compressed frames [...] - // The first frame presented to the decompressor is [...] a key frame. [...] - // [E]very compressed frame has three or more pieces. It begins with an uncompressed data chunk comprising 10 bytes in the case of key frames - - u8 const* data = vp8_data.data(); - - // https://datatracker.ietf.org/doc/html/rfc6386#section-9.1 "Uncompressed Data Chunk" - u32 frame_tag = data[0] | (data[1] << 8) | (data[2] << 16); - bool is_key_frame = (frame_tag & 1) == 0; // https://www.rfc-editor.org/errata/eid5534 - u8 version = (frame_tag & 0xe) >> 1; - bool show_frame = (frame_tag & 0x10) != 0; - u32 size_of_first_partition = frame_tag >> 5; - - if (!is_key_frame) - return Error::from_string_literal("WebPImageDecoderPlugin: 'VP8 ' chunk not a key frame"); - - // FIXME: !show_frame does not make sense in a webp file either, probably? - - u32 start_code = data[3] | (data[4] << 8) | (data[5] << 16); - if (start_code != 0x2a019d) // https://www.rfc-editor.org/errata/eid7370 - return Error::from_string_literal("WebPImageDecoderPlugin: 'VP8 ' chunk invalid start_code"); - - // "The scaling specifications for each dimension are encoded as follows. - // 0 | No upscaling (the most common case). - // 1 | Upscale by 5/4. - // 2 | Upscale by 5/3. - // 3 | Upscale by 2." - // This is a display-time operation and doesn't affect decoding. - u16 width_and_horizontal_scale = data[6] | (data[7] << 8); - u16 width = width_and_horizontal_scale & 0x3fff; - u8 horizontal_scale = width_and_horizontal_scale >> 14; - - u16 heigth_and_vertical_scale = data[8] | (data[9] << 8); - u16 height = heigth_and_vertical_scale & 0x3fff; - u8 vertical_scale = heigth_and_vertical_scale >> 14; - - dbgln_if(WEBP_DEBUG, "version {}, show_frame {}, size_of_first_partition {}, width {}, horizontal_scale {}, height {}, vertical_scale {}", - version, show_frame, size_of_first_partition, width, horizontal_scale, height, vertical_scale); - - return VP8Header { version, show_frame, size_of_first_partition, width, horizontal_scale, height, vertical_scale, vp8_data.slice(10) }; -} - -static ErrorOr<NonnullRefPtr<Bitmap>> decode_webp_chunk_VP8_contents(VP8Header const& vp8_header, bool include_alpha_channel) -{ - auto bitmap_format = include_alpha_channel ? BitmapFormat::BGRA8888 : BitmapFormat::BGRx8888; - - // Uncomment this to test ALPH decoding for WebP-lossy-with-alpha images while lossy decoding isn't implemented yet. -#if 0 - return Bitmap::create(bitmap_format, { vp8_header.width, vp8_header.height }); -#else - // FIXME: Implement webp lossy decoding. - (void)vp8_header; - (void)bitmap_format; - return Error::from_string_literal("WebPImageDecoderPlugin: decoding lossy webps not yet implemented"); -#endif -} - // https://developers.google.com/speed/webp/docs/riff_container#alpha static ErrorOr<void> decode_webp_chunk_ALPH(Chunk const& alph_chunk, Bitmap& bitmap) { diff --git a/Userland/Libraries/LibGfx/ImageFormats/WebPLoaderLossy.cpp b/Userland/Libraries/LibGfx/ImageFormats/WebPLoaderLossy.cpp new file mode 100644 index 0000000000..928cddada0 --- /dev/null +++ b/Userland/Libraries/LibGfx/ImageFormats/WebPLoaderLossy.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2023, Nico Weber <thakis@chromium.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <AK/Debug.h> +#include <AK/Endian.h> +#include <AK/Format.h> +#include <AK/MemoryStream.h> +#include <AK/Vector.h> +#include <LibGfx/ImageFormats/WebPLoaderLossy.h> + +// Lossy format: https://datatracker.ietf.org/doc/html/rfc6386 + +namespace Gfx { + +// https://developers.google.com/speed/webp/docs/riff_container#simple_file_format_lossy +// https://datatracker.ietf.org/doc/html/rfc6386#section-19 "Annex A: Bitstream Syntax" +ErrorOr<VP8Header> decode_webp_chunk_VP8_header(ReadonlyBytes vp8_data) +{ + if (vp8_data.size() < 10) + return Error::from_string_literal("WebPImageDecoderPlugin: 'VP8 ' chunk too small"); + + // FIXME: Eventually, this should probably call into LibVideo/VP8, + // and image decoders should move into LibImageDecoders which depends on both LibGfx and LibVideo. + // (LibVideo depends on LibGfx, so LibGfx can't depend on LibVideo itself.) + + // https://datatracker.ietf.org/doc/html/rfc6386#section-4 "Overview of Compressed Data Format" + // "The decoder is simply presented with a sequence of compressed frames [...] + // The first frame presented to the decompressor is [...] a key frame. [...] + // [E]very compressed frame has three or more pieces. It begins with an uncompressed data chunk comprising 10 bytes in the case of key frames + + u8 const* data = vp8_data.data(); + + // https://datatracker.ietf.org/doc/html/rfc6386#section-9.1 "Uncompressed Data Chunk" + u32 frame_tag = data[0] | (data[1] << 8) | (data[2] << 16); + bool is_key_frame = (frame_tag & 1) == 0; // https://www.rfc-editor.org/errata/eid5534 + u8 version = (frame_tag & 0xe) >> 1; + bool show_frame = (frame_tag & 0x10) != 0; + u32 size_of_first_partition = frame_tag >> 5; + + if (!is_key_frame) + return Error::from_string_literal("WebPImageDecoderPlugin: 'VP8 ' chunk not a key frame"); + + // FIXME: !show_frame does not make sense in a webp file either, probably? + + u32 start_code = data[3] | (data[4] << 8) | (data[5] << 16); + if (start_code != 0x2a019d) // https://www.rfc-editor.org/errata/eid7370 + return Error::from_string_literal("WebPImageDecoderPlugin: 'VP8 ' chunk invalid start_code"); + + // "The scaling specifications for each dimension are encoded as follows. + // 0 | No upscaling (the most common case). + // 1 | Upscale by 5/4. + // 2 | Upscale by 5/3. + // 3 | Upscale by 2." + // This is a display-time operation and doesn't affect decoding. + u16 width_and_horizontal_scale = data[6] | (data[7] << 8); + u16 width = width_and_horizontal_scale & 0x3fff; + u8 horizontal_scale = width_and_horizontal_scale >> 14; + + u16 heigth_and_vertical_scale = data[8] | (data[9] << 8); + u16 height = heigth_and_vertical_scale & 0x3fff; + u8 vertical_scale = heigth_and_vertical_scale >> 14; + + dbgln_if(WEBP_DEBUG, "version {}, show_frame {}, size_of_first_partition {}, width {}, horizontal_scale {}, height {}, vertical_scale {}", + version, show_frame, size_of_first_partition, width, horizontal_scale, height, vertical_scale); + + return VP8Header { version, show_frame, size_of_first_partition, width, horizontal_scale, height, vertical_scale, vp8_data.slice(10) }; +} + +ErrorOr<NonnullRefPtr<Bitmap>> decode_webp_chunk_VP8_contents(VP8Header const& vp8_header, bool include_alpha_channel) +{ + auto bitmap_format = include_alpha_channel ? BitmapFormat::BGRA8888 : BitmapFormat::BGRx8888; + + // Uncomment this to test ALPH decoding for WebP-lossy-with-alpha images while lossy decoding isn't implemented yet. +#if 0 + return Bitmap::create(bitmap_format, { vp8_header.width, vp8_header.height }); +#else + // FIXME: Implement webp lossy decoding. + (void)vp8_header; + (void)bitmap_format; + return Error::from_string_literal("WebPImageDecoderPlugin: decoding lossy webps not yet implemented"); +#endif +} + +} diff --git a/Userland/Libraries/LibGfx/ImageFormats/WebPLoaderLossy.h b/Userland/Libraries/LibGfx/ImageFormats/WebPLoaderLossy.h new file mode 100644 index 0000000000..fdf19d61a4 --- /dev/null +++ b/Userland/Libraries/LibGfx/ImageFormats/WebPLoaderLossy.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023, Nico Weber <thakis@chromium.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/Span.h> +#include <AK/Types.h> +#include <LibGfx/Bitmap.h> + +namespace Gfx { + +struct VP8Header { + u8 version; + bool show_frame; + u32 size_of_first_partition; + u32 width; + u8 horizontal_scale; + u32 height; + u8 vertical_scale; + ReadonlyBytes lossy_data; +}; + +// Parses the header data in a VP8 chunk. Pass the payload of a `VP8 ` chunk, after the tag and after the tag's data size. +ErrorOr<VP8Header> decode_webp_chunk_VP8_header(ReadonlyBytes vp8_data); + +ErrorOr<NonnullRefPtr<Bitmap>> decode_webp_chunk_VP8_contents(VP8Header const&, bool include_alpha_channel); + +} |