Skip to content

Commit fa00d94

Browse files
nicolinusg
authored andcommitted
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".
1 parent 67f718a commit fa00d94

File tree

3 files changed

+172
-2
lines changed

3 files changed

+172
-2
lines changed

Userland/Libraries/LibGfx/ICCProfile.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,58 @@ ErrorOr<NonnullRefPtr<MultiLocalizedUnicodeTagData>> MultiLocalizedUnicodeTagDat
635635
return adopt_ref(*new MultiLocalizedUnicodeTagData(offset, size, move(records)));
636636
}
637637

638+
unsigned ParametricCurveTagData::parameter_count(FunctionType function_type)
639+
{
640+
switch (function_type) {
641+
case FunctionType::Type0:
642+
return 1;
643+
case FunctionType::Type1:
644+
return 3;
645+
case FunctionType::Type2:
646+
return 4;
647+
case FunctionType::Type3:
648+
return 5;
649+
case FunctionType::Type4:
650+
return 7;
651+
}
652+
VERIFY_NOT_REACHED();
653+
}
654+
655+
ErrorOr<NonnullRefPtr<ParametricCurveTagData>> ParametricCurveTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size)
656+
{
657+
// ICC v4, 10.18 parametricCurveType
658+
VERIFY(tag_type(bytes) == Type);
659+
TRY(check_reserved(bytes));
660+
661+
// "The parametricCurveType describes a one-dimensional curve by specifying one of a predefined set of functions
662+
// using the parameters."
663+
664+
if (bytes.size() < 2 * sizeof(u32) + 2 * sizeof(u16))
665+
return Error::from_string_literal("ICC::Profile: parametricCurveType has not enough data");
666+
667+
u16 raw_function_type = *bit_cast<BigEndian<u16> const*>(bytes.data() + 8);
668+
u16 reserved = *bit_cast<BigEndian<u16> const*>(bytes.data() + 10);
669+
if (reserved != 0)
670+
return Error::from_string_literal("ICC::Profile: parametricCurveType reserved u16 after function type not 0");
671+
672+
if (raw_function_type > 4)
673+
return Error::from_string_literal("ICC::Profile: parametricCurveType unknown function type");
674+
675+
FunctionType function_type = (FunctionType)raw_function_type;
676+
unsigned count = parameter_count(function_type);
677+
678+
if (bytes.size() < 2 * sizeof(u32) + 2 * sizeof(u16) + count * sizeof(s15Fixed16Number))
679+
return Error::from_string_literal("ICC::Profile: parametricCurveType has not enough data for parameters");
680+
681+
BigEndian<s15Fixed16Number> const* raw_parameters = bit_cast<BigEndian<s15Fixed16Number> const*>(bytes.data() + 12);
682+
Array<S15Fixed16, 7> parameters;
683+
parameters.fill(0);
684+
for (unsigned i = 0; i < count; ++i)
685+
parameters[i] = S15Fixed16::create_raw(raw_parameters[i]);
686+
687+
return adopt_ref(*new ParametricCurveTagData(offset, size, function_type, move(parameters)));
688+
}
689+
638690
ErrorOr<NonnullRefPtr<S15Fixed16ArrayTagData>> S15Fixed16ArrayTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size)
639691
{
640692
// ICC v4, 10.22 s15Fixed16ArrayType
@@ -897,6 +949,8 @@ ErrorOr<NonnullRefPtr<TagData>> Profile::read_tag(ReadonlyBytes bytes, Detail::T
897949
return CurveTagData::from_bytes(tag_bytes, entry.offset_to_beginning_of_tag_data_element, entry.size_of_tag_data_element);
898950
case MultiLocalizedUnicodeTagData::Type:
899951
return MultiLocalizedUnicodeTagData::from_bytes(tag_bytes, entry.offset_to_beginning_of_tag_data_element, entry.size_of_tag_data_element);
952+
case ParametricCurveTagData::Type:
953+
return ParametricCurveTagData::from_bytes(tag_bytes, entry.offset_to_beginning_of_tag_data_element, entry.size_of_tag_data_element);
900954
case S15Fixed16ArrayTagData::Type:
901955
return S15Fixed16ArrayTagData::from_bytes(tag_bytes, entry.offset_to_beginning_of_tag_data_element, entry.size_of_tag_data_element);
902956
case TextDescriptionTagData::Type:

Userland/Libraries/LibGfx/ICCProfile.h

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,8 @@ class DeviceAttributes {
224224
u64 m_bits = 0;
225225
};
226226

227+
using S15Fixed16 = FixedPoint<16, i32>;
228+
227229
struct XYZ {
228230
double x { 0 };
229231
double y { 0 };
@@ -313,13 +315,100 @@ class MultiLocalizedUnicodeTagData : public TagData {
313315
Vector<Record> m_records;
314316
};
315317

318+
// ICC v4, 10.18 parametricCurveType
319+
class ParametricCurveTagData : public TagData {
320+
public:
321+
// Table 68 — parametricCurveType function type encoding
322+
enum class FunctionType {
323+
// Y = X**g
324+
Type0,
325+
326+
// Y = (a*X + b)**g if X >= -b/a
327+
// = 0 else
328+
Type1,
329+
CIE_122_1966 = Type1,
330+
331+
// Y = (a*X + b)**g + c if X >= -b/a
332+
// = c else
333+
Type2,
334+
IEC_61966_1 = Type2,
335+
336+
// Y = (a*X + b)**g if X >= d
337+
// = c*X else
338+
Type3,
339+
IEC_61966_2_1 = Type3,
340+
sRGB = Type3,
341+
342+
// Y = (a*X + b)**g + e if X >= d
343+
// = c*X + f else
344+
Type4,
345+
};
346+
347+
// "The domain and range of each function shall be [0,0 1,0]. Any function value outside the range shall be clipped
348+
// to the range of the function."
349+
// "NOTE 1 The parameters selected for a parametric curve can result in complex or undefined values for the input range
350+
// used. This can occur, for example, if d < -b/a. In such cases the behaviour of the curve is undefined."
351+
352+
static constexpr TagTypeSignature Type { 0x70617261 }; // 'para'
353+
354+
static ErrorOr<NonnullRefPtr<ParametricCurveTagData>> from_bytes(ReadonlyBytes, u32 offset, u32 size);
355+
356+
ParametricCurveTagData(u32 offset, u32 size, FunctionType function_type, Array<S15Fixed16, 7> parameters)
357+
: TagData(offset, size, Type)
358+
, m_function_type(function_type)
359+
, m_parameters(move(parameters))
360+
{
361+
}
362+
363+
FunctionType function_type() const { return m_function_type; }
364+
365+
static unsigned parameter_count(FunctionType);
366+
367+
S15Fixed16 g() const { return m_parameters[0]; }
368+
S15Fixed16 a() const
369+
{
370+
VERIFY(function_type() >= FunctionType::Type1);
371+
return m_parameters[1];
372+
}
373+
S15Fixed16 b() const
374+
{
375+
VERIFY(function_type() >= FunctionType::Type1);
376+
return m_parameters[2];
377+
}
378+
S15Fixed16 c() const
379+
{
380+
VERIFY(function_type() >= FunctionType::Type2);
381+
return m_parameters[3];
382+
}
383+
S15Fixed16 d() const
384+
{
385+
VERIFY(function_type() >= FunctionType::Type3);
386+
return m_parameters[4];
387+
}
388+
S15Fixed16 e() const
389+
{
390+
VERIFY(function_type() >= FunctionType::Type4);
391+
return m_parameters[5];
392+
}
393+
S15Fixed16 f() const
394+
{
395+
VERIFY(function_type() >= FunctionType::Type4);
396+
return m_parameters[6];
397+
}
398+
399+
private:
400+
FunctionType m_function_type;
401+
402+
// Contains, in this order, g a b c d e f.
403+
// Not all FunctionTypes use all parameters.
404+
Array<S15Fixed16, 7> m_parameters;
405+
};
406+
316407
// ICC v4, 10.22 s15Fixed16ArrayType
317408
class S15Fixed16ArrayTagData : public TagData {
318409
public:
319410
static constexpr TagTypeSignature Type { 0x73663332 }; // 'sf32'
320411

321-
using S15Fixed16 = FixedPoint<16, i32>;
322-
323412
static ErrorOr<NonnullRefPtr<S15Fixed16ArrayTagData>> from_bytes(ReadonlyBytes, u32 offset, u32 size);
324413

325414
S15Fixed16ArrayTagData(u32 offset, u32 size, Vector<S15Fixed16, 9> values)

Userland/Utilities/icc.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,33 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
112112
record.iso_3166_1_country_code >> 8, record.iso_3166_1_country_code & 0xff,
113113
record.text);
114114
}
115+
} else if (tag_data->type() == Gfx::ICC::ParametricCurveTagData::Type) {
116+
auto& parametric_curve = static_cast<Gfx::ICC::ParametricCurveTagData&>(*tag_data);
117+
switch (parametric_curve.function_type()) {
118+
case Gfx::ICC::ParametricCurveTagData::FunctionType::Type0:
119+
outln(" Y = X**{}", parametric_curve.g());
120+
break;
121+
case Gfx::ICC::ParametricCurveTagData::FunctionType::Type1:
122+
outln(" Y = ({}*X + {})**{} if X >= -{}/{}",
123+
parametric_curve.a(), parametric_curve.b(), parametric_curve.g(), parametric_curve.b(), parametric_curve.a());
124+
outln(" Y = 0 else");
125+
break;
126+
case Gfx::ICC::ParametricCurveTagData::FunctionType::Type2:
127+
outln(" Y = ({}*X + {})**{} + {} if X >= -{}/{}",
128+
parametric_curve.a(), parametric_curve.b(), parametric_curve.g(), parametric_curve.c(), parametric_curve.b(), parametric_curve.a());
129+
outln(" Y = {} else", parametric_curve.c());
130+
break;
131+
case Gfx::ICC::ParametricCurveTagData::FunctionType::Type3:
132+
outln(" Y = ({}*X + {})**{} if X >= {}",
133+
parametric_curve.a(), parametric_curve.b(), parametric_curve.g(), parametric_curve.d());
134+
outln(" Y = {}*X else", parametric_curve.c());
135+
break;
136+
case Gfx::ICC::ParametricCurveTagData::FunctionType::Type4:
137+
outln(" Y = ({}*X + {})**{} + {} if X >= {}",
138+
parametric_curve.a(), parametric_curve.b(), parametric_curve.g(), parametric_curve.e(), parametric_curve.d());
139+
outln(" Y = {}*X + {} else", parametric_curve.c(), parametric_curve.f());
140+
break;
141+
}
115142
} else if (tag_data->type() == Gfx::ICC::S15Fixed16ArrayTagData::Type) {
116143
// This tag can contain arbitrarily many fixed-point numbers, but in practice it's
117144
// exclusively used for the 'chad' tag, where it always contains 9 values that

0 commit comments

Comments
 (0)