From 0ba532e14e9c29a4948e0b47fb6b54054e9c63b5 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Mon, 27 Feb 2023 08:37:56 -0500 Subject: [PATCH] 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.) --- Userland/Libraries/LibGfx/CMakeLists.txt | 1 + .../LibGfx/ICC/WellKnownProfiles.cpp | 77 +++++++++++++++++++ .../Libraries/LibGfx/ICC/WellKnownProfiles.h | 18 +++++ 3 files changed, 96 insertions(+) create mode 100644 Userland/Libraries/LibGfx/ICC/WellKnownProfiles.cpp create mode 100644 Userland/Libraries/LibGfx/ICC/WellKnownProfiles.h diff --git a/Userland/Libraries/LibGfx/CMakeLists.txt b/Userland/Libraries/LibGfx/CMakeLists.txt index d0199b82c9..e1b90ad9e4 100644 --- a/Userland/Libraries/LibGfx/CMakeLists.txt +++ b/Userland/Libraries/LibGfx/CMakeLists.txt @@ -32,6 +32,7 @@ set(SOURCES ICC/Profile.cpp ICC/Tags.cpp ICC/TagTypes.cpp + ICC/WellKnownProfiles.cpp ICOLoader.cpp ImageDecoder.cpp JPEGLoader.cpp diff --git a/Userland/Libraries/LibGfx/ICC/WellKnownProfiles.cpp b/Userland/Libraries/LibGfx/ICC/WellKnownProfiles.cpp new file mode 100644 index 0000000000..389c40c7b7 --- /dev/null +++ b/Userland/Libraries/LibGfx/ICC/WellKnownProfiles.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2023, Nico Weber + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +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> en_US(StringView text) +{ + Vector records; + TRY(records.try_append({ ('e' << 8) | 'n', ('U' << 8) | 'S', TRY(String::from_utf8(text)) })); + return try_make_ref_counted(0, 0, records); +} + +static ErrorOr> XYZ_data(XYZ xyz) +{ + Vector xyzs; + TRY(xyzs.try_append(xyz)); + return try_make_ref_counted(0, 0, move(xyzs)); +} + +ErrorOr> 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> 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 curve_parameters = { 2.4, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045 }; + auto curve = TRY(try_make_ref_counted(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 chromatic_adaptation_matrix = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + TRY(tag_table.try_set(chromaticAdaptationTag, TRY(try_make_ref_counted(0, 0, move(chromatic_adaptation_matrix))))); + + return Profile::create(header, move(tag_table)); +} + +} diff --git a/Userland/Libraries/LibGfx/ICC/WellKnownProfiles.h b/Userland/Libraries/LibGfx/ICC/WellKnownProfiles.h new file mode 100644 index 0000000000..aedbc14973 --- /dev/null +++ b/Userland/Libraries/LibGfx/ICC/WellKnownProfiles.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2023, Nico Weber + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Gfx::ICC { + +class Profile; + +ErrorOr> sRGB(); + +}