1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 12:47:35 +00:00

ICC: Implement TRC inversion in from_pcs for point curves

This allows converting to a color space that uses a non-parametric
curve, for example:

    Build/lagom/image -o foo.png \
        --convert-to-color-profile .../profiles/sRGB-v2-micro.icc \
        input.jpg

...where profiles/sRGB-v2-micro.icc is from
https://github.com/saucecontrol/Compact-ICC-Profiles/

(Parametric curves are new in ICC v4, which means all v2 profiles
use point curves.)
This commit is contained in:
Nico Weber 2023-05-01 20:52:21 -04:00 committed by Andreas Kling
parent 926c0d8676
commit 9c3e36e72c
2 changed files with 37 additions and 4 deletions

View file

@ -1570,10 +1570,8 @@ ErrorOr<void> Profile::from_pcs(FloatVector3 const& pcs, Bytes color) const
auto evaluate_curve_inverse = [this](TagSignature curve_tag, float f) { auto evaluate_curve_inverse = [this](TagSignature curve_tag, float f) {
auto const& trc = *m_tag_table.get(curve_tag).value(); auto const& trc = *m_tag_table.get(curve_tag).value();
VERIFY(trc.type() == CurveTagData::Type || trc.type() == ParametricCurveTagData::Type); VERIFY(trc.type() == CurveTagData::Type || trc.type() == ParametricCurveTagData::Type);
if (trc.type() == CurveTagData::Type) { if (trc.type() == CurveTagData::Type)
TODO(); return static_cast<CurveTagData const&>(trc).evaluate_inverse(f);
return 0.f;
}
return static_cast<ParametricCurveTagData const&>(trc).evaluate_inverse(f); return static_cast<ParametricCurveTagData const&>(trc).evaluate_inverse(f);
}; };

View file

@ -179,6 +179,41 @@ public:
return mix(values()[i] / 65535.f, values()[i + 1] / 65535.f, f); return mix(values()[i] / 65535.f, values()[i + 1] / 65535.f, f);
} }
// y must be in [0..1].
float evaluate_inverse(float y) const
{
VERIFY(0.f <= y && y <= 1.f);
if (values().is_empty())
return y;
if (values().size() == 1)
return powf(y, 1.f / (values()[0] / (float)0x100));
// FIXME: Verify somewhere that:
// * values() is non-decreasing
// * values()[0] is 0, values()[values().size() - 1] is 65535
// FIXME: Use binary search.
size_t n = values().size() - 1;
size_t i = 0;
for (; i < n; ++i) {
if (values()[i] / 65535.f <= y && y <= values()[i + 1] / 65535.f)
break;
}
float x1 = i / (float)n;
float y1 = values()[i] / 65535.f;
float x2 = (i + 1) / (float)n;
float y2 = values()[i + 1] / 65535.f;
// Flat line segment?
if (y1 == y2)
return (x1 + x2) / 2;
return (y - y1) / (y2 - y1) * (x2 - x1) + x1; // Same as `((y - y1) / (y2 - y1) + i) / (float)n`
}
private: private:
Vector<u16> m_values; Vector<u16> m_values;
}; };