1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 18:17:45 +00:00

LibGfx/TIFF: Introduce a code generator

This will allow us to generate code that handle and provide easy access
to metadata stored in TIFF's tags. The generator is a Python script, and
it output both TIFFMetadata.h and TIFFTagHandler.cpp files.

The generator will definitely need some update to support all TIFF and
EXIF tags, but that will still be easier than writing everything
ourselves.

Some small modifications are needed in TIFFLoader.cpp to make it
compatible with the new `Metadata` class.
This commit is contained in:
Lucas CHOLLET 2023-11-12 23:59:52 -05:00 committed by Andrew Kaster
parent 8c65cc185a
commit 9836a9ad0e
5 changed files with 372 additions and 251 deletions

View file

@ -52,7 +52,7 @@ public:
IntSize size() const
{
return m_metadata.size;
return { *m_metadata.image_width(), *m_metadata.image_height() };
}
State state() const
@ -74,21 +74,24 @@ private:
template<typename ByteReader>
ErrorOr<void> loop_over_pixels(ByteReader&& byte_reader, Function<ErrorOr<void>(u32)> initializer = {})
{
for (u32 strip_index = 0; strip_index < m_metadata.strip_offsets.size(); ++strip_index) {
TRY(m_stream->seek(m_metadata.strip_offsets[strip_index]));
auto const strips_offset = *m_metadata.strip_offsets();
auto const strip_byte_counts = *m_metadata.strip_byte_counts();
for (u32 strip_index = 0; strip_index < strips_offset.size(); ++strip_index) {
TRY(m_stream->seek(strips_offset[strip_index]));
if (initializer)
TRY(initializer(m_metadata.strip_bytes_count[strip_index]));
for (u32 row = 0; row < m_metadata.rows_per_strip; row++) {
auto const scanline = row + m_metadata.rows_per_strip * strip_index;
if (scanline >= static_cast<u32>(m_metadata.size.height()))
TRY(initializer(strip_byte_counts[strip_index]));
for (u32 row = 0; row < *m_metadata.rows_per_strip(); row++) {
auto const scanline = row + *m_metadata.rows_per_strip() * strip_index;
if (scanline >= *m_metadata.image_height())
break;
Optional<Color> last_color {};
for (u32 column = 0; column < static_cast<u32>(m_metadata.size.width()); ++column) {
for (u32 column = 0; column < *m_metadata.image_width(); ++column) {
auto color = Color { TRY(byte_reader()), TRY(byte_reader()), TRY(byte_reader()) };
if (m_metadata.predictor == Predictor::HorizontalDifferencing && last_color.has_value()) {
if (m_metadata.predictor() == Predictor::HorizontalDifferencing && last_color.has_value()) {
color.set_red(last_color->red() + color.red());
color.set_green(last_color->green() + color.green());
color.set_blue(last_color->blue() + color.blue());
@ -105,9 +108,9 @@ private:
ErrorOr<void> decode_frame_impl()
{
m_bitmap = TRY(Bitmap::create(BitmapFormat::BGRA8888, m_metadata.size));
m_bitmap = TRY(Bitmap::create(BitmapFormat::BGRA8888, size()));
switch (m_metadata.compression) {
switch (*m_metadata.compression()) {
case Compression::NoCompression:
TRY(loop_over_pixels([this]() { return read_value<u8>(); }));
break;
@ -341,7 +344,7 @@ private:
result.empend(T { TRY(read_value<typename T::Type>()), TRY(read_value<typename T::Type>()) });
} else {
for (u32 i = 0; i < count; ++i)
result.empend(TRY(read_value<T>()));
result.empend(typename TypePromoter<T>::Type(TRY(read_value<T>())));
}
return result;
};

View file

@ -1,73 +0,0 @@
/*
* Copyright (c) 2023, Lucas Chollet <lucas.chollet@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Variant.h>
#include <AK/Vector.h>
#include <LibGfx/Size.h>
namespace Gfx {
struct Metadata;
namespace TIFF {
enum class Type {
Byte = 1,
ASCII = 2,
UnsignedShort = 3,
UnsignedLong = 4,
UnsignedRational = 5,
Undefined = 7,
SignedLong = 9,
SignedRational = 10,
Float = 11,
Double = 12,
UTF8 = 129,
};
template<OneOf<u32, i32> x32>
struct Rational {
using Type = x32;
x32 numerator;
x32 denominator;
};
using Value = Variant<u8, String, u16, u32, Rational<u32>, i32, Rational<i32>>;
// This enum is progessively defined across sections but summarized in:
// Appendix A: TIFF Tags Sorted by Number
enum class Compression {
NoCompression = 1,
CCITT = 2,
Group3Fax = 3,
Group4Fax = 4,
LZW = 5,
JPEG = 6,
PackBits = 32773,
};
enum class Predictor {
None = 1,
HorizontalDifferencing = 2,
};
ErrorOr<void> handle_tag(Metadata& metadata, u16 tag, Type type, u32 count, Vector<Value>&& value);
}
struct Metadata {
IntSize size {};
Array<u16, 3> bits_per_sample {};
TIFF::Compression compression {};
TIFF::Predictor predictor {};
Vector<u32> strip_offsets {};
u32 rows_per_strip {};
Vector<u32> strip_bytes_count {};
};
}

View file

@ -1,165 +0,0 @@
/*
* Copyright (c) 2023, Lucas Chollet <lucas.chollet@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Debug.h>
#include <AK/String.h>
#include <LibGfx/ImageFormats/TIFFMetadata.h>
namespace Gfx::TIFF {
ErrorOr<void> handle_tag(Metadata& metadata, u16 tag, Type type, u32 count, Vector<Value>&& value)
{
// FIXME: Make that easy to extend
switch (tag) {
case 256:
// ImageWidth
if ((type != Type::UnsignedShort && type != Type::UnsignedLong) || count != 1)
return Error::from_string_literal("TIFFImageDecoderPlugin: Invalid tag 256");
value[0].visit(
[&metadata]<OneOf<u16, u32> T>(T const& width) {
metadata.size.set_width(width);
},
[&](auto const&) {
VERIFY_NOT_REACHED();
});
break;
case 257:
// ImageLength
if ((type != Type::UnsignedShort && type != Type::UnsignedLong) || count != 1)
return Error::from_string_literal("TIFFImageDecoderPlugin: Invalid tag 257");
value[0].visit(
[&metadata]<OneOf<u16, u32> T>(T const& width) {
metadata.size.set_height(width);
},
[&](auto const&) {
VERIFY_NOT_REACHED();
});
break;
case 258:
// BitsPerSample
if (type != Type::UnsignedShort || count != 3)
return Error::from_string_literal("TIFFImageDecoderPlugin: Invalid tag 258");
for (u8 i = 0; i < metadata.bits_per_sample.size(); ++i) {
value[i].visit(
[&metadata, i](u16 const& bits_per_sample) {
metadata.bits_per_sample[i] = bits_per_sample;
},
[&](auto const&) {
VERIFY_NOT_REACHED();
});
}
break;
case 259:
// Compression
if (type != Type::UnsignedShort || count != 1)
return Error::from_string_literal("TIFFImageDecoderPlugin: Invalid tag 259");
TRY(value[0].visit(
[&metadata](u16 const& compression) -> ErrorOr<void> {
if (compression > 6 && compression != to_underlying(Compression::PackBits))
return Error::from_string_literal("TIFFImageDecoderPlugin: Invalid compression value");
metadata.compression = static_cast<Compression>(compression);
return {};
},
[&](auto const&) -> ErrorOr<void> {
VERIFY_NOT_REACHED();
}));
break;
case 273:
// StripOffsets
if (type != Type::UnsignedShort && type != Type::UnsignedLong)
return Error::from_string_literal("TIFFImageDecoderPlugin: Invalid tag 273");
TRY(metadata.strip_offsets.try_ensure_capacity(count));
for (u32 i = 0; i < count; ++i) {
value[i].visit(
[&metadata]<OneOf<u16, u32> T>(T const& offset) {
metadata.strip_offsets.append(offset);
},
[&](auto const&) {
VERIFY_NOT_REACHED();
});
}
break;
case 277:
// SamplesPerPixel
if (type != Type::UnsignedShort || count != 1)
return Error::from_string_literal("TIFFImageDecoderPlugin: Invalid tag 277");
TRY(value[0].visit(
[](u16 const& samples_per_pixels) -> ErrorOr<void> {
if (samples_per_pixels != 3)
return Error::from_string_literal("TIFFImageDecoderPlugin: Invalid tag 277");
return {};
},
[&](auto const&) -> ErrorOr<void> {
VERIFY_NOT_REACHED();
}));
break;
case 278:
// RowsPerStrip
if ((type != Type::UnsignedShort && type != Type::UnsignedLong) || count != 1)
return Error::from_string_literal("TIFFImageDecoderPlugin: Invalid tag 278");
value[0].visit(
[&metadata]<OneOf<u16, u32> T>(T const& rows_per_strip) {
metadata.rows_per_strip = rows_per_strip;
},
[&](auto const&) {
VERIFY_NOT_REACHED();
});
break;
case 279:
// StripByteCounts
if (type != Type::UnsignedShort && type != Type::UnsignedLong)
return Error::from_string_literal("TIFFImageDecoderPlugin: Invalid tag 279");
TRY(metadata.strip_bytes_count.try_ensure_capacity(count));
for (u32 i = 0; i < count; ++i) {
value[i].visit(
[&metadata]<OneOf<u16, u32> T>(T const& offset) {
metadata.strip_bytes_count.append(offset);
},
[&](auto const&) {
VERIFY_NOT_REACHED();
});
}
break;
case 317:
// Predictor
if (type != Type::UnsignedShort || count != 1)
return Error::from_string_literal("TIFFImageDecoderPlugin: Invalid tag 317");
TRY(value[0].visit(
[&metadata](u16 const& predictor) -> ErrorOr<void> {
if (predictor != 1 && predictor != 2)
return Error::from_string_literal("TIFFImageDecoderPlugin: Invalid predictor value");
metadata.predictor = static_cast<Predictor>(predictor);
return {};
},
[&](auto const&) -> ErrorOr<void> {
VERIFY_NOT_REACHED();
}));
break;
default:
dbgln_if(TIFF_DEBUG, "Unknown tag: {}", tag);
}
return {};
}
}