From 4a5136fc8ca8f17a35188349b274d486cad3a0b0 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Thu, 2 Nov 2023 08:49:34 -0400 Subject: [PATCH] LibPDF: Implement CalGrayColorSpace I haven't seen this being used in the wild, but it's used in Tests/LibPDF/colorspaces.pdf. --- Userland/Libraries/LibPDF/ColorSpace.cpp | 73 ++++++++++++++++++++++++ Userland/Libraries/LibPDF/ColorSpace.h | 19 ++++++ Userland/Libraries/LibPDF/CommonNames.h | 1 + 3 files changed, 93 insertions(+) diff --git a/Userland/Libraries/LibPDF/ColorSpace.cpp b/Userland/Libraries/LibPDF/ColorSpace.cpp index 0736373090..ae464a2c37 100644 --- a/Userland/Libraries/LibPDF/ColorSpace.cpp +++ b/Userland/Libraries/LibPDF/ColorSpace.cpp @@ -54,6 +54,9 @@ PDFErrorOr> ColorSpace::create(Document* document, Non for (size_t i = 1; i < color_space_array->size(); i++) parameters.unchecked_append(color_space_array->at(i)); + if (color_space_name == CommonNames::CalGray) + return TRY(CalGrayColorSpace::create(document, move(parameters))); + if (color_space_name == CommonNames::CalRGB) return TRY(CalRGBColorSpace::create(document, move(parameters))); @@ -263,6 +266,76 @@ constexpr Array convert_to_srgb(Array xyz) return { pow(linear_srgb[0], 1.0f / 2.2f), pow(linear_srgb[1], 1.0f / 2.2f), pow(linear_srgb[2], 1.0f / 2.2f) }; } +PDFErrorOr> CalGrayColorSpace::create(Document* document, Vector&& parameters) +{ + if (parameters.size() != 1) + return Error { Error::Type::MalformedPDF, "Gray color space expects one parameter" }; + + auto param = parameters[0]; + if (!param.has>() || !param.get>()->is()) + return Error { Error::Type::MalformedPDF, "Gray color space expects a dict parameter" }; + + auto dict = param.get>()->cast(); + if (!dict->contains(CommonNames::WhitePoint)) + return Error { Error::Type::MalformedPDF, "Gray color space expects a Whitepoint key" }; + + auto white_point_array = TRY(dict->get_array(document, CommonNames::WhitePoint)); + if (white_point_array->size() != 3) + return Error { Error::Type::MalformedPDF, "Gray color space expects 3 Whitepoint parameters" }; + + auto color_space = adopt_ref(*new CalGrayColorSpace()); + + 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 Error { Error::Type::MalformedPDF, "Gray color space expects 2nd Whitepoint to be 1.0" }; + + if (dict->contains(CommonNames::BlackPoint)) { + auto black_point_array = TRY(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)) { + color_space->m_gamma = TRY(document->resolve(dict->get_value(CommonNames::Gamma))).to_float(); + } + + return color_space; +} + +PDFErrorOr CalGrayColorSpace::color(ReadonlySpan arguments) const +{ + VERIFY(arguments.size() == 1); + auto a = clamp(arguments[0].to_float(), 0.0f, 1.0f); + + auto ag = powf(a, m_gamma); + + auto x = m_whitepoint[0] * ag; + auto y = m_whitepoint[1] * ag; + auto z = m_whitepoint[2] * ag; + + 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(scaled_black_point_xyz); + auto srgb = convert_to_srgb(d65_normalized); + + auto red = static_cast(clamp(srgb[0], 0.0f, 1.0f) * 255.0f); + auto green = static_cast(clamp(srgb[1], 0.0f, 1.0f) * 255.0f); + auto blue = static_cast(clamp(srgb[2], 0.0f, 1.0f) * 255.0f); + + return Color(red, green, blue); +} + +Vector CalGrayColorSpace::default_decode() const +{ + return { 0.0f, 1.0f }; +} + PDFErrorOr> CalRGBColorSpace::create(Document* document, Vector&& parameters) { if (parameters.size() != 1) diff --git a/Userland/Libraries/LibPDF/ColorSpace.h b/Userland/Libraries/LibPDF/ColorSpace.h index 59696649fb..0e80b5c6ad 100644 --- a/Userland/Libraries/LibPDF/ColorSpace.h +++ b/Userland/Libraries/LibPDF/ColorSpace.h @@ -123,6 +123,25 @@ private: size_t m_number_of_components { 0 }; }; +class CalGrayColorSpace final : public ColorSpace { +public: + static PDFErrorOr> create(Document*, Vector&& parameters); + + ~CalGrayColorSpace() override = default; + + PDFErrorOr color(ReadonlySpan arguments) const override; + int number_of_components() const override { return 1; } + Vector default_decode() const override; + ColorSpaceFamily const& family() const override { return ColorSpaceFamily::CalGray; } + +private: + CalGrayColorSpace() = default; + + Array m_whitepoint { 0, 0, 0 }; + Array m_blackpoint { 0, 0, 0 }; + float m_gamma { 1 }; +}; + class CalRGBColorSpace final : public ColorSpace { public: static PDFErrorOr> create(Document*, Vector&& parameters); diff --git a/Userland/Libraries/LibPDF/CommonNames.h b/Userland/Libraries/LibPDF/CommonNames.h index 87f1f71827..5daf5b7f81 100644 --- a/Userland/Libraries/LibPDF/CommonNames.h +++ b/Userland/Libraries/LibPDF/CommonNames.h @@ -27,6 +27,7 @@ X(CCITTFaxDecode) \ X(CF) \ X(CFM) \ + X(CalGray) \ X(CalRGB) \ X(CIDFontType0) \ X(CIDFontType2) \