1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-14 07:24:58 +00:00
serenity/Userland/Libraries/LibGfx/ICC/WellKnownProfiles.cpp
Nico Weber 0160f737e2 LibGfx/ICC+icc: Be lenient about invalid profile creation datetimes
Before, we used to reject profiles where the creation datetime was
invalid per spec. But invalid dates happen in practice (most commonly,
all fields set to 0). They don't affect profile conversion at all,
so be lenient about this, in exchange for slightly more wordy code
in the places that want to show the creation datetime.

Fixes a crash rendering page 2 of
https://fredrikbk.com/publications/copy-and-patch.pdf
2024-02-21 13:37:08 +01:00

90 lines
3.9 KiB
C++

/*
* 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 = MUST(DateTime::from_time_t(0));
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<TagData>> sRGB_curve()
{
// 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 };
return try_make_ref_counted<ParametricCurveTagData>(0, 0, ParametricCurveTagData::FunctionType::sRGB, curve_parameters);
}
ErrorOr<NonnullRefPtr<Profile>> sRGB()
{
// Returns an sRGB profile.
// https://en.wikipedia.org/wiki/SRGB
// FIXME: There are many different sRGB ICC profiles in the wild.
// Explain why, and why this picks the numbers it does.
// In the meantime, https://github.com/SerenityOS/serenity/pull/17714 has a few notes.
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.
auto curve = TRY(sRGB_curve());
TRY(tag_table.try_set(redTRCTag, curve));
TRY(tag_table.try_set(greenTRCTag, curve));
TRY(tag_table.try_set(blueTRCTag, curve));
// White point.
// ICC v4, 9.2.36 mediaWhitePointTag: "For displays, the values specified shall be those of the PCS illuminant as defined in 7.2.16."
TRY(tag_table.try_set(mediaWhitePointTag, TRY(XYZ_data(header.pcs_illuminant))));
// The chromatic_adaptation_matrix values are from https://www.color.org/chadtag.xalter
// That leads to exactly the S15Fixed16 values in the sRGB profiles in GIMP, Android, RawTherapee (but not in Compact-ICC-Profiles's v4 sRGB profile).
Vector<S15Fixed16, 9> chromatic_adaptation_matrix = { 1.047882, 0.022918, -0.050217, 0.029586, 0.990478, -0.017075, -0.009247, 0.015075, 0.751678 };
TRY(tag_table.try_set(chromaticAdaptationTag, TRY(try_make_ref_counted<S15Fixed16ArrayTagData>(0, 0, move(chromatic_adaptation_matrix)))));
// The chromaticity values are from https://www.color.org/srgb.pdf
// The chromatic adaptation matrix in that document is slightly different from the one on https://www.color.org/chadtag.xalter,
// so the values in our sRGB profile are currently not fully self-consistent.
// FIXME: Make values self-consistent (probably by using slightly different chromaticities).
TRY(tag_table.try_set(redMatrixColumnTag, TRY(XYZ_data(XYZ { 0.436030342570117, 0.222438466210245, 0.013897440074263 }))));
TRY(tag_table.try_set(greenMatrixColumnTag, TRY(XYZ_data(XYZ { 0.385101860087134, 0.716942745571917, 0.097076381494207 }))));
TRY(tag_table.try_set(blueMatrixColumnTag, TRY(XYZ_data(XYZ { 0.143067806654203, 0.060618777416563, 0.713926257896652 }))));
return Profile::create(header, move(tag_table));
}
}