mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 07:17:35 +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
|
@ -36,7 +36,6 @@ set(SOURCES
|
||||||
ImageFormats/BMPWriter.cpp
|
ImageFormats/BMPWriter.cpp
|
||||||
ImageFormats/BooleanDecoder.cpp
|
ImageFormats/BooleanDecoder.cpp
|
||||||
ImageFormats/TIFFLoader.cpp
|
ImageFormats/TIFFLoader.cpp
|
||||||
ImageFormats/TIFFTagHandler.cpp
|
|
||||||
ImageFormats/DDSLoader.cpp
|
ImageFormats/DDSLoader.cpp
|
||||||
ImageFormats/GIFLoader.cpp
|
ImageFormats/GIFLoader.cpp
|
||||||
ImageFormats/ICOLoader.cpp
|
ImageFormats/ICOLoader.cpp
|
||||||
|
@ -79,3 +78,22 @@ set(SOURCES
|
||||||
|
|
||||||
serenity_lib(LibGfx gfx)
|
serenity_lib(LibGfx gfx)
|
||||||
target_link_libraries(LibGfx PRIVATE LibCompress LibCore LibCrypto LibFileSystem LibTextCodec LibIPC LibUnicode)
|
target_link_libraries(LibGfx PRIVATE LibCompress LibCore LibCrypto LibFileSystem LibTextCodec LibIPC LibUnicode)
|
||||||
|
|
||||||
|
set(generated_sources TIFFMetadata.h TIFFTagHandler.cpp)
|
||||||
|
list(TRANSFORM generated_sources PREPEND "ImageFormats/")
|
||||||
|
|
||||||
|
find_package(Python COMPONENTS Interpreter REQUIRED)
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${generated_sources}
|
||||||
|
COMMAND ${Python_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/TIFFGenerator.py" -o "${CMAKE_CURRENT_BINARY_DIR}/ImageFormats"
|
||||||
|
DEPENDS "TIFFGenerator.py"
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
target_sources(LibGfx PRIVATE ${generated_sources})
|
||||||
|
add_custom_target(generate_tiff_files_handler DEPENDS ${generated_sources})
|
||||||
|
add_dependencies(all_generated generate_tiff_files_handler)
|
||||||
|
add_dependencies(LibGfx generate_tiff_files_handler)
|
||||||
|
|
||||||
|
list(TRANSFORM generated_sources PREPEND "${CMAKE_CURRENT_BINARY_DIR}/")
|
||||||
|
install(FILES ${generated_sources} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/LibGfx/ImageFormats")
|
||||||
|
|
|
@ -52,7 +52,7 @@ public:
|
||||||
|
|
||||||
IntSize size() const
|
IntSize size() const
|
||||||
{
|
{
|
||||||
return m_metadata.size;
|
return { *m_metadata.image_width(), *m_metadata.image_height() };
|
||||||
}
|
}
|
||||||
|
|
||||||
State state() const
|
State state() const
|
||||||
|
@ -74,21 +74,24 @@ private:
|
||||||
template<typename ByteReader>
|
template<typename ByteReader>
|
||||||
ErrorOr<void> loop_over_pixels(ByteReader&& byte_reader, Function<ErrorOr<void>(u32)> initializer = {})
|
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) {
|
auto const strips_offset = *m_metadata.strip_offsets();
|
||||||
TRY(m_stream->seek(m_metadata.strip_offsets[strip_index]));
|
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)
|
if (initializer)
|
||||||
TRY(initializer(m_metadata.strip_bytes_count[strip_index]));
|
TRY(initializer(strip_byte_counts[strip_index]));
|
||||||
for (u32 row = 0; row < m_metadata.rows_per_strip; row++) {
|
for (u32 row = 0; row < *m_metadata.rows_per_strip(); row++) {
|
||||||
auto const scanline = row + m_metadata.rows_per_strip * strip_index;
|
auto const scanline = row + *m_metadata.rows_per_strip() * strip_index;
|
||||||
if (scanline >= static_cast<u32>(m_metadata.size.height()))
|
if (scanline >= *m_metadata.image_height())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
Optional<Color> last_color {};
|
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()) };
|
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_red(last_color->red() + color.red());
|
||||||
color.set_green(last_color->green() + color.green());
|
color.set_green(last_color->green() + color.green());
|
||||||
color.set_blue(last_color->blue() + color.blue());
|
color.set_blue(last_color->blue() + color.blue());
|
||||||
|
@ -105,9 +108,9 @@ private:
|
||||||
|
|
||||||
ErrorOr<void> decode_frame_impl()
|
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:
|
case Compression::NoCompression:
|
||||||
TRY(loop_over_pixels([this]() { return read_value<u8>(); }));
|
TRY(loop_over_pixels([this]() { return read_value<u8>(); }));
|
||||||
break;
|
break;
|
||||||
|
@ -341,7 +344,7 @@ private:
|
||||||
result.empend(T { TRY(read_value<typename T::Type>()), TRY(read_value<typename T::Type>()) });
|
result.empend(T { TRY(read_value<typename T::Type>()), TRY(read_value<typename T::Type>()) });
|
||||||
} else {
|
} else {
|
||||||
for (u32 i = 0; i < count; ++i)
|
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;
|
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 {};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
338
Userland/Libraries/LibGfx/TIFFGenerator.py
Executable file
338
Userland/Libraries/LibGfx/TIFFGenerator.py
Executable file
|
@ -0,0 +1,338 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Copyright (c) 2023, Lucas Chollet <lucas.chollet@serenityos.org>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import re
|
||||||
|
from enum import Enum
|
||||||
|
from collections import namedtuple
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import List, Type
|
||||||
|
|
||||||
|
|
||||||
|
class TIFFType(Enum):
|
||||||
|
Byte = 1
|
||||||
|
ASCII = 2
|
||||||
|
UnsignedShort = 3
|
||||||
|
UnsignedLong = 4
|
||||||
|
UnsignedRational = 5
|
||||||
|
Undefined = 7
|
||||||
|
SignedLong = 9
|
||||||
|
SignedRational = 10
|
||||||
|
Float = 11
|
||||||
|
Double = 12
|
||||||
|
UTF8 = 129
|
||||||
|
|
||||||
|
|
||||||
|
class Predictor(Enum):
|
||||||
|
NoPrediction = 1
|
||||||
|
HorizontalDifferencing = 2
|
||||||
|
|
||||||
|
|
||||||
|
class Compression(Enum):
|
||||||
|
NoCompression = 1
|
||||||
|
CCITT = 2
|
||||||
|
Group3Fax = 3
|
||||||
|
Group4Fax = 4
|
||||||
|
LZW = 5
|
||||||
|
JPEG = 6
|
||||||
|
PackBits = 32773
|
||||||
|
|
||||||
|
|
||||||
|
tag_fields = ['id', 'types', 'counts', 'default', 'name', 'associated_enum']
|
||||||
|
|
||||||
|
Tag = namedtuple(
|
||||||
|
'Tag',
|
||||||
|
field_names=tag_fields,
|
||||||
|
defaults=(None,) * len(tag_fields)
|
||||||
|
)
|
||||||
|
|
||||||
|
# FIXME: Some tag have only a few allowed values, we should ensure that
|
||||||
|
known_tags: List[Tag] = [
|
||||||
|
Tag('256', [TIFFType.UnsignedShort, TIFFType.UnsignedLong], [1], None, "ImageWidth"),
|
||||||
|
Tag('257', [TIFFType.UnsignedShort, TIFFType.UnsignedLong], [1], None, "ImageHeight"),
|
||||||
|
Tag('258', [TIFFType.UnsignedShort], [3], None, "BitPerSample"),
|
||||||
|
Tag('259', [TIFFType.UnsignedShort], [1], None, "Compression", Compression),
|
||||||
|
Tag('273', [TIFFType.UnsignedShort, TIFFType.UnsignedLong], [], None, "StripOffsets"),
|
||||||
|
Tag('278', [TIFFType.UnsignedShort, TIFFType.UnsignedLong], [1], None, "RowsPerStrip"),
|
||||||
|
Tag('279', [TIFFType.UnsignedShort, TIFFType.UnsignedLong], [], None, "StripByteCounts"),
|
||||||
|
Tag('317', [TIFFType.UnsignedShort], [1], Predictor.NoPrediction, "Predictor", Predictor),
|
||||||
|
]
|
||||||
|
|
||||||
|
HANDLE_TAG_SIGNATURE_TEMPLATE = ("ErrorOr<void> {namespace}handle_tag(Metadata& metadata, u16 tag,"
|
||||||
|
" {namespace}Type type, u32 count, Vector<{namespace}Value>&& value)")
|
||||||
|
HANDLE_TAG_SIGNATURE = HANDLE_TAG_SIGNATURE_TEMPLATE.format(namespace="")
|
||||||
|
HANDLE_TAG_SIGNATURE_TIFF_NAMESPACE = HANDLE_TAG_SIGNATURE_TEMPLATE.format(namespace="TIFF::")
|
||||||
|
|
||||||
|
LICENSE = R"""/*
|
||||||
|
* Copyright (c) 2023, Lucas Chollet <lucas.chollet@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/"""
|
||||||
|
|
||||||
|
|
||||||
|
def export_enum_to_cpp(e: Type[Enum], special_name: str | None = None) -> str:
|
||||||
|
output = f'enum class {special_name if special_name else e.__name__} {{\n'
|
||||||
|
|
||||||
|
for entry in e:
|
||||||
|
output += f' {entry.name} = {entry.value},\n'
|
||||||
|
|
||||||
|
output += "};"
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def promote_type(t: TIFFType) -> TIFFType:
|
||||||
|
if t == TIFFType.UnsignedShort:
|
||||||
|
return TIFFType.UnsignedLong
|
||||||
|
return t
|
||||||
|
|
||||||
|
|
||||||
|
def tiff_type_to_cpp(t: TIFFType, without_promotion: bool = False) -> str:
|
||||||
|
# To simplify the code generator and the Metadata class API, all u16 are promoted to u32
|
||||||
|
# Note that the Value<> type doesn't include u16 for this reason
|
||||||
|
if not without_promotion:
|
||||||
|
t = promote_type(t)
|
||||||
|
match t:
|
||||||
|
case TIFFType.UnsignedShort:
|
||||||
|
return 'u16'
|
||||||
|
case TIFFType.UnsignedLong:
|
||||||
|
return 'u32'
|
||||||
|
case _:
|
||||||
|
raise RuntimeError(f'Type "{t}" not recognized, please update tiff_type_to_read_only_cpp()')
|
||||||
|
|
||||||
|
|
||||||
|
def export_promoter() -> str:
|
||||||
|
output = R"""template<typename T>
|
||||||
|
struct TypePromoter {
|
||||||
|
using Type = T;
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
specialization_template = R"""template<>
|
||||||
|
struct TypePromoter<{}> {{
|
||||||
|
using Type = {};
|
||||||
|
}};
|
||||||
|
"""
|
||||||
|
for t in TIFFType:
|
||||||
|
if promote_type(t) != t:
|
||||||
|
output += specialization_template.format(tiff_type_to_cpp(t, without_promotion=True), tiff_type_to_cpp(t))
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def retrieve_biggest_type(types: List[TIFFType]) -> TIFFType:
|
||||||
|
return TIFFType(max([t.value for t in types]))
|
||||||
|
|
||||||
|
|
||||||
|
def pascal_case_to_snake_case(name: str) -> str:
|
||||||
|
return re.sub(r'(?<!^)(?=[A-Z])', '_', name).lower()
|
||||||
|
|
||||||
|
|
||||||
|
def generate_getter(tag: Tag) -> str:
|
||||||
|
variant_inner_type = tiff_type_to_cpp(retrieve_biggest_type(tag.types))
|
||||||
|
|
||||||
|
extracted_value_template = f"(*possible_value)[{{}}].get<{variant_inner_type}>()"
|
||||||
|
|
||||||
|
tag_final_type = variant_inner_type
|
||||||
|
if tag.associated_enum:
|
||||||
|
tag_final_type = f"TIFF::{tag.associated_enum.__name__}"
|
||||||
|
extracted_value_template = f"static_cast<{tag_final_type}>({extracted_value_template})"
|
||||||
|
|
||||||
|
if len(tag.counts) == 1 and tag.counts[0] == 1:
|
||||||
|
return_type = tag_final_type
|
||||||
|
unpacked_if_needed = f"return {extracted_value_template.format(0)};"
|
||||||
|
else:
|
||||||
|
if len(tag.counts) == 1:
|
||||||
|
container_type = f'Array<{tag_final_type}, {tag.counts[0]}>'
|
||||||
|
container_initialization = f'{container_type} tmp{{}};'
|
||||||
|
else:
|
||||||
|
container_type = f'Vector<{tag_final_type}>'
|
||||||
|
container_initialization = fR"""{container_type} tmp{{}};
|
||||||
|
auto maybe_failure = tmp.try_resize(possible_value->size());
|
||||||
|
if (maybe_failure.is_error())
|
||||||
|
return OptionalNone {{}};
|
||||||
|
"""
|
||||||
|
|
||||||
|
return_type = container_type
|
||||||
|
unpacked_if_needed = fR"""
|
||||||
|
{container_initialization}
|
||||||
|
for (u32 i = 0; i < possible_value->size(); ++i)
|
||||||
|
tmp[i] = {extracted_value_template.format('i')};
|
||||||
|
|
||||||
|
return tmp;"""
|
||||||
|
|
||||||
|
signature = fR" Optional<{return_type}> {pascal_case_to_snake_case(tag.name)}() const"
|
||||||
|
|
||||||
|
body = fR"""
|
||||||
|
{{
|
||||||
|
auto const& possible_value = m_data.get("{tag.name}"sv);
|
||||||
|
if (!possible_value.has_value())
|
||||||
|
return OptionalNone {{}};
|
||||||
|
{unpacked_if_needed}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
return signature + body
|
||||||
|
|
||||||
|
|
||||||
|
def generate_metadata_class(tags: List[Tag]) -> str:
|
||||||
|
getters = '\n'.join([generate_getter(tag) for tag in tags])
|
||||||
|
|
||||||
|
output = fR"""class Metadata {{
|
||||||
|
public:
|
||||||
|
{getters}
|
||||||
|
private:
|
||||||
|
friend {HANDLE_TAG_SIGNATURE_TIFF_NAMESPACE};
|
||||||
|
|
||||||
|
void add_entry(StringView key, Vector<TIFF::Value>&& value) {{
|
||||||
|
m_data.set(key, move(value));
|
||||||
|
}}
|
||||||
|
|
||||||
|
HashMap<StringView, Vector<TIFF::Value>> m_data;
|
||||||
|
}};
|
||||||
|
"""
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def generate_metadata_file(tags: List[Tag]) -> str:
|
||||||
|
output = fR"""{LICENSE}
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/HashMap.h>
|
||||||
|
#include <AK/Variant.h>
|
||||||
|
#include <AK/Vector.h>
|
||||||
|
#include <LibGfx/Size.h>
|
||||||
|
|
||||||
|
namespace Gfx {{
|
||||||
|
|
||||||
|
class Metadata;
|
||||||
|
|
||||||
|
namespace TIFF {{
|
||||||
|
|
||||||
|
{export_enum_to_cpp(TIFFType, 'Type')}
|
||||||
|
|
||||||
|
template<OneOf<u32, i32> x32>
|
||||||
|
struct Rational {{
|
||||||
|
using Type = x32;
|
||||||
|
x32 numerator;
|
||||||
|
x32 denominator;
|
||||||
|
}};
|
||||||
|
|
||||||
|
{export_promoter()}
|
||||||
|
|
||||||
|
// Note that u16 is not include on purpose
|
||||||
|
using Value = Variant<u8, String, u32, Rational<u32>, i32, Rational<i32>>;
|
||||||
|
|
||||||
|
// This enum is progressively defined across sections but summarized in:
|
||||||
|
// Appendix A: TIFF Tags Sorted by Number
|
||||||
|
{export_enum_to_cpp(Compression)}
|
||||||
|
|
||||||
|
{export_enum_to_cpp(Predictor)}
|
||||||
|
|
||||||
|
{HANDLE_TAG_SIGNATURE};
|
||||||
|
|
||||||
|
}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
output += generate_metadata_class(tags)
|
||||||
|
|
||||||
|
output += '\n}\n'
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def generate_tag_handler(tag: Tag) -> str:
|
||||||
|
not_in_type_list = f"({' && '.join([f'type != Type::{t.name}' for t in tag.types])})"
|
||||||
|
|
||||||
|
not_in_count_list = ''
|
||||||
|
if len(tag.counts) != 0:
|
||||||
|
not_in_count_list = f"|| ({' && '.join([f'count != {c}' for c in tag.counts])})"
|
||||||
|
pre_condition = fR"""if ({not_in_type_list}
|
||||||
|
{not_in_count_list})
|
||||||
|
return Error::from_string_literal("TIFFImageDecoderPlugin: Tag {tag.name} invalid");"""
|
||||||
|
|
||||||
|
check_value = ''
|
||||||
|
if tag.associated_enum is not None:
|
||||||
|
not_in_value_list = f"({' && '.join([f'v != {v.value}' for v in tag.associated_enum])})"
|
||||||
|
check_value = fR"""TRY(value[0].visit(
|
||||||
|
[]({tiff_type_to_cpp(tag.types[0])} const& v) -> ErrorOr<void> {{
|
||||||
|
if ({not_in_value_list})
|
||||||
|
return Error::from_string_literal("TIFFImageDecoderPlugin: Invalid value for tag {tag.name}");
|
||||||
|
return {{}};
|
||||||
|
}},
|
||||||
|
[&](auto const&) -> ErrorOr<void> {{
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}}));
|
||||||
|
"""
|
||||||
|
|
||||||
|
output = fR""" case {tag.id}:
|
||||||
|
// {tag.name}
|
||||||
|
{pre_condition}
|
||||||
|
{check_value}
|
||||||
|
metadata.add_entry("{tag.name}"sv, move(value));
|
||||||
|
break;
|
||||||
|
"""
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def generate_tag_handler_file(tags: List[Tag]) -> str:
|
||||||
|
output = fR"""{LICENSE}
|
||||||
|
|
||||||
|
#include <AK/Debug.h>
|
||||||
|
#include <AK/String.h>
|
||||||
|
#include <LibGfx/ImageFormats/TIFFMetadata.h>
|
||||||
|
|
||||||
|
namespace Gfx::TIFF {{
|
||||||
|
|
||||||
|
{HANDLE_TAG_SIGNATURE}
|
||||||
|
{{
|
||||||
|
switch (tag) {{
|
||||||
|
"""
|
||||||
|
|
||||||
|
output += '\n'.join([generate_tag_handler(t) for t in tags])
|
||||||
|
|
||||||
|
output += R"""
|
||||||
|
default:
|
||||||
|
dbgln_if(TIFF_DEBUG, "Unknown tag: {}", tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def update_file(target: Path, new_content: str):
|
||||||
|
should_update = True
|
||||||
|
|
||||||
|
if target.exists():
|
||||||
|
with target.open('r') as file:
|
||||||
|
content = file.read()
|
||||||
|
if content == new_content:
|
||||||
|
should_update = False
|
||||||
|
|
||||||
|
if should_update:
|
||||||
|
with target.open('w') as file:
|
||||||
|
file.write(new_content)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('-o', '--output')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
output_path = Path(args.output)
|
||||||
|
|
||||||
|
update_file(output_path / 'TIFFMetadata.h', generate_metadata_file(known_tags))
|
||||||
|
update_file(output_path / 'TIFFTagHandler.cpp', generate_tag_handler_file(known_tags))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
Loading…
Add table
Add a link
Reference in a new issue