1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-15 10:14:58 +00:00
serenity/Userland/Libraries/LibPDF/ColorSpace.cpp
Ben Wiederhake edc0cd29f8 LibPDF: Break weird dependency cycle
Old situation:
Object.h defines Object
Object.h defines ArrayObject
ArrayObject requires the definition of Object
ArrayObject requires the definition of Value
Value.h defines Value
Value requires the definition of Object

Therefore, a file with the single line "#include <Value.h>" used to
raise compilation errors; certainly not something that one might expect
from a library.

This patch splits up the definitions in Object.h to break the cycle.
Now, Object.h only defines Object, Value.h still only defines Value (and
includes Object.h), and the new header ObjectDerivatives.h defines
ArrayObject (and includes both Object.h and Value.h).
2021-09-20 17:39:36 +04:30

227 lines
7.3 KiB
C++

/*
* Copyright (c) 2021, Matthew Olsson <mattco@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibPDF/ColorSpace.h>
#include <LibPDF/CommonNames.h>
#include <LibPDF/ObjectDerivatives.h>
namespace PDF {
RefPtr<DeviceGrayColorSpace> DeviceGrayColorSpace::the()
{
static auto instance = adopt_ref(*new DeviceGrayColorSpace());
return instance;
}
Color DeviceGrayColorSpace::color(Vector<Value> const& arguments) const
{
VERIFY(arguments.size() == 1);
auto gray = static_cast<u8>(arguments[0].to_float() * 255.0f);
return Color(gray, gray, gray);
}
RefPtr<DeviceRGBColorSpace> DeviceRGBColorSpace::the()
{
static auto instance = adopt_ref(*new DeviceRGBColorSpace());
return instance;
}
Color DeviceRGBColorSpace::color(Vector<Value> const& arguments) const
{
VERIFY(arguments.size() == 3);
auto r = static_cast<u8>(arguments[0].to_float() * 255.0f);
auto g = static_cast<u8>(arguments[1].to_float() * 255.0f);
auto b = static_cast<u8>(arguments[2].to_float() * 255.0f);
return Color(r, g, b);
}
RefPtr<DeviceCMYKColorSpace> DeviceCMYKColorSpace::the()
{
static auto instance = adopt_ref(*new DeviceCMYKColorSpace());
return instance;
}
Color DeviceCMYKColorSpace::color(Vector<Value> const& arguments) const
{
VERIFY(arguments.size() == 4);
auto c = arguments[0].to_float();
auto m = arguments[1].to_float();
auto y = arguments[2].to_float();
auto k = arguments[3].to_float();
return Color::from_cmyk(c, m, y, k);
}
RefPtr<CalRGBColorSpace> CalRGBColorSpace::create(RefPtr<Document> document, Vector<Value>&& parameters)
{
if (parameters.size() != 1)
return {};
auto param = parameters[0];
if (!param.is_object() || !param.as_object()->is_dict())
return {};
auto dict = object_cast<DictObject>(param.as_object());
if (!dict->contains(CommonNames::WhitePoint))
return {};
auto white_point_array = dict->get_array(document, CommonNames::WhitePoint);
if (white_point_array->size() != 3)
return {};
auto color_space = adopt_ref(*new CalRGBColorSpace());
color_space->m_whitepoint[0] = white_point_array->at(0).to_float();
color_space->m_whitepoint[1] = white_point_array->at(1).to_float();
color_space->m_whitepoint[2] = white_point_array->at(2).to_float();
if (color_space->m_whitepoint[1] != 1.0f)
return {};
if (dict->contains(CommonNames::BlackPoint)) {
auto black_point_array = dict->get_array(document, CommonNames::BlackPoint);
if (black_point_array->size() == 3) {
color_space->m_blackpoint[0] = black_point_array->at(0).to_float();
color_space->m_blackpoint[1] = black_point_array->at(1).to_float();
color_space->m_blackpoint[2] = black_point_array->at(2).to_float();
}
}
if (dict->contains(CommonNames::Gamma)) {
auto gamma_array = dict->get_array(document, CommonNames::Gamma);
if (gamma_array->size() == 3) {
color_space->m_gamma[0] = gamma_array->at(0).to_float();
color_space->m_gamma[1] = gamma_array->at(1).to_float();
color_space->m_gamma[2] = gamma_array->at(2).to_float();
}
}
if (dict->contains(CommonNames::Matrix)) {
auto matrix_array = dict->get_array(document, CommonNames::Matrix);
if (matrix_array->size() == 3) {
color_space->m_matrix[0] = matrix_array->at(0).to_float();
color_space->m_matrix[1] = matrix_array->at(1).to_float();
color_space->m_matrix[2] = matrix_array->at(2).to_float();
color_space->m_matrix[3] = matrix_array->at(3).to_float();
color_space->m_matrix[4] = matrix_array->at(4).to_float();
color_space->m_matrix[5] = matrix_array->at(5).to_float();
color_space->m_matrix[6] = matrix_array->at(6).to_float();
color_space->m_matrix[7] = matrix_array->at(7).to_float();
color_space->m_matrix[8] = matrix_array->at(8).to_float();
}
}
return color_space;
}
constexpr Array<float, 3> matrix_multiply(Array<float, 9> a, Array<float, 3> b)
{
return Array<float, 3> {
a[0] * b[0] + a[1] * b[1] + a[2] * b[2],
a[3] * b[0] + a[4] * b[1] + a[5] * b[2],
a[6] * b[0] + a[7] * b[1] + a[8] * b[2]
};
}
// Converts to a flat XYZ space with white point = (1, 1, 1)
// Step 2 of https://www.adobe.com/content/dam/acom/en/devnet/photoshop/sdk/AdobeBPC.pdf
constexpr Array<float, 3> flatten_and_normalize_whitepoint(Array<float, 3> whitepoint, Array<float, 3> xyz)
{
VERIFY(whitepoint[1] == 1.0f);
return {
(1.0f / whitepoint[0]) * xyz[0],
xyz[1],
(1.0f / whitepoint[2]) * xyz[2],
};
}
constexpr float decode_l(float input)
{
constexpr float decode_l_scaling_constant = 0.00110705646f; // (((8 + 16) / 116) ^ 3) / 8
if (input < 0.0f)
return -decode_l(-input);
if (input >= 0.0f && input <= 8.0f)
return input * decode_l_scaling_constant;
return powf(((input + 16.0f) / 116.0f), 3.0f);
}
constexpr Array<float, 3> scale_black_point(Array<float, 3> blackpoint, Array<float, 3> xyz)
{
auto y_dst = decode_l(0); // DestinationBlackPoint is just [0, 0, 0]
auto y_src = decode_l(blackpoint[0]);
auto scale = (1 - y_dst) / (1 - y_src);
auto offset = 1 - scale;
return {
xyz[0] * scale + offset,
xyz[1] * scale + offset,
xyz[2] * scale + offset,
};
}
// https://en.wikipedia.org/wiki/Illuminant_D65
constexpr Array<float, 3> convert_to_d65(Array<float, 3> whitepoint, Array<float, 3> xyz)
{
constexpr float d65x = 0.95047f;
constexpr float d65y = 1.0f;
constexpr float d65z = 1.08883f;
return {
(xyz[0] * d65x) / whitepoint[0],
(xyz[1] * d65y) / whitepoint[1],
(xyz[2] * d65z) / whitepoint[2],
};
}
// https://en.wikipedia.org/wiki/SRGB
constexpr Array<float, 3> convert_to_srgb(Array<float, 3> xyz)
{
// See the sRGB D65 [M]^-1 matrix in the following page
// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
constexpr Array<float, 9> conversion_matrix = {
3.2404542,
-1.5371385,
-0.4985314,
-0.969266,
1.8760108,
0.0415560,
0.0556434,
-0.2040259,
1.0572252,
};
return matrix_multiply(conversion_matrix, xyz);
}
Color CalRGBColorSpace::color(Vector<Value> const& arguments) const
{
VERIFY(arguments.size() == 3);
auto a = clamp(arguments[0].to_float(), 0.0f, 1.0f);
auto b = clamp(arguments[1].to_float(), 0.0f, 1.0f);
auto c = clamp(arguments[2].to_float(), 0.0f, 1.0f);
auto agr = powf(a, m_gamma[0]);
auto bgg = powf(b, m_gamma[1]);
auto cgb = powf(c, m_gamma[2]);
auto x = m_matrix[0] * agr + m_matrix[3] * bgg + m_matrix[6] * cgb;
auto y = m_matrix[1] * agr + m_matrix[4] * bgg + m_matrix[7] * cgb;
auto z = m_matrix[2] * agr + m_matrix[5] * bgg + m_matrix[8] * cgb;
auto flattened_xyz = flatten_and_normalize_whitepoint(m_whitepoint, { x, y, z });
auto scaled_black_point_xyz = scale_black_point(m_blackpoint, flattened_xyz);
auto d65_normalized = convert_to_d65(m_whitepoint, scaled_black_point_xyz);
auto srgb = convert_to_srgb(d65_normalized);
auto red = static_cast<u8>(srgb[0] * 255.0f);
auto green = static_cast<u8>(srgb[1] * 255.0f);
auto blue = static_cast<u8>(srgb[2] * 255.0f);
return Color(red, green, blue);
}
}