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:
parent
8c65cc185a
commit
9836a9ad0e
5 changed files with 372 additions and 251 deletions
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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 {};
|
||||
};
|
||||
|
||||
}
|
|
@ -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 {};
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue