diff --git a/AK/Debug.h.in b/AK/Debug.h.in index a303c386bcf..fca37af7a2f 100644 --- a/AK/Debug.h.in +++ b/AK/Debug.h.in @@ -122,10 +122,6 @@ # cmakedefine01 JOB_DEBUG #endif -#ifndef JPEG2000_DEBUG -# cmakedefine01 JPEG2000_DEBUG -#endif - #ifndef JS_BYTECODE_DEBUG # cmakedefine01 JS_BYTECODE_DEBUG #endif diff --git a/Meta/CMake/all_the_debug_macros.cmake b/Meta/CMake/all_the_debug_macros.cmake index b0cc186cba1..1a3f4d9aa57 100644 --- a/Meta/CMake/all_the_debug_macros.cmake +++ b/Meta/CMake/all_the_debug_macros.cmake @@ -26,7 +26,6 @@ set(IDL_DEBUG ON) set(IMAGE_DECODER_DEBUG ON) set(IMAGE_LOADER_DEBUG ON) set(JOB_DEBUG ON) -set(JPEG2000_DEBUG ON) set(JS_BYTECODE_DEBUG ON) set(JS_MODULE_DEBUG ON) set(LEXER_DEBUG ON) diff --git a/Meta/Lagom/CMakeLists.txt b/Meta/Lagom/CMakeLists.txt index 207f7925e45..54411d2e150 100644 --- a/Meta/Lagom/CMakeLists.txt +++ b/Meta/Lagom/CMakeLists.txt @@ -462,7 +462,6 @@ endif() lagom_utility(icc SOURCES ../../Userland/Utilities/icc.cpp LIBS LibGfx LibMain LibURL) lagom_utility(image SOURCES ../../Userland/Utilities/image.cpp LIBS LibGfx LibMain) -lagom_utility(isobmff SOURCES ../../Userland/Utilities/isobmff.cpp LIBS LibGfx LibMain) lagom_utility(ttfdisasm SOURCES ../../Userland/Utilities/ttfdisasm.cpp LIBS LibGfx LibMain) lagom_utility(js SOURCES ../../Userland/Utilities/js.cpp LIBS LibCrypto LibJS LibLine LibLocale LibMain LibTextCodec Threads::Threads) diff --git a/Meta/Lagom/Fuzzers/FuzzJPEG2000Loader.cpp b/Meta/Lagom/Fuzzers/FuzzJPEG2000Loader.cpp deleted file mode 100644 index 93ebf66228a..00000000000 --- a/Meta/Lagom/Fuzzers/FuzzJPEG2000Loader.cpp +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2024, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) -{ - AK::set_debug_enabled(false); - auto decoder_or_error = Gfx::JPEG2000ImageDecoderPlugin::create({ data, size }); - if (decoder_or_error.is_error()) - return 0; - auto decoder = decoder_or_error.release_value(); - (void)decoder->frame(0); - return 0; -} diff --git a/Meta/Lagom/Fuzzers/fuzzers.cmake b/Meta/Lagom/Fuzzers/fuzzers.cmake index d31233e41db..f43de736cde 100644 --- a/Meta/Lagom/Fuzzers/fuzzers.cmake +++ b/Meta/Lagom/Fuzzers/fuzzers.cmake @@ -13,7 +13,6 @@ set(FUZZER_TARGETS HttpRequest ICCProfile ICOLoader - JPEG2000Loader JPEGLoader Js JsonParser @@ -70,7 +69,6 @@ set(FUZZER_DEPENDENCIES_GzipRoundtrip LibCompress) set(FUZZER_DEPENDENCIES_HttpRequest LibHTTP) set(FUZZER_DEPENDENCIES_ICCProfile LibGfx) set(FUZZER_DEPENDENCIES_ICOLoader LibGfx) -set(FUZZER_DEPENDENCIES_JPEG2000Loader LibGfx) set(FUZZER_DEPENDENCIES_JPEGLoader LibGfx) set(FUZZER_DEPENDENCIES_Js LibJS) set(FUZZER_DEPENDENCIES_LzmaDecompression LibArchive LibCompress) diff --git a/Meta/gn/secondary/AK/BUILD.gn b/Meta/gn/secondary/AK/BUILD.gn index 223f69d2e25..8aa5e8fb40b 100644 --- a/Meta/gn/secondary/AK/BUILD.gn +++ b/Meta/gn/secondary/AK/BUILD.gn @@ -247,7 +247,6 @@ write_cmake_config("ak_debug_gen") { "IMAGE_DECODER_DEBUG=", "IMAGE_LOADER_DEBUG=", "JOB_DEBUG=", - "JPEG2000_DEBUG=", "JS_BYTECODE_DEBUG=", "JS_MODULE_DEBUG=", "LEXER_DEBUG=", diff --git a/Meta/gn/secondary/Userland/Libraries/LibGfx/BUILD.gn b/Meta/gn/secondary/Userland/Libraries/LibGfx/BUILD.gn index a12f60ac25a..220ab65101f 100644 --- a/Meta/gn/secondary/Userland/Libraries/LibGfx/BUILD.gn +++ b/Meta/gn/secondary/Userland/Libraries/LibGfx/BUILD.gn @@ -63,11 +63,7 @@ shared_library("LibGfx") { "ImageFormats/CCITTDecoder.cpp", "ImageFormats/GIFLoader.cpp", "ImageFormats/ICOLoader.cpp", - "ImageFormats/ISOBMFF/Boxes.cpp", - "ImageFormats/ISOBMFF/JPEG2000Boxes.cpp", - "ImageFormats/ISOBMFF/Reader.cpp", "ImageFormats/ImageDecoder.cpp", - "ImageFormats/JPEG2000Loader.cpp", "ImageFormats/JPEGLoader.cpp", "ImageFormats/JPEGWriter.cpp", "ImageFormats/JPEGXLLoader.cpp", diff --git a/Tests/LibGfx/CMakeLists.txt b/Tests/LibGfx/CMakeLists.txt index bee0b7f3f51..225246101f0 100644 --- a/Tests/LibGfx/CMakeLists.txt +++ b/Tests/LibGfx/CMakeLists.txt @@ -9,7 +9,6 @@ set(TEST_SOURCES TestImageWriter.cpp TestMedianCut.cpp TestPainter.cpp - TestParseISOBMFF.cpp TestRect.cpp TestScalingFunctions.cpp TestWOFF.cpp diff --git a/Tests/LibGfx/TestImageDecoder.cpp b/Tests/LibGfx/TestImageDecoder.cpp index 1b9d8e7655c..fee5aec79b1 100644 --- a/Tests/LibGfx/TestImageDecoder.cpp +++ b/Tests/LibGfx/TestImageDecoder.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -334,137 +333,6 @@ TEST_CASE(test_jpeg_malformed_frame) } } -TEST_CASE(test_jpeg2000_spec_annex_j_10) -{ - // J.10 An example of decoding showing intermediate steps - // clang-format off - constexpr Array data = to_array({ - 0xFF, 0x4F, 0xFF, 0x51, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0x01, 0x01, 0xFF, 0x5C, 0x00, - 0x07, 0x40, 0x40, 0x48, 0x48, 0x50, 0xFF, 0x52, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, - 0x04, 0x04, 0x00, 0x01, 0xFF, 0x90, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x01, - 0xFF, 0x93, 0xC7, 0xd4, 0x0C, 0x01, 0x8F, 0x0D, 0xC8, 0x75, 0x5D, 0xC0, 0x7C, 0x21, 0x80, 0x0F, - 0xB1, 0x76, 0xFF, 0xD9, - }); - // clang-format on - - auto plugin_decoder = TRY_OR_FAIL(Gfx::JPEG2000ImageDecoderPlugin::create(data)); - EXPECT_EQ(plugin_decoder->size(), Gfx::IntSize(1, 9)); - - // FIXME: Do something with this. - // For now, this is useful for debugging: - // `Build/lagom/bin/TestImageDecoder test_jpeg2000_spec_annex_j_10` prints internal state with JPEG2000_DEBUG=1. - (void)plugin_decoder->frame(0); -} - -TEST_CASE(test_jpeg2000_simple) -{ - auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("jpeg2000/simple.jp2"sv))); - EXPECT(Gfx::JPEG2000ImageDecoderPlugin::sniff(file->bytes())); - auto plugin_decoder = TRY_OR_FAIL(Gfx::JPEG2000ImageDecoderPlugin::create(file->bytes())); - - EXPECT_EQ(plugin_decoder->size(), Gfx::IntSize(119, 101)); - - auto icc_bytes = MUST(plugin_decoder->icc_data()); - EXPECT(icc_bytes.has_value()); - EXPECT_EQ(icc_bytes->size(), 3144u); -} - -TEST_CASE(test_jpeg2000_gray) -{ - auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("jpeg2000/buggie-gray.jpf"sv))); - EXPECT(Gfx::JPEG2000ImageDecoderPlugin::sniff(file->bytes())); - auto plugin_decoder = TRY_OR_FAIL(Gfx::JPEG2000ImageDecoderPlugin::create(file->bytes())); - - EXPECT_EQ(plugin_decoder->size(), Gfx::IntSize(64, 138)); - - // The file contains both a simple and a real profile. Make sure we get the bigger one. - auto icc_bytes = MUST(plugin_decoder->icc_data()); - EXPECT(icc_bytes.has_value()); - EXPECT_EQ(icc_bytes->size(), 912u); -} - -TEST_CASE(test_jpeg2000_tag_tree) -{ - { - // The example from the NOTE at the end of B.10.2 Tag trees: - auto tree = TRY_OR_FAIL(Gfx::JPEG2000::TagTree::create(6, 3)); - auto bits = to_array({ - 0, 1, 1, 1, 1, // q3(0, 0) - 0, 0, 1, // q3(1, 0) - 1, 0, 1, // q3(2, 0) - }); - size_t index = 0; - Function()> read_bit = [&]() -> bool { - return bits[index++]; - }; - EXPECT_EQ(1u, MUST(tree.read_value(0, 0, read_bit))); - EXPECT_EQ(index, 5u); - EXPECT_EQ(3u, MUST(tree.read_value(1, 0, read_bit))); - EXPECT_EQ(index, 8u); - EXPECT_EQ(2u, MUST(tree.read_value(2, 0, read_bit))); - EXPECT_EQ(index, 11u); - } - - { - // The inclusion tag tree bits from Table B.5 – Example packet header bit stream. - auto tree = TRY_OR_FAIL(Gfx::JPEG2000::TagTree::create(3, 2)); - auto bits = to_array({ - 1, 1, 1, // Code-block 0, 0 included for the first time (partial inclusion tag tree) - 1, // Code-block 1, 0 included for the first time (partial inclusion tag tree) - 0, // Code-block 2, 0 not yet included (partial tag tree) - 0, // Code-block 0, 1 not yet included - 0, // Code-block 1, 2 not yet included - // Code-block 2, 1 not yet included (no data needed, already conveyed by partial tag tree for code-block 2, 0) - }); - size_t index = 0; - Function()> read_bit = [&]() -> bool { - return bits[index++]; - }; - u32 next_layer = 1; - EXPECT_EQ(0u, MUST(tree.read_value(0, 0, read_bit, next_layer))); - EXPECT_EQ(index, 3u); - EXPECT_EQ(0u, MUST(tree.read_value(1, 0, read_bit, next_layer))); - EXPECT_EQ(index, 4u); - EXPECT_EQ(1u, MUST(tree.read_value(2, 0, read_bit, next_layer))); - EXPECT_EQ(index, 5u); - EXPECT_EQ(1u, MUST(tree.read_value(0, 1, read_bit, next_layer))); - EXPECT_EQ(index, 6u); - EXPECT_EQ(1u, MUST(tree.read_value(1, 1, read_bit, next_layer))); - EXPECT_EQ(index, 7u); - EXPECT_EQ(1u, MUST(tree.read_value(2, 1, read_bit, next_layer))); - EXPECT_EQ(index, 7u); // Didn't change! - } - - { - // This isn't in the spec. If one dimension is 2^n + 1 and the other side is just 1, then the topmost node will have - // 2^n x 1 and 1 x 1 children. The first child will have n levels of children. The 1 x 1 child could end immediately, - // or it could require that it also has n levels of (all 1 x 1) children. The spec isn't clear on which of - // the two alternatives should happen. We currently have n levels of 1 x 1 blocks. - constexpr auto n = 5; - auto tree = TRY_OR_FAIL(Gfx::JPEG2000::TagTree::create((1 << n) + 1, 1)); - Vector bits; - bits.append(1); // Finalize topmost node. - bits.append(0); // Increment value in 1 x 1 child. - bits.append(1); // Finalize 1 x 1 child. - - // Finalize further 1 x 1 children, if present. - for (size_t i = 0; i < n; ++i) - bits.append(1); - - size_t index = 0; - Function()> read_bit = [&]() -> bool { - return bits[index++]; - }; - - EXPECT_EQ(1u, MUST(tree.read_value(1 << n, 0, read_bit))); - - // This will read either 3 or 3 + n bits, depending on the interpretation. - EXPECT_EQ(index, 3u + n); - } -} - TEST_CASE(test_png) { auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("png/buggie.png"sv))); diff --git a/Tests/LibGfx/TestParseISOBMFF.cpp b/Tests/LibGfx/TestParseISOBMFF.cpp deleted file mode 100644 index b936794f6c3..00000000000 --- a/Tests/LibGfx/TestParseISOBMFF.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2023, Gregory Bertilson - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -#include -#include -#include - -TEST_CASE(parse_animated_avif) -{ - auto file = MUST(Core::MappedFile::map("./test-inputs/loop_forever.avif"sv)); - auto reader = MUST(Gfx::ISOBMFF::Reader::create(MUST(try_make(file->bytes())))); - auto boxes = MUST(reader.read_entire_file()); - - for (auto& box : boxes) - box->dump(); - - VERIFY(boxes.size() == 4); - VERIFY(boxes[0]->box_type() == Gfx::ISOBMFF::BoxType::FileTypeBox); - auto& file_type_box = static_cast(*boxes[0]); - VERIFY(file_type_box.major_brand == Gfx::ISOBMFF::BrandIdentifier::avis); - VERIFY(file_type_box.minor_version == 0); - Vector expected_compatible_brands = { - Gfx::ISOBMFF::BrandIdentifier::avif, - Gfx::ISOBMFF::BrandIdentifier::avis, - Gfx::ISOBMFF::BrandIdentifier::msf1, - Gfx::ISOBMFF::BrandIdentifier::iso8, - Gfx::ISOBMFF::BrandIdentifier::mif1, - Gfx::ISOBMFF::BrandIdentifier::miaf, - Gfx::ISOBMFF::BrandIdentifier::MA1A, - }; - VERIFY(file_type_box.compatible_brands == expected_compatible_brands); -} diff --git a/Tests/LibGfx/test-inputs/jpeg2000/buggie-gray.jpf b/Tests/LibGfx/test-inputs/jpeg2000/buggie-gray.jpf deleted file mode 100644 index 9203eaf6a04..00000000000 Binary files a/Tests/LibGfx/test-inputs/jpeg2000/buggie-gray.jpf and /dev/null differ diff --git a/Tests/LibGfx/test-inputs/jpeg2000/simple.jp2 b/Tests/LibGfx/test-inputs/jpeg2000/simple.jp2 deleted file mode 100644 index d4e4cfc9c53..00000000000 Binary files a/Tests/LibGfx/test-inputs/jpeg2000/simple.jp2 and /dev/null differ diff --git a/Userland/Libraries/LibGfx/CMakeLists.txt b/Userland/Libraries/LibGfx/CMakeLists.txt index 1d47fa64b0d..892af1ffaa3 100644 --- a/Userland/Libraries/LibGfx/CMakeLists.txt +++ b/Userland/Libraries/LibGfx/CMakeLists.txt @@ -42,10 +42,6 @@ set(SOURCES ImageFormats/GIFWriter.cpp ImageFormats/ICOLoader.cpp ImageFormats/ImageDecoder.cpp - ImageFormats/ISOBMFF/Boxes.cpp - ImageFormats/ISOBMFF/JPEG2000Boxes.cpp - ImageFormats/ISOBMFF/Reader.cpp - ImageFormats/JPEG2000Loader.cpp ImageFormats/JPEGLoader.cpp ImageFormats/JPEGXLLoader.cpp ImageFormats/JPEGWriter.cpp diff --git a/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/BoxStream.h b/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/BoxStream.h deleted file mode 100644 index 5a9783c80d8..00000000000 --- a/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/BoxStream.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2023, Gregory Bertilson - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Gfx::ISOBMFF { - -class BoxStream final : public Stream { -public: - explicit BoxStream(MaybeOwned stream, size_t size) - : m_stream(move(stream)) - , m_data_left(size) - { - } - - virtual bool is_eof() const override { return m_stream->is_eof() || remaining() == 0; } - virtual bool is_open() const override { return m_stream->is_open(); } - virtual void close() override { m_stream->close(); } - virtual ErrorOr read_some(Bytes bytes) override - { - auto read_bytes = TRY(m_stream->read_some(bytes)); - m_data_left -= min(read_bytes.size(), m_data_left); - return read_bytes; - } - - virtual ErrorOr write_some(ReadonlyBytes) override { VERIFY_NOT_REACHED(); } - virtual ErrorOr write_until_depleted(ReadonlyBytes) override { VERIFY_NOT_REACHED(); } - - size_t remaining() const - { - return m_data_left; - } - ErrorOr discard_remaining() - { - return discard(remaining()); - } - -private: - MaybeOwned m_stream; - size_t m_data_left; -}; - -} diff --git a/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Boxes.cpp b/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Boxes.cpp deleted file mode 100644 index af8c916a59d..00000000000 --- a/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Boxes.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (c) 2023, Gregory Bertilson - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Boxes.h" -#include "Reader.h" -#include - -namespace Gfx::ISOBMFF { - -ErrorOr read_box_header(BoxStream& stream) -{ - BoxHeader header; - u64 total_size = TRY(stream.read_value>()); - header.type = TRY(stream.read_value>()); - - u64 data_size_read = sizeof(u32) + sizeof(BoxType); - - if (total_size == 0) { - header.contents_size = stream.remaining(); - } else { - if (total_size == 1) { - total_size = TRY(stream.read_value>()); - data_size_read += sizeof(u64); - } - - header.contents_size = total_size - data_size_read; - } - return header; -} - -void Box::dump(String const& prepend) const -{ - outln("{}{}", prepend, box_type()); -} - -ErrorOr FullBox::read_from_stream(BoxStream& stream) -{ - u32 data = TRY(stream.read_value>()); - // unsigned int(8) version - version = static_cast(data >> 24); - // unsigned int(24) flags - flags = data & 0xFFF; - return {}; -} - -void FullBox::dump(String const& prepend) const -{ - outln("{}{} (version = {}, flags = {:#x})", prepend, box_type(), version, flags); -} - -static String add_indent(String const& string) -{ - return MUST(String::formatted("{} ", string)); -} - -ErrorOr UnknownBox::read_from_stream(BoxStream& stream) -{ - m_contents_size = stream.remaining(); - TRY(stream.discard_remaining()); - return {}; -} - -void UnknownBox::dump(String const& prepend) const -{ - Box::dump(prepend); - - auto indented_prepend = add_indent(prepend); - outln("{}[ {} bytes ]", prepend, m_contents_size); -} - -ErrorOr FileTypeBox::read_from_stream(BoxStream& stream) -{ - // unsigned int(32) major_brand; - major_brand = TRY(stream.read_value>()); - // unsigned int(32) minor_version; - minor_version = TRY(stream.read_value>()); - - // unsigned int(32) compatible_brands[]; // to end of the box - if (stream.remaining() % sizeof(BrandIdentifier) != 0) - return Error::from_string_literal("FileTypeBox compatible_brands contains a partial brand"); - - for (auto minor_brand_count = stream.remaining() / sizeof(BrandIdentifier); minor_brand_count > 0; minor_brand_count--) - TRY(compatible_brands.try_append(TRY(stream.read_value>()))); - - return {}; -} - -void FileTypeBox::dump(String const& prepend) const -{ - Box::dump(prepend); - - auto indented_prepend = add_indent(prepend); - - outln("{}- major_brand = {}", prepend, major_brand); - outln("{}- minor_version = {}", prepend, minor_version); - - StringBuilder compatible_brands_string; - compatible_brands_string.append("- compatible_brands = { "sv); - for (size_t i = 0; i < compatible_brands.size() - 1; i++) - compatible_brands_string.appendff("{}, ", compatible_brands[i]); - compatible_brands_string.appendff("{} }}", compatible_brands[compatible_brands.size() - 1]); - outln("{}{}", prepend, compatible_brands_string.string_view()); -} - -ErrorOr SuperBox::read_from_stream(BoxStream& stream, BoxCallback box_factory) -{ - auto reader = TRY(Gfx::ISOBMFF::Reader::create(MaybeOwned { stream })); - m_child_boxes = TRY(reader.read_entire_file(move(box_factory))); - return {}; -} - -void SuperBox::dump(String const& prepend) const -{ - Box::dump(prepend); - auto indented_prepend = add_indent(prepend); - for (auto const& child_box : m_child_boxes) - child_box->dump(indented_prepend); -} - -ErrorOr UserExtensionBox::read_from_stream(BoxStream& stream) -{ - // unsigned int(8)[16] uuid; - TRY(stream.read_until_filled(uuid)); - // unsigned int(8) data[]; - data = TRY(ByteBuffer::create_uninitialized(stream.remaining())); - TRY(stream.read_until_filled(data)); - return {}; -} - -void UserExtensionBox::dump(String const& prepend) const -{ - Box::dump(prepend); - - auto indented_prepend = add_indent(prepend); - - out("{}- uuid = ", prepend); - for (auto byte : uuid) - out("{:02x}"sv, byte); - outln(); - - outln("{}- data = [ {} bytes ]", prepend, data.size()); -} - -} diff --git a/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Boxes.h b/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Boxes.h deleted file mode 100644 index d909e1fddb1..00000000000 --- a/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Boxes.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2023, Gregory Bertilson - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "BoxStream.h" -#include "Enums.h" - -namespace Gfx::ISOBMFF { - -// ISO/IEC 14496-12 Fifth Edition - -// 4.2 Object Structure -struct BoxHeader { - BoxType type { BoxType::None }; - u64 contents_size { 0 }; -}; - -ErrorOr read_box_header(BoxStream& stream); - -struct Box { - Box() = default; - virtual ~Box() = default; - virtual BoxType box_type() const { return BoxType::None; } - virtual void dump(String const& prepend = {}) const; -}; - -using BoxList = Vector>; - -struct FullBox : public Box { - ErrorOr read_from_stream(BoxStream& stream); - virtual void dump(String const& prepend = {}) const override; - - u8 version { 0 }; - u32 flags { 0 }; -}; - -struct UnknownBox final : public Box { - static ErrorOr> create_from_stream(BoxType type, BoxStream& stream) - { - auto box = TRY(try_make(type, stream.remaining())); - TRY(box->read_from_stream(stream)); - return box; - } - UnknownBox(BoxType type, size_t contents_size) - : m_box_type(type) - , m_contents_size(contents_size) - { - } - virtual ~UnknownBox() override = default; - ErrorOr read_from_stream(BoxStream&); - virtual BoxType box_type() const override { return m_box_type; } - virtual void dump(String const& prepend = {}) const override; - -private: - BoxType m_box_type { BoxType::None }; - size_t m_contents_size { 0 }; -}; - -#define BOX_SUBTYPE(BoxName) \ - static ErrorOr> create_from_stream(BoxStream& stream) \ - { \ - auto box = TRY(try_make()); \ - TRY(box->read_from_stream(stream)); \ - return box; \ - } \ - BoxName() = default; \ - virtual ~BoxName() override = default; \ - ErrorOr read_from_stream(BoxStream& stream); \ - virtual BoxType box_type() const override \ - { \ - return BoxType::BoxName; \ - } \ - virtual void dump(String const& prepend = {}) const override; - -// 4.3 File Type Box -struct FileTypeBox final : public Box { - BOX_SUBTYPE(FileTypeBox); - - BrandIdentifier major_brand { BrandIdentifier::None }; - u32 minor_version; - Vector compatible_brands; -}; - -// A box that contains other boxes. -struct SuperBox : public Box { - SuperBox() = default; - - using BoxCallback = Function>>(BoxType, BoxStream&)>; - ErrorOr read_from_stream(BoxStream&, BoxCallback); - virtual void dump(String const& prepend = {}) const override; - - BoxList const& child_boxes() const { return m_child_boxes; } - -private: - BoxList m_child_boxes; -}; - -struct UserExtensionBox final : public Box { - BOX_SUBTYPE(UserExtensionBox); - - Array uuid; - ByteBuffer data; -}; - -} diff --git a/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Enums.h b/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Enums.h deleted file mode 100644 index b6b92643c7b..00000000000 --- a/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Enums.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2023, Gregory Bertilson - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Gfx::ISOBMFF { - -// Define all Box types. -// In FourCC-asciibetical order. -// The JPEG2000 types are from T-REC-T.800-201511-S!!PDF-E.pdf, Table I.2 – Defined boxes -#define ENUMERATE_ALL() \ - ENUMERATE_ONE(JPEG2000BitsPerComponentBox, "bpcc") \ - ENUMERATE_ONE(JPEG2000ChannelDefinitionBox, "cdef") \ - ENUMERATE_ONE(JPEG2000ComponentMappingBox, "cmap") \ - ENUMERATE_ONE(JPEG2000ColorSpecificationBox, "colr") \ - ENUMERATE_ONE(FreeBox, "free") \ - ENUMERATE_ONE(FileTypeBox, "ftyp") \ - ENUMERATE_ONE(JPEG2000ImageHeaderBox, "ihdr") \ - ENUMERATE_ONE(JPEG2000SignatureBox, "jP ") \ - ENUMERATE_ONE(JPEG2000ContiguousCodestreamBox, "jp2c") \ - ENUMERATE_ONE(JPEG2000HeaderBox, "jp2h") \ - ENUMERATE_ONE(JPEG2000IntellectualPropertyBox, "jp2i") \ - ENUMERATE_ONE(MediaDataBox, "mdat") \ - ENUMERATE_ONE(MetaBox, "meta") \ - ENUMERATE_ONE(MovieBox, "moov") \ - ENUMERATE_ONE(JPEG2000PaletteBox, "pclr") \ - ENUMERATE_ONE(JPEG2000ResolutionBox, "res ") \ - ENUMERATE_ONE(JPEG2000CaptureResolutionBox, "resc") \ - ENUMERATE_ONE(JPEG2000DefaultDisplayResolutionBox, "resd") \ - ENUMERATE_ONE(JPEG2000UUIDInfoBox, "uinf") \ - ENUMERATE_ONE(JPEG2000UUIDListBox, "ulst") \ - ENUMERATE_ONE(JPEG2000URLBox, "url ") \ - ENUMERATE_ONE(UserExtensionBox, "uuid") \ - ENUMERATE_ONE(XMLBox, "xml ") - -enum class BoxType : u32 { - None = 0, - -#define ENUMERATE_ONE(box_name, box_4cc) box_name = RIFF::ChunkID(box_4cc).as_number(), - - ENUMERATE_ALL() - -#undef ENUMERATE_ONE -}; - -static bool is_valid_box_type(BoxType type) -{ - return ( -#define ENUMERATE_ONE(box_name, _) type == BoxType::box_name || - ENUMERATE_ALL() -#undef ENUMERATE_ONE - false); -} - -#undef ENUMERATE_ALL - -// Define all FileTypeBox brand identifiers: -#define ENUMERATE_ALL() \ - ENUMERATE_ONE(iso8) \ - ENUMERATE_ONE(avif) \ - ENUMERATE_ONE(avis) \ - ENUMERATE_ONE(mif1) \ - ENUMERATE_ONE(msf1) \ - ENUMERATE_ONE(miaf) \ - ENUMERATE_ONE(MA1A) - -enum class BrandIdentifier : u32 { - None = 0, - -#define ENUMERATE_ONE(brand_4cc) brand_4cc = RIFF::ChunkID(#brand_4cc).as_number(), - - ENUMERATE_ALL() - -#undef ENUMERATE_ONE -}; - -#undef ENUMERATE_ALL - -} - -template<> -struct AK::Formatter : Formatter { - ErrorOr format(FormatBuilder& builder, Gfx::ISOBMFF::BoxType const& box_type) - { - StringView format_string = Gfx::ISOBMFF::is_valid_box_type(box_type) ? "({})"sv : "Unknown Box ({})"sv; - return Formatter::format(builder, format_string, RIFF::ChunkID::from_number(to_underlying(box_type))); - } -}; - -template<> -struct AK::Formatter : Formatter { - ErrorOr format(FormatBuilder& builder, Gfx::ISOBMFF::BrandIdentifier const& brand_identifier) - { - return Formatter::format(builder, "{}"sv, RIFF::ChunkID::from_number(to_underlying(brand_identifier))); - } -}; diff --git a/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/JPEG2000Boxes.cpp b/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/JPEG2000Boxes.cpp deleted file mode 100644 index f44731e5167..00000000000 --- a/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/JPEG2000Boxes.cpp +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright (c) 2024, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "JPEG2000Boxes.h" -#include - -namespace Gfx::ISOBMFF { - -ErrorOr JPEG2000HeaderBox::read_from_stream(BoxStream& stream) -{ - auto make_subbox = [](BoxType type, BoxStream& stream) -> ErrorOr>> { - switch (type) { - case BoxType::JPEG2000ChannelDefinitionBox: - return TRY(JPEG2000ChannelDefinitionBox::create_from_stream(stream)); - case BoxType::JPEG2000ColorSpecificationBox: - return TRY(JPEG2000ColorSpecificationBox::create_from_stream(stream)); - case BoxType::JPEG2000ImageHeaderBox: - return TRY(JPEG2000ImageHeaderBox::create_from_stream(stream)); - case BoxType::JPEG2000ResolutionBox: - return TRY(JPEG2000ResolutionBox::create_from_stream(stream)); - default: - return OptionalNone {}; - } - }; - - TRY(SuperBox::read_from_stream(stream, move(make_subbox))); - return {}; -} - -void JPEG2000HeaderBox::dump(String const& prepend) const -{ - SuperBox::dump(prepend); -} - -ErrorOr JPEG2000ImageHeaderBox::read_from_stream(BoxStream& stream) -{ - height = TRY(stream.read_value>()); - width = TRY(stream.read_value>()); - num_components = TRY(stream.read_value>()); - bits_per_component = TRY(stream.read_value()); - compression_type = TRY(stream.read_value()); - is_colorspace_unknown = TRY(stream.read_value()); - contains_intellectual_property_rights = TRY(stream.read_value()); - return {}; -} - -void JPEG2000ImageHeaderBox::dump(String const& prepend) const -{ - Box::dump(prepend); - outln("{}- height = {}", prepend, height); - outln("{}- width = {}", prepend, width); - outln("{}- num_components = {}", prepend, num_components); - if (bits_per_component == 0xFF) { - outln("{}- components vary in bit depth", prepend); - } else { - outln("{}- are_components_signed = {}", prepend, (bits_per_component & 0x80) != 0); - outln("{}- bits_per_component = {}", prepend, (bits_per_component & 0x7f) + 1); - } - outln("{}- compression_type = {}", prepend, compression_type); - outln("{}- is_colorspace_unknown = {}", prepend, is_colorspace_unknown); - outln("{}- contains_intellectual_property_rights = {}", prepend, contains_intellectual_property_rights); -} - -ErrorOr JPEG2000ColorSpecificationBox::read_from_stream(BoxStream& stream) -{ - method = TRY(stream.read_value()); - precedence = TRY(stream.read_value()); - approximation = TRY(stream.read_value()); - if (method == 1) - enumerated_color_space = TRY(stream.read_value>()); - if (method == 2) { - ByteBuffer local_icc_data = TRY(ByteBuffer::create_uninitialized(stream.remaining())); - TRY(stream.read_until_filled(local_icc_data)); - icc_data = move(local_icc_data); - } - - // T.801 JPX extended file format syntax, - // Table M.22 – Legal METH values - if (method == 3) { - ByteBuffer local_icc_data = TRY(ByteBuffer::create_uninitialized(stream.remaining())); - TRY(stream.read_until_filled(local_icc_data)); - icc_data = move(local_icc_data); - } - if (method == 4) - return Error::from_string_literal("Method 4 is not yet implemented"); - if (method == 5) - return Error::from_string_literal("Method 5 is not yet implemented"); - - return {}; -} - -void JPEG2000ColorSpecificationBox::dump(String const& prepend) const -{ - Box::dump(prepend); - outln("{}- method = {}", prepend, method); - outln("{}- precedence = {}", prepend, precedence); - outln("{}- approximation = {}", prepend, approximation); - if (method == 1) - outln("{}- enumerated_color_space = {}", prepend, enumerated_color_space); - if (method == 2 || method == 3) - outln("{}- icc_data = {} bytes", prepend, icc_data.size()); -} - -ErrorOr JPEG2000ChannelDefinitionBox::read_from_stream(BoxStream& stream) -{ - u16 count = TRY(stream.read_value>()); - for (u32 i = 0; i < count; ++i) { - Channel channel; - channel.channel_index = TRY(stream.read_value>()); - channel.channel_type = TRY(stream.read_value>()); - channel.channel_association = TRY(stream.read_value>()); - channels.append(channel); - } - return {}; -} - -void JPEG2000ChannelDefinitionBox::dump(String const& prepend) const -{ - Box::dump(prepend); - for (auto const& channel : channels) { - outln("{}- channel_index = {}", prepend, channel.channel_index); - - out("{}- channel_type = {}", prepend, channel.channel_type); - if (channel.channel_type == 0) - outln(" (color)"); - else if (channel.channel_type == 1) - outln(" (opacity)"); - else if (channel.channel_type == 2) - outln(" (premultiplied opacity)"); - else - outln(" (unknown)"); - - outln("{}- channel_association = {}", prepend, channel.channel_association); - } -} - -ErrorOr JPEG2000ResolutionBox::read_from_stream(BoxStream& stream) -{ - auto make_subbox = [](BoxType type, BoxStream& stream) -> ErrorOr>> { - switch (type) { - case BoxType::JPEG2000CaptureResolutionBox: - return TRY(JPEG2000CaptureResolutionBox::create_from_stream(stream)); - case BoxType::JPEG2000DefaultDisplayResolutionBox: - return TRY(JPEG2000DefaultDisplayResolutionBox::create_from_stream(stream)); - default: - return OptionalNone {}; - } - }; - - TRY(SuperBox::read_from_stream(stream, move(make_subbox))); - return {}; -} - -void JPEG2000ResolutionBox::dump(String const& prepend) const -{ - SuperBox::dump(prepend); -} - -ErrorOr JPEG2000ResolutionSubboxBase::read_from_stream(BoxStream& stream) -{ - vertical_capture_grid_resolution_numerator = TRY(stream.read_value>()); - vertical_capture_grid_resolution_denominator = TRY(stream.read_value>()); - horizontal_capture_grid_resolution_numerator = TRY(stream.read_value>()); - horizontal_capture_grid_resolution_denominator = TRY(stream.read_value>()); - vertical_capture_grid_resolution_exponent = TRY(stream.read_value()); - horizontal_capture_grid_resolution_exponent = TRY(stream.read_value()); - return {}; -} - -void JPEG2000ResolutionSubboxBase::dump(String const& prepend) const -{ - Box::dump(prepend); - outln("{}- vertical_capture_grid_resolution = {}/{} * 10^{}", prepend, vertical_capture_grid_resolution_numerator, vertical_capture_grid_resolution_denominator, vertical_capture_grid_resolution_exponent); - outln("{}- horizontal_capture_grid_resolution = {}/{} * 10^{}", prepend, horizontal_capture_grid_resolution_numerator, horizontal_capture_grid_resolution_denominator, horizontal_capture_grid_resolution_exponent); -} - -ErrorOr JPEG2000CaptureResolutionBox::read_from_stream(BoxStream& stream) -{ - return JPEG2000ResolutionSubboxBase::read_from_stream(stream); -} - -void JPEG2000CaptureResolutionBox::dump(String const& prepend) const -{ - JPEG2000ResolutionSubboxBase::dump(prepend); -} - -ErrorOr JPEG2000DefaultDisplayResolutionBox::read_from_stream(BoxStream& stream) -{ - return JPEG2000ResolutionSubboxBase::read_from_stream(stream); -} - -void JPEG2000DefaultDisplayResolutionBox::dump(String const& prepend) const -{ - JPEG2000ResolutionSubboxBase::dump(prepend); -} - -ErrorOr JPEG2000ContiguousCodestreamBox::read_from_stream(BoxStream& stream) -{ - // FIXME: It's wasteful to make a copy of all the image data here. Having just a ReadonlyBytes - // or streaming it into the jpeg2000 decoder would be nicer. - ByteBuffer local_codestream = TRY(ByteBuffer::create_uninitialized(stream.remaining())); - TRY(stream.read_until_filled(local_codestream)); - codestream = move(local_codestream); - return {}; -} - -void JPEG2000ContiguousCodestreamBox::dump(String const& prepend) const -{ - Box::dump(prepend); - outln("{}- codestream = {} bytes", prepend, codestream.size()); -} - -ErrorOr JPEG2000SignatureBox::read_from_stream(BoxStream& stream) -{ - signature = TRY(stream.read_value>()); - return {}; -} - -void JPEG2000SignatureBox::dump(String const& prepend) const -{ - Box::dump(prepend); - outln("{}- signature = {:#08x}", prepend, signature); -} - -ErrorOr JPEG2000UUIDInfoBox::read_from_stream(BoxStream& stream) -{ - auto make_subbox = [](BoxType type, BoxStream& stream) -> ErrorOr>> { - switch (type) { - case BoxType::JPEG2000UUIDListBox: - return TRY(JPEG2000UUIDListBox::create_from_stream(stream)); - case BoxType::JPEG2000URLBox: - return TRY(JPEG2000URLBox::create_from_stream(stream)); - default: - return OptionalNone {}; - } - }; - - TRY(SuperBox::read_from_stream(stream, move(make_subbox))); - return {}; -} - -void JPEG2000UUIDInfoBox::dump(String const& prepend) const -{ - SuperBox::dump(prepend); -} - -ErrorOr JPEG2000UUIDListBox::read_from_stream(BoxStream& stream) -{ - u16 count = TRY(stream.read_value>()); - for (u32 i = 0; i < count; ++i) { - Array uuid; - TRY(stream.read_until_filled(uuid)); - uuids.append(uuid); - } - return {}; -} - -void JPEG2000UUIDListBox::dump(String const& prepend) const -{ - Box::dump(prepend); - for (auto const& uuid : uuids) { - out("{}- ", prepend); - for (auto byte : uuid) { - out("{:02x}", byte); - } - outln(); - } -} - -ErrorOr JPEG2000URLBox::url_as_string() const -{ - // Zero-terminated UTF-8 per spec. - if (url_bytes.is_empty() || url_bytes.bytes().last() != '\0') - return Error::from_string_literal("URL not zero-terminated"); - return String::from_utf8(StringView { url_bytes.bytes().trim(url_bytes.size() - 1) }); -} - -ErrorOr JPEG2000URLBox::read_from_stream(BoxStream& stream) -{ - version_number = TRY(stream.read_value()); - flag = TRY(stream.read_value()) << 16; - flag |= TRY(stream.read_value>()); - - url_bytes = TRY(ByteBuffer::create_uninitialized(stream.remaining())); - TRY(stream.read_until_filled(url_bytes)); - - return {}; -} - -void JPEG2000URLBox::dump(String const& prepend) const -{ - Box::dump(prepend); - outln("{}- version_number = {}", prepend, version_number); - outln("{}- flag = {:#06x}", prepend, flag); - - auto url_or_err = url_as_string(); - if (url_or_err.is_error()) - outln("{}- url = ", prepend, url_or_err.release_error(), url_bytes.size()); - else - outln("{}- url = {}", prepend, url_or_err.release_value()); -} - -} diff --git a/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/JPEG2000Boxes.h b/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/JPEG2000Boxes.h deleted file mode 100644 index c66f523ea79..00000000000 --- a/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/JPEG2000Boxes.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2024, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Boxes.h" - -namespace Gfx::ISOBMFF { - -struct JPEG2000HeaderBox final : public SuperBox { - BOX_SUBTYPE(JPEG2000HeaderBox); -}; - -// I.5.3.1 Image Header box -struct JPEG2000ImageHeaderBox final : public Box { - BOX_SUBTYPE(JPEG2000ImageHeaderBox); - - u32 height { 0 }; - u32 width { 0 }; - u16 num_components { 0 }; - u8 bits_per_component { 0 }; - u8 compression_type { 0 }; - u8 is_colorspace_unknown { 0 }; - u8 contains_intellectual_property_rights { 0 }; -}; - -// I.5.3.3 Colour Specification box -struct JPEG2000ColorSpecificationBox final : public Box { - BOX_SUBTYPE(JPEG2000ColorSpecificationBox); - - u8 method { 0 }; - i8 precedence { 0 }; - u8 approximation { 0 }; - u32 enumerated_color_space { 0 }; // Only set if method == 1 - ByteBuffer icc_data; // Only set if method == 2 -}; - -// I.5.3.6 Channel Definition box -struct JPEG2000ChannelDefinitionBox final : public Box { - BOX_SUBTYPE(JPEG2000ChannelDefinitionBox); - - struct Channel { - u16 channel_index; - u16 channel_type; - u16 channel_association; - }; - Vector channels; -}; - -// I.5.3.7 Resolution box -struct JPEG2000ResolutionBox final : public SuperBox { - BOX_SUBTYPE(JPEG2000ResolutionBox); -}; - -struct JPEG2000ResolutionSubboxBase : public Box { - ErrorOr read_from_stream(BoxStream&); - virtual void dump(String const& prepend) const override; - - u16 vertical_capture_grid_resolution_numerator { 0 }; - u16 vertical_capture_grid_resolution_denominator { 0 }; - u16 horizontal_capture_grid_resolution_numerator { 0 }; - u16 horizontal_capture_grid_resolution_denominator { 0 }; - i8 vertical_capture_grid_resolution_exponent { 0 }; - i8 horizontal_capture_grid_resolution_exponent { 0 }; -}; - -// I.5.3.7.1 Capture Resolution box -struct JPEG2000CaptureResolutionBox final : public JPEG2000ResolutionSubboxBase { - BOX_SUBTYPE(JPEG2000CaptureResolutionBox); -}; - -// I.5.3.7.2 Default Display Resolution box -struct JPEG2000DefaultDisplayResolutionBox final : public JPEG2000ResolutionSubboxBase { - BOX_SUBTYPE(JPEG2000DefaultDisplayResolutionBox); -}; - -// I.5.4 Contiguous Codestream box -struct JPEG2000ContiguousCodestreamBox final : public Box { - BOX_SUBTYPE(JPEG2000ContiguousCodestreamBox); - - ByteBuffer codestream; -}; - -struct JPEG2000SignatureBox final : public Box { - BOX_SUBTYPE(JPEG2000SignatureBox); - - u32 signature { 0 }; -}; - -// I.7.3 UUID Info boxes (superbox) -struct JPEG2000UUIDInfoBox final : public SuperBox { - BOX_SUBTYPE(JPEG2000UUIDInfoBox); -}; - -// I.7.3.1 UUID List box -struct JPEG2000UUIDListBox final : public Box { - BOX_SUBTYPE(JPEG2000UUIDListBox); - - Vector> uuids; -}; - -// I.7.3.2 Data Entry URL box -struct JPEG2000URLBox final : public Box { - BOX_SUBTYPE(JPEG2000URLBox); - - ErrorOr url_as_string() const; - - u8 version_number; - u32 flag; - ByteBuffer url_bytes; -}; - -} diff --git a/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Reader.cpp b/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Reader.cpp deleted file mode 100644 index 5b5e8a73b05..00000000000 --- a/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Reader.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2023, Gregory Bertilson - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Reader.h" -#include "JPEG2000Boxes.h" -#include - -namespace Gfx::ISOBMFF { - -ErrorOr Reader::create(MaybeOwned stream) -{ - size_t size = TRY(stream->size()); - return Reader(make(move(stream), size)); -} - -ErrorOr Reader::create(MaybeOwned stream) -{ - return Reader(move(stream)); -} - -ErrorOr Reader::read_entire_file() -{ - auto make_top_level_box = [](BoxType type, BoxStream& stream) -> ErrorOr>> { - switch (type) { - case BoxType::FileTypeBox: - return TRY(FileTypeBox::create_from_stream(stream)); - case BoxType::JPEG2000ContiguousCodestreamBox: - return TRY(JPEG2000ContiguousCodestreamBox::create_from_stream(stream)); - case BoxType::JPEG2000HeaderBox: - return TRY(JPEG2000HeaderBox::create_from_stream(stream)); - case BoxType::JPEG2000SignatureBox: - return TRY(JPEG2000SignatureBox::create_from_stream(stream)); - case BoxType::JPEG2000UUIDInfoBox: - return TRY(JPEG2000UUIDInfoBox::create_from_stream(stream)); - case BoxType::UserExtensionBox: - return TRY(UserExtensionBox::create_from_stream(stream)); - default: - return OptionalNone {}; - } - }; - return read_entire_file((ErrorOr>>(*)(BoxType, BoxStream&))(make_top_level_box)); -} - -ErrorOr Reader::read_entire_file(BoxCallback box_factory) -{ - BoxList top_level_boxes; - - while (!m_box_stream->is_eof()) { - auto box_header = TRY(read_box_header(*m_box_stream)); - BoxStream box_stream { MaybeOwned { *m_box_stream }, static_cast(box_header.contents_size) }; - - auto maybe_box = TRY(box_factory(box_header.type, box_stream)); - if (maybe_box.has_value()) { - TRY(top_level_boxes.try_append(move(maybe_box.value()))); - } else { - TRY(top_level_boxes.try_append(TRY(UnknownBox::create_from_stream(box_header.type, box_stream)))); - } - - if (!box_stream.is_eof()) { - dbgln("Reader did not consume all data for box type {}, {} bytes remaining", RIFF::ChunkID::from_number(to_underlying(box_header.type)), box_stream.remaining()); - return Error::from_string_literal("Reader did not consume all data"); - } - } - return top_level_boxes; -} - -} diff --git a/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Reader.h b/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Reader.h deleted file mode 100644 index 5b03a8758ee..00000000000 --- a/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Reader.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2023, Gregory Bertilson - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -#include "Boxes.h" - -namespace Gfx::ISOBMFF { - -class Reader { -public: - static ErrorOr create(MaybeOwned stream); - static ErrorOr create(MaybeOwned stream); - - ErrorOr read_entire_file(); - - using BoxCallback = Function>>(BoxType, BoxStream&)>; - ErrorOr read_entire_file(BoxCallback); - -private: - Reader(MaybeOwned stream) - : m_box_stream(move(stream)) - { - } - - MaybeOwned m_box_stream; -}; - -} diff --git a/Userland/Libraries/LibGfx/ImageFormats/ImageDecoder.cpp b/Userland/Libraries/LibGfx/ImageFormats/ImageDecoder.cpp index d7645c97ca7..d5656872926 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/ImageDecoder.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/ImageDecoder.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -30,7 +29,6 @@ static ErrorOr> probe_and_sniff_for_appropriate_plugi { BMPImageDecoderPlugin::sniff, BMPImageDecoderPlugin::create }, { GIFImageDecoderPlugin::sniff, GIFImageDecoderPlugin::create }, { ICOImageDecoderPlugin::sniff, ICOImageDecoderPlugin::create }, - { JPEG2000ImageDecoderPlugin::sniff, JPEG2000ImageDecoderPlugin::create }, { JPEGImageDecoderPlugin::sniff, JPEGImageDecoderPlugin::create }, { JPEGXLImageDecoderPlugin::sniff, JPEGXLImageDecoderPlugin::create }, { PNGImageDecoderPlugin::sniff, PNGImageDecoderPlugin::create }, diff --git a/Userland/Libraries/LibGfx/ImageFormats/JPEG2000Loader.cpp b/Userland/Libraries/LibGfx/ImageFormats/JPEG2000Loader.cpp deleted file mode 100644 index 752b37c9369..00000000000 --- a/Userland/Libraries/LibGfx/ImageFormats/JPEG2000Loader.cpp +++ /dev/null @@ -1,1026 +0,0 @@ -/* - * Copyright (c) 2024, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -// Core coding system spec (.jp2 format): T-REC-T.800-201511-S!!PDF-E.pdf available here: -// https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-T.800-201511-S!!PDF-E&type=items - -// There is a useful example bitstream in the spec in: -// J.10 An example of decoding showing intermediate - -// Extensions (.jpx format): T-REC-T.801-202106-S!!PDF-E.pdf available here: -// https://handle.itu.int/11.1002/1000/14666-en?locatt=format:pdf&auth - -// rfc3745 lists the MIME type. It only mentions the jp2_id_string as magic number. - -namespace Gfx { - -// A JPEG2000 image can be stored in a codestream with markers, similar to a JPEG image, -// or in a JP2 file, which is a container format based on boxes similar to ISOBMFF. - -// This is the marker for the codestream version. We don't support this yet. -// T.800 Annex A, Codestream syntax, A.2 Information in the marker segments and A.3 Construction of the codestream -static constexpr u8 marker_id_string[] = { 0xFF, 0x4F, 0xFF, 0x51 }; - -// This is the marker for the box version. -// T.800 Annex I, JP2 file format syntax, I.5.1 JPEG 2000 Signature box -static constexpr u8 jp2_id_string[] = { 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A }; - -// Table A.2 – List of markers and marker segments -// "Delimiting markers and marker segments" -#define J2K_SOC 0xFF4F // "Start of codestream" -#define J2K_SOT 0xFF90 // "Start of tile-part" -#define J2K_SOD 0xFF93 // "Start of data" -#define J2K_EOC 0xFFD9 // "End of codestream" -// "Fixed information marker segments" -#define J2K_SIZ 0xFF51 // "Image and tile size" -// "Functional marker segments" -#define J2K_COD 0xFF52 // "Coding style default" -#define J2K_COC 0xFF53 // "Coding style component" -#define J2K_RGN 0xFF5E // "Region-of-interest" -#define J2K_QCD 0xFF5C // "Quantization default" -#define J2K_QCC 0xFF5D // "Quantization component" -#define J2K_POC 0xFF5F // "Progression order change" -// "Pointer marker segments" -#define J2K_TLM 0xFF55 // "Tile-part lengths" -#define J2K_PLM 0xFF57 // "Packet length, main header" -#define J2K_PLT 0xFF58 // "Packet length, tile-part header" -#define J2K_PPM 0xFF60 // "Packed packet headers, main header" -#define J2K_PPT 0xFF61 // "Packed packet headers, tile-part header" -// "In-bit-stream markers and marker segments" -#define J2K_SOP 0xFF91 // "Start of packet" -#define J2K_EPH 0xFF92 // "End of packet header" -// "Informational marker segments" -#define J2K_CRG 0xFF63 // "Component registration" -#define J2K_COM 0xFF64 // "Comment" - -// A.4.2 Start of tile-part (SOT) -struct StartOfTilePart { - // "Tile index. This number refers to the tiles in raster order starting at the number 0." - u16 tile_index { 0 }; // "Isot" in spec. - - // "Length, in bytes, from the beginning of the first byte of this SOT marker segment of the tile-part to - // the end of the data of that tile-part. Figure A.16 shows this alignment. Only the last tile-part in the - // codestream may contain a 0 for Psot. If the Psot is 0, this tile-part is assumed to contain all data until - // the EOC marker." - u32 tile_part_length { 0 }; // "Psot" in spec. - - // "Tile-part index. There is a specific order required for decoding tile-parts; this index denotes the order - // from 0. If there is only one tile-part for a tile, then this value is zero. The tile-parts of this tile shall - // appear in the codestream in this order, although not necessarily consecutively." - u8 tile_part_index { 0 }; // "TPsot" in spec. - - // "Number of tile-parts of a tile in the codestream. Two values are allowed: the correct number of tile- - // parts for that tile and zero. A zero value indicates that the number of tile-parts of this tile is not - // specified in this tile-part. - u8 number_of_tile_parts { 0 }; // "TNsot" in spec. -}; - -static ErrorOr read_start_of_tile_part(ReadonlyBytes data) -{ - FixedMemoryStream stream { data }; - - StartOfTilePart sot; - sot.tile_index = TRY(stream.read_value>()); - sot.tile_part_length = TRY(stream.read_value>()); - sot.tile_part_index = TRY(stream.read_value()); - sot.number_of_tile_parts = TRY(stream.read_value()); - - dbgln_if(JPEG2000_DEBUG, "JPEG2000ImageDecoderPlugin: SOT marker segment: tile_index={}, tile_part_length={}, tile_part_index={}, number_of_tile_parts={}", sot.tile_index, sot.tile_part_length, sot.tile_part_index, sot.number_of_tile_parts); - - return sot; -} - -// A.5.1 Image and tile size (SIZ) -struct ImageAndTileSize { - // "Denotes capabilities that a decoder needs to properly decode the codestream." - u16 needed_decoder_capabilities { 0 }; // "Rsiz" in spec. - - // "Width of the reference grid." - u32 width { 0 }; // "Xsiz" in spec. - - // "Height of the reference grid." - u32 height { 0 }; // "Ysiz" in spec. - - // "Horizontal offset from the origin of the reference grid to the left side of the image area." - u32 x_offset { 0 }; // "XOsiz" in spec. - - // "Vertical offset from the origin of the reference grid to the top side of the image area." - u32 y_offset { 0 }; // "YOsiz" in spec. - - // "Width of one reference tile with respect to the reference grid." - u32 tile_width { 0 }; // "XTsiz" in spec. - - // "Height of one reference tile with respect to the reference grid." - u32 tile_height { 0 }; // "YTsiz" in spec. - - // "Horizontal offset from the origin of the reference grid to the left side of the first tile." - u32 tile_x_offset { 0 }; // "XTOsiz" in spec. - - // "Vertical offset from the origin of the reference grid to the top side of the first tile." - u32 tile_y_offset { 0 }; // "YTOsiz" in spec. - - // "Csiz" isn't stored in this struct. It corresponds to `components.size()`. - - struct ComponentInformation { - // "Precision (depth) in bits and sign of the ith component samples." - u8 depth_and_sign { 0 }; // "Ssiz" in spec. - - // Table A.11 – Component Ssiz parameter - u8 bit_depth() const { return (depth_and_sign & 0x7F) + 1; } - bool is_signed() const { return depth_and_sign & 0x80; } - - // "Horizontal separation of a sample of the ith component with respect to the reference grid." - u8 horizontal_separation { 0 }; // "XRsiz" in spec. - - // "Vertical separation of a sample of the ith component with respect to the reference grid." - u8 vertical_separation { 0 }; // "YRsiz" in spec. - }; - Vector components; -}; - -static ErrorOr read_image_and_tile_size(ReadonlyBytes data) -{ - FixedMemoryStream stream { data }; - - ImageAndTileSize siz; - siz.needed_decoder_capabilities = TRY(stream.read_value>()); - siz.width = TRY(stream.read_value>()); - siz.height = TRY(stream.read_value>()); - siz.x_offset = TRY(stream.read_value>()); - siz.y_offset = TRY(stream.read_value>()); - siz.tile_width = TRY(stream.read_value>()); - siz.tile_height = TRY(stream.read_value>()); - siz.tile_x_offset = TRY(stream.read_value>()); - siz.tile_y_offset = TRY(stream.read_value>()); - u16 component_count = TRY(stream.read_value>()); // "Csiz" in spec. - - for (size_t i = 0; i < component_count; ++i) { - ImageAndTileSize::ComponentInformation component; - component.depth_and_sign = TRY(stream.read_value()); - if (component.bit_depth() > 38) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Invalid component depth"); - component.horizontal_separation = TRY(stream.read_value()); - component.vertical_separation = TRY(stream.read_value()); - siz.components.append(component); - } - - dbgln_if(JPEG2000_DEBUG, "JPEG2000ImageDecoderPlugin: SIZ marker segment: needed_decoder_capabilities={}, width={}, height={}, x_offset={}, y_offset={}, tile_width={}, tile_height={}, tile_x_offset={}, tile_y_offset={}", siz.needed_decoder_capabilities, siz.width, siz.height, siz.x_offset, siz.y_offset, siz.tile_width, siz.tile_height, siz.tile_x_offset, siz.tile_y_offset); - dbgln_if(JPEG2000_DEBUG, "JPEG2000ImageDecoderPlugin: SIZ marker segment: {} components:", component_count); - for (auto [i, component] : enumerate(siz.components)) - dbgln_if(JPEG2000_DEBUG, "JPEG2000ImageDecoderPlugin: SIZ marker segment: component[{}]: is_signed={}, bit_depth={}, horizontal_separation={}, vertical_separation={}", i, component.is_signed(), component.bit_depth(), component.horizontal_separation, component.vertical_separation); - - return siz; -} - -// Data shared by COD and COC marker segments -struct CodingStyleParameters { - // Table A.20 – Transformation for the SPcod and SPcoc parameters - enum Transformation { - Irreversible_9_7_Filter = 0, - Reversible_5_3_Filter = 1, - }; - - // Table A.15 – Coding style parameter values of the SPcod and SPcoc parameters - // "Number of decomposition levels, NL, Zero implies no transformation." - u8 number_of_decomposition_levels { 0 }; - u8 code_block_width_exponent { 0 }; // "xcb" in spec; 2 already added. - u8 code_block_height_exponent { 0 }; // "ycb" in spec; 2 already added. - u8 code_block_style { 0 }; - Transformation transformation { Irreversible_9_7_Filter }; - - // Table A.19 – Code-block style for the SPcod and SPcoc parameters - bool uses_selective_arithmetic_coding_bypass() const { return code_block_style & 1; } - bool reset_context_probabilities() const { return code_block_style & 2; } - bool uses_termination_on_each_coding_pass() const { return code_block_style & 4; } - bool uses_vertically_causal_context() const { return code_block_style & 8; } - bool uses_predictable_termination() const { return code_block_style & 0x10; } - bool uses_segmentation_symbols() const { return code_block_style & 0x20; } - - // If has_explicit_precinct_size is false, this contains the default { 15, 15 } number_of_decomposition_levels + 1 times. - // If has_explicit_precinct_size is true, this contains number_of_decomposition_levels + 1 explicit values stored in the COD marker segment. - struct PrecinctSize { - u8 PPx { 0 }; - u8 PPy { 0 }; - }; - Vector precinct_sizes; -}; - -static ErrorOr read_coding_style_parameters(ReadonlyBytes data, StringView name, bool has_explicit_precinct_size) -{ - FixedMemoryStream stream { data }; - - CodingStyleParameters parameters; - - parameters.number_of_decomposition_levels = TRY(stream.read_value()); - if (parameters.number_of_decomposition_levels > 32) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Invalid number of decomposition levels"); - - // Table A.18 – Width or height exponent of the code-blocks for the SPcod and SPcoc parameters - u8 xcb = (TRY(stream.read_value()) & 0xF) + 2; - u8 ycb = (TRY(stream.read_value()) & 0xF) + 2; - if (xcb > 10 || ycb > 10 || xcb + ycb > 12) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Invalid code block size"); - parameters.code_block_width_exponent = xcb; - parameters.code_block_height_exponent = ycb; - - parameters.code_block_style = TRY(stream.read_value()); - - u8 transformation = TRY(stream.read_value()); - if (transformation > 1) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Invalid transformation"); - parameters.transformation = static_cast(transformation); - - if (has_explicit_precinct_size) { - for (size_t i = 0; i < parameters.number_of_decomposition_levels + 1u; ++i) { - u8 b = TRY(stream.read_value()); - - // Table A.21 – Precinct width and height for the SPcod and SPcoc parameters - CodingStyleParameters::PrecinctSize precinct_size; - precinct_size.PPx = b & 0xF; - precinct_size.PPy = b >> 4; - if ((precinct_size.PPx == 0 || precinct_size.PPy == 0) && i > 0) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Invalid precinct size"); - parameters.precinct_sizes.append(precinct_size); - } - } else { - for (size_t i = 0; i < parameters.number_of_decomposition_levels + 1u; ++i) - parameters.precinct_sizes.append({ 15, 15 }); - } - - dbgln_if(JPEG2000_DEBUG, "JPEG2000ImageDecoderPlugin: {} marker segment: number_of_decomposition_levels={}, code_block_width_exponent={}, code_block_height_exponent={}", name, parameters.number_of_decomposition_levels, parameters.code_block_width_exponent, parameters.code_block_height_exponent); - dbgln_if(JPEG2000_DEBUG, "JPEG2000ImageDecoderPlugin: {} marker segment: code_block_style={}, transformation={}", name, parameters.code_block_style, (int)parameters.transformation); - if (has_explicit_precinct_size) { - dbgln_if(JPEG2000_DEBUG, "JPEG2000ImageDecoderPlugin: {} marker segment: {} explicit precinct sizes:", name, parameters.precinct_sizes.size()); - for (auto [i, precinct_size] : enumerate(parameters.precinct_sizes)) - dbgln_if(JPEG2000_DEBUG, "JPEG2000ImageDecoderPlugin: {} marker segment: precinct_size[{}]: PPx={}, PPy={}", name, i, precinct_size.PPx, precinct_size.PPy); - } - - return parameters; -} - -// A.6.1 Coding style default (COD) -struct CodingStyleDefault { - // Table A.13 – Coding style parameter values for the Scod parameter - bool has_explicit_precinct_size { false }; - bool may_use_SOP_marker { false }; - bool may_use_EPH_marker { false }; - - // Table A.16 – Progression order for the SGcod, SPcoc, and Ppoc parameters - // B.12 Progression order - enum ProgressionOrder { - LayerResolutionComponentPosition = 0, - ResolutionLayerComponentPosition = 1, - ResolutionPositionComponentLayer = 2, - PositionComponentResolutionLayer = 3, - ComponentPositionResolutionLayer = 4, - }; - - // Table A.17 – Multiple component transformation for the SGcod parameters - enum MultipleComponentTransformationType { - None = 0, - MultipleComponentTransformationUsed = 1, // See Annex G - }; - - // Table A.14 – Coding style parameter values of the SGcod parameter - ProgressionOrder progression_order { LayerResolutionComponentPosition }; - u16 number_of_layers { 0 }; - MultipleComponentTransformationType multiple_component_transformation_type { None }; - - CodingStyleParameters parameters; -}; - -static ErrorOr read_coding_style_default(ReadonlyBytes data) -{ - FixedMemoryStream stream { data }; - - CodingStyleDefault cod; - - u8 Scod = TRY(stream.read_value()); - cod.has_explicit_precinct_size = Scod & 1; - cod.may_use_SOP_marker = Scod & 2; - cod.may_use_EPH_marker = Scod & 4; - - u32 SGcod = TRY(stream.read_value>()); - u8 progression_order = SGcod >> 24; - if (progression_order > 4) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Invalid progression order"); - cod.progression_order = static_cast(progression_order); - - cod.number_of_layers = (SGcod >> 8) & 0xFFFF; - if (cod.number_of_layers == 0) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Invalid number of layers"); - - u8 multiple_component_transformation_type = SGcod & 0xFF; - if (multiple_component_transformation_type > 1) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Invalid multiple component transformation type"); - cod.multiple_component_transformation_type = static_cast(multiple_component_transformation_type); - - dbgln_if(JPEG2000_DEBUG, "JPEG2000ImageDecoderPlugin: COD marker segment: has_explicit_precinct_size={}, may_use_SOP_marker={}, may_use_EPH_marker={}", cod.has_explicit_precinct_size, cod.may_use_SOP_marker, cod.may_use_EPH_marker); - dbgln_if(JPEG2000_DEBUG, "JPEG2000ImageDecoderPlugin: COD marker segment: progression_order={}, number_of_layers={}, multiple_component_transformation_type={}", (int)cod.progression_order, cod.number_of_layers, (int)cod.multiple_component_transformation_type); - - cod.parameters = TRY(read_coding_style_parameters(data.slice(stream.offset()), "COD"sv, cod.has_explicit_precinct_size)); - - return cod; -} - -// A.6.2 Coding style component (COC) -struct CodingStyleComponent { - u16 component_index { 0 }; // "Ccoc" in spec. - - // Table A.23 – Coding style parameter values for the Scoc parameter - bool has_explicit_precinct_size { false }; // "Scoc" in spec. - - CodingStyleParameters parameters; -}; - -static ErrorOr read_coding_style_component(ReadonlyBytes data, size_t number_of_components) -{ - FixedMemoryStream stream { data }; - - // Table A.22 – Coding style component parameter values - CodingStyleComponent coc; - if (number_of_components < 257) - coc.component_index = TRY(stream.read_value()); - else - coc.component_index = TRY(stream.read_value>()); - - u8 Scoc = TRY(stream.read_value()); - coc.has_explicit_precinct_size = Scoc & 1; - - dbgln_if(JPEG2000_DEBUG, "JPEG2000ImageDecoderPlugin: COC marker segment: component_index={}", coc.component_index); - coc.parameters = TRY(read_coding_style_parameters(data.slice(TRY(stream.tell())), "COC"sv, coc.has_explicit_precinct_size)); - - return coc; -} - -// A.6.4 Quantization default (QCD) -struct QuantizationDefault { - enum QuantizationStyle { - NoQuantization = 0, - ScalarDerived = 1, - ScalarExpounded = 2, - }; - QuantizationStyle quantization_style { NoQuantization }; - u8 number_of_guard_bits { 0 }; - - struct ReversibleStepSize { - u8 exponent { 0 }; - }; - struct IrreversibleStepSize { - u16 mantissa { 0 }; - u8 exponent { 0 }; - }; - - // Stores a Vector if quantization_style is NoQuantization, and a Vector otherwise. - // The size of the vector is >= 3*number_of_decomposition_levels + 1 if quantization_style is not ScalarDerived, and 1 otherwise. - using StepSizeType = Variant, Vector>; - StepSizeType step_sizes; -}; - -static ErrorOr read_quantization_default(ReadonlyBytes data, StringView marker_name = "QCD"sv) -{ - FixedMemoryStream stream { data }; - - QuantizationDefault qcd; - - u8 sqcd = TRY(stream.read_value()); - u8 quantization_style = sqcd & 0x1F; - if (quantization_style > 2) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Invalid quantization style"); - qcd.quantization_style = static_cast(quantization_style); - qcd.number_of_guard_bits = sqcd >> 5; - - qcd.step_sizes = TRY([&]() -> ErrorOr { - if (quantization_style == QuantizationDefault::NoQuantization) { - // Table A.29 – Reversible step size values for the SPqcd and SPqcc parameters (reversible transform only) - if (data.size() < 2) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Not enough data for QCD marker segment"); - u8 number_of_decomposition_levels = (data.size() - 2) / 3; - - Vector reversible_step_sizes; - for (size_t i = 0; i < 1u + 3u * number_of_decomposition_levels; ++i) - reversible_step_sizes.append({ static_cast(TRY(stream.read_value()) >> 3) }); - return reversible_step_sizes; - } - - // Table A.30 – Quantization values for the SPqcd and SPqcc parameters (irreversible transformation only) - if (data.size() < 3) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Not enough data for QCD marker segment"); - u8 number_of_decomposition_levels = 0; - if (quantization_style == QuantizationDefault::ScalarExpounded) - number_of_decomposition_levels = (data.size() - 3) / 6; - - Vector irreversible_step_sizes; - for (size_t i = 0; i < 1u + 3u * number_of_decomposition_levels; ++i) { - u16 value = TRY(stream.read_value>()); - QuantizationDefault::IrreversibleStepSize step_size; - step_size.mantissa = value & 0x7FF; - step_size.exponent = value >> 11; - irreversible_step_sizes.append(step_size); - } - return irreversible_step_sizes; - }()); - - dbgln_if(JPEG2000_DEBUG, "JPEG2000ImageDecoderPlugin: {} marker segment: quantization_style={}, number_of_guard_bits={}", marker_name, (int)qcd.quantization_style, qcd.number_of_guard_bits); - qcd.step_sizes.visit( - [](Empty) { VERIFY_NOT_REACHED(); }, - [&](Vector const& step_sizes) { - dbgln_if(JPEG2000_DEBUG, "JPEG2000ImageDecoderPlugin: {} marker segment: {} step sizes:", marker_name, step_sizes.size()); - for (auto [i, step_size] : enumerate(step_sizes)) { - dbgln_if(JPEG2000_DEBUG, "JPEG2000ImageDecoderPlugin: {} marker segment: step_size[{}]: exponent={}", marker_name, i, step_size.exponent); - } - }, - [&](Vector const& step_sizes) { - dbgln_if(JPEG2000_DEBUG, "JPEG2000ImageDecoderPlugin: {} marker segment: {} step sizes:", marker_name, step_sizes.size()); - for (auto [i, step_size] : enumerate(step_sizes)) { - dbgln_if(JPEG2000_DEBUG, "JPEG2000ImageDecoderPlugin: {} marker segment: step_size[{}]: mantissa={}, exponent={}", marker_name, i, step_size.mantissa, step_size.exponent); - } - }); - - return qcd; -} - -// A.6.5 Quantization component (QCC) -struct QuantizationComponent { - u16 component_index { 0 }; // "Cqcc" in spec. - QuantizationDefault qcd; -}; - -static ErrorOr read_quantization_component(ReadonlyBytes data, size_t number_of_components) -{ - FixedMemoryStream stream { data }; - - QuantizationComponent qcc; - if (number_of_components < 257) - qcc.component_index = TRY(stream.read_value()); - else - qcc.component_index = TRY(stream.read_value>()); - - dbgln_if(JPEG2000_DEBUG, "JPEG2000ImageDecoderPlugin: QCC marker segment: component_index={}", qcc.component_index); - qcc.qcd = TRY(read_quantization_default(data.slice(TRY(stream.tell())), "QCC"sv)); - - return qcc; -} - -// A.9.2 Comment (COM) -struct Comment { - enum CommentType { - Binary = 0, - ISO_IEC_8859_15 = 1, - }; - CommentType type { Binary }; // "Rcom" in spec. - ReadonlyBytes data; -}; - -static ErrorOr read_comment(ReadonlyBytes data) -{ - FixedMemoryStream stream { data }; - - Comment com; - u16 comment_type = TRY(stream.read_value>()); - if (comment_type > 1) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Invalid comment type"); - com.type = static_cast(comment_type); - com.data = data.slice(TRY(stream.tell())); - - dbgln_if(JPEG2000_DEBUG, "JPEG2000ImageDecoderPlugin: COM marker segment: comment_type={}, size()={}", (int)com.type, com.data.size()); - if (com.type == Comment::ISO_IEC_8859_15) - dbgln_if(JPEG2000_DEBUG, "JPEG2000ImageDecoderPlugin: COM marker segment, ISO/IEC 8859-15 text: '{}'", TRY(TextCodec::decoder_for("ISO-8859-15"sv)->to_utf8(StringView { com.data }))); - - return com; -} - -struct TilePartData { - StartOfTilePart sot; - Vector coms; - ReadonlyBytes data; -}; - -struct TileData { - Optional cod; - Vector cocs; - Optional qcd; - Vector qccs; - Vector tile_parts; -}; - -struct JPEG2000LoadingContext { - enum class State { - NotDecoded = 0, - DecodedTileHeaders, - Error, - }; - State state { State::NotDecoded }; - ReadonlyBytes codestream_data; - size_t codestream_cursor { 0 }; - Optional icc_data; - - IntSize size; - - ISOBMFF::BoxList boxes; - - // Data from marker segments: - ImageAndTileSize siz; - CodingStyleDefault cod; - Vector cocs; - QuantizationDefault qcd; - Vector qccs; - Vector coms; - Vector tiles; -}; - -struct MarkerSegment { - u16 marker; - - // OptionalNone for markers that don't have data. - // For markers that do have data, this does not include the marker length data. (`data.size() + 2` is the value of the marker length field.) - Optional data; -}; - -static ErrorOr peek_marker(JPEG2000LoadingContext& context) -{ - if (context.codestream_cursor + 2 > context.codestream_data.size()) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Not enough data for marker"); - return *reinterpret_cast const*>(context.codestream_data.data() + context.codestream_cursor); -} - -static ErrorOr read_marker_at_cursor(JPEG2000LoadingContext& context) -{ - u16 marker = TRY(peek_marker(context)); - // "All markers with the marker code between 0xFF30 and 0xFF3F have no marker segment parameters. They shall be skipped by the decoder." - // "The SOC, SOD and EOC are delimiting markers not marker segments, and have no explicit length information or other parameters." - bool is_marker_segment = !(marker >= 0xFF30 && marker <= 0xFF3F) && marker != J2K_SOC && marker != J2K_SOD && marker != J2K_EOC; - - MarkerSegment marker_segment; - marker_segment.marker = marker; - - if (is_marker_segment) { - if (context.codestream_cursor + 4 > context.codestream_data.size()) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Not enough data for marker segment length"); - u16 marker_length = *reinterpret_cast const*>(context.codestream_data.data() + context.codestream_cursor + 2); - if (marker_length < 2) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Marker segment length too small"); - if (context.codestream_cursor + 2 + marker_length > context.codestream_data.size()) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Not enough data for marker segment data"); - marker_segment.data = ReadonlyBytes { context.codestream_data.data() + context.codestream_cursor + 4, marker_length - 2u }; - } - - context.codestream_cursor += 2; - if (is_marker_segment) - context.codestream_cursor += 2 + marker_segment.data->size(); - - return marker_segment; -} - -static ErrorOr parse_codestream_main_header(JPEG2000LoadingContext& context) -{ - // Figure A.3 – Construction of the main header - // "Required as the first marker" - auto marker = TRY(read_marker_at_cursor(context)); - if (marker.marker != J2K_SOC) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Expected SOC marker"); - - // "Required as the second marker segment" - marker = TRY(read_marker_at_cursor(context)); - if (marker.marker != J2K_SIZ) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Expected SIZ marker"); - context.siz = TRY(read_image_and_tile_size(marker.data.value())); - - bool saw_COD_marker = false; - bool saw_QCD_marker = false; - while (true) { - u16 marker = TRY(peek_marker(context)); - switch (marker) { - case J2K_COD: - case J2K_COC: - case J2K_QCD: - case J2K_QCC: - case J2K_RGN: - case J2K_POC: - case J2K_PPM: - case J2K_TLM: - case J2K_PLM: - case J2K_CRG: - case J2K_COM: { - auto marker = TRY(read_marker_at_cursor(context)); - if (marker.marker == J2K_COD) { - if (saw_COD_marker) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Multiple COD markers in main header"); - context.cod = TRY(read_coding_style_default(marker.data.value())); - saw_COD_marker = true; - } else if (marker.marker == J2K_COC) { - context.cocs.append(TRY(read_coding_style_component(marker.data.value(), context.siz.components.size()))); - } else if (marker.marker == J2K_QCD) { - if (saw_QCD_marker) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Multiple QCD markers in main header"); - context.qcd = TRY(read_quantization_default(marker.data.value())); - saw_QCD_marker = true; - } else if (marker.marker == J2K_QCC) { - context.qccs.append(TRY(read_quantization_component(marker.data.value(), context.siz.components.size()))); - } else if (marker.marker == J2K_COM) { - context.coms.append(TRY(read_comment(marker.data.value()))); - } else { - // FIXME: These are valid main header markers. Parse contents. - dbgln("JPEG2000ImageDecoderPlugin: marker {:#04x} not yet implemented", marker.marker); - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: marker not yet implemented"); - } - break; - } - case J2K_SOT: { - // SOT terminates the main header. - // A.4.2: "There shall be at least one SOT in a codestream." - if (!saw_COD_marker) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Required COD marker not present in main header"); - if (!saw_QCD_marker) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Required QCD marker not present in main header"); - - // A.6.4: "there is not necessarily a correspondence with the number of sub-bands present because the sub-bands - // can be truncated with no requirement to correct [the QCD] marker segment." - size_t step_sizes_count = context.qcd.step_sizes.visit( - [](Empty) -> size_t { VERIFY_NOT_REACHED(); }, - [](Vector const& step_sizes) { return step_sizes.size(); }, - [](Vector const& step_sizes) { return step_sizes.size(); }); - // FIXME: What if number_of_decomposition_levels is in context.cocs and varies by component? - if (context.qcd.quantization_style != QuantizationDefault::ScalarDerived && step_sizes_count < context.cod.parameters.number_of_decomposition_levels * 3u + 1u) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Not enough step sizes for number of decomposition levels"); - - return {}; - } - default: - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Unexpected marker in main header"); - } - } -} - -static ErrorOr parse_codestream_tile_header(JPEG2000LoadingContext& context) -{ - // Figure A.4 – Construction of the first tile-part header of a given tile - // Figure A.5 – Construction of a non-first tile-part header - - // "Required as the first marker segment of every tile-part header" - auto tile_start = context.codestream_cursor; - auto marker = TRY(read_marker_at_cursor(context)); - if (marker.marker != J2K_SOT) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Expected SOT marker"); - auto start_of_tile = TRY(read_start_of_tile_part(marker.data.value())); - // FIXME: Store start_of_tile on context somewhere. - - context.tiles.resize(max(context.tiles.size(), (size_t)start_of_tile.tile_index + 1)); - auto& tile = context.tiles[start_of_tile.tile_index]; - - if (tile.tile_parts.size() != start_of_tile.tile_part_index) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Tile part index out of order"); - tile.tile_parts.append({}); - auto& tile_part = tile.tile_parts.last(); - tile_part.sot = start_of_tile; - - bool found_start_of_data = false; - while (!found_start_of_data) { - u16 marker = TRY(peek_marker(context)); - switch (marker) { - case J2K_SOD: - // "Required as the last marker segment of every tile-part header" - context.codestream_cursor += 2; - found_start_of_data = true; - break; - case J2K_COD: - case J2K_COC: - case J2K_QCD: - case J2K_QCC: - case J2K_RGN: - if (start_of_tile.tile_part_index != 0) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: COD, COC, QCD, QCC, RGN markers are only valid in the first tile-part header"); - [[fallthrough]]; - case J2K_POC: - case J2K_PPT: - case J2K_PLT: - case J2K_COM: { - auto marker = TRY(read_marker_at_cursor(context)); - if (marker.marker == J2K_COD) { - if (tile.cod.has_value()) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Multiple COD markers in tile header"); - tile.cod = TRY(read_coding_style_default(marker.data.value())); - } else if (marker.marker == J2K_COC) { - tile.cocs.append(TRY(read_coding_style_component(marker.data.value(), context.siz.components.size()))); - } else if (marker.marker == J2K_QCD) { - if (tile.qcd.has_value()) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Multiple QCD markers in tile header"); - tile.qcd = TRY(read_quantization_default(marker.data.value())); - } else if (marker.marker == J2K_QCC) { - tile.qccs.append(TRY(read_quantization_component(marker.data.value(), context.siz.components.size()))); - } else if (marker.marker == J2K_COM) { - tile_part.coms.append(TRY(read_comment(marker.data.value()))); - } else { - // FIXME: These are valid main header markers. Parse contents. - dbgln("JPEG2000ImageDecoderPlugin: marker {:#04x} not yet implemented in tile header", marker.marker); - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: marker not yet implemented in tile header"); - } - break; - } - default: - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Unexpected marker in tile header"); - } - } - - u32 tile_bitstream_length; - if (start_of_tile.tile_part_length == 0) { - // Leave room for EOC marker. - if (context.codestream_data.size() - context.codestream_cursor < 2) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Not enough data for EOC marker"); - tile_bitstream_length = context.codestream_data.size() - context.codestream_cursor - 2; - } else { - u32 tile_header_length = context.codestream_cursor - tile_start; - if (start_of_tile.tile_part_length < tile_header_length) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Invalid tile part length"); - tile_bitstream_length = start_of_tile.tile_part_length - tile_header_length; - } - - if (context.codestream_cursor + tile_bitstream_length > context.codestream_data.size()) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Not enough data for tile bitstream"); - tile_part.data = context.codestream_data.slice(context.codestream_cursor, tile_bitstream_length); - - context.codestream_cursor += tile_bitstream_length; - dbgln_if(JPEG2000_DEBUG, "JPEG2000ImageDecoderPlugin: Tile bitstream length: {}", tile_bitstream_length); - - return {}; -} - -static ErrorOr parse_codestream_tile_headers(JPEG2000LoadingContext& context) -{ - while (true) { - auto marker = TRY(peek_marker(context)); - if (marker == J2K_EOC) { - context.codestream_cursor += 2; - break; - } - TRY(parse_codestream_tile_header(context)); - } - - if (context.codestream_cursor < context.codestream_data.size()) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Unexpected data after EOC marker"); - return {}; -} - -static ErrorOr decode_jpeg2000_header(JPEG2000LoadingContext& context, ReadonlyBytes data) -{ - if (!JPEG2000ImageDecoderPlugin::sniff(data)) - return Error::from_string_literal("JPEG2000LoadingContext: Invalid JPEG2000 header"); - - if (data.starts_with(marker_id_string)) { - context.codestream_data = data; - TRY(parse_codestream_main_header(context)); - context.size = { context.siz.width, context.siz.height }; - return {}; - } - - auto reader = TRY(Gfx::ISOBMFF::Reader::create(TRY(try_make(data)))); - context.boxes = TRY(reader.read_entire_file()); - - // I.2.2 File organization - // "A particular order of those boxes in the file is not generally implied. However, the JPEG 2000 Signature box - // shall be the first box in a JP2 file, the File Type box shall immediately follow the JPEG 2000 Signature box - // and the JP2 Header box shall fall before the Contiguous Codestream box." - if (context.boxes.size() < 4) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Expected at least four boxes"); - - // Required toplevel boxes: signature box, file type box, jp2 header box, contiguous codestream box. - - if (context.boxes[0]->box_type() != ISOBMFF::BoxType::JPEG2000SignatureBox) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Expected JPEG2000SignatureBox as first box"); - if (context.boxes[1]->box_type() != ISOBMFF::BoxType::FileTypeBox) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Expected FileTypeBox as second box"); - - Optional jp2_header_box_index; - Optional contiguous_codestream_box_index; - for (size_t i = 2; i < context.boxes.size(); ++i) { - if (context.boxes[i]->box_type() == ISOBMFF::BoxType::JPEG2000HeaderBox) { - // "Within a JP2 file, there shall be one and only one JP2 Header box." - if (jp2_header_box_index.has_value()) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Multiple JP2 Header boxes"); - jp2_header_box_index = i; - } - if (context.boxes[i]->box_type() == ISOBMFF::BoxType::JPEG2000ContiguousCodestreamBox && !contiguous_codestream_box_index.has_value()) { - // "a conforming reader shall ignore all codestreams after the first codestream found in the file. - // Contiguous Codestream boxes may be found anywhere in the file except before the JP2 Header box." - contiguous_codestream_box_index = i; - if (!jp2_header_box_index.has_value() || contiguous_codestream_box_index.value() < jp2_header_box_index.value()) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: JP2 Header box must come before Contiguous Codestream box"); - } - } - - if (!jp2_header_box_index.has_value()) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Expected JP2 Header box"); - if (!contiguous_codestream_box_index.has_value()) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Expected Contiguous Codestream box"); - - // FIXME: JPEG2000ContiguousCodestreamBox makes a copy of the codestream data. That's too heavy for header scanning. - // Add a mode to ISOBMFF::Reader where it only stores offsets for the codestream data and the ICC profile. - auto const& codestream_box = static_cast(*context.boxes[contiguous_codestream_box_index.value()]); - context.codestream_data = codestream_box.codestream.bytes(); - - // Required child boxes of the jp2 header box: image header box, color box. - - Optional image_header_box_index; - Optional color_header_box_index; - auto const& header_box = static_cast(*context.boxes[jp2_header_box_index.value()]); - for (size_t i = 0; i < header_box.child_boxes().size(); ++i) { - auto const& subbox = header_box.child_boxes()[i]; - if (subbox->box_type() == ISOBMFF::BoxType::JPEG2000ImageHeaderBox) { - if (image_header_box_index.has_value()) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Multiple Image Header boxes"); - image_header_box_index = i; - } - if (subbox->box_type() == ISOBMFF::BoxType::JPEG2000ColorSpecificationBox) { - // T.800 says there should be just one 'colr' box, but T.801 allows several and says to pick the one with highest precedence. - bool use_this_color_box; - if (!color_header_box_index.has_value()) { - use_this_color_box = true; - } else { - auto const& new_header_box = static_cast(*header_box.child_boxes()[i]); - auto const& current_color_box = static_cast(*header_box.child_boxes()[color_header_box_index.value()]); - use_this_color_box = new_header_box.precedence > current_color_box.precedence; - } - - if (use_this_color_box) - color_header_box_index = i; - } - } - - if (!image_header_box_index.has_value()) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Expected Image Header box"); - if (!color_header_box_index.has_value()) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Expected Color Specification box"); - - auto const& image_header_box = static_cast(*header_box.child_boxes()[image_header_box_index.value()]); - context.size = { image_header_box.width, image_header_box.height }; - - auto const& color_header_box = static_cast(*header_box.child_boxes()[color_header_box_index.value()]); - if (color_header_box.method == 2 || color_header_box.method == 3) - context.icc_data = color_header_box.icc_data.bytes(); - - TRY(parse_codestream_main_header(context)); - - auto size_from_siz = IntSize { context.siz.width, context.siz.height }; - if (size_from_siz != context.size) { - // FIXME: If this is common, warn and use size from SIZ marker. - dbgln("JPEG2000ImageDecoderPlugin: Image size from SIZ marker ({}) does not match image size from JP2 header ({})", size_from_siz, context.size); - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Image size from SIZ marker does not match image size from JP2 header"); - } - - return {}; -} - -namespace JPEG2000 { - -// Tag trees are used to store the code-block inclusion bits and the zero bit-plane information. -// B.10.2 Tag trees -// "At every node of this tree the minimum integer of the (up to four) nodes below it is recorded. [...] -// Level 0 is the lowest level of the tag tree; it contains the top node. [...] -// Each node has a [...] current value, [...] initialized to zero. A 0 bit in the tag tree means that the minimum -// (or the value in the case of the highest level) is larger than the current value and a 1 bit means that the minimum -// (or the value in the case of the highest level) is equal to the current value. -// For each contiguous 0 bit in the tag tree the current value is incremented by one. -// Nodes at higher levels cannot be coded until lower level node values are fixed (i.e, a 1 bit is coded). [...] -// Only the information needed for the current code-block is stored at the current point in the packet header." -// The example in Figure B.13 / Table B.5 is useful to understand what exactly "only the information needed" means. -struct TagTreeNode { - u32 value { 0 }; - enum State { - Pending, - Final, - }; - State state { Pending }; - Array, 4> children {}; - u32 level { 0 }; // 0 for leaf nodes, 1 for the next level, etc. - - bool is_leaf() const { return level == 0; } - - ErrorOr read_value(u32 x, u32 y, Function()> const& read_bit, u32 start_value, Optional stop_at = {}) - { - value = max(value, start_value); - while (true) { - if (stop_at.has_value() && value == stop_at.value()) - return value; - - if (state == Final) { - if (is_leaf()) - return value; - u32 x_index = (x >> (level - 1)) & 1; - u32 y_index = (y >> (level - 1)) & 1; - return children[y_index * 2 + x_index]->read_value(x, y, read_bit, value, stop_at); - } - - bool bit = TRY(read_bit()); - if (!bit) - value++; - else - state = Final; - } - } - - static ErrorOr> create(u32 x_count, u32 y_count, u32 level) - { - VERIFY(x_count > 0); - VERIFY(y_count > 0); - - auto node = TRY(try_make()); - node->level = level; - if (node->is_leaf()) { - VERIFY(x_count == 1); - VERIFY(y_count == 1); - return node; - } - - u32 top_left_x_child_count = min(x_count, 1u << (max(level, 1) - 1)); - u32 top_left_y_child_count = min(y_count, 1u << (max(level, 1) - 1)); - for (u32 y = 0; y < 2; ++y) { - for (u32 x = 0; x < 2; ++x) { - u32 child_x_count = x == 1 ? x_count - top_left_x_child_count : top_left_x_child_count; - u32 child_y_count = y == 1 ? y_count - top_left_y_child_count : top_left_y_child_count; - if (child_x_count == 0 || child_y_count == 0) - continue; - node->children[y * 2 + x] = TRY(create(child_x_count, child_y_count, level - 1)); - } - } - return node; - } -}; - -TagTree::TagTree(NonnullOwnPtr root) - : m_root(move(root)) -{ -} - -TagTree::TagTree(TagTree&&) = default; -TagTree::~TagTree() = default; - -ErrorOr TagTree::create(u32 x_count, u32 y_count) -{ - auto level = ceil(log2(max(x_count, y_count))); - return TagTree { TRY(TagTreeNode::create(x_count, y_count, level)) }; -} - -ErrorOr TagTree::read_value(u32 x, u32 y, Function()> const& read_bit, Optional stop_at) const -{ - return m_root->read_value(x, y, read_bit, m_root->value, stop_at); -} - -} - -bool JPEG2000ImageDecoderPlugin::sniff(ReadonlyBytes data) -{ - return data.starts_with(jp2_id_string) || data.starts_with(marker_id_string); -} - -JPEG2000ImageDecoderPlugin::JPEG2000ImageDecoderPlugin() -{ - m_context = make(); -} - -JPEG2000ImageDecoderPlugin::~JPEG2000ImageDecoderPlugin() = default; - -IntSize JPEG2000ImageDecoderPlugin::size() -{ - return m_context->size; -} - -ErrorOr> JPEG2000ImageDecoderPlugin::create(ReadonlyBytes data) -{ - auto plugin = TRY(adopt_nonnull_own_or_enomem(new (nothrow) JPEG2000ImageDecoderPlugin())); - TRY(decode_jpeg2000_header(*plugin->m_context, data)); - return plugin; -} - -ErrorOr JPEG2000ImageDecoderPlugin::frame(size_t index, Optional) -{ - if (index != 0) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Invalid frame index"); - - if (m_context->state == JPEG2000LoadingContext::State::Error) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Decoding failed"); - - if (m_context->state < JPEG2000LoadingContext::State::DecodedTileHeaders) { - TRY(parse_codestream_tile_headers(*m_context)); - m_context->state = JPEG2000LoadingContext::State::DecodedTileHeaders; - } - - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Draw the rest of the owl"); -} - -ErrorOr> JPEG2000ImageDecoderPlugin::icc_data() -{ - return m_context->icc_data; -} - -} diff --git a/Userland/Libraries/LibGfx/ImageFormats/JPEG2000Loader.h b/Userland/Libraries/LibGfx/ImageFormats/JPEG2000Loader.h deleted file mode 100644 index 0d8c6450b5e..00000000000 --- a/Userland/Libraries/LibGfx/ImageFormats/JPEG2000Loader.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2024, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Gfx { - -namespace JPEG2000 { - -struct TagTreeNode; -class TagTree { -public: - TagTree(TagTree&&); - ~TagTree(); - - static ErrorOr create(u32 x_count, u32 y_count); - - ErrorOr read_value(u32 x, u32 y, Function()> const& read_bit, Optional stop_at = {}) const; - -private: - TagTree(NonnullOwnPtr); - - NonnullOwnPtr m_root; -}; - -} - -struct JPEG2000LoadingContext; - -class JPEG2000ImageDecoderPlugin : public ImageDecoderPlugin { -public: - static bool sniff(ReadonlyBytes); - static ErrorOr> create(ReadonlyBytes); - - virtual ~JPEG2000ImageDecoderPlugin() override; - - virtual IntSize size() override; - - virtual ErrorOr frame(size_t index, Optional ideal_size = {}) override; - - virtual ErrorOr> icc_data() override; - -private: - JPEG2000ImageDecoderPlugin(); - - OwnPtr m_context; -}; - -} diff --git a/Userland/Utilities/CMakeLists.txt b/Userland/Utilities/CMakeLists.txt index 9670ebd54ce..1974103f271 100644 --- a/Userland/Utilities/CMakeLists.txt +++ b/Userland/Utilities/CMakeLists.txt @@ -6,7 +6,6 @@ set(CMD_SOURCES headless-browser.cpp icc.cpp image.cpp - isobmff.cpp js.cpp lzcat.cpp tar.cpp @@ -38,7 +37,6 @@ target_link_libraries(gzip PRIVATE LibCompress) target_link_libraries(headless-browser PRIVATE LibCrypto LibFileSystem LibGfx LibHTTP LibImageDecoderClient LibTLS LibWeb LibWebView LibWebSocket LibIPC LibJS LibDiff LibURL) target_link_libraries(icc PRIVATE LibGfx LibVideo LibURL) target_link_libraries(image PRIVATE LibGfx) -target_link_libraries(isobmff PRIVATE LibGfx) target_link_libraries(js PRIVATE LibCrypto LibJS LibLine LibLocale LibTextCodec) target_link_libraries(lzcat PRIVATE LibCompress) target_link_libraries(tar PRIVATE LibArchive LibCompress LibFileSystem) diff --git a/Userland/Utilities/isobmff.cpp b/Userland/Utilities/isobmff.cpp deleted file mode 100644 index 6178933e2a9..00000000000 --- a/Userland/Utilities/isobmff.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2023, Gregory Bertilson - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments arguments) -{ - Core::ArgsParser args_parser; - - StringView path; - args_parser.add_positional_argument(path, "Path to ISO Base Media File Format file", "FILE"); - - args_parser.parse(arguments); - - auto file = TRY(Core::MappedFile::map(path)); - auto reader = TRY(Gfx::ISOBMFF::Reader::create(TRY(try_make(file->bytes())))); - auto boxes = TRY(reader.read_entire_file()); - - for (auto& box : boxes) - box->dump(); - return 0; -}