LibGfx: Add a function to create an in-memory sRGB profile

This might be useful for converting data from arbitrary profiles to
sRGB.

For now, this only encodes the transfer function and puts in zero values
for chromaticities, whitepoint, and chromatic adaptation matrix.
This makes the profile unusable for now. But I've spent a very long time
reading things and need to check in some code, and it's some progress.

The encoded transfer function exactly matches the one in GIMP's built-in
sRGB ICC profile (but not the Compact-ICC-Profiles v4 one or the
RawTherapee v4 one -- I'll add a comment about why later.)
This commit is contained in:
Nico Weber 2023-02-27 08:37:56 -05:00 committed by Linus Groh
parent 8f415e7b21
commit 0ba532e14e
Notes: sideshowbarker 2024-07-17 06:35:23 +09:00
3 changed files with 96 additions and 0 deletions

View File

@ -32,6 +32,7 @@ set(SOURCES
ICC/Profile.cpp
ICC/Tags.cpp
ICC/TagTypes.cpp
ICC/WellKnownProfiles.cpp
ICOLoader.cpp
ImageDecoder.cpp
JPEGLoader.cpp

View File

@ -0,0 +1,77 @@
/*
* Copyright (c) 2023, Nico Weber <thakis@chromium.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibGfx/ICC/Profile.h>
#include <LibGfx/ICC/Tags.h>
#include <LibGfx/ICC/WellKnownProfiles.h>
#include <time.h>
namespace Gfx::ICC {
static ProfileHeader rgb_header()
{
ProfileHeader header;
header.version = Version(4, 0x40);
header.device_class = DeviceClass::DisplayDevice;
header.data_color_space = ColorSpace::RGB;
header.connection_space = ColorSpace::PCSXYZ;
header.creation_timestamp = time(NULL);
header.rendering_intent = RenderingIntent::Perceptual;
header.pcs_illuminant = XYZ { 0.9642, 1.0, 0.8249 };
return header;
}
static ErrorOr<NonnullRefPtr<MultiLocalizedUnicodeTagData>> en_US(StringView text)
{
Vector<MultiLocalizedUnicodeTagData::Record> records;
TRY(records.try_append({ ('e' << 8) | 'n', ('U' << 8) | 'S', TRY(String::from_utf8(text)) }));
return try_make_ref_counted<MultiLocalizedUnicodeTagData>(0, 0, records);
}
static ErrorOr<NonnullRefPtr<XYZTagData>> XYZ_data(XYZ xyz)
{
Vector<XYZ> xyzs;
TRY(xyzs.try_append(xyz));
return try_make_ref_counted<XYZTagData>(0, 0, move(xyzs));
}
ErrorOr<NonnullRefPtr<Profile>> sRGB()
{
// Returns an sRGB profile.
// https://en.wikipedia.org/wiki/SRGB
// FIXME: There's a surprising amount of variety in sRGB ICC profiles in the wild.
// Explain why, and why this picks the numbers it does.
auto header = rgb_header();
OrderedHashMap<TagSignature, NonnullRefPtr<TagData>> tag_table;
TRY(tag_table.try_set(profileDescriptionTag, TRY(en_US("SerenityOS sRGB"sv))));
TRY(tag_table.try_set(copyrightTag, TRY(en_US("Public Domain"sv))));
// Transfer function.
// Numbers from https://en.wikipedia.org/wiki/SRGB#From_sRGB_to_CIE_XYZ
Array<S15Fixed16, 7> curve_parameters = { 2.4, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045 };
auto curve = TRY(try_make_ref_counted<ParametricCurveTagData>(0, 0, ParametricCurveTagData::FunctionType::sRGB, curve_parameters));
TRY(tag_table.try_set(redTRCTag, curve));
TRY(tag_table.try_set(greenTRCTag, curve));
TRY(tag_table.try_set(blueTRCTag, curve));
// Chromatic adaptation matrix, chromacities and whitepoint.
// FIXME: Actual values for chromatic adaptation matrix and chromacities.
TRY(tag_table.try_set(mediaWhitePointTag, TRY(XYZ_data(XYZ { 0, 0, 0 }))));
TRY(tag_table.try_set(redMatrixColumnTag, TRY(XYZ_data(XYZ { 0, 0, 0 }))));
TRY(tag_table.try_set(greenMatrixColumnTag, TRY(XYZ_data(XYZ { 0, 0, 0 }))));
TRY(tag_table.try_set(blueMatrixColumnTag, TRY(XYZ_data(XYZ { 0, 0, 0 }))));
Vector<S15Fixed16, 9> chromatic_adaptation_matrix = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
TRY(tag_table.try_set(chromaticAdaptationTag, TRY(try_make_ref_counted<S15Fixed16ArrayTagData>(0, 0, move(chromatic_adaptation_matrix)))));
return Profile::create(header, move(tag_table));
}
}

View File

@ -0,0 +1,18 @@
/*
* Copyright (c) 2023, Nico Weber <thakis@chromium.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Error.h>
#include <AK/NonnullRefPtr.h>
namespace Gfx::ICC {
class Profile;
ErrorOr<NonnullRefPtr<Profile>> sRGB();
}