From a169a8c06bc2faa0105f8cb4c1d14dd75cf0ab08 Mon Sep 17 00:00:00 2001 From: Jakub Melka Date: Fri, 8 Jan 2021 18:52:24 +0100 Subject: [PATCH] Color transformation finishing and bugfixes --- Pdf4QtLib/sources/pdfcolorspaces.cpp | 216 ++++++++++++++++++++++++++- Pdf4QtLib/sources/pdfcolorspaces.h | 23 ++- 2 files changed, 237 insertions(+), 2 deletions(-) diff --git a/Pdf4QtLib/sources/pdfcolorspaces.cpp b/Pdf4QtLib/sources/pdfcolorspaces.cpp index 1899757..6aee1d5 100644 --- a/Pdf4QtLib/sources/pdfcolorspaces.cpp +++ b/Pdf4QtLib/sources/pdfcolorspaces.cpp @@ -186,6 +186,11 @@ void PDFDeviceCMYKColorSpace::fillRGBBuffer(const std::vector& colors, un } } +bool PDFAbstractColorSpace::equals(const PDFAbstractColorSpace* other) const +{ + return getColorSpace() == other->getColorSpace(); +} + bool PDFAbstractColorSpace::isBlendColorSpace() const { switch (getColorSpace()) @@ -721,8 +726,12 @@ bool PDFAbstractColorSpace::transform(const PDFAbstractColorSpace* source, { const PDFColorComponent A = clip01(gray); const PDFColorComponent xyzColor = std::powf(A, gamma); + + Q_ASSERT(it != transformedInputColorsVector.end()); *it++ = xyzColor; + Q_ASSERT(it != transformedInputColorsVector.end()); *it++ = xyzColor; + Q_ASSERT(it != transformedInputColorsVector.end()); *it++ = xyzColor; } Q_ASSERT(it == transformedInputColorsVector.end()); @@ -745,8 +754,11 @@ bool PDFAbstractColorSpace::transform(const PDFAbstractColorSpace* source, for (auto sourceIt = input.cbegin(); sourceIt != input.cend(); sourceIt = std::next(sourceIt, 3)) { PDFColor3 ABC = { }; + Q_ASSERT(sourceIt != input.end()); ABC[0] = *sourceIt; + Q_ASSERT(sourceIt + 1 != input.end()); ABC[1] = *std::next(sourceIt, 1); + Q_ASSERT(sourceIt + 2 != input.end()); ABC[2] = *std::next(sourceIt, 2); ABC = colorPowerByFactors(ABC, gamma); @@ -780,6 +792,10 @@ bool PDFAbstractColorSpace::transform(const PDFAbstractColorSpace* source, for (auto sourceIt = input.cbegin(); sourceIt != input.cend(); sourceIt = std::next(sourceIt, 3)) { + Q_ASSERT(sourceIt != input.end()); + Q_ASSERT(sourceIt + 1 != input.end()); + Q_ASSERT(sourceIt + 2 != input.end()); + PDFColorComponent LStar = qBound(0.0, interpolate(*sourceIt, 0.0, 1.0, 0.0, 100.0), 100.0); PDFColorComponent aStar = qBound(aMin, interpolate(*std::next(sourceIt, 1), 0.0, 1.0, aMin, aMax), aMax); PDFColorComponent bStar = qBound(bMin, interpolate(*std::next(sourceIt, 2), 0.0, 1.0, bMin, bMax), bMax); @@ -951,12 +967,16 @@ bool PDFAbstractColorSpace::transform(const PDFAbstractColorSpace* source, for (auto transformedOutputIt = transformedOutput.cbegin(); transformedOutputIt != transformedOutput.cend(); transformedOutputIt = std::next(transformedOutputIt, 3)) { PDFColor3 XYZ = { }; + Q_ASSERT(transformedOutputIt != transformedOutput.end()); XYZ[0] = *transformedOutputIt; + Q_ASSERT(transformedOutputIt + 1 != transformedOutput.end()); XYZ[1] = *std::next(transformedOutputIt, 1); + Q_ASSERT(transformedOutputIt + 2 != transformedOutput.end()); XYZ[2] = *std::next(transformedOutputIt, 2); const PDFColorComponent gray = (XYZ[0] + XYZ[1] + XYZ[2]) * 0.333333333333333; const PDFColorComponent grayWithGamma = std::powf(gray, gamma); + Q_ASSERT(outputIt != output.cend()); *outputIt++ = grayWithGamma; } Q_ASSERT(outputIt == output.cend()); @@ -1191,6 +1211,18 @@ QColor PDFXYZColorSpace::getDefaultColor(const PDFCMS* cms, RenderingIntent inte return getColor(color, cms, intent, reporter, true); } +bool PDFXYZColorSpace::equals(const PDFAbstractColorSpace* other) const +{ + if (!PDFAbstractColorSpace::equals(other)) + { + return false; + } + + Q_ASSERT(dynamic_cast(other)); + const PDFXYZColorSpace* typedOther = static_cast(other); + return m_whitePoint == typedOther->getWhitePoint() && m_correctionCoefficients == typedOther->getCorrectionCoefficients(); +} + PDFXYZColorSpace::PDFXYZColorSpace(PDFColor3 whitePoint) : m_whitePoint(whitePoint), m_correctionCoefficients() @@ -1202,6 +1234,11 @@ PDFXYZColorSpace::PDFXYZColorSpace(PDFColor3 whitePoint) : m_correctionCoefficients[2] = 1.0f / mappedWhitePoint[2]; } +const PDFColor3& PDFXYZColorSpace::getCorrectionCoefficients() const +{ + return m_correctionCoefficients; +} + PDFCalGrayColorSpace::PDFCalGrayColorSpace(PDFColor3 whitePoint, PDFColor3 blackPoint, PDFColorComponent gamma) : PDFXYZColorSpace(whitePoint), m_blackPoint(blackPoint), @@ -1210,6 +1247,18 @@ PDFCalGrayColorSpace::PDFCalGrayColorSpace(PDFColor3 whitePoint, PDFColor3 black } +bool PDFCalGrayColorSpace::equals(const PDFAbstractColorSpace* other) const +{ + if (!PDFXYZColorSpace::equals(other)) + { + return false; + } + + Q_ASSERT(dynamic_cast(other)); + const PDFCalGrayColorSpace* typedOther = static_cast(other); + return m_blackPoint == typedOther->getBlackPoint() && m_gamma == typedOther->getGamma(); +} + QColor PDFCalGrayColorSpace::getColor(const PDFColor& color, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter, bool isRange01) const { Q_ASSERT(color.size() == getColorComponentCount()); @@ -1279,6 +1328,18 @@ PDFCalRGBColorSpace::PDFCalRGBColorSpace(PDFColor3 whitePoint, PDFColor3 blackPo } +bool PDFCalRGBColorSpace::equals(const PDFAbstractColorSpace* other) const +{ + if (!PDFXYZColorSpace::equals(other)) + { + return false; + } + + Q_ASSERT(dynamic_cast(other)); + const PDFCalRGBColorSpace* typedOther = static_cast(other); + return m_blackPoint == typedOther->getBlackPoint() && m_gamma == typedOther->getGamma() && m_matrix == typedOther->getMatrix(); +} + QColor PDFCalRGBColorSpace::getColor(const PDFColor& color, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter, bool isRange01) const { Q_ASSERT(color.size() == getColorComponentCount()); @@ -1379,6 +1440,20 @@ PDFLabColorSpace::PDFLabColorSpace(PDFColor3 whitePoint, } +bool PDFLabColorSpace::equals(const PDFAbstractColorSpace* other) const +{ + if (!PDFXYZColorSpace::equals(other)) + { + return false; + } + + Q_ASSERT(dynamic_cast(other)); + const PDFLabColorSpace* typedOther = static_cast(other); + return m_blackPoint == typedOther->getBlackPoint() && + m_aMin == typedOther->getAMin() && m_aMax == typedOther->getAMax() && + m_bMin == typedOther->getBMin() && m_bMax == typedOther->getBMax(); +} + QColor PDFLabColorSpace::getColor(const PDFColor& color, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter, bool isRange01) const { Q_ASSERT(color.size() == getColorComponentCount()); @@ -1600,6 +1675,31 @@ void PDFICCBasedColorSpace::fillRGBBuffer(const std::vector& colors, unsi } } +bool PDFICCBasedColorSpace::equals(const PDFAbstractColorSpace* other) const +{ + if (!PDFAbstractColorSpace::equals(other)) + { + return false; + } + + Q_ASSERT(dynamic_cast(other)); + const PDFICCBasedColorSpace* typedOther = static_cast(other); + + const PDFAbstractColorSpace* alternateColorSpace = typedOther->getAlternateColorSpace(); + + if (static_cast(m_alternateColorSpace.data()) != static_cast(alternateColorSpace)) + { + return false; + } + + if (m_alternateColorSpace && alternateColorSpace && !m_alternateColorSpace->equals(alternateColorSpace)) + { + return false; + } + + return m_range == typedOther->getRange() && m_iccProfileDataChecksum == typedOther->getIccProfileDataChecksum(); +} + PDFColorSpacePointer PDFICCBasedColorSpace::createICCBasedColorSpace(const PDFDictionary* colorSpaceDictionary, const PDFDocument* document, const PDFStream* stream, @@ -1687,6 +1787,11 @@ const QByteArray& PDFICCBasedColorSpace::getIccProfileDataChecksum() const return m_iccProfileDataChecksum; } +const PDFAbstractColorSpace* PDFICCBasedColorSpace::getAlternateColorSpace() const +{ + return m_alternateColorSpace.data(); +} + PDFIndexedColorSpace::PDFIndexedColorSpace(PDFColorSpacePointer baseColorSpace, QByteArray&& colors, int maxValue) : m_baseColorSpace(qMove(baseColorSpace)), m_colors(qMove(colors)), @@ -1695,6 +1800,31 @@ PDFIndexedColorSpace::PDFIndexedColorSpace(PDFColorSpacePointer baseColorSpace, } +bool PDFIndexedColorSpace::equals(const PDFAbstractColorSpace* other) const +{ + if (!PDFAbstractColorSpace::equals(other)) + { + return false; + } + + Q_ASSERT(dynamic_cast(other)); + const PDFIndexedColorSpace* typedOther = static_cast(other); + + const PDFAbstractColorSpace* baseColorSpace = typedOther->getBaseColorSpace().data(); + + if (static_cast(m_baseColorSpace.data()) != static_cast(baseColorSpace)) + { + return false; + } + + if (m_baseColorSpace && baseColorSpace && !m_baseColorSpace->equals(baseColorSpace)) + { + return false; + } + + return m_colors == typedOther->getColors() && m_maxValue == typedOther->getMaxValue(); +} + QColor PDFIndexedColorSpace::getDefaultColor(const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const { return getColor(PDFColor(0.0f), cms, intent, reporter, true); @@ -1897,7 +2027,7 @@ std::vector PDFIndexedColorSpace::transformColorsToBaseColorS for (PDFColorComponent input : buffer) { const int colorIndex = qBound(MIN_VALUE, static_cast(input), m_maxValue); - const int byteOffset = colorIndex * colorComponentCount; + const int byteOffset = int(colorIndex * colorComponentCount); // We must point into the array. Check first and last component. Q_ASSERT(byteOffset + colorComponentCount - 1 < m_colors.size()); @@ -1908,6 +2038,7 @@ std::vector PDFIndexedColorSpace::transformColorsToBaseColorS { const unsigned char value = *bytePointer++; const PDFColorComponent component = static_cast(value) / 255.0f; + Q_ASSERT(outputIt != result.cend()); *outputIt++ = component; } } @@ -1916,6 +2047,16 @@ std::vector PDFIndexedColorSpace::transformColorsToBaseColorS return result; } +int PDFIndexedColorSpace::getMaxValue() const +{ + return m_maxValue; +} + +const QByteArray& PDFIndexedColorSpace::getColors() const +{ + return m_colors; +} + PDFSeparationColorSpace::PDFSeparationColorSpace(QByteArray&& colorName, PDFColorSpacePointer alternateColorSpace, PDFFunctionPtr tintTransform) : m_colorName(qMove(colorName)), m_alternateColorSpace(qMove(alternateColorSpace)), @@ -1926,6 +2067,31 @@ PDFSeparationColorSpace::PDFSeparationColorSpace(QByteArray&& colorName, PDFColo } +bool PDFSeparationColorSpace::equals(const PDFAbstractColorSpace* other) const +{ + if (!PDFAbstractColorSpace::equals(other)) + { + return false; + } + + Q_ASSERT(dynamic_cast(other)); + const PDFSeparationColorSpace* typedOther = static_cast(other); + + const PDFAbstractColorSpace* alternateColorSpace = typedOther->getAlternateColorSpace().data(); + + if (static_cast(m_alternateColorSpace.data()) != static_cast(alternateColorSpace)) + { + return false; + } + + if (m_alternateColorSpace && alternateColorSpace && !m_alternateColorSpace->equals(alternateColorSpace)) + { + return false; + } + + return m_colorName == typedOther->getColorName(); +} + QColor PDFSeparationColorSpace::getDefaultColor(const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const { return getColor(PDFColor(1.0f), cms, intent, reporter, true); @@ -1993,6 +2159,8 @@ std::vector PDFSeparationColorSpace::transformColorsToBaseCol // Input value double tint = input; + Q_ASSERT(outputIt + (colorComponentCount - 1) != result.cend()); + if (m_isAll) { const double inversedTint = qBound(0.0, 1.0 - tint, 1.0); @@ -2061,6 +2229,12 @@ const unsigned char* PDFImageData::getRow(unsigned int rowIndex) const return data + (rowIndex * m_stride); } +bool PDFPatternColorSpace::equals(const PDFAbstractColorSpace* other) const +{ + // Compare pointers + return this == other; +} + QColor PDFPatternColorSpace::getDefaultColor(const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const { Q_UNUSED(cms); @@ -2105,6 +2279,46 @@ PDFDeviceNColorSpace::PDFDeviceNColorSpace(PDFDeviceNColorSpace::Type type, m_isNone = std::all_of(m_colorants.cbegin(), m_colorants.cend(), [](const auto& colorant) { return colorant.name == "None"; }); } +bool PDFDeviceNColorSpace::equals(const PDFAbstractColorSpace* other) const +{ + if (!PDFAbstractColorSpace::equals(other)) + { + return false; + } + + Q_ASSERT(dynamic_cast(other)); + const PDFDeviceNColorSpace* typedOther = static_cast(other); + + const PDFAbstractColorSpace* alternateColorSpace = typedOther->getAlternateColorSpace().data(); + + if (static_cast(m_alternateColorSpace.data()) != static_cast(alternateColorSpace)) + { + return false; + } + + if (m_alternateColorSpace && alternateColorSpace && !m_alternateColorSpace->equals(alternateColorSpace)) + { + return false; + } + + const Colorants& colorants = typedOther->getColorants(); + + if (m_colorants.size() != colorants.size()) + { + return false; + } + + for (size_t i = 0; i < m_colorants.size(); ++i) + { + if (m_colorants[i].name != colorants[i].name) + { + return false; + } + } + + return true; +} + QColor PDFDeviceNColorSpace::getDefaultColor(const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const { PDFColor color; diff --git a/Pdf4QtLib/sources/pdfcolorspaces.h b/Pdf4QtLib/sources/pdfcolorspaces.h index a375357..485b4f2 100644 --- a/Pdf4QtLib/sources/pdfcolorspaces.h +++ b/Pdf4QtLib/sources/pdfcolorspaces.h @@ -196,6 +196,9 @@ public: template explicit constexpr inline PDFColorComponentMatrix(Components... components) : m_values({ static_cast(components)... }) { } + bool operator==(const PDFColorComponentMatrix&) const = default; + bool operator!=(const PDFColorComponentMatrix&) const = default; + std::array operator*(const std::array& color) const { std::array result = { }; @@ -274,6 +277,9 @@ public: /// Returns color space identification virtual ColorSpace getColorSpace() const = 0; + /// Returns true, if two color spaces are equal + virtual bool equals(const PDFAbstractColorSpace* other) const; + /// Returns true, if this color space can be used for blending bool isBlendColorSpace() const; @@ -532,7 +538,10 @@ class PDFXYZColorSpace : public PDFAbstractColorSpace public: virtual QColor getDefaultColor(const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const override; + virtual bool equals(const PDFAbstractColorSpace* other) const override; + const PDFColor3& getWhitePoint() const { return m_whitePoint; } + const PDFColor3& getCorrectionCoefficients() const; protected: explicit PDFXYZColorSpace(PDFColor3 whitePoint); @@ -554,12 +563,13 @@ public: virtual ~PDFCalGrayColorSpace() = default; virtual ColorSpace getColorSpace() const override { return ColorSpace::CalGray; } + virtual bool equals(const PDFAbstractColorSpace* other) const override; virtual QColor getColor(const PDFColor& color, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter, bool isRange01) const override; virtual size_t getColorComponentCount() const override; virtual void fillRGBBuffer(const std::vector& colors,unsigned char* outputBuffer, RenderingIntent intent, const PDFCMS* cms, PDFRenderErrorReporter* reporter) const override; PDFColorComponent getGamma() const { return m_gamma; } - PDFColor3 getBlackPoint() const { m_blackPoint; } + const PDFColor3& getBlackPoint() const { return m_blackPoint; } /// Creates CalGray color space from provided values. /// \param document Document @@ -578,6 +588,7 @@ public: virtual ~PDFCalRGBColorSpace() = default; virtual ColorSpace getColorSpace() const override { return ColorSpace::CalRGB; } + virtual bool equals(const PDFAbstractColorSpace* other) const override; virtual QColor getColor(const PDFColor& color, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter, bool isRange01) const override; virtual size_t getColorComponentCount() const override; virtual void fillRGBBuffer(const std::vector& colors,unsigned char* outputBuffer, RenderingIntent intent, const PDFCMS* cms, PDFRenderErrorReporter* reporter) const override; @@ -604,6 +615,7 @@ public: virtual ~PDFLabColorSpace() = default; virtual ColorSpace getColorSpace() const override { return ColorSpace::Lab; } + virtual bool equals(const PDFAbstractColorSpace* other) const override; virtual QColor getColor(const PDFColor& color, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter, bool isRange01) const override; virtual size_t getColorComponentCount() const override; virtual void fillRGBBuffer(const std::vector& colors,unsigned char* outputBuffer, RenderingIntent intent, const PDFCMS* cms, PDFRenderErrorReporter* reporter) const override; @@ -642,6 +654,7 @@ public: virtual QColor getColor(const PDFColor& color, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter, bool isRange01) const override; virtual size_t getColorComponentCount() const override; virtual void fillRGBBuffer(const std::vector& colors, unsigned char* outputBuffer, RenderingIntent intent, const PDFCMS* cms, PDFRenderErrorReporter* reporter) const override; + virtual bool equals(const PDFAbstractColorSpace* other) const override; PDFObjectReference getMetadata() const { return m_metadata; } @@ -660,6 +673,7 @@ public: const Ranges& getRange() const; const QByteArray& getIccProfileData() const; const QByteArray& getIccProfileDataChecksum() const; + const PDFAbstractColorSpace* getAlternateColorSpace() const; private: PDFColorSpacePointer m_alternateColorSpace; @@ -676,6 +690,7 @@ public: virtual ~PDFIndexedColorSpace() = default; virtual ColorSpace getColorSpace() const override { return ColorSpace::Indexed; } + virtual bool equals(const PDFAbstractColorSpace* other) const override; virtual QColor getDefaultColor(const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const override; virtual QColor getColor(const PDFColor& color, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter, bool isRange01) const override; virtual size_t getColorComponentCount() const override; @@ -700,6 +715,9 @@ public: PDFColorSpacePointer getBaseColorSpace() const; std::vector transformColorsToBaseColorSpace(const PDFColorBuffer buffer) const; + int getMaxValue() const; + const QByteArray& getColors() const; + private: static constexpr const int MIN_VALUE = 0; static constexpr const int MAX_VALUE = 255; @@ -716,6 +734,7 @@ public: virtual ~PDFSeparationColorSpace() = default; virtual ColorSpace getColorSpace() const override { return ColorSpace::Separation; } + virtual bool equals(const PDFAbstractColorSpace* other) const override; virtual QColor getDefaultColor(const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const override; virtual QColor getColor(const PDFColor& color, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter, bool isRange01) const override; virtual size_t getColorComponentCount() const override; @@ -778,6 +797,7 @@ public: virtual ~PDFDeviceNColorSpace() = default; virtual ColorSpace getColorSpace() const override { return ColorSpace::DeviceN; } + virtual bool equals(const PDFAbstractColorSpace* other) const override; virtual QColor getDefaultColor(const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const override; virtual QColor getColor(const PDFColor& color, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter, bool isRange01) const override; virtual size_t getColorComponentCount() const override; @@ -832,6 +852,7 @@ public: virtual ~PDFPatternColorSpace() override = default; virtual ColorSpace getColorSpace() const override { return ColorSpace::Pattern; } + virtual bool equals(const PDFAbstractColorSpace* other) const override; virtual QColor getDefaultColor(const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const override; virtual QColor getColor(const PDFColor& color, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter, bool isRange01) const override; virtual size_t getColorComponentCount() const override;