LibGfx: Remove JPEG2000 image format support

This format is not supported by other browsers.
This commit is contained in:
Andreas Kling 2024-06-17 20:17:23 +02:00 committed by Andreas Kling
parent a34a5af939
commit 1039acca8c
Notes: sideshowbarker 2024-07-17 04:10:16 +09:00
26 changed files with 0 additions and 2261 deletions

View file

@ -122,10 +122,6 @@
# cmakedefine01 JOB_DEBUG
#endif
#ifndef JPEG2000_DEBUG
# cmakedefine01 JPEG2000_DEBUG
#endif
#ifndef JS_BYTECODE_DEBUG
# cmakedefine01 JS_BYTECODE_DEBUG
#endif

View file

@ -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)

View file

@ -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)

View file

@ -1,20 +0,0 @@
/*
* Copyright (c) 2024, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibGfx/ImageFormats/JPEG2000Loader.h>
#include <stddef.h>
#include <stdint.h>
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;
}

View file

@ -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)

View file

@ -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=",

View file

@ -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",

View file

@ -9,7 +9,6 @@ set(TEST_SOURCES
TestImageWriter.cpp
TestMedianCut.cpp
TestPainter.cpp
TestParseISOBMFF.cpp
TestRect.cpp
TestScalingFunctions.cpp
TestWOFF.cpp

View file

@ -11,7 +11,6 @@
#include <LibGfx/ImageFormats/GIFLoader.h>
#include <LibGfx/ImageFormats/ICOLoader.h>
#include <LibGfx/ImageFormats/ImageDecoder.h>
#include <LibGfx/ImageFormats/JPEG2000Loader.h>
#include <LibGfx/ImageFormats/JPEGLoader.h>
#include <LibGfx/ImageFormats/JPEGXLLoader.h>
#include <LibGfx/ImageFormats/PNGLoader.h>
@ -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<u8>({
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<u8>({
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<ErrorOr<bool>()> 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<u8>({
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<ErrorOr<bool>()> 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<u8> 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<ErrorOr<bool>()> 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)));

View file

@ -1,37 +0,0 @@
/*
* Copyright (c) 2023, Gregory Bertilson <zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibTest/TestCase.h>
#include <AK/MemoryStream.h>
#include <LibCore/MappedFile.h>
#include <LibGfx/ImageFormats/ISOBMFF/Reader.h>
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<FixedMemoryStream>(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<Gfx::ISOBMFF::FileTypeBox&>(*boxes[0]);
VERIFY(file_type_box.major_brand == Gfx::ISOBMFF::BrandIdentifier::avis);
VERIFY(file_type_box.minor_version == 0);
Vector<Gfx::ISOBMFF::BrandIdentifier, 7> 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);
}

View file

@ -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

View file

@ -1,49 +0,0 @@
/*
* Copyright (c) 2023, Gregory Bertilson <Zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/MaybeOwned.h>
#include <AK/Stream.h>
namespace Gfx::ISOBMFF {
class BoxStream final : public Stream {
public:
explicit BoxStream(MaybeOwned<Stream> 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<Bytes> 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<size_t> write_some(ReadonlyBytes) override { VERIFY_NOT_REACHED(); }
virtual ErrorOr<void> write_until_depleted(ReadonlyBytes) override { VERIFY_NOT_REACHED(); }
size_t remaining() const
{
return m_data_left;
}
ErrorOr<void> discard_remaining()
{
return discard(remaining());
}
private:
MaybeOwned<Stream> m_stream;
size_t m_data_left;
};
}

View file

@ -1,147 +0,0 @@
/*
* Copyright (c) 2023, Gregory Bertilson <Zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "Boxes.h"
#include "Reader.h"
#include <AK/Function.h>
namespace Gfx::ISOBMFF {
ErrorOr<BoxHeader> read_box_header(BoxStream& stream)
{
BoxHeader header;
u64 total_size = TRY(stream.read_value<BigEndian<u32>>());
header.type = TRY(stream.read_value<BigEndian<BoxType>>());
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<BigEndian<u64>>());
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<void> FullBox::read_from_stream(BoxStream& stream)
{
u32 data = TRY(stream.read_value<BigEndian<u32>>());
// unsigned int(8) version
version = static_cast<u8>(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<void> 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<void> FileTypeBox::read_from_stream(BoxStream& stream)
{
// unsigned int(32) major_brand;
major_brand = TRY(stream.read_value<BigEndian<BrandIdentifier>>());
// unsigned int(32) minor_version;
minor_version = TRY(stream.read_value<BigEndian<u32>>());
// 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<BigEndian<BrandIdentifier>>())));
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<void> 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<void> 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());
}
}

View file

@ -1,116 +0,0 @@
/*
* Copyright (c) 2023, Gregory Bertilson <Zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Endian.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/String.h>
#include <AK/Traits.h>
#include <AK/Vector.h>
#include <LibVideo/DecoderError.h>
#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<BoxHeader> 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<NonnullOwnPtr<Box>>;
struct FullBox : public Box {
ErrorOr<void> 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<NonnullOwnPtr<UnknownBox>> create_from_stream(BoxType type, BoxStream& stream)
{
auto box = TRY(try_make<UnknownBox>(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<void> 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<NonnullOwnPtr<BoxName>> create_from_stream(BoxStream& stream) \
{ \
auto box = TRY(try_make<BoxName>()); \
TRY(box->read_from_stream(stream)); \
return box; \
} \
BoxName() = default; \
virtual ~BoxName() override = default; \
ErrorOr<void> 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<BrandIdentifier> compatible_brands;
};
// A box that contains other boxes.
struct SuperBox : public Box {
SuperBox() = default;
using BoxCallback = Function<ErrorOr<Optional<NonnullOwnPtr<Box>>>(BoxType, BoxStream&)>;
ErrorOr<void> 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<u8, 16> uuid;
ByteBuffer data;
};
}

View file

@ -1,103 +0,0 @@
/*
* Copyright (c) 2023, Gregory Bertilson <Zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Endian.h>
#include <AK/Types.h>
#include <LibRIFF/ChunkID.h>
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<Gfx::ISOBMFF::BoxType> : Formatter<FormatString> {
ErrorOr<void> 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<FormatString>::format(builder, format_string, RIFF::ChunkID::from_number(to_underlying(box_type)));
}
};
template<>
struct AK::Formatter<Gfx::ISOBMFF::BrandIdentifier> : Formatter<FormatString> {
ErrorOr<void> format(FormatBuilder& builder, Gfx::ISOBMFF::BrandIdentifier const& brand_identifier)
{
return Formatter<FormatString>::format(builder, "{}"sv, RIFF::ChunkID::from_number(to_underlying(brand_identifier)));
}
};

View file

@ -1,306 +0,0 @@
/*
* Copyright (c) 2024, Nico Weber <thakis@chromium.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "JPEG2000Boxes.h"
#include <AK/Function.h>
namespace Gfx::ISOBMFF {
ErrorOr<void> JPEG2000HeaderBox::read_from_stream(BoxStream& stream)
{
auto make_subbox = [](BoxType type, BoxStream& stream) -> ErrorOr<Optional<NonnullOwnPtr<Box>>> {
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<void> JPEG2000ImageHeaderBox::read_from_stream(BoxStream& stream)
{
height = TRY(stream.read_value<BigEndian<u32>>());
width = TRY(stream.read_value<BigEndian<u32>>());
num_components = TRY(stream.read_value<BigEndian<u16>>());
bits_per_component = TRY(stream.read_value<u8>());
compression_type = TRY(stream.read_value<u8>());
is_colorspace_unknown = TRY(stream.read_value<u8>());
contains_intellectual_property_rights = TRY(stream.read_value<u8>());
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<void> JPEG2000ColorSpecificationBox::read_from_stream(BoxStream& stream)
{
method = TRY(stream.read_value<u8>());
precedence = TRY(stream.read_value<i8>());
approximation = TRY(stream.read_value<u8>());
if (method == 1)
enumerated_color_space = TRY(stream.read_value<BigEndian<u32>>());
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<void> JPEG2000ChannelDefinitionBox::read_from_stream(BoxStream& stream)
{
u16 count = TRY(stream.read_value<BigEndian<u16>>());
for (u32 i = 0; i < count; ++i) {
Channel channel;
channel.channel_index = TRY(stream.read_value<BigEndian<u16>>());
channel.channel_type = TRY(stream.read_value<BigEndian<u16>>());
channel.channel_association = TRY(stream.read_value<BigEndian<u16>>());
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<void> JPEG2000ResolutionBox::read_from_stream(BoxStream& stream)
{
auto make_subbox = [](BoxType type, BoxStream& stream) -> ErrorOr<Optional<NonnullOwnPtr<Box>>> {
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<void> JPEG2000ResolutionSubboxBase::read_from_stream(BoxStream& stream)
{
vertical_capture_grid_resolution_numerator = TRY(stream.read_value<BigEndian<u16>>());
vertical_capture_grid_resolution_denominator = TRY(stream.read_value<BigEndian<u16>>());
horizontal_capture_grid_resolution_numerator = TRY(stream.read_value<BigEndian<u16>>());
horizontal_capture_grid_resolution_denominator = TRY(stream.read_value<BigEndian<u16>>());
vertical_capture_grid_resolution_exponent = TRY(stream.read_value<i8>());
horizontal_capture_grid_resolution_exponent = TRY(stream.read_value<i8>());
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<void> JPEG2000CaptureResolutionBox::read_from_stream(BoxStream& stream)
{
return JPEG2000ResolutionSubboxBase::read_from_stream(stream);
}
void JPEG2000CaptureResolutionBox::dump(String const& prepend) const
{
JPEG2000ResolutionSubboxBase::dump(prepend);
}
ErrorOr<void> JPEG2000DefaultDisplayResolutionBox::read_from_stream(BoxStream& stream)
{
return JPEG2000ResolutionSubboxBase::read_from_stream(stream);
}
void JPEG2000DefaultDisplayResolutionBox::dump(String const& prepend) const
{
JPEG2000ResolutionSubboxBase::dump(prepend);
}
ErrorOr<void> 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<void> JPEG2000SignatureBox::read_from_stream(BoxStream& stream)
{
signature = TRY(stream.read_value<BigEndian<u32>>());
return {};
}
void JPEG2000SignatureBox::dump(String const& prepend) const
{
Box::dump(prepend);
outln("{}- signature = {:#08x}", prepend, signature);
}
ErrorOr<void> JPEG2000UUIDInfoBox::read_from_stream(BoxStream& stream)
{
auto make_subbox = [](BoxType type, BoxStream& stream) -> ErrorOr<Optional<NonnullOwnPtr<Box>>> {
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<void> JPEG2000UUIDListBox::read_from_stream(BoxStream& stream)
{
u16 count = TRY(stream.read_value<BigEndian<u16>>());
for (u32 i = 0; i < count; ++i) {
Array<u8, 16> 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<String> 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<void> JPEG2000URLBox::read_from_stream(BoxStream& stream)
{
version_number = TRY(stream.read_value<u8>());
flag = TRY(stream.read_value<u8>()) << 16;
flag |= TRY(stream.read_value<BigEndian<u16>>());
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 = <invalid {}; {} bytes>", prepend, url_or_err.release_error(), url_bytes.size());
else
outln("{}- url = {}", prepend, url_or_err.release_value());
}
}

View file

@ -1,116 +0,0 @@
/*
* Copyright (c) 2024, Nico Weber <thakis@chromium.org>
*
* 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<Channel> channels;
};
// I.5.3.7 Resolution box
struct JPEG2000ResolutionBox final : public SuperBox {
BOX_SUBTYPE(JPEG2000ResolutionBox);
};
struct JPEG2000ResolutionSubboxBase : public Box {
ErrorOr<void> 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<Array<u8, 16>> uuids;
};
// I.7.3.2 Data Entry URL box
struct JPEG2000URLBox final : public Box {
BOX_SUBTYPE(JPEG2000URLBox);
ErrorOr<String> url_as_string() const;
u8 version_number;
u32 flag;
ByteBuffer url_bytes;
};
}

View file

@ -1,70 +0,0 @@
/*
* Copyright (c) 2023, Gregory Bertilson <Zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "Reader.h"
#include "JPEG2000Boxes.h"
#include <AK/Function.h>
namespace Gfx::ISOBMFF {
ErrorOr<Reader> Reader::create(MaybeOwned<SeekableStream> stream)
{
size_t size = TRY(stream->size());
return Reader(make<BoxStream>(move(stream), size));
}
ErrorOr<Reader> Reader::create(MaybeOwned<BoxStream> stream)
{
return Reader(move(stream));
}
ErrorOr<BoxList> Reader::read_entire_file()
{
auto make_top_level_box = [](BoxType type, BoxStream& stream) -> ErrorOr<Optional<NonnullOwnPtr<Box>>> {
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<Optional<NonnullOwnPtr<Box>>>(*)(BoxType, BoxStream&))(make_top_level_box));
}
ErrorOr<BoxList> 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<Stream> { *m_box_stream }, static_cast<size_t>(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;
}
}

View file

@ -1,35 +0,0 @@
/*
* Copyright (c) 2023, Gregory Bertilson <Zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/MaybeOwned.h>
#include <AK/Stream.h>
#include "Boxes.h"
namespace Gfx::ISOBMFF {
class Reader {
public:
static ErrorOr<Reader> create(MaybeOwned<SeekableStream> stream);
static ErrorOr<Reader> create(MaybeOwned<BoxStream> stream);
ErrorOr<BoxList> read_entire_file();
using BoxCallback = Function<ErrorOr<Optional<NonnullOwnPtr<Box>>>(BoxType, BoxStream&)>;
ErrorOr<BoxList> read_entire_file(BoxCallback);
private:
Reader(MaybeOwned<BoxStream> stream)
: m_box_stream(move(stream))
{
}
MaybeOwned<BoxStream> m_box_stream;
};
}

View file

@ -9,7 +9,6 @@
#include <LibGfx/ImageFormats/GIFLoader.h>
#include <LibGfx/ImageFormats/ICOLoader.h>
#include <LibGfx/ImageFormats/ImageDecoder.h>
#include <LibGfx/ImageFormats/JPEG2000Loader.h>
#include <LibGfx/ImageFormats/JPEGLoader.h>
#include <LibGfx/ImageFormats/JPEGXLLoader.h>
#include <LibGfx/ImageFormats/PNGLoader.h>
@ -30,7 +29,6 @@ static ErrorOr<OwnPtr<ImageDecoderPlugin>> 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 },

File diff suppressed because it is too large Load diff

View file

@ -1,54 +0,0 @@
/*
* Copyright (c) 2024, Nico Weber <thakis@chromium.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibGfx/ImageFormats/ImageDecoder.h>
namespace Gfx {
namespace JPEG2000 {
struct TagTreeNode;
class TagTree {
public:
TagTree(TagTree&&);
~TagTree();
static ErrorOr<TagTree> create(u32 x_count, u32 y_count);
ErrorOr<u32> read_value(u32 x, u32 y, Function<ErrorOr<bool>()> const& read_bit, Optional<u32> stop_at = {}) const;
private:
TagTree(NonnullOwnPtr<TagTreeNode>);
NonnullOwnPtr<TagTreeNode> m_root;
};
}
struct JPEG2000LoadingContext;
class JPEG2000ImageDecoderPlugin : public ImageDecoderPlugin {
public:
static bool sniff(ReadonlyBytes);
static ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> create(ReadonlyBytes);
virtual ~JPEG2000ImageDecoderPlugin() override;
virtual IntSize size() override;
virtual ErrorOr<ImageFrameDescriptor> frame(size_t index, Optional<IntSize> ideal_size = {}) override;
virtual ErrorOr<Optional<ReadonlyBytes>> icc_data() override;
private:
JPEG2000ImageDecoderPlugin();
OwnPtr<JPEG2000LoadingContext> m_context;
};
}

View file

@ -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)

View file

@ -1,28 +0,0 @@
/*
* Copyright (c) 2023, Gregory Bertilson <Zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/MemoryStream.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/MappedFile.h>
#include <LibGfx/ImageFormats/ISOBMFF/Reader.h>
ErrorOr<int> 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<FixedMemoryStream>(file->bytes()))));
auto boxes = TRY(reader.read_entire_file());
for (auto& box : boxes)
box->dump();
return 0;
}