mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 00:50:22 +00:00
LibGfx+icc: Read namedColor2Type
This is the type of namedColor2Tag, which is a required tag in NamedColor profiles. The implementation is pretty basic for now and only exposes the numbers stored in the file directly (after endian conversion).
This commit is contained in:
parent
1ce34805f8
commit
cbcf8471a6
Notes:
sideshowbarker
2024-07-17 03:03:44 +09:00
Author: https://github.com/nico Commit: https://github.com/SerenityOS/serenity/commit/cbcf8471a6 Pull-request: https://github.com/SerenityOS/serenity/pull/17357
4 changed files with 176 additions and 1 deletions
|
@ -580,6 +580,8 @@ ErrorOr<NonnullRefPtr<TagData>> Profile::read_tag(ReadonlyBytes bytes, u32 offse
|
|||
return Lut8TagData::from_bytes(tag_bytes, offset_to_beginning_of_tag_data_element, size_of_tag_data_element);
|
||||
case MultiLocalizedUnicodeTagData::Type:
|
||||
return MultiLocalizedUnicodeTagData::from_bytes(tag_bytes, offset_to_beginning_of_tag_data_element, size_of_tag_data_element);
|
||||
case NamedColor2TagData::Type:
|
||||
return NamedColor2TagData::from_bytes(tag_bytes, offset_to_beginning_of_tag_data_element, size_of_tag_data_element);
|
||||
case ParametricCurveTagData::Type:
|
||||
return ParametricCurveTagData::from_bytes(tag_bytes, offset_to_beginning_of_tag_data_element, size_of_tag_data_element);
|
||||
case S15Fixed16ArrayTagData::Type:
|
||||
|
@ -1137,7 +1139,14 @@ ErrorOr<void> Profile::check_tag_types()
|
|||
|
||||
// ICC v4, 9.2.37 namedColor2Tag
|
||||
// "Permitted tag types: namedColor2Type"
|
||||
// FIXME
|
||||
if (auto type = m_tag_table.get(namedColor2Tag); type.has_value()) {
|
||||
if (type.value()->type() != NamedColor2TagData::Type)
|
||||
return Error::from_string_literal("ICC::Profile: namedColor2Tag has unexpected type");
|
||||
// ICC v4, 10.17 namedColor2Type
|
||||
// "The device representation corresponds to the header’s “data colour space” field.
|
||||
// This representation should be consistent with the “number of device coordinates” field in the namedColor2Type."
|
||||
// FIXME: check that
|
||||
}
|
||||
|
||||
// ICC v4, 9.2.38 outputResponseTag
|
||||
// "Permitted tag types: responseCurveSet16Type"
|
||||
|
|
|
@ -230,6 +230,7 @@ ErrorOr<NonnullRefPtr<MultiLocalizedUnicodeTagData>> MultiLocalizedUnicodeTagDat
|
|||
BigEndian<u32> string_length_in_bytes;
|
||||
BigEndian<u32> string_offset_in_bytes;
|
||||
};
|
||||
static_assert(AssertSize<RawRecord, 12>());
|
||||
|
||||
for (u32 i = 0; i < number_of_records; ++i) {
|
||||
size_t offset = 16 + i * record_size;
|
||||
|
@ -268,6 +269,75 @@ unsigned ParametricCurveTagData::parameter_count(FunctionType function_type)
|
|||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<NamedColor2TagData>> NamedColor2TagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size)
|
||||
{
|
||||
// ICC v4, 10.17 namedColor2Type
|
||||
VERIFY(tag_type(bytes) == Type);
|
||||
TRY(check_reserved(bytes));
|
||||
|
||||
// Table 66 — namedColor2Type encoding
|
||||
struct NamedColorHeader {
|
||||
BigEndian<u32> vendor_specific_flag;
|
||||
BigEndian<u32> count_of_named_colors;
|
||||
BigEndian<u32> number_of_device_coordinates_of_each_named_color;
|
||||
u8 prefix_for_each_color_name[32]; // null-terminated
|
||||
u8 suffix_for_each_color_name[32]; // null-terminated
|
||||
};
|
||||
static_assert(AssertSize<NamedColorHeader, 76>());
|
||||
|
||||
if (bytes.size() < 2 * sizeof(u32) + sizeof(NamedColorHeader))
|
||||
return Error::from_string_literal("ICC::Profile: namedColor2Type has not enough data");
|
||||
|
||||
auto& header = *bit_cast<NamedColorHeader const*>(bytes.data() + 8);
|
||||
|
||||
unsigned const record_byte_size = 32 + sizeof(u16) * (3 + header.number_of_device_coordinates_of_each_named_color);
|
||||
if (bytes.size() < 2 * sizeof(u32) + sizeof(NamedColorHeader) + header.count_of_named_colors * record_byte_size)
|
||||
return Error::from_string_literal("ICC::Profile: namedColor2Type has not enough color data");
|
||||
|
||||
auto buffer_to_string = [](u8 const* buffer) -> ErrorOr<String> {
|
||||
size_t length = strnlen((char const*)buffer, 32);
|
||||
if (length == 32)
|
||||
return Error::from_string_literal("ICC::Profile: namedColor2Type string not \\0-terminated");
|
||||
for (size_t i = 0; i < length; ++i)
|
||||
if (buffer[i] >= 128)
|
||||
return Error::from_string_literal("ICC::Profile: namedColor2Type not 7-bit ASCII");
|
||||
return String::from_utf8({ buffer, length });
|
||||
};
|
||||
|
||||
String prefix = TRY(buffer_to_string(header.prefix_for_each_color_name));
|
||||
String suffix = TRY(buffer_to_string(header.suffix_for_each_color_name));
|
||||
|
||||
Vector<String> root_names;
|
||||
Vector<XYZOrLAB> pcs_coordinates;
|
||||
Vector<u16> device_coordinates;
|
||||
|
||||
TRY(root_names.try_resize(header.count_of_named_colors));
|
||||
TRY(pcs_coordinates.try_resize(header.count_of_named_colors));
|
||||
TRY(device_coordinates.try_resize(header.count_of_named_colors * header.number_of_device_coordinates_of_each_named_color));
|
||||
|
||||
for (unsigned i = 0; i < header.count_of_named_colors; ++i) {
|
||||
u8 const* root_name = bytes.data() + 8 + sizeof(NamedColorHeader) + i * record_byte_size;
|
||||
auto* components = bit_cast<BigEndian<u16> const*>(root_name + 32);
|
||||
|
||||
root_names[i] = TRY(buffer_to_string(root_name));
|
||||
pcs_coordinates[i] = { { { components[0], components[1], components[2] } } };
|
||||
for (unsigned j = 0; j < header.number_of_device_coordinates_of_each_named_color; ++j)
|
||||
device_coordinates[i * header.number_of_device_coordinates_of_each_named_color + j] = components[3 + j];
|
||||
}
|
||||
|
||||
return adopt_ref(*new NamedColor2TagData(offset, size, header.vendor_specific_flag, header.number_of_device_coordinates_of_each_named_color,
|
||||
move(prefix), move(suffix), move(root_names), move(pcs_coordinates), move(device_coordinates)));
|
||||
}
|
||||
|
||||
ErrorOr<String> NamedColor2TagData::color_name(u32 index)
|
||||
{
|
||||
StringBuilder builder;
|
||||
builder.append(prefix());
|
||||
builder.append(root_name(index));
|
||||
builder.append(suffix());
|
||||
return builder.to_string();
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<ParametricCurveTagData>> ParametricCurveTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size)
|
||||
{
|
||||
// ICC v4, 10.18 parametricCurveType
|
||||
|
|
|
@ -247,6 +247,82 @@ private:
|
|||
Vector<Record> m_records;
|
||||
};
|
||||
|
||||
// ICC v4, 10.17 namedColor2Type
|
||||
class NamedColor2TagData : public TagData {
|
||||
public:
|
||||
static constexpr TagTypeSignature Type { 0x6E636C32 }; // 'ncl2'
|
||||
|
||||
static ErrorOr<NonnullRefPtr<NamedColor2TagData>> from_bytes(ReadonlyBytes, u32 offset, u32 size);
|
||||
|
||||
// "The encoding is the same as the encodings for the PCS colour spaces
|
||||
// as described in 6.3.4.2 and 10.8. Only PCSXYZ and
|
||||
// legacy 16-bit PCSLAB encodings are permitted. PCS
|
||||
// values shall be relative colorimetric."
|
||||
// (Which I suppose implies this type must not be used in DeviceLink profiles unless
|
||||
// the device's PCS happens to be PCSXYZ or PCSLAB.)
|
||||
struct XYZOrLAB {
|
||||
union {
|
||||
struct {
|
||||
u16 x, y, z;
|
||||
} xyz;
|
||||
struct {
|
||||
u16 L, a, b;
|
||||
} lab;
|
||||
};
|
||||
};
|
||||
|
||||
NamedColor2TagData(u32 offset, u32 size, u32 vendor_specific_flag, u32 number_of_device_coordinates, String prefix, String suffix,
|
||||
Vector<String> root_names, Vector<XYZOrLAB> pcs_coordinates, Vector<u16> device_coordinates)
|
||||
: TagData(offset, size, Type)
|
||||
, m_vendor_specific_flag(vendor_specific_flag)
|
||||
, m_number_of_device_coordinates(number_of_device_coordinates)
|
||||
, m_prefix(move(prefix))
|
||||
, m_suffix(move(suffix))
|
||||
, m_root_names(move(root_names))
|
||||
, m_pcs_coordinates(move(pcs_coordinates))
|
||||
, m_device_coordinates(move(device_coordinates))
|
||||
{
|
||||
VERIFY(root_names.size() == pcs_coordinates.size());
|
||||
VERIFY(root_names.size() * number_of_device_coordinates == device_coordinates.size());
|
||||
}
|
||||
|
||||
// "(least-significant 16 bits reserved for ICC use)"
|
||||
u32 vendor_specific_flag() const { return m_vendor_specific_flag; }
|
||||
|
||||
// "If this field is 0, device coordinates are not provided."
|
||||
u32 number_of_device_coordinates() const { return m_number_of_device_coordinates; }
|
||||
|
||||
u32 size() { return m_root_names.size(); }
|
||||
|
||||
// "In order to maintain maximum portability, it is strongly recommended that
|
||||
// special characters of the 7-bit ASCII set not be used."
|
||||
String const& prefix() const { return m_prefix; } // "7-bit ASCII"
|
||||
String const& suffix() const { return m_suffix; } // "7-bit ASCII"
|
||||
String const& root_name(u32 index) const { return m_root_names[index]; } // "7-bit ASCII"
|
||||
|
||||
// Returns 7-bit ASCII.
|
||||
ErrorOr<String> color_name(u32 index);
|
||||
|
||||
// "The PCS representation corresponds to the header’s PCS field."
|
||||
XYZOrLAB const& pcs_coordinates(u32 index) { return m_pcs_coordinates[index]; }
|
||||
|
||||
// "The device representation corresponds to the header’s “data colour space” field."
|
||||
u16 const* device_coordinates(u32 index)
|
||||
{
|
||||
VERIFY((index + 1) * m_number_of_device_coordinates <= m_device_coordinates.size());
|
||||
return m_device_coordinates.data() + index * m_number_of_device_coordinates;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 m_vendor_specific_flag;
|
||||
u32 m_number_of_device_coordinates;
|
||||
String m_prefix;
|
||||
String m_suffix;
|
||||
Vector<String> m_root_names;
|
||||
Vector<XYZOrLAB> m_pcs_coordinates;
|
||||
Vector<u16> m_device_coordinates;
|
||||
};
|
||||
|
||||
// ICC v4, 10.18 parametricCurveType
|
||||
class ParametricCurveTagData : public TagData {
|
||||
public:
|
||||
|
|
|
@ -163,6 +163,26 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|||
record.iso_3166_1_country_code >> 8, record.iso_3166_1_country_code & 0xff,
|
||||
record.text);
|
||||
}
|
||||
} else if (tag_data->type() == Gfx::ICC::NamedColor2TagData::Type) {
|
||||
auto& named_colors = static_cast<Gfx::ICC::NamedColor2TagData&>(*tag_data);
|
||||
outln(" vendor specific flag: 0x{:08x}", named_colors.vendor_specific_flag());
|
||||
outln(" common name prefix: \"{}\"", named_colors.prefix());
|
||||
outln(" common name suffix: \"{}\"", named_colors.suffix());
|
||||
outln(" {} colors:", named_colors.size());
|
||||
for (size_t i = 0; i < min(named_colors.size(), 5u); ++i) {
|
||||
const auto& pcs = named_colors.pcs_coordinates(i);
|
||||
|
||||
// FIXME: Display decoded values? (See ICC v4 6.3.4.2 and 10.8.)
|
||||
out(" \"{}\", PCS coordinates: 0x{:04x} 0x{:04x} 0x{:04x}", MUST(named_colors.color_name(i)), pcs.xyz.x, pcs.xyz.y, pcs.xyz.z);
|
||||
if (auto number_of_device_coordinates = named_colors.number_of_device_coordinates(); number_of_device_coordinates > 0) {
|
||||
out(", device coordinates:");
|
||||
for (size_t j = 0; j < number_of_device_coordinates; ++j)
|
||||
out(" 0x{:04x}", named_colors.device_coordinates(i)[j]);
|
||||
}
|
||||
outln();
|
||||
}
|
||||
if (named_colors.size() > 5u)
|
||||
outln(" ...");
|
||||
} else if (tag_data->type() == Gfx::ICC::ParametricCurveTagData::Type) {
|
||||
auto& parametric_curve = static_cast<Gfx::ICC::ParametricCurveTagData&>(*tag_data);
|
||||
switch (parametric_curve.function_type()) {
|
||||
|
|
Loading…
Reference in a new issue