Rework of color spaces, some bugfixing of color spaces

This commit is contained in:
Jakub Melka 2021-01-02 18:27:22 +01:00
parent b29791b890
commit a2dc4e16a8
8 changed files with 729 additions and 9 deletions

View File

@ -81,6 +81,7 @@ SOURCES += \
sources/pdfsnapper.cpp \
sources/pdfstructuretree.cpp \
sources/pdftextlayout.cpp \
sources/pdftransparencyrenderer.cpp \
sources/pdfutils.cpp \
sources/pdfwidgettool.cpp \
sources/pdfwidgetutils.cpp \
@ -150,6 +151,7 @@ HEADERS += \
sources/pdfsnapper.h \
sources/pdfstructuretree.h \
sources/pdftextlayout.h \
sources/pdftransparencyrenderer.h \
sources/pdfwidgettool.h \
sources/pdfwidgetutils.h \
sources/pdfxreftable.h \

View File

@ -48,6 +48,7 @@ public:
virtual bool fillRGBBufferFromDeviceCMYK(const std::vector<float>& colors, RenderingIntent intent, unsigned char* outputBuffer, PDFRenderErrorReporter* reporter) const override;
virtual bool fillRGBBufferFromXYZ(const PDFColor3& whitePoint, const std::vector<float>& colors, RenderingIntent intent, unsigned char* outputBuffer, PDFRenderErrorReporter* reporter) const override;
virtual bool fillRGBBufferFromICC(const std::vector<float>& colors, RenderingIntent renderingIntent, unsigned char* outputBuffer, const QByteArray& iccID, const QByteArray& iccData, PDFRenderErrorReporter* reporter) const override;
virtual bool transformColorSpace(const ColorSpaceTransformParams& params) const override;
private:
void init();
@ -279,6 +280,11 @@ bool PDFLittleCMS::fillRGBBufferFromICC(const std::vector<float>& colors, Render
return false;
}
bool PDFLittleCMS::transformColorSpace(const PDFCMS::ColorSpaceTransformParams& params) const
{
return false;
}
PDFLittleCMS::PDFLittleCMS(const PDFCMSManager* manager, const PDFCMSSettings& settings) :
m_manager(manager),
m_settings(settings),
@ -410,6 +416,7 @@ QColor PDFLittleCMS::getColorFromDeviceCMYK(const PDFColor& color, RenderingInte
QColor PDFLittleCMS::getColorFromXYZ(const PDFColor3& whitePoint, const PDFColor3& color, RenderingIntent intent, PDFRenderErrorReporter* reporter) const
{
Q_UNUSED(whitePoint);
cmsHTRANSFORM transform = getTransform(XYZ, getEffectiveRenderingIntent(intent), false);
if (!transform)
@ -422,8 +429,10 @@ QColor PDFLittleCMS::getColorFromXYZ(const PDFColor3& whitePoint, const PDFColor
{
Q_ASSERT(cmsGetTransformOutputFormat(transform) == TYPE_RGB_FLT);
// Jakub Melka: It seems, that Adobe Acrobat Reader doesn't do the gamut remapping (whitepoint remapping).
// so, we don't do that too.
const cmsCIEXYZ* d50WhitePoint = cmsD50_XYZ();
std::array<float, 3> xyzInputColor = { color[0] / whitePoint[0] * float(d50WhitePoint->X), color[1] / whitePoint[1] * float(d50WhitePoint->Y), color[2] / whitePoint[2] * float(d50WhitePoint->Z) };
std::array<float, 3> xyzInputColor = { color[0] * float(d50WhitePoint->X), color[1] * float(d50WhitePoint->Y), color[2] * float(d50WhitePoint->Z)};
std::array<float, 3> rgbOutputColor = { };
cmsDoTransform(transform, xyzInputColor.data(), rgbOutputColor.data(), 1);
return getColorFromOutputColor(rgbOutputColor);
@ -933,6 +942,11 @@ bool PDFCMSGeneric::fillRGBBufferFromICC(const std::vector<float>& colors, Rende
return false;
}
bool PDFCMSGeneric::transformColorSpace(const PDFCMS::ColorSpaceTransformParams& params) const
{
return false;
}
PDFCMSManager::PDFCMSManager(QObject* parent) :
BaseClass(parent),
m_mutex(QMutex::Recursive)
@ -1242,4 +1256,10 @@ PDFColorProfileIdentifier PDFColorProfileIdentifier::createFile(Type type, QStri
return result;
}
PDFColor3 PDFCMS::getDefaultXYZWhitepoint()
{
const cmsCIEXYZ* whitePoint = cmsD50_XYZ();
return PDFColor3{ PDFColorComponent(whitePoint->X), PDFColorComponent(whitePoint->Y), PDFColorComponent(whitePoint->Z) };
}
} // namespace pdf

View File

@ -127,14 +127,21 @@ public:
/// \param Three color channel value (X,Y,Z channel)
/// \param intent Rendering intent
/// \param reporter Render error reporter (used, when color transform fails)
virtual QColor getColorFromXYZ(const PDFColor3& whitePoint, const PDFColor3& color, RenderingIntent intent, PDFRenderErrorReporter* reporter) const = 0;
virtual QColor getColorFromXYZ(const PDFColor3& whitePoint,
const PDFColor3& color,
RenderingIntent intent,
PDFRenderErrorReporter* reporter) const = 0;
/// Computes color from ICC color profile
/// \param color Input color
/// \param iccID Unique ICC profile identifier
/// \param iccData Color profile data
/// \param reporter Render error reporter (used, when color transform fails)
virtual QColor getColorFromICC(const PDFColor& color, RenderingIntent renderingIntent, const QByteArray& iccID, const QByteArray& iccData, PDFRenderErrorReporter* reporter) const = 0;
virtual QColor getColorFromICC(const PDFColor& color,
RenderingIntent renderingIntent,
const QByteArray& iccID,
const QByteArray& iccData,
PDFRenderErrorReporter* reporter) const = 0;
/// Fills colors in Device Gray color space to the RGB buffer. If error occurs, then false is returned.
/// Caller then should handle this - try to convert color as accurate as possible.
@ -142,7 +149,10 @@ public:
/// \param intent Rendering intent
/// \param outputBuffer Output buffer in format RGB_888 (8-bit RGB values)
/// \param reporter Render error reporter (used, when color transform fails)
virtual bool fillRGBBufferFromDeviceGray(const std::vector<float>& colors, RenderingIntent intent, unsigned char* outputBuffer, PDFRenderErrorReporter* reporter) const = 0;
virtual bool fillRGBBufferFromDeviceGray(const std::vector<float>& colors,
RenderingIntent intent,
unsigned char* outputBuffer,
PDFRenderErrorReporter* reporter) const = 0;
/// Fills colors in Device RGB color space to RGB buffer. If error occurs, then false is returned.
/// Caller then should handle this - try to convert color as accurate as possible.
@ -150,7 +160,10 @@ public:
/// \param intent Rendering intent
/// \param outputBuffer Output buffer in format RGB_888 (8-bit RGB values)
/// \param reporter Render error reporter (used, when color transform fails)
virtual bool fillRGBBufferFromDeviceRGB(const std::vector<float>& colors, RenderingIntent intent, unsigned char* outputBuffer, PDFRenderErrorReporter* reporter) const = 0;
virtual bool fillRGBBufferFromDeviceRGB(const std::vector<float>& colors,
RenderingIntent intent,
unsigned char* outputBuffer,
PDFRenderErrorReporter* reporter) const = 0;
/// Fills colors in Device CMYK color space to the RGB buffer. If error occurs, then false is returned.
/// Caller then should handle this - try to convert color as accurate as possible.
@ -158,7 +171,10 @@ public:
/// \param intent Rendering intent
/// \param outputBuffer Output buffer in format RGB_888 (8-bit RGB values)
/// \param reporter Render error reporter (used, when color transform fails)
virtual bool fillRGBBufferFromDeviceCMYK(const std::vector<float>& colors, RenderingIntent intent, unsigned char* outputBuffer, PDFRenderErrorReporter* reporter) const = 0;
virtual bool fillRGBBufferFromDeviceCMYK(const std::vector<float>& colors,
RenderingIntent intent,
unsigned char* outputBuffer,
PDFRenderErrorReporter* reporter) const = 0;
/// Fills colors in XYZ color space to the RGB buffer. If error occurs, then false is returned.
/// Caller then should handle this - try to convert color as accurate as possible.
@ -166,14 +182,51 @@ public:
/// \param Three color channel value (X,Y,Z channel)
/// \param intent Rendering intent
/// \param reporter Render error reporter (used, when color transform fails)
virtual bool fillRGBBufferFromXYZ(const PDFColor3& whitePoint, const std::vector<float>& colors, RenderingIntent intent, unsigned char* outputBuffer, PDFRenderErrorReporter* reporter) const = 0;
virtual bool fillRGBBufferFromXYZ(const PDFColor3& whitePoint,
const std::vector<float>& colors,
RenderingIntent intent,
unsigned char* outputBuffer,
PDFRenderErrorReporter* reporter) const = 0;
/// Fills RGB buffer from ICC color profile colors
/// \param colors Input colors
/// \param iccID Unique ICC profile identifier
/// \param iccData Color profile data
/// \param reporter Render error reporter (used, when color transform fails)
virtual bool fillRGBBufferFromICC(const std::vector<float>& colors, RenderingIntent renderingIntent, unsigned char* outputBuffer, const QByteArray& iccID, const QByteArray& iccData, PDFRenderErrorReporter* reporter) const = 0;
virtual bool fillRGBBufferFromICC(const std::vector<float>& colors,
RenderingIntent renderingIntent,
unsigned char* outputBuffer,
const QByteArray& iccID,
const QByteArray& iccData,
PDFRenderErrorReporter* reporter) const = 0;
enum ColorSpaceType
{
Invalid,
DeviceGray,
DeviceRGB,
DeviceCMYK,
XYZ,
ICC
};
struct ColorSpaceTransformParams
{
ColorSpaceType sourceType = ColorSpaceType::Invalid;
ColorSpaceType targetType = ColorSpaceType::Invalid;
QByteArray sourceIccId;
QByteArray targetIccId;
QByteArray sourceIccData;
QByteArray targetIccData;
};
/// Transforms color between two color spaces.
virtual bool transformColorSpace(const ColorSpaceTransformParams& params) const = 0;
/// Get D50 white point for XYZ color space
static PDFColor3 getDefaultXYZWhitepoint();
};
using PDFCMSPointer = QSharedPointer<PDFCMS>;
@ -195,6 +248,7 @@ public:
virtual bool fillRGBBufferFromDeviceCMYK(const std::vector<float>& colors, RenderingIntent intent, unsigned char* outputBuffer, PDFRenderErrorReporter* reporter) const override;
virtual bool fillRGBBufferFromXYZ(const PDFColor3& whitePoint, const std::vector<float>& colors, RenderingIntent intent, unsigned char* outputBuffer, PDFRenderErrorReporter* reporter) const override;
virtual bool fillRGBBufferFromICC(const std::vector<float>& colors, RenderingIntent renderingIntent, unsigned char* outputBuffer, const QByteArray& iccID, const QByteArray& iccData, PDFRenderErrorReporter* reporter) const override;
virtual bool transformColorSpace(const ColorSpaceTransformParams& params) const override;
};
struct PDFColorProfileIdentifier

View File

@ -31,6 +31,43 @@
namespace pdf
{
static PDFColorComponent getDeterminant(const PDFColorComponentMatrix_3x3& matrix)
{
const PDFColorComponent a_11 = matrix.getValue(0, 0);
const PDFColorComponent a_12 = matrix.getValue(0, 1);
const PDFColorComponent a_13 = matrix.getValue(0, 2);
const PDFColorComponent a_21 = matrix.getValue(1, 0);
const PDFColorComponent a_22 = matrix.getValue(1, 1);
const PDFColorComponent a_23 = matrix.getValue(1, 2);
const PDFColorComponent a_31 = matrix.getValue(2, 0);
const PDFColorComponent a_32 = matrix.getValue(2, 1);
const PDFColorComponent a_33 = matrix.getValue(2, 2);
return -a_13* a_22 * a_31 + a_12 * a_23 * a_31 + a_13 * a_21 * a_32 - a_11 * a_23 * a_32 - a_12 * a_21 * a_33 + a_11 * a_22 * a_33;
}
PDFColorComponentMatrix_3x3 getInverseMatrix(const PDFColorComponentMatrix_3x3& matrix)
{
const PDFColorComponent a_11 = matrix.getValue(0, 0);
const PDFColorComponent a_12 = matrix.getValue(0, 1);
const PDFColorComponent a_13 = matrix.getValue(0, 2);
const PDFColorComponent a_21 = matrix.getValue(1, 0);
const PDFColorComponent a_22 = matrix.getValue(1, 1);
const PDFColorComponent a_23 = matrix.getValue(1, 2);
const PDFColorComponent a_31 = matrix.getValue(2, 0);
const PDFColorComponent a_32 = matrix.getValue(2, 1);
const PDFColorComponent a_33 = matrix.getValue(2, 2);
const PDFColorComponent determinant = -a_13* a_22 * a_31 + a_12 * a_23 * a_31 + a_13 * a_21 * a_32 - a_11 * a_23 * a_32 - a_12 * a_21 * a_33 + a_11 * a_22 * a_33;
const PDFColorComponent coefficient = !qIsNull(determinant) ? 1.0 / determinant : 0.0;
PDFColorComponentMatrix_3x3 inversedMatrix { a_22 * a_33 - a_23 * a_32, a_13 * a_32 - a_12 * a_33, a_12 * a_23 - a_13 * a_22,
a_23 * a_31 - a_21 * a_33, a_11 * a_33 - a_13 * a_31, a_13 * a_21 - a_11 * a_23,
a_21 * a_32 - a_22 * a_31, a_12 * a_31 - a_11 * a_32, a_11 * a_22 - a_12 * a_21 };
inversedMatrix.multiplyByFactor(coefficient);
return inversedMatrix;
}
QColor PDFDeviceGrayColorSpace::getDefaultColor(const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const
{
return getColor(PDFColor(0.0f), cms, intent, reporter, true);
@ -149,6 +186,23 @@ void PDFDeviceCMYKColorSpace::fillRGBBuffer(const std::vector<float>& colors, un
}
}
bool PDFAbstractColorSpace::isBlendColorSpace() const
{
switch (getColorSpace())
{
case pdf::PDFAbstractColorSpace::ColorSpace::DeviceGray:
case pdf::PDFAbstractColorSpace::ColorSpace::DeviceRGB:
case pdf::PDFAbstractColorSpace::ColorSpace::DeviceCMYK:
case pdf::PDFAbstractColorSpace::ColorSpace::CalGray:
case pdf::PDFAbstractColorSpace::ColorSpace::CalRGB:
case pdf::PDFAbstractColorSpace::ColorSpace::ICCBased:
return true;
default:
return false;
}
}
QImage PDFAbstractColorSpace::getImage(const PDFImageData& imageData,
const PDFImageData& softMask,
const PDFCMS* cms,
@ -582,6 +636,227 @@ PDFColor PDFAbstractColorSpace::mixColors(const PDFColor& color1, const PDFColor
return result;
}
bool PDFAbstractColorSpace::transform(const PDFAbstractColorSpace* source,
const PDFAbstractColorSpace* target,
const PDFCMS* cms,
RenderingIntent intent,
const PDFColorBuffer input,
PDFColorBuffer output,
PDFRenderErrorReporter* reporter)
{
Q_ASSERT(source);
Q_ASSERT(target);
Q_ASSERT(cms);
Q_ASSERT(target->isBlendColorSpace());
Q_ASSERT(input.size() % source->getColorComponentCount() == 0);
if (source->equals(target))
{
// Just copy input buffer to output buffer
Q_ASSERT(input.size() == output.size());
std::copy(input.begin(), input.end(), output.begin());
return true;
}
// We will use following algorithm to transform colors:
// 1. Determine source color space type for cms,
// and adjust colors to this color space type
// 2. Prepare work output buffer of target size (can have
// different size than real output buffer)
// 3. Transform colors using CMS
// 4. Transform output color buffer to target color buffer
PDFCMS::ColorSpaceTransformParams params;
std::vector<PDFColorComponent> transformedInputColorsVector;
PDFColorBuffer transformedInput = input;
switch (source->getColorSpace())
{
case ColorSpace::DeviceGray:
params.sourceType = PDFCMS::ColorSpaceType::DeviceGray;
break;
case ColorSpace::DeviceRGB:
params.sourceType = PDFCMS::ColorSpaceType::DeviceRGB;
break;
case ColorSpace::DeviceCMYK:
params.sourceType = PDFCMS::ColorSpaceType::DeviceCMYK;
break;
case ColorSpace::CalGray:
{
params.sourceType = PDFCMS::ColorSpaceType::XYZ;
// Transform gray to XYZ
const PDFCalGrayColorSpace* calGrayColorSpace = static_cast<const PDFCalGrayColorSpace*>(source);
const PDFColorComponent gamma = calGrayColorSpace->getGamma();
transformedInputColorsVector.resize(input.size() * 3, 0.0f);
auto it = transformedInputColorsVector.begin();
for (PDFColorComponent gray : input)
{
const PDFColorComponent A = clip01(gray);
const PDFColorComponent xyzColor = std::powf(A, gamma);
*it++ = xyzColor;
*it++ = xyzColor;
*it++ = xyzColor;
}
Q_ASSERT(it == transformedInputColorsVector.end());
transformedInput = PDFColorBuffer(transformedInputColorsVector.data(), transformedInputColorsVector.size());
break;
}
case ColorSpace::CalRGB:
{
params.sourceType = PDFCMS::ColorSpaceType::XYZ;
const PDFCalRGBColorSpace* calRGBColorSpace = static_cast<const PDFCalRGBColorSpace*>(source);
const PDFColor3 gamma = calRGBColorSpace->getGamma();
const PDFColorComponentMatrix_3x3 matrix = calRGBColorSpace->getMatrix();
transformedInputColorsVector.resize(input.size(), 0.0f);
auto it = transformedInputColorsVector.begin();
for (auto sourceIt = input.cbegin(); sourceIt != input.cend(); sourceIt = std::next(sourceIt, 3))
{
const PDFColor3 ABC = { };
ABC[0] = *sourceIt;
ABC[1] = *std::next(sourceIt, 1);
ABC[2] = *std::next(sourceIt, 2);
ABC = colorPowerByFactors(ABC, gamma);
const PDFColor3 XYZ = matrix * ABC;
*it++ = XYZ[0];
*it++ = XYZ[1];
*it++ = XYZ[2];
}
Q_ASSERT(it == transformedInputColorsVector.end());
transformedInput = PDFColorBuffer(transformedInputColorsVector.data(), transformedInputColorsVector.size());
break;
}
case ColorSpace::Lab:
{
params.sourceType = PDFCMS::ColorSpaceType::XYZ;
const PDFLabColorSpace* labColorSpace = static_cast<const PDFLabColorSpace*>(source);
const PDFColor aMin = labColorSpace->getAMin();
const PDFColor aMax = labColorSpace->getAMax();
const PDFColor bMin = labColorSpace->getBMin();
const PDFColor bMax = labColorSpace->getBMax();
transformedInputColorsVector.resize(input.size(), 0.0f);
auto it = transformedInputColorsVector.begin();
for (auto sourceIt = input.cbegin(); sourceIt != input.cend(); sourceIt = std::next(sourceIt, 3))
{
PDFColorComponent LStar = qBound(0.0, interpolate(*sourceIt, 0.0, 1.0, 0.0, 100.0), 100.0);
PDFColorComponent aStar = qBound<PDFColorComponent>(aMin, interpolate(*std::next(sourceIt, 1), 0.0, 1.0, aMin, aMax), aMax);
PDFColorComponent bStar = qBound<PDFColorComponent>(bMin, interpolate(*std::next(sourceIt, 2), 0.0, 1.0, bMin, bMax), bMax);
const PDFColorComponent param1 = (LStar + 16.0f) / 116.0f;
const PDFColorComponent param2 = aStar / 500.0f;
const PDFColorComponent param3 = bStar / 200.0f;
const PDFColorComponent L = param1 + param2;
const PDFColorComponent M = param1;
const PDFColorComponent N = param1 - param3;
auto g = [](PDFColorComponent x) -> PDFColorComponent
{
if (x >= 6.0f / 29.0f)
{
return x * x * x;
}
else
{
return (108.0f / 841.0f) * (x - 4.0f / 29.0f);
}
};
const PDFColorComponent gL = g(L);
const PDFColorComponent gM = g(M);
const PDFColorComponent gN = g(N);
*it++ = gL;
*it++ = gM;
*it++ = gN;
}
Q_ASSERT(it == transformedInputColorsVector.end());
transformedInput = PDFColorBuffer(transformedInputColorsVector.data(), transformedInputColorsVector.size());
break;
}
case ColorSpace::ICCBased:
{
const PDFICCBasedColorSpace* iccBasedColorSpace = static_cast<const PDFICCBasedColorSpace*>(source);
params.sourceType = PDFCMS::ColorSpaceType::ICC;
params.sourceIccId = iccBasedColorSpace->getIccProfileDataChecksum();
params.sourceIccData = iccBasedColorSpace->getIccProfileData();
size_t colorComponentCount = iccBasedColorSpace->getColorComponentCount();
const PDFICCBasedColorSpace::Ranges& ranges = iccBasedColorSpace->getRange();
transformedInputColorsVector.resize(input.size(), 0.0f);
auto outputIt = transformedInputColorsVector.begin();
for (auto inputIt = input.cbegin(); inputIt != input.cend();)
{
for (size_t i = 0; i < colorComponentCount; ++i)
{
const size_t imin = 2 * i + 0;
const size_t imax = 2 * i + 1;
*outputIt++ = qBound(ranges[imin], *inputIt++, ranges[imax]);
}
}
transformedInput = PDFColorBuffer(transformedInputColorsVector.data(), transformedInputColorsVector.size());
break;
}
case ColorSpace::Indexed:
{
const PDFIndexedColorSpace* indexedColorSpace = static_cast<const PDFIndexedColorSpace*>(source);
PDFColorSpacePointer baseColorSpace = indexedColorSpace->getBaseColorSpace();
std::vector<PDFColorComponent> transformedToBaseColorSpaceInput = indexedColorSpace->transformColorsToBaseColorSpace(input);
PDFColorBuffer transformedInputBuffer(transformedToBaseColorSpaceInput.data(), transformedToBaseColorSpaceInput.size());
return transform(baseColorSpace.data(), target, cms, intent, transformedInputBuffer, output, reporter);
}
case ColorSpace::Separation:
{
const PDFSeparationColorSpace* separationColorSpace = static_cast<const PDFSeparationColorSpace*>(source);
PDFColorSpacePointer alternateColorSpace = separationColorSpace->getAlternateColorSpace();
std::vector<PDFColorComponent> transformedToBaseColorSpaceInput = separationColorSpace->transformColorsToBaseColorSpace(input);
PDFColorBuffer transformedInputBuffer(transformedToBaseColorSpaceInput.data(), transformedToBaseColorSpaceInput.size());
return transform(alternateColorSpace.data(), target, cms, intent, transformedInputBuffer, output, reporter);
}
case ColorSpace::DeviceN:
{
const PDFDeviceNColorSpace* separationColorSpace = static_cast<const PDFDeviceNColorSpace*>(source);
PDFColorSpacePointer alternateColorSpace = separationColorSpace->getAlternateColorSpace();
std::vector<PDFColorComponent> transformedToBaseColorSpaceInput = separationColorSpace->transformColorsToBaseColorSpace(input);
PDFColorBuffer transformedInputBuffer(transformedToBaseColorSpaceInput.data(), transformedToBaseColorSpaceInput.size());
return transform(alternateColorSpace.data(), target, cms, intent, transformedInputBuffer, output, reporter);
}
case ColorSpace::Pattern:
return false;
default:
Q_ASSERT(false);
return false;
}
}
PDFColorSpacePointer PDFAbstractColorSpace::createColorSpaceImpl(const PDFDictionary* colorSpaceDictionary,
const PDFDocument* document,
const PDFObject& colorSpace,
@ -762,7 +1037,6 @@ PDFColor3 PDFAbstractColorSpace::convertXYZtoRGB(const PDFColor3& xyzColor)
return matrixXYZtoRGB * xyzColor;
}
QColor PDFXYZColorSpace::getDefaultColor(const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const
{
PDFColor color;
@ -926,9 +1200,26 @@ PDFColorSpacePointer PDFCalRGBColorSpace::createCalRGBColorSpace(const PDFDocume
loader.readNumberArrayFromDictionary(dictionary, CAL_GAMMA, gamma.begin(), gamma.end());
loader.readNumberArrayFromDictionary(dictionary, CAL_MATRIX, matrix.begin(), matrix.end());
matrix.transpose();
return PDFColorSpacePointer(new PDFCalRGBColorSpace(whitePoint, blackPoint, gamma, matrix));
}
PDFColor3 PDFCalRGBColorSpace::getBlackPoint() const
{
return m_blackPoint;
}
PDFColor3 PDFCalRGBColorSpace::getGamma() const
{
return m_gamma;
}
const PDFColorComponentMatrix_3x3& PDFCalRGBColorSpace::getMatrix() const
{
return m_matrix;
}
PDFLabColorSpace::PDFLabColorSpace(PDFColor3 whitePoint,
PDFColor3 blackPoint,
PDFColorComponent aMin,
@ -1071,6 +1362,31 @@ PDFColorSpacePointer PDFLabColorSpace::createLabColorSpace(const PDFDocument* do
return PDFColorSpacePointer(new PDFLabColorSpace(whitePoint, blackPoint, minMax[0], minMax[1], minMax[2], minMax[3]));
}
PDFColorComponent PDFLabColorSpace::getAMin() const
{
return m_aMin;
}
PDFColorComponent PDFLabColorSpace::getAMax() const
{
return m_aMax;
}
PDFColorComponent PDFLabColorSpace::getBMin() const
{
return m_bMin;
}
PDFColorComponent PDFLabColorSpace::getBMax() const
{
return m_bMax;
}
PDFColor3 PDFLabColorSpace::getBlackPoint() const
{
return m_blackPoint;
}
PDFICCBasedColorSpace::PDFICCBasedColorSpace(PDFColorSpacePointer alternateColorSpace, Ranges range, QByteArray iccProfileData, PDFObjectReference metadata) :
m_alternateColorSpace(qMove(alternateColorSpace)),
m_range(range),
@ -1213,6 +1529,21 @@ PDFColorSpacePointer PDFICCBasedColorSpace::createICCBasedColorSpace(const PDFDi
return PDFColorSpacePointer(new PDFICCBasedColorSpace(qMove(alternateColorSpace), ranges, qMove(iccProfileData), loader.readReferenceFromDictionary(dictionary, "Metadata")));
}
const PDFICCBasedColorSpace::Ranges& PDFICCBasedColorSpace::getRange() const
{
return m_range;
}
const QByteArray& PDFICCBasedColorSpace::getIccProfileData() const
{
return m_iccProfileData;
}
const QByteArray& PDFICCBasedColorSpace::getIccProfileDataChecksum() const
{
return m_iccProfileDataChecksum;
}
PDFIndexedColorSpace::PDFIndexedColorSpace(PDFColorSpacePointer baseColorSpace, QByteArray&& colors, int maxValue) :
m_baseColorSpace(qMove(baseColorSpace)),
m_colors(qMove(colors)),
@ -1409,6 +1740,39 @@ PDFColorSpacePointer PDFIndexedColorSpace::createIndexedColorSpace(const PDFDict
return PDFColorSpacePointer(new PDFIndexedColorSpace(qMove(baseColorSpace), qMove(colors), maxValue));
}
PDFColorSpacePointer PDFIndexedColorSpace::getBaseColorSpace() const
{
return m_baseColorSpace;
}
std::vector<PDFColorComponent> PDFIndexedColorSpace::transformColorsToBaseColorSpace(const PDFColorBuffer buffer) const
{
const std::size_t colorComponentCount = m_baseColorSpace->getColorComponentCount();
std::vector<PDFColorComponent> result(buffer.size() * colorComponentCount, 0.0f);
auto outputIt = result.begin();
for (PDFColorComponent input : buffer)
{
const int colorIndex = qBound(MIN_VALUE, static_cast<int>(input), m_maxValue);
const int byteOffset = colorIndex * colorComponentCount;
// We must point into the array. Check first and last component.
Q_ASSERT(byteOffset + colorComponentCount - 1 < m_colors.size());
const char* bytePointer = m_colors.constData() + byteOffset;
for (int i = 0; i < colorComponentCount; ++i)
{
const unsigned char value = *bytePointer++;
const PDFColorComponent component = static_cast<PDFColorComponent>(value) / 255.0f;
*outputIt++ = component;
}
}
Q_ASSERT(outputIt == result.cend());
return result;
}
PDFSeparationColorSpace::PDFSeparationColorSpace(QByteArray&& colorName, PDFColorSpacePointer alternateColorSpace, PDFFunctionPtr tintTransform) :
m_colorName(qMove(colorName)),
m_alternateColorSpace(qMove(alternateColorSpace)),
@ -1472,6 +1836,38 @@ size_t PDFSeparationColorSpace::getColorComponentCount() const
return 1;
}
std::vector<PDFColorComponent> PDFSeparationColorSpace::transformColorsToBaseColorSpace(const PDFColorBuffer buffer) const
{
const std::size_t colorComponentCount = m_alternateColorSpace->getColorComponentCount();
std::vector<PDFColorComponent> result(buffer.size() * colorComponentCount, 0.0f);
std::vector<double> outputColor;
outputColor.resize(colorComponentCount, 0.0);
auto outputIt = result.begin();
for (PDFColorComponent input : buffer)
{
// Input value
double tint = input;
if (m_isAll)
{
const double inversedTint = qBound(0.0, 1.0 - tint, 1.0);
std::fill(outputIt, outputIt + colorComponentCount, inversedTint);
}
else
{
m_tintTransform->apply(&tint, &tint + 1, outputColor.data(), outputColor.data() + outputColor.size());
std::copy(outputColor.cbegin(), outputColor.cend(), outputIt);
}
outputIt = std::next(outputIt, colorComponentCount);
}
Q_ASSERT(outputIt == result.cend());
return result;
}
PDFColorSpacePointer PDFSeparationColorSpace::createSeparationColorSpace(const PDFDictionary* colorSpaceDictionary,
const PDFDocument* document,
const PDFArray* array,
@ -1504,6 +1900,16 @@ PDFColorSpacePointer PDFSeparationColorSpace::createSeparationColorSpace(const P
return PDFColorSpacePointer(new PDFSeparationColorSpace(qMove(colorName), qMove(alternateColorSpace), qMove(tintTransform)));
}
PDFColorSpacePointer PDFSeparationColorSpace::getAlternateColorSpace() const
{
return m_alternateColorSpace;
}
const QByteArray& PDFSeparationColorSpace::getColorName() const
{
return m_colorName;
}
const unsigned char* PDFImageData::getRow(unsigned int rowIndex) const
{
const unsigned char* data = reinterpret_cast<const unsigned char*>(m_data.constData());
@ -1612,6 +2018,34 @@ size_t PDFDeviceNColorSpace::getColorComponentCount() const
return m_colorants.size();
}
std::vector<PDFColorComponent> PDFDeviceNColorSpace::transformColorsToBaseColorSpace(const PDFColorBuffer buffer) const
{
std::vector<PDFColorComponent> result;
const std::size_t colorantCount = getColorComponentCount();
if (colorantCount > 0)
{
const std::size_t inputColorCount = buffer.size() / colorantCount;
const std::size_t alternateColorSpaceComponentCount = m_alternateColorSpace->getColorComponentCount();
result.resize(inputColorCount * alternateColorSpaceComponentCount, 0.0f);
std::vector<double> inputColor(colorantCount, 0.0);
std::vector<double> outputColor(alternateColorSpaceComponentCount, 0.0);
auto outputIt = result.begin();
for (auto it = buffer.begin(); it != buffer.end(); it = std::next(it, colorantCount))
{
std::copy(it, it + colorantCount, inputColor.begin());
m_tintTransform->apply(inputColor.data(), inputColor.data() + inputColor.size(), outputColor.data(), outputColor.data() + outputColor.size());
std::copy(outputColor.cbegin(), outputColor.cend(), outputIt);
outputIt = std::next(outputIt, alternateColorSpaceComponentCount);
}
Q_ASSERT(outputIt == result.cend());
}
return result;
}
PDFColorSpacePointer PDFDeviceNColorSpace::createDeviceNColorSpace(const PDFDictionary* colorSpaceDictionary,
const PDFDocument* document,
const PDFArray* array,

View File

@ -20,6 +20,7 @@
#include "pdfflatarray.h"
#include "pdffunction.h"
#include "pdfutils.h"
#include <QColor>
#include <QImage>
@ -43,6 +44,7 @@ class PDFRenderErrorReporter;
using PDFColorComponent = float;
using PDFColor = PDFFlatArray<PDFColorComponent, 4>;
using PDFColorSpacePointer = QSharedPointer<PDFAbstractColorSpace>;
using PDFColorBuffer = PDFBuffer<PDFColorComponent>;
static constexpr const int COLOR_SPACE_MAX_LEVEL_OF_RECURSION = 12;
@ -209,6 +211,34 @@ public:
return result;
}
inline PDFColorComponent getValue(size_t row, size_t column) const
{
return m_values[row * Cols + column];
}
void transpose()
{
Q_ASSERT(Rows == Cols);
for (size_t row = 0; row < Rows; ++row)
{
for (size_t column = row; column < Cols; ++column)
{
const size_t index1 = row * Cols + column;
const size_t index2 = column * Cols + row;
std::swap(m_values[index1], m_values[index2]);
}
}
}
void multiplyByFactor(PDFColorComponent factor)
{
for (auto it = begin(); it != end(); ++it)
{
*it *= factor;
}
}
inline typename std::array<PDFColorComponent, Rows * Cols>::iterator begin() { return m_values.begin(); }
inline typename std::array<PDFColorComponent, Rows * Cols>::iterator end() { return m_values.end(); }
@ -226,6 +256,27 @@ public:
explicit PDFAbstractColorSpace() = default;
virtual ~PDFAbstractColorSpace() = default;
enum class ColorSpace
{
DeviceGray,
DeviceRGB,
DeviceCMYK,
CalGray,
CalRGB,
Lab,
ICCBased,
Indexed,
Separation,
DeviceN,
Pattern
};
/// Returns color space identification
virtual ColorSpace getColorSpace() const = 0;
/// Returns true, if this color space can be used for blending
bool isBlendColorSpace() const;
/// Returns default color for the color space
virtual QColor getDefaultColor(const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const = 0;
@ -320,6 +371,23 @@ public:
/// \param ratio Mixing ratio
static PDFColor mixColors(const PDFColor& color1, const PDFColor& color2, PDFReal ratio);
/// Transforms color from source color space to target color space. Target color space
/// must be blend color space.
/// \param source Source color space
/// \param target Target color space (must be blend color space)
/// \param cms Color management system
/// \param intent Rendering intent
/// \param input Input color buffer
/// \param output Output color buffer, must match size of input color buffer
/// \param reporter Error reporter
static bool transform(const PDFAbstractColorSpace* source,
const PDFAbstractColorSpace* target,
const PDFCMS* cms,
RenderingIntent intent,
const PDFColorBuffer input,
PDFColorBuffer output,
PDFRenderErrorReporter* reporter);
protected:
/// Clips the color component to range [0, 1]
static constexpr PDFColorComponent clip01(PDFColorComponent component) { return qBound<PDFColorComponent>(0.0, component, 1.0); }
@ -426,6 +494,7 @@ public:
explicit PDFDeviceGrayColorSpace() = default;
virtual ~PDFDeviceGrayColorSpace() = default;
virtual ColorSpace getColorSpace() const override { return ColorSpace::DeviceGray; }
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;
@ -438,6 +507,7 @@ public:
explicit PDFDeviceRGBColorSpace() = default;
virtual ~PDFDeviceRGBColorSpace() = default;
virtual ColorSpace getColorSpace() const override { return ColorSpace::DeviceRGB; }
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;
@ -450,6 +520,7 @@ public:
explicit PDFDeviceCMYKColorSpace() = default;
virtual ~PDFDeviceCMYKColorSpace() = default;
virtual ColorSpace getColorSpace() const override { return ColorSpace::DeviceCMYK; }
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;
@ -461,6 +532,8 @@ class PDFXYZColorSpace : public PDFAbstractColorSpace
public:
virtual QColor getDefaultColor(const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const override;
const PDFColor3& getWhitePoint() const { return m_whitePoint; }
protected:
explicit PDFXYZColorSpace(PDFColor3 whitePoint);
virtual ~PDFXYZColorSpace() = default;
@ -480,10 +553,14 @@ public:
explicit PDFCalGrayColorSpace(PDFColor3 whitePoint, PDFColor3 blackPoint, PDFColorComponent gamma);
virtual ~PDFCalGrayColorSpace() = default;
virtual ColorSpace getColorSpace() const override { return ColorSpace::CalGray; }
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<float>& colors,unsigned char* outputBuffer, RenderingIntent intent, const PDFCMS* cms, PDFRenderErrorReporter* reporter) const override;
PDFColorComponent getGamma() const { return m_gamma; }
PDFColor3 getBlackPoint() const { m_blackPoint; }
/// Creates CalGray color space from provided values.
/// \param document Document
/// \param dictionary Dictionary
@ -500,6 +577,7 @@ public:
explicit PDFCalRGBColorSpace(PDFColor3 whitePoint, PDFColor3 blackPoint, PDFColor3 gamma, PDFColorComponentMatrix_3x3 matrix);
virtual ~PDFCalRGBColorSpace() = default;
virtual ColorSpace getColorSpace() const override { return ColorSpace::CalRGB; }
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<float>& colors,unsigned char* outputBuffer, RenderingIntent intent, const PDFCMS* cms, PDFRenderErrorReporter* reporter) const override;
@ -509,6 +587,10 @@ public:
/// \param dictionary Dictionary
static PDFColorSpacePointer createCalRGBColorSpace(const PDFDocument* document, const PDFDictionary* dictionary);
PDFColor3 getBlackPoint() const;
PDFColor3 getGamma() const;
const PDFColorComponentMatrix_3x3& getMatrix() const;
private:
PDFColor3 m_blackPoint;
PDFColor3 m_gamma;
@ -521,6 +603,7 @@ public:
explicit PDFLabColorSpace(PDFColor3 whitePoint, PDFColor3 blackPoint, PDFColorComponent aMin, PDFColorComponent aMax, PDFColorComponent bMin, PDFColorComponent bMax);
virtual ~PDFLabColorSpace() = default;
virtual ColorSpace getColorSpace() const override { return ColorSpace::Lab; }
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<float>& colors,unsigned char* outputBuffer, RenderingIntent intent, const PDFCMS* cms, PDFRenderErrorReporter* reporter) const override;
@ -530,6 +613,13 @@ public:
/// \param dictionary Dictionary
static PDFColorSpacePointer createLabColorSpace(const PDFDocument* document, const PDFDictionary* dictionary);
PDFColorComponent getAMin() const;
PDFColorComponent getAMax() const;
PDFColorComponent getBMin() const;
PDFColorComponent getBMax() const;
PDFColor3 getBlackPoint() const;
private:
PDFColor3 m_blackPoint;
PDFColorComponent m_aMin;
@ -547,6 +637,7 @@ public:
explicit PDFICCBasedColorSpace(PDFColorSpacePointer alternateColorSpace, Ranges range, QByteArray iccProfileData, PDFObjectReference metadata);
virtual ~PDFICCBasedColorSpace() = default;
virtual ColorSpace getColorSpace() const override { return ColorSpace::ICCBased; }
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;
@ -566,6 +657,10 @@ public:
int recursion,
std::set<QByteArray>& usedNames);
const Ranges& getRange() const;
const QByteArray& getIccProfileData() const;
const QByteArray& getIccProfileDataChecksum() const;
private:
PDFColorSpacePointer m_alternateColorSpace;
Ranges m_range;
@ -580,6 +675,7 @@ public:
explicit PDFIndexedColorSpace(PDFColorSpacePointer baseColorSpace, QByteArray&& colors, int maxValue);
virtual ~PDFIndexedColorSpace() = default;
virtual ColorSpace getColorSpace() const override { return ColorSpace::Indexed; }
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;
@ -601,6 +697,9 @@ public:
int recursion,
std::set<QByteArray>& usedNames);
PDFColorSpacePointer getBaseColorSpace() const;
std::vector<PDFColorComponent> transformColorsToBaseColorSpace(const PDFColorBuffer buffer) const;
private:
static constexpr const int MIN_VALUE = 0;
static constexpr const int MAX_VALUE = 255;
@ -616,6 +715,7 @@ public:
explicit PDFSeparationColorSpace(QByteArray&& colorName, PDFColorSpacePointer alternateColorSpace, PDFFunctionPtr tintTransform);
virtual ~PDFSeparationColorSpace() = default;
virtual ColorSpace getColorSpace() const override { return ColorSpace::Separation; }
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;
@ -623,6 +723,8 @@ public:
bool isNone() const { return m_isNone; }
bool isAll() const { return m_isAll; }
std::vector<PDFColorComponent> transformColorsToBaseColorSpace(const PDFColorBuffer buffer) const;
/// Creates separation color space from provided values.
/// \param colorSpaceDictionary Color space dictionary
/// \param document Document
@ -635,6 +737,9 @@ public:
int recursion,
std::set<QByteArray>& usedNames);
PDFColorSpacePointer getAlternateColorSpace() const;
const QByteArray& getColorName() const;
private:
QByteArray m_colorName;
PDFColorSpacePointer m_alternateColorSpace;
@ -672,6 +777,7 @@ public:
std::vector<QByteArray> processColorSpaceComponents);
virtual ~PDFDeviceNColorSpace() = default;
virtual ColorSpace getColorSpace() const override { return ColorSpace::DeviceN; }
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;
@ -687,6 +793,8 @@ public:
const std::vector<QByteArray>& getProcessColorSpaceComponents() const { return m_processColorSpaceComponents; }
bool isNone() const { return m_isNone; }
std::vector<PDFColorComponent> transformColorsToBaseColorSpace(const PDFColorBuffer buffer) const;
/// Creates DeviceN color space from provided values.
/// \param colorSpaceDictionary Color space dictionary
/// \param document Document
@ -723,6 +831,7 @@ public:
virtual ~PDFPatternColorSpace() override = default;
virtual ColorSpace getColorSpace() const override { return ColorSpace::Pattern; }
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;

View File

@ -0,0 +1,29 @@
// Copyright (C) 2020 Jakub Melka
//
// This file is part of Pdf4Qt.
//
// Pdf4Qt is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pdf4Qt is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Pdf4Qt. If not, see <https://www.gnu.org/licenses/>.
#include "pdftransparencyrenderer.h"
namespace pdf
{
PDFTransparencyRenderer::PDFTransparencyRenderer()
{
}
} // namespace pdf

View File

@ -0,0 +1,32 @@
// Copyright (C) 2020 Jakub Melka
//
// This file is part of Pdf4Qt.
//
// Pdf4Qt is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pdf4Qt is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Pdf4Qt. If not, see <https://www.gnu.org/licenses/>.
#ifndef PDFTRANSPARENCYRENDERER_H
#define PDFTRANSPARENCYRENDERER_H
namespace pdf
{
class PDFTransparencyRenderer
{
public:
PDFTransparencyRenderer();
};
} // namespace pdf
#endif // PDFTRANSPARENCYRENDERER_H

View File

@ -500,6 +500,46 @@ static inline bool isFuzzyComparedPointsSame(const QPointF& p1, const QPointF& p
return squaredDistance < squaredTolerance;
}
/// View on the array
template<typename T>
class PDFBuffer
{
public:
using value_type = T;
using value_ptr = value_type*;
using const_value_type = const value_type;
using const_value_ptr = const_value_type*;
explicit inline PDFBuffer() :
m_begin(nullptr),
m_end(nullptr)
{
}
explicit inline PDFBuffer(value_ptr value, size_t size) :
m_begin(value),
m_end(value + size)
{
}
inline value_ptr begin() { return m_begin; }
inline value_ptr end() { return m_end; }
inline const_value_ptr begin() const { return m_begin; }
inline const_value_ptr end() const { return m_end; }
inline const_value_ptr cbegin() const { return m_begin; }
inline const_value_ptr cend() const { return m_end; }
size_t size() const { return m_end - m_begin; }
private:
value_ptr m_begin;
value_ptr m_end;
};
/// Storage for result of some operation. Stores, if operation was successful, or not and
/// also error message, why operation has failed. Can be converted explicitly to bool.
class PDFOperationResult