Skip to content

Commit c25bb83

Browse files
committed
LibGfx/ICC: Add a built-in LAB mft2 profile
This is even weirder than the XYZ mft2 profile added in an earlier commit in this PR: mft2 uses a "legacy 16-bit PCSLAB encoding" instead of the "normal" 16-bit ICC PCSLAB encoding, and this being an identity mapping leaks this into the outputs. We again linearly map this internal 16-bit encoding to 8 bits. This legacy encoding maps 0 to 0.0 and FF00 to 1.0 for L (and FFFF to a bit more than 1.0), and 0 to -128.0 and FF00 to 127.0 for a* and b* (and FFFF to a bit more than 127.0). This means this produces different values than our built-in LAB mft1 profile: % echo 'pcslab(100, 127, -128)' | \ icc -n LAB_mft2 --stdin-u8-from-pcs | \ icc -n LAB --stdin-u8-to-pcs pcslab(99.60784, 126, -128) Staying in LAB for both steps is exact, of course: % echo 'pcslab(100, 127, -128)' | \ icc -n LAB --stdin-u8-from-pcs | \ icc -n LAB --stdin-u8-to-pcs pcslab(100, 127, -128) Staying in LAB_mft2 for both steps is also exact, up to u8 resolution (100 and 127 don't exactly map to an u8 value with our "16-bit legacy PCSLAB encoding mapped to u8" encoding): % echo 'pcslab(100, 127, -128)' | \ icc -n LAB_mft2 --stdin-u8-from-pcs | \ icc -n LAB_mft2 --stdin-u8-to-pcs pcslab(99.99693, 126.99219, -128) This is just because the same PCSLAB value is encoded slightly differently in the mft1 and mft2 LAB encodings: % echo 'pcslab(100, 127, -128)' | \ icc -n LAB --stdin-u8-from-pcs 255, 255, 0 % echo 'pcslab(100, 127, -128)' | \ icc -n LAB_mft2 --stdin-u8-from-pcs 254, 254, 0 (There's no XYZ mft1 encoding, so at least we don't have mft1 and mft2 profiles that have different output.) Use this profile to add a roundtrip test for Lut16TagData when PCS is PCSLAB.
1 parent 1680a30 commit c25bb83

File tree

4 files changed

+47
-0
lines changed

4 files changed

+47
-0
lines changed

Tests/LibGfx/TestICCProfile.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,12 @@ TEST_CASE(roundtrip_lab_mft1)
300300
test_roundtrip(*profile);
301301
}
302302

303+
TEST_CASE(roundtrip_lab_mft2)
304+
{
305+
auto profile = TRY_OR_FAIL(Gfx::ICC::IdentityLAB_mft2());
306+
test_roundtrip(*profile);
307+
}
308+
303309
TEST_CASE(roundtrip_xyz_mft2)
304310
{
305311
auto profile = TRY_OR_FAIL(Gfx::ICC::IdentityXYZ_D50());

Userland/Libraries/LibGfx/ICC/WellKnownProfiles.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,44 @@ ErrorOr<NonnullRefPtr<Profile>> IdentityLAB()
112112
return Profile::create(header, move(tag_table));
113113
}
114114

115+
ErrorOr<NonnullRefPtr<Profile>> IdentityLAB_mft2()
116+
{
117+
// Identity mapping between CIELAB and PCSXYZ, using an unnecessarily large mft2.
118+
119+
auto header = profile_header(ColorSpace::CIELAB, ColorSpace::PCSLAB);
120+
header.device_class = DeviceClass::ColorSpace;
121+
122+
OrderedHashMap<TagSignature, NonnullRefPtr<TagData>> tag_table;
123+
124+
TRY(tag_table.try_set(profileDescriptionTag, TRY(en_US("SerenityOS Generic LAB, mft2"sv))));
125+
TRY(tag_table.try_set(copyrightTag, TRY(en_US("Public Domain"sv))));
126+
127+
// mft1 is plenty; this is just for testing mft2 LAB codepaths.
128+
EMatrix3x3 e_matrix = identity_matrix();
129+
130+
Vector<u16> input_tables;
131+
for (int c = 0; c < 3; ++c) {
132+
// mft2 allows between 2 and 4096 entries. If there are just two values, it linearly maps between them.
133+
input_tables.append(0);
134+
input_tables.append(65535);
135+
}
136+
137+
auto clut_values = make_2x2x2_cube<u16>();
138+
Vector<u16> output_tables = input_tables;
139+
140+
auto mft2 = TRY(try_make_ref_counted<Lut16TagData>(0, 0, e_matrix,
141+
3, 3, 2,
142+
2, 2,
143+
move(input_tables), move(clut_values), move(output_tables)));
144+
TRY(tag_table.try_set(AToB0Tag, mft2));
145+
TRY(tag_table.try_set(BToA0Tag, mft2));
146+
147+
// White point.
148+
TRY(tag_table.try_set(mediaWhitePointTag, TRY(XYZ_data(header.pcs_illuminant))));
149+
150+
return Profile::create(header, move(tag_table));
151+
}
152+
115153
ErrorOr<NonnullRefPtr<Profile>> IdentityXYZ_D50()
116154
{
117155
// Identity mapping between nCIEXYZ and PCSXYZ.

Userland/Libraries/LibGfx/ICC/WellKnownProfiles.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class Profile;
1515
class ParametricCurveTagData;
1616

1717
ErrorOr<NonnullRefPtr<Profile>> IdentityLAB();
18+
ErrorOr<NonnullRefPtr<Profile>> IdentityLAB_mft2();
1819
ErrorOr<NonnullRefPtr<Profile>> IdentityXYZ_D50();
1920

2021
ErrorOr<NonnullRefPtr<Profile>> sRGB();

Userland/Utilities/icc.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,8 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
381381
if (!name.is_empty()) {
382382
if (name == "LAB")
383383
return Gfx::ICC::IdentityLAB();
384+
if (name == "LAB_mft2")
385+
return Gfx::ICC::IdentityLAB_mft2();
384386
if (name == "sRGB")
385387
return Gfx::ICC::sRGB();
386388
if (name == "XYZ")

0 commit comments

Comments
 (0)