Skip to content

Commit 6a0d0a4

Browse files
committed
LibGfx/ICCC: Implement converting from PCS to LutBToATagData with clut
Similar to what SerenityOS#26452 did for Lut8TagData and SerenityOS#26462 for Lut16TagData. With this, we can convert from PCS to all tag types :^) Which means our ICC implementation is now fairly complete! We don't support DeviceLink or Abstract profile types (both don't appear in images; both are arguably useful in some not-embedded-in-images scenarios), nor the optiona BToD / DToB tags, and are missing some other minor things (like grayscale profiles -- but not much is missing here). But overall, this is now a fairly complete (if slow) basic ICC implementation :^)
1 parent fa0544f commit 6a0d0a4

File tree

2 files changed

+63
-2
lines changed

2 files changed

+63
-2
lines changed

Tests/LibGfx/TestICCProfile.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,18 @@ TEST_CASE(roundtrip_lab_mABmBA_no_clut)
312312
test_roundtrip(*profile);
313313
}
314314

315+
TEST_CASE(roundtrip_lab_mABmBA_u8_clut)
316+
{
317+
auto profile = TRY_OR_FAIL(Gfx::ICC::IdentityLAB_mABmBA_u8_clut());
318+
test_roundtrip(*profile);
319+
}
320+
321+
TEST_CASE(roundtrip_lab_mABmBA_u16_clut)
322+
{
323+
auto profile = TRY_OR_FAIL(Gfx::ICC::IdentityLAB_mABmBA_u16_clut());
324+
test_roundtrip(*profile);
325+
}
326+
315327
TEST_CASE(roundtrip_xyz_mft2)
316328
{
317329
auto profile = TRY_OR_FAIL(Gfx::ICC::IdentityXYZ_D50());
@@ -330,6 +342,12 @@ TEST_CASE(roundtrip_xyz_mABmBA_no_clut)
330342
test_roundtrip(*profile);
331343
}
332344

345+
TEST_CASE(roundtrip_xyz_mABmBA_u16_clut)
346+
{
347+
auto profile = TRY_OR_FAIL(Gfx::ICC::IdentityXYZ_D50_mABmBA_u16_clut());
348+
test_roundtrip(*profile);
349+
}
350+
333351
TEST_CASE(malformed_profile)
334352
{
335353
Array test_inputs = {

Userland/Libraries/LibGfx/ICC/TagTypes.h

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1543,6 +1543,7 @@ inline ErrorOr<void> LutBToATagData::evaluate(ColorSpace connection_space, Float
15431543
color[1] = (in_color[1] + 128.0f) / 255.0f;
15441544
color[2] = (in_color[2] + 128.0f) / 255.0f;
15451545
}
1546+
color = color.clamped(0.f, 1.f);
15461547

15471548
color = FloatVector3 {
15481549
evaluate_curve(m_b_curves[0], color[0]),
@@ -1572,8 +1573,50 @@ inline ErrorOr<void> LutBToATagData::evaluate(ColorSpace connection_space, Float
15721573

15731574
VERIFY(m_clut.has_value() == m_a_curves.has_value());
15741575
if (m_clut.has_value()) {
1575-
// FIXME
1576-
return Error::from_string_literal("LutBToATagData::evaluate: Not yet implemented when CLUT present");
1576+
Vector<float, 4> scratch;
1577+
Vector<float, 4> out;
1578+
scratch.resize(number_of_output_channels());
1579+
out.resize(number_of_output_channels());
1580+
1581+
auto const& clut = m_clut.value();
1582+
auto sample1 = [&clut]<typename T>(Vector<T> const& data, IntVector3 const& coordinates, Span<float> out) {
1583+
size_t stride = out.size();
1584+
size_t offset = 0;
1585+
for (int i = 3 - 1; i >= 0; --i) {
1586+
offset += coordinates[i] * stride;
1587+
stride *= clut.number_of_grid_points_in_dimension[i];
1588+
}
1589+
for (size_t c = 0; c < out.size(); ++c)
1590+
out[c] = (float)data[offset + c];
1591+
};
1592+
auto sample = [&clut, &sample1](IntVector3 const& coordinates, Span<float> out) {
1593+
clut.values.visit(
1594+
[&](Vector<u8> const& v) {
1595+
sample1(v, coordinates, out);
1596+
for (auto& f : out)
1597+
f /= 255.0f;
1598+
},
1599+
[&](Vector<u16> const& v) {
1600+
sample1(v, coordinates, out);
1601+
for (auto& f : out)
1602+
f /= 65535.0f;
1603+
});
1604+
};
1605+
1606+
VERIFY(clut.number_of_grid_points_in_dimension.size() == 3);
1607+
Gfx::IntVector3 size {
1608+
clut.number_of_grid_points_in_dimension[0],
1609+
clut.number_of_grid_points_in_dimension[1],
1610+
clut.number_of_grid_points_in_dimension[2],
1611+
};
1612+
lerp_nd(size, move(sample), color, scratch, out);
1613+
1614+
auto const& a_curves = m_a_curves.value();
1615+
VERIFY(a_curves.size() == out.size());
1616+
for (unsigned c = 0; c < out.size(); ++c) {
1617+
out[c] = evaluate_curve(a_curves[c], out[c]);
1618+
out_bytes[c] = round_to<u8>(clamp(out[c] * 255.0f, 0.0f, 255.0f));
1619+
}
15771620
} else {
15781621
VERIFY(number_of_output_channels() == 3);
15791622
out_bytes[0] = round_to<u8>(color[0] * 255.0f);

0 commit comments

Comments
 (0)