mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-21 23:20:20 +00:00
LibGfx: Remove JPEG2000 image format support
This format is not supported by other browsers.
This commit is contained in:
parent
a34a5af939
commit
1039acca8c
Notes:
sideshowbarker
2024-07-17 04:10:16 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/LadybirdBrowser/ladybird/commit/1039acca8c Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/191 Reviewed-by: https://github.com/nico
26 changed files with 0 additions and 2261 deletions
|
@ -122,10 +122,6 @@
|
|||
# cmakedefine01 JOB_DEBUG
|
||||
#endif
|
||||
|
||||
#ifndef JPEG2000_DEBUG
|
||||
# cmakedefine01 JPEG2000_DEBUG
|
||||
#endif
|
||||
|
||||
#ifndef JS_BYTECODE_DEBUG
|
||||
# cmakedefine01 JS_BYTECODE_DEBUG
|
||||
#endif
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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=",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -9,7 +9,6 @@ set(TEST_SOURCES
|
|||
TestImageWriter.cpp
|
||||
TestMedianCut.cpp
|
||||
TestPainter.cpp
|
||||
TestParseISOBMFF.cpp
|
||||
TestRect.cpp
|
||||
TestScalingFunctions.cpp
|
||||
TestWOFF.cpp
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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);
|
||||
}
|
Binary file not shown.
Binary file not shown.
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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)));
|
||||
}
|
||||
};
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in a new issue