LibGfx+icc: Add ICCProfile support for parametricCurveType and print it

With this, we can parse all types required in v4
"Three-component matrix-based Input profiles".
This commit is contained in:
Nico Weber 2023-01-23 13:55:57 -05:00 committed by Linus Groh
parent 67f718aa3f
commit fa00d94e82
Notes: sideshowbarker 2024-07-17 23:00:03 +09:00
3 changed files with 172 additions and 2 deletions

View file

@ -635,6 +635,58 @@ ErrorOr<NonnullRefPtr<MultiLocalizedUnicodeTagData>> MultiLocalizedUnicodeTagDat
return adopt_ref(*new MultiLocalizedUnicodeTagData(offset, size, move(records)));
}
unsigned ParametricCurveTagData::parameter_count(FunctionType function_type)
{
switch (function_type) {
case FunctionType::Type0:
return 1;
case FunctionType::Type1:
return 3;
case FunctionType::Type2:
return 4;
case FunctionType::Type3:
return 5;
case FunctionType::Type4:
return 7;
}
VERIFY_NOT_REACHED();
}
ErrorOr<NonnullRefPtr<ParametricCurveTagData>> ParametricCurveTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size)
{
// ICC v4, 10.18 parametricCurveType
VERIFY(tag_type(bytes) == Type);
TRY(check_reserved(bytes));
// "The parametricCurveType describes a one-dimensional curve by specifying one of a predefined set of functions
// using the parameters."
if (bytes.size() < 2 * sizeof(u32) + 2 * sizeof(u16))
return Error::from_string_literal("ICC::Profile: parametricCurveType has not enough data");
u16 raw_function_type = *bit_cast<BigEndian<u16> const*>(bytes.data() + 8);
u16 reserved = *bit_cast<BigEndian<u16> const*>(bytes.data() + 10);
if (reserved != 0)
return Error::from_string_literal("ICC::Profile: parametricCurveType reserved u16 after function type not 0");
if (raw_function_type > 4)
return Error::from_string_literal("ICC::Profile: parametricCurveType unknown function type");
FunctionType function_type = (FunctionType)raw_function_type;
unsigned count = parameter_count(function_type);
if (bytes.size() < 2 * sizeof(u32) + 2 * sizeof(u16) + count * sizeof(s15Fixed16Number))
return Error::from_string_literal("ICC::Profile: parametricCurveType has not enough data for parameters");
BigEndian<s15Fixed16Number> const* raw_parameters = bit_cast<BigEndian<s15Fixed16Number> const*>(bytes.data() + 12);
Array<S15Fixed16, 7> parameters;
parameters.fill(0);
for (unsigned i = 0; i < count; ++i)
parameters[i] = S15Fixed16::create_raw(raw_parameters[i]);
return adopt_ref(*new ParametricCurveTagData(offset, size, function_type, move(parameters)));
}
ErrorOr<NonnullRefPtr<S15Fixed16ArrayTagData>> S15Fixed16ArrayTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size)
{
// ICC v4, 10.22 s15Fixed16ArrayType
@ -897,6 +949,8 @@ ErrorOr<NonnullRefPtr<TagData>> Profile::read_tag(ReadonlyBytes bytes, Detail::T
return CurveTagData::from_bytes(tag_bytes, entry.offset_to_beginning_of_tag_data_element, entry.size_of_tag_data_element);
case MultiLocalizedUnicodeTagData::Type:
return MultiLocalizedUnicodeTagData::from_bytes(tag_bytes, entry.offset_to_beginning_of_tag_data_element, entry.size_of_tag_data_element);
case ParametricCurveTagData::Type:
return ParametricCurveTagData::from_bytes(tag_bytes, entry.offset_to_beginning_of_tag_data_element, entry.size_of_tag_data_element);
case S15Fixed16ArrayTagData::Type:
return S15Fixed16ArrayTagData::from_bytes(tag_bytes, entry.offset_to_beginning_of_tag_data_element, entry.size_of_tag_data_element);
case TextDescriptionTagData::Type:

View file

@ -224,6 +224,8 @@ private:
u64 m_bits = 0;
};
using S15Fixed16 = FixedPoint<16, i32>;
struct XYZ {
double x { 0 };
double y { 0 };
@ -313,13 +315,100 @@ private:
Vector<Record> m_records;
};
// ICC v4, 10.18 parametricCurveType
class ParametricCurveTagData : public TagData {
public:
// Table 68 — parametricCurveType function type encoding
enum class FunctionType {
// Y = X**g
Type0,
// Y = (a*X + b)**g if X >= -b/a
// = 0 else
Type1,
CIE_122_1966 = Type1,
// Y = (a*X + b)**g + c if X >= -b/a
// = c else
Type2,
IEC_61966_1 = Type2,
// Y = (a*X + b)**g if X >= d
// = c*X else
Type3,
IEC_61966_2_1 = Type3,
sRGB = Type3,
// Y = (a*X + b)**g + e if X >= d
// = c*X + f else
Type4,
};
// "The domain and range of each function shall be [0,0 1,0]. Any function value outside the range shall be clipped
// to the range of the function."
// "NOTE 1 The parameters selected for a parametric curve can result in complex or undefined values for the input range
// used. This can occur, for example, if d < -b/a. In such cases the behaviour of the curve is undefined."
static constexpr TagTypeSignature Type { 0x70617261 }; // 'para'
static ErrorOr<NonnullRefPtr<ParametricCurveTagData>> from_bytes(ReadonlyBytes, u32 offset, u32 size);
ParametricCurveTagData(u32 offset, u32 size, FunctionType function_type, Array<S15Fixed16, 7> parameters)
: TagData(offset, size, Type)
, m_function_type(function_type)
, m_parameters(move(parameters))
{
}
FunctionType function_type() const { return m_function_type; }
static unsigned parameter_count(FunctionType);
S15Fixed16 g() const { return m_parameters[0]; }
S15Fixed16 a() const
{
VERIFY(function_type() >= FunctionType::Type1);
return m_parameters[1];
}
S15Fixed16 b() const
{
VERIFY(function_type() >= FunctionType::Type1);
return m_parameters[2];
}
S15Fixed16 c() const
{
VERIFY(function_type() >= FunctionType::Type2);
return m_parameters[3];
}
S15Fixed16 d() const
{
VERIFY(function_type() >= FunctionType::Type3);
return m_parameters[4];
}
S15Fixed16 e() const
{
VERIFY(function_type() >= FunctionType::Type4);
return m_parameters[5];
}
S15Fixed16 f() const
{
VERIFY(function_type() >= FunctionType::Type4);
return m_parameters[6];
}
private:
FunctionType m_function_type;
// Contains, in this order, g a b c d e f.
// Not all FunctionTypes use all parameters.
Array<S15Fixed16, 7> m_parameters;
};
// ICC v4, 10.22 s15Fixed16ArrayType
class S15Fixed16ArrayTagData : public TagData {
public:
static constexpr TagTypeSignature Type { 0x73663332 }; // 'sf32'
using S15Fixed16 = FixedPoint<16, i32>;
static ErrorOr<NonnullRefPtr<S15Fixed16ArrayTagData>> from_bytes(ReadonlyBytes, u32 offset, u32 size);
S15Fixed16ArrayTagData(u32 offset, u32 size, Vector<S15Fixed16, 9> values)

View file

@ -112,6 +112,33 @@ 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::ParametricCurveTagData::Type) {
auto& parametric_curve = static_cast<Gfx::ICC::ParametricCurveTagData&>(*tag_data);
switch (parametric_curve.function_type()) {
case Gfx::ICC::ParametricCurveTagData::FunctionType::Type0:
outln(" Y = X**{}", parametric_curve.g());
break;
case Gfx::ICC::ParametricCurveTagData::FunctionType::Type1:
outln(" Y = ({}*X + {})**{} if X >= -{}/{}",
parametric_curve.a(), parametric_curve.b(), parametric_curve.g(), parametric_curve.b(), parametric_curve.a());
outln(" Y = 0 else");
break;
case Gfx::ICC::ParametricCurveTagData::FunctionType::Type2:
outln(" Y = ({}*X + {})**{} + {} if X >= -{}/{}",
parametric_curve.a(), parametric_curve.b(), parametric_curve.g(), parametric_curve.c(), parametric_curve.b(), parametric_curve.a());
outln(" Y = {} else", parametric_curve.c());
break;
case Gfx::ICC::ParametricCurveTagData::FunctionType::Type3:
outln(" Y = ({}*X + {})**{} if X >= {}",
parametric_curve.a(), parametric_curve.b(), parametric_curve.g(), parametric_curve.d());
outln(" Y = {}*X else", parametric_curve.c());
break;
case Gfx::ICC::ParametricCurveTagData::FunctionType::Type4:
outln(" Y = ({}*X + {})**{} + {} if X >= {}",
parametric_curve.a(), parametric_curve.b(), parametric_curve.g(), parametric_curve.e(), parametric_curve.d());
outln(" Y = {}*X + {} else", parametric_curve.c(), parametric_curve.f());
break;
}
} else if (tag_data->type() == Gfx::ICC::S15Fixed16ArrayTagData::Type) {
// This tag can contain arbitrarily many fixed-point numbers, but in practice it's
// exclusively used for the 'chad' tag, where it always contains 9 values that