summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibPDF
diff options
context:
space:
mode:
authorMatthew Olsson <matthewcolsson@gmail.com>2021-05-27 14:03:29 -0700
committerAli Mohammad Pur <Ali.mpfard@gmail.com>2021-06-12 22:45:01 +0430
commit006f5498dec856c9586f382b9ce96574c86d19cb (patch)
tree677e260b6d150356bae56cd6160deed2424f6e34 /Userland/Libraries/LibPDF
parent7b4e36bf88ae1498287c262c910874e9ad9c5cbf (diff)
downloadserenity-006f5498dec856c9586f382b9ce96574c86d19cb.zip
LibPDF: Add support for the CalRGB ColorSpace
This isn't tested all that well, as the PDF I am testing with only uses it for black (which is trivial). It can be tested further when LibPDF is able to process more complex PDFs that actually use this color space non-trivially.
Diffstat (limited to 'Userland/Libraries/LibPDF')
-rw-r--r--Userland/Libraries/LibPDF/ColorSpace.cpp170
-rw-r--r--Userland/Libraries/LibPDF/ColorSpace.h16
-rw-r--r--Userland/Libraries/LibPDF/Renderer.cpp19
3 files changed, 205 insertions, 0 deletions
diff --git a/Userland/Libraries/LibPDF/ColorSpace.cpp b/Userland/Libraries/LibPDF/ColorSpace.cpp
index 1651c3fff8..fba6b6c453 100644
--- a/Userland/Libraries/LibPDF/ColorSpace.cpp
+++ b/Userland/Libraries/LibPDF/ColorSpace.cpp
@@ -53,4 +53,174 @@ Color DeviceCMYKColorSpace::color(const Vector<Value>& arguments) const
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(const Vector<Value>& 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);
+}
+
}
diff --git a/Userland/Libraries/LibPDF/ColorSpace.h b/Userland/Libraries/LibPDF/ColorSpace.h
index cea624a6ac..90fa479775 100644
--- a/Userland/Libraries/LibPDF/ColorSpace.h
+++ b/Userland/Libraries/LibPDF/ColorSpace.h
@@ -69,4 +69,20 @@ private:
DeviceCMYKColorSpace() = default;
};
+class CalRGBColorSpace final : public ColorSpace {
+public:
+ static RefPtr<CalRGBColorSpace> create(RefPtr<Document>, Vector<Value>&& parameters);
+ virtual ~CalRGBColorSpace() override = default;
+
+ virtual Color color(const Vector<Value>& arguments) const override;
+
+private:
+ CalRGBColorSpace() = default;
+
+ Array<float, 3> m_whitepoint { 0, 0, 0 };
+ Array<float, 3> m_blackpoint { 0, 0, 0 };
+ Array<float, 3> m_gamma { 1, 1, 1 };
+ Array<float, 9> m_matrix { 1, 0, 0, 0, 1, 0, 0, 0, 1 };
+};
+
}
diff --git a/Userland/Libraries/LibPDF/Renderer.cpp b/Userland/Libraries/LibPDF/Renderer.cpp
index 009b660d06..814869c3eb 100644
--- a/Userland/Libraries/LibPDF/Renderer.cpp
+++ b/Userland/Libraries/LibPDF/Renderer.cpp
@@ -517,6 +517,25 @@ RefPtr<ColorSpace> Renderer::get_color_space(const Value& value)
return DeviceRGBColorSpace::the();
if (name == CommonNames::DeviceCMYK)
return DeviceCMYKColorSpace::the();
+ if (name == CommonNames::Pattern)
+ TODO();
+
+ // The color space is a complex color space with parameters that resides in
+ // the resource dictionary
+ auto color_space_resource_dict = m_page.resources->get_dict(m_document, CommonNames::ColorSpace);
+ if (!color_space_resource_dict->contains(name))
+ TODO();
+
+ auto color_space_array = color_space_resource_dict->get_array(m_document, name);
+ name = color_space_array->get_name_at(m_document, 0)->name();
+
+ Vector<Value> parameters;
+ parameters.ensure_capacity(color_space_array->size() - 1);
+ for (size_t i = 1; i < color_space_array->size(); i++)
+ parameters.unchecked_append(color_space_array->at(i));
+
+ if (name == CommonNames::CalRGB)
+ return CalRGBColorSpace::create(m_document, move(parameters));
TODO();
}