mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-06-11 18:20:43 +09:00
LibGfx/ICC: Implement conversion between different connection spaces
If one profile uses PCSXYZ and the other PCSLAB as connection space, we now do the necessary XYZ/LAB conversion. With this and the previous commits, we can now convert from profiles that use PCSLAB with mAB, such as stress.jpeg from https://littlecms.com/blog/2020/09/09/browser-check/ : % Build/lagom/icc --name sRGB --reencode-to serenity-sRGB.icc % Build/lagom/bin/image -o out.png \ --convert-to-color-profile serenity-sRGB.icc \ ~/src/jpegfiles/stress.jpeg
This commit is contained in:
parent
9f7b33c31f
commit
b2a1130556
Notes:
sideshowbarker
2024-07-17 07:31:31 +09:00
Author: https://github.com/nico
Commit: b2a1130556
Pull-request: https://github.com/SerenityOS/serenity/pull/22155
4 changed files with 40 additions and 10 deletions
|
@ -184,7 +184,10 @@ TEST_CASE(from_pcs)
|
||||||
|
|
||||||
auto sRGB_from_xyz = [&sRGB](FloatVector3 const& XYZ) {
|
auto sRGB_from_xyz = [&sRGB](FloatVector3 const& XYZ) {
|
||||||
u8 rgb[3];
|
u8 rgb[3];
|
||||||
MUST(sRGB->from_pcs(XYZ, rgb));
|
// The first parameter, the source profile, is used to check if the PCS data is XYZ or LAB,
|
||||||
|
// and what the source whitepoint is. We just need any profile with an XYZ PCS space,
|
||||||
|
// so passing sRGB as source profile too is fine.
|
||||||
|
MUST(sRGB->from_pcs(sRGB, XYZ, rgb));
|
||||||
return Color(rgb[0], rgb[1], rgb[2]);
|
return Color(rgb[0], rgb[1], rgb[2]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1376,6 +1376,27 @@ static FloatVector3 lab_from_xyz(FloatVector3 xyz, XYZ white_point)
|
||||||
return { L, a, b };
|
return { L, a, b };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static FloatVector3 xyz_from_lab(FloatVector3 lab, XYZ white_point)
|
||||||
|
{
|
||||||
|
// Inverse of lab_from_xyz().
|
||||||
|
auto L_star = lab[0];
|
||||||
|
auto a_star = lab[1];
|
||||||
|
auto b_star = lab[2];
|
||||||
|
|
||||||
|
auto L = (L_star + 16) / 116 + a_star / 500; // f(x)
|
||||||
|
auto M = (L_star + 16) / 116; // f(y)
|
||||||
|
auto N = (L_star + 16) / 116 - b_star / 200; // f(z)
|
||||||
|
|
||||||
|
// Inverse of f in lab_from_xyz().
|
||||||
|
auto g = [](float x) {
|
||||||
|
if (x >= 6.0f / 29.0f)
|
||||||
|
return powf(x, 3);
|
||||||
|
return (x - 4.0f / 29.0f) * (3 * powf(6.f / 29.f, 2));
|
||||||
|
};
|
||||||
|
|
||||||
|
return { white_point.X * g(L), white_point.Y * g(M), white_point.Z * g(N) };
|
||||||
|
}
|
||||||
|
|
||||||
static TagSignature backward_transform_tag_for_rendering_intent(RenderingIntent rendering_intent)
|
static TagSignature backward_transform_tag_for_rendering_intent(RenderingIntent rendering_intent)
|
||||||
{
|
{
|
||||||
// ICCv4, Table 25 — Profile type/profile tag and defined rendering intents
|
// ICCv4, Table 25 — Profile type/profile tag and defined rendering intents
|
||||||
|
@ -1408,8 +1429,19 @@ ErrorOr<void> Profile::from_pcs_b_to_a(TagData const& tag_data, FloatVector3 con
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> Profile::from_pcs(FloatVector3 const& pcs, Bytes color) const
|
ErrorOr<void> Profile::from_pcs(Profile const& source_profile, FloatVector3 pcs, Bytes color) const
|
||||||
{
|
{
|
||||||
|
if (source_profile.connection_space() != connection_space()) {
|
||||||
|
if (source_profile.connection_space() == ColorSpace::PCSLAB) {
|
||||||
|
VERIFY(connection_space() == ColorSpace::PCSXYZ);
|
||||||
|
pcs = xyz_from_lab(pcs, source_profile.pcs_illuminant());
|
||||||
|
} else {
|
||||||
|
VERIFY(source_profile.connection_space() == ColorSpace::PCSXYZ);
|
||||||
|
VERIFY(connection_space() == ColorSpace::PCSLAB);
|
||||||
|
pcs = lab_from_xyz(pcs, pcs_illuminant());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// See `to_pcs()` for spec links.
|
// See `to_pcs()` for spec links.
|
||||||
// This function is very similar, but uses BToAn instead of AToBn for LUT profiles,
|
// This function is very similar, but uses BToAn instead of AToBn for LUT profiles,
|
||||||
// and an inverse transform for matrix profiles.
|
// and an inverse transform for matrix profiles.
|
||||||
|
@ -1543,15 +1575,10 @@ ErrorOr<CIELAB> Profile::to_lab(ReadonlyBytes color) const
|
||||||
|
|
||||||
ErrorOr<void> Profile::convert_image(Gfx::Bitmap& bitmap, Profile const& source_profile) const
|
ErrorOr<void> Profile::convert_image(Gfx::Bitmap& bitmap, Profile const& source_profile) const
|
||||||
{
|
{
|
||||||
// FIXME: Convert XYZ<->Lab conversion when needed.
|
|
||||||
// Currently, to_pcs() and from_pcs() are only implemented for matrix profiles, which are always XYZ anyways.
|
|
||||||
if (connection_space() != source_profile.connection_space())
|
|
||||||
return Error::from_string_literal("ICC::Profile::convert_image: mismatching profile connection spaces not yet implemented");
|
|
||||||
|
|
||||||
for (auto& pixel : bitmap) {
|
for (auto& pixel : bitmap) {
|
||||||
u8 rgb[] = { Color::from_argb(pixel).red(), Color::from_argb(pixel).green(), Color::from_argb(pixel).blue() };
|
u8 rgb[] = { Color::from_argb(pixel).red(), Color::from_argb(pixel).green(), Color::from_argb(pixel).blue() };
|
||||||
auto pcs = TRY(source_profile.to_pcs(rgb));
|
auto pcs = TRY(source_profile.to_pcs(rgb));
|
||||||
TRY(from_pcs(pcs, rgb));
|
TRY(from_pcs(source_profile, pcs, rgb));
|
||||||
pixel = Color(rgb[0], rgb[1], rgb[2], Color::from_argb(pixel).alpha()).value();
|
pixel = Color(rgb[0], rgb[1], rgb[2], Color::from_argb(pixel).alpha()).value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -213,7 +213,7 @@ public:
|
||||||
|
|
||||||
// Converts from the profile connection space to an 8-bits-per-channel color.
|
// Converts from the profile connection space to an 8-bits-per-channel color.
|
||||||
// The notes on `to_pcs()` apply to this too.
|
// The notes on `to_pcs()` apply to this too.
|
||||||
ErrorOr<void> from_pcs(FloatVector3 const&, Bytes) const;
|
ErrorOr<void> from_pcs(Profile const& source_profile, FloatVector3, Bytes) const;
|
||||||
|
|
||||||
ErrorOr<CIELAB> to_lab(ReadonlyBytes) const;
|
ErrorOr<CIELAB> to_lab(ReadonlyBytes) const;
|
||||||
|
|
||||||
|
|
|
@ -506,7 +506,7 @@ PDFErrorOr<Color> ICCBasedColorSpace::color(ReadonlySpan<Value> arguments) const
|
||||||
|
|
||||||
auto pcs = TRY(m_profile->to_pcs(bytes));
|
auto pcs = TRY(m_profile->to_pcs(bytes));
|
||||||
Array<u8, 3> output;
|
Array<u8, 3> output;
|
||||||
TRY(s_srgb_profile->from_pcs(pcs, output.span()));
|
TRY(s_srgb_profile->from_pcs(m_profile, pcs, output.span()));
|
||||||
|
|
||||||
return Color(output[0], output[1], output[2]);
|
return Color(output[0], output[1], output[2]);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue