mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Transformation between two color spaces via cms
This commit is contained in:
@ -111,6 +111,11 @@ private:
|
|||||||
/// \param color01 Rgb color (range 0-1 is assumed).
|
/// \param color01 Rgb color (range 0-1 is assumed).
|
||||||
static QColor getColorFromOutputColor(std::array<float, 3> color01);
|
static QColor getColorFromOutputColor(std::array<float, 3> color01);
|
||||||
|
|
||||||
|
/// Returns transform key for transformation between various color spaces
|
||||||
|
static QByteArray getTransformColorSpaceKey(const ColorSpaceTransformParams& params);
|
||||||
|
|
||||||
|
cmsHTRANSFORM getTransformBetweenColorSpaces(const ColorSpaceTransformParams& params) const;
|
||||||
|
|
||||||
const PDFCMSManager* m_manager;
|
const PDFCMSManager* m_manager;
|
||||||
PDFCMSSettings m_settings;
|
PDFCMSSettings m_settings;
|
||||||
QColor m_paperColor;
|
QColor m_paperColor;
|
||||||
@ -121,6 +126,9 @@ private:
|
|||||||
|
|
||||||
mutable QReadWriteLock m_customIccProfileCacheLock;
|
mutable QReadWriteLock m_customIccProfileCacheLock;
|
||||||
mutable std::map<std::pair<QByteArray, RenderingIntent>, cmsHTRANSFORM> m_customIccProfileCache;
|
mutable std::map<std::pair<QByteArray, RenderingIntent>, cmsHTRANSFORM> m_customIccProfileCache;
|
||||||
|
|
||||||
|
mutable QReadWriteLock m_transformColorSpaceCacheLock;
|
||||||
|
mutable std::map<QByteArray, cmsHTRANSFORM> m_transformColorSpaceCache;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool PDFLittleCMS::fillRGBBufferFromDeviceGray(const std::vector<float>& colors,
|
bool PDFLittleCMS::fillRGBBufferFromDeviceGray(const std::vector<float>& colors,
|
||||||
@ -266,7 +274,7 @@ bool PDFLittleCMS::fillRGBBufferFromICC(const std::vector<float>& colors, Render
|
|||||||
inputColors = cmykColors.data();
|
inputColors = cmykColors.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((colors.size()) % T_CHANNELS(format) == 0)
|
if (colors.size() % channels == 0)
|
||||||
{
|
{
|
||||||
const cmsUInt32Number pixels = static_cast<cmsUInt32Number>(colors.size()) / channels;
|
const cmsUInt32Number pixels = static_cast<cmsUInt32Number>(colors.size()) / channels;
|
||||||
cmsDoTransform(transform, inputColors, outputBuffer, pixels);
|
cmsDoTransform(transform, inputColors, outputBuffer, pixels);
|
||||||
@ -282,6 +290,63 @@ bool PDFLittleCMS::fillRGBBufferFromICC(const std::vector<float>& colors, Render
|
|||||||
|
|
||||||
bool PDFLittleCMS::transformColorSpace(const PDFCMS::ColorSpaceTransformParams& params) const
|
bool PDFLittleCMS::transformColorSpace(const PDFCMS::ColorSpaceTransformParams& params) const
|
||||||
{
|
{
|
||||||
|
PDFCMS::ColorSpaceTransformParams transformedParams = params;
|
||||||
|
transformedParams.intent = getEffectiveRenderingIntent(transformedParams.intent);
|
||||||
|
|
||||||
|
cmsHTRANSFORM transform = getTransformBetweenColorSpaces(transformedParams);
|
||||||
|
if (!transform)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cmsUInt32Number inputProfileFormat = cmsGetTransformInputFormat(transform);
|
||||||
|
const cmsUInt32Number inputChannels = T_CHANNELS(inputProfileFormat);
|
||||||
|
const cmsUInt32Number inputColorSpace = T_COLORSPACE(inputProfileFormat);
|
||||||
|
const bool isInputCMYK = inputColorSpace == PT_CMYK;
|
||||||
|
const float* inputColors = params.input.begin();
|
||||||
|
std::vector<float> cmykColors;
|
||||||
|
|
||||||
|
const cmsUInt32Number outputProfileFormat = cmsGetTransformOutputFormat(transform);
|
||||||
|
const cmsUInt32Number outputChannels = T_CHANNELS(outputProfileFormat);
|
||||||
|
const cmsUInt32Number outputColorSpace = T_COLORSPACE(outputProfileFormat);
|
||||||
|
const bool isOutputCMYK = outputColorSpace == PT_CMYK;
|
||||||
|
|
||||||
|
if (isInputCMYK)
|
||||||
|
{
|
||||||
|
cmykColors = std::vector<float>(params.input.cbegin(), params.input.cend());
|
||||||
|
for (size_t i = 0; i < cmykColors.size(); ++i)
|
||||||
|
{
|
||||||
|
cmykColors[i] = cmykColors[i] * 100.0;
|
||||||
|
}
|
||||||
|
inputColors = cmykColors.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
const cmsUInt32Number inputPixelCount = static_cast<cmsUInt32Number>(params.input.size()) / inputChannels;
|
||||||
|
const cmsUInt32Number outputPixelCount = static_cast<cmsUInt32Number>(params.output.size()) / outputChannels;
|
||||||
|
|
||||||
|
if (params.input.size() % inputChannels == 0 &&
|
||||||
|
params.output.size() % outputChannels == 0 &&
|
||||||
|
inputPixelCount == outputPixelCount)
|
||||||
|
{
|
||||||
|
PDFColorBuffer outputBuffer = params.output;
|
||||||
|
cmsDoTransform(transform, inputColors, outputBuffer.begin(), inputPixelCount);
|
||||||
|
|
||||||
|
if (isOutputCMYK)
|
||||||
|
{
|
||||||
|
const PDFColorComponent colorQuotient = 1.0f / 100.0f;
|
||||||
|
for (PDFColorComponent& color : outputBuffer)
|
||||||
|
{
|
||||||
|
color *= colorQuotient;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,6 +379,15 @@ PDFLittleCMS::~PDFLittleCMS()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const auto& transformItem : m_transformColorSpaceCache)
|
||||||
|
{
|
||||||
|
cmsHTRANSFORM transform = transformItem.second;
|
||||||
|
if (transform)
|
||||||
|
{
|
||||||
|
cmsDeleteTransform(transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (cmsHPROFILE profile : m_profiles)
|
for (cmsHPROFILE profile : m_profiles)
|
||||||
{
|
{
|
||||||
if (profile)
|
if (profile)
|
||||||
@ -797,6 +871,126 @@ QColor PDFLittleCMS::getColorFromOutputColor(std::array<float, 3> color01)
|
|||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QByteArray PDFLittleCMS::getTransformColorSpaceKey(const PDFCMS::ColorSpaceTransformParams& params)
|
||||||
|
{
|
||||||
|
QByteArray key;
|
||||||
|
|
||||||
|
QBuffer buffer(&key);
|
||||||
|
buffer.open(QBuffer::WriteOnly);
|
||||||
|
|
||||||
|
QDataStream stream(&buffer);
|
||||||
|
stream << params.sourceType;
|
||||||
|
stream << params.sourceIccId;
|
||||||
|
stream << params.targetType;
|
||||||
|
stream << params.targetIccId;
|
||||||
|
stream << params.intent;
|
||||||
|
|
||||||
|
buffer.close();
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmsHTRANSFORM PDFLittleCMS::getTransformBetweenColorSpaces(const PDFCMS::ColorSpaceTransformParams& params) const
|
||||||
|
{
|
||||||
|
QByteArray key = getTransformColorSpaceKey(params);
|
||||||
|
QReadLocker lock(&m_transformColorSpaceCacheLock);
|
||||||
|
auto it = m_transformColorSpaceCache.find(key);
|
||||||
|
if (it == m_transformColorSpaceCache.cend())
|
||||||
|
{
|
||||||
|
lock.unlock();
|
||||||
|
QWriteLocker writeLock(&m_transformColorSpaceCacheLock);
|
||||||
|
|
||||||
|
// Now, we have locked cache for writing. We must find out,
|
||||||
|
// if some other thread doesn't created the transformation already.
|
||||||
|
it = m_transformColorSpaceCache.find(key);
|
||||||
|
if (it == m_transformColorSpaceCache.cend())
|
||||||
|
{
|
||||||
|
cmsHPROFILE inputProfile = cmsHPROFILE();
|
||||||
|
cmsHPROFILE outputProfile = cmsHPROFILE();
|
||||||
|
cmsHTRANSFORM transform = cmsHTRANSFORM();
|
||||||
|
|
||||||
|
switch (params.sourceType)
|
||||||
|
{
|
||||||
|
case ColorSpaceType::DeviceGray:
|
||||||
|
inputProfile = m_profiles[Gray];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ColorSpaceType::DeviceRGB:
|
||||||
|
inputProfile = m_profiles[RGB];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ColorSpaceType::DeviceCMYK:
|
||||||
|
inputProfile = m_profiles[CMYK];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ColorSpaceType::XYZ:
|
||||||
|
inputProfile = m_profiles[XYZ];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ColorSpaceType::ICC:
|
||||||
|
inputProfile = cmsOpenProfileFromMem(params.sourceIccData.data(), params.sourceIccData.size());
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Q_ASSERT(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (params.targetType)
|
||||||
|
{
|
||||||
|
case ColorSpaceType::DeviceGray:
|
||||||
|
outputProfile = m_profiles[Gray];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ColorSpaceType::DeviceRGB:
|
||||||
|
outputProfile = m_profiles[RGB];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ColorSpaceType::DeviceCMYK:
|
||||||
|
outputProfile = m_profiles[CMYK];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ColorSpaceType::XYZ:
|
||||||
|
outputProfile = m_profiles[XYZ];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ColorSpaceType::ICC:
|
||||||
|
outputProfile = cmsOpenProfileFromMem(params.targetIccData.data(), params.targetIccData.size());
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Q_ASSERT(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputProfile && outputProfile)
|
||||||
|
{
|
||||||
|
transform = cmsCreateTransform(inputProfile, getProfileDataFormat(inputProfile), outputProfile, getProfileDataFormat(outputProfile), getLittleCMSRenderingIntent(params.intent), getTransformationFlags());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.sourceType == ColorSpaceType::ICC)
|
||||||
|
{
|
||||||
|
cmsCloseProfile(inputProfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.targetType == ColorSpaceType::ICC)
|
||||||
|
{
|
||||||
|
cmsCloseProfile(outputProfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
it = m_transformColorSpaceCache.insert(std::make_pair(key, transform)).first;
|
||||||
|
}
|
||||||
|
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmsHTRANSFORM();
|
||||||
|
}
|
||||||
|
|
||||||
QString getInfoFromProfile(cmsHPROFILE profile, cmsInfoType infoType)
|
QString getInfoFromProfile(cmsHPROFILE profile, cmsInfoType infoType)
|
||||||
{
|
{
|
||||||
QLocale locale;
|
QLocale locale;
|
||||||
|
@ -220,9 +220,15 @@ public:
|
|||||||
|
|
||||||
QByteArray sourceIccData;
|
QByteArray sourceIccData;
|
||||||
QByteArray targetIccData;
|
QByteArray targetIccData;
|
||||||
|
|
||||||
|
PDFColorBuffer input;
|
||||||
|
PDFColorBuffer output;
|
||||||
|
|
||||||
|
RenderingIntent intent = RenderingIntent::Unknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Transforms color between two color spaces.
|
/// Transforms color between two color spaces. Doesn't do soft-proofing,
|
||||||
|
/// it just transforms two float buffers from input color space to output color space.
|
||||||
virtual bool transformColorSpace(const ColorSpaceTransformParams& params) const = 0;
|
virtual bool transformColorSpace(const ColorSpaceTransformParams& params) const = 0;
|
||||||
|
|
||||||
/// Get D50 white point for XYZ color space
|
/// Get D50 white point for XYZ color space
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2019-2020 Jakub Melka
|
// Copyright (C) 2019-2020 Jakub Melka
|
||||||
//
|
//
|
||||||
// This file is part of Pdf4Qt.
|
// This file is part of Pdf4Qt.
|
||||||
//
|
//
|
||||||
@ -650,6 +650,27 @@ bool PDFAbstractColorSpace::transform(const PDFAbstractColorSpace* source,
|
|||||||
Q_ASSERT(target->isBlendColorSpace());
|
Q_ASSERT(target->isBlendColorSpace());
|
||||||
Q_ASSERT(input.size() % source->getColorComponentCount() == 0);
|
Q_ASSERT(input.size() % source->getColorComponentCount() == 0);
|
||||||
|
|
||||||
|
const std::size_t sourceColors = input.size() / source->getColorComponentCount();
|
||||||
|
const std::size_t targetColors = output.size() / target->getColorComponentCount();
|
||||||
|
|
||||||
|
if (sourceColors != targetColors)
|
||||||
|
{
|
||||||
|
// This is bad input values. Function should not be called with these parameters.
|
||||||
|
// Assert and return false. We do not want crash.
|
||||||
|
Q_ASSERT(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::size_t sourceColorsRemainder = input.size() % source->getColorComponentCount();
|
||||||
|
const std::size_t targetColorsRemainder = output.size() % target->getColorComponentCount();
|
||||||
|
|
||||||
|
if (sourceColorsRemainder > 0 || targetColorsRemainder > 0)
|
||||||
|
{
|
||||||
|
// Input/output buffer size is incorrect
|
||||||
|
Q_ASSERT(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (source->equals(target))
|
if (source->equals(target))
|
||||||
{
|
{
|
||||||
// Just copy input buffer to output buffer
|
// Just copy input buffer to output buffer
|
||||||
@ -668,7 +689,9 @@ bool PDFAbstractColorSpace::transform(const PDFAbstractColorSpace* source,
|
|||||||
|
|
||||||
PDFCMS::ColorSpaceTransformParams params;
|
PDFCMS::ColorSpaceTransformParams params;
|
||||||
std::vector<PDFColorComponent> transformedInputColorsVector;
|
std::vector<PDFColorComponent> transformedInputColorsVector;
|
||||||
|
std::vector<PDFColorComponent> transformedOutputColorsVector;
|
||||||
PDFColorBuffer transformedInput = input;
|
PDFColorBuffer transformedInput = input;
|
||||||
|
PDFColorBuffer transformedOutput = output;
|
||||||
|
|
||||||
switch (source->getColorSpace())
|
switch (source->getColorSpace())
|
||||||
{
|
{
|
||||||
@ -721,15 +744,19 @@ bool PDFAbstractColorSpace::transform(const PDFAbstractColorSpace* source,
|
|||||||
|
|
||||||
for (auto sourceIt = input.cbegin(); sourceIt != input.cend(); sourceIt = std::next(sourceIt, 3))
|
for (auto sourceIt = input.cbegin(); sourceIt != input.cend(); sourceIt = std::next(sourceIt, 3))
|
||||||
{
|
{
|
||||||
const PDFColor3 ABC = { };
|
PDFColor3 ABC = { };
|
||||||
ABC[0] = *sourceIt;
|
ABC[0] = *sourceIt;
|
||||||
ABC[1] = *std::next(sourceIt, 1);
|
ABC[1] = *std::next(sourceIt, 1);
|
||||||
ABC[2] = *std::next(sourceIt, 2);
|
ABC[2] = *std::next(sourceIt, 2);
|
||||||
ABC = colorPowerByFactors(ABC, gamma);
|
ABC = colorPowerByFactors(ABC, gamma);
|
||||||
|
|
||||||
const PDFColor3 XYZ = matrix * ABC;
|
const PDFColor3 XYZ = matrix * ABC;
|
||||||
|
|
||||||
|
Q_ASSERT(it != transformedInputColorsVector.end());
|
||||||
*it++ = XYZ[0];
|
*it++ = XYZ[0];
|
||||||
|
Q_ASSERT(it != transformedInputColorsVector.end());
|
||||||
*it++ = XYZ[1];
|
*it++ = XYZ[1];
|
||||||
|
Q_ASSERT(it != transformedInputColorsVector.end());
|
||||||
*it++ = XYZ[2];
|
*it++ = XYZ[2];
|
||||||
}
|
}
|
||||||
Q_ASSERT(it == transformedInputColorsVector.end());
|
Q_ASSERT(it == transformedInputColorsVector.end());
|
||||||
@ -743,17 +770,17 @@ bool PDFAbstractColorSpace::transform(const PDFAbstractColorSpace* source,
|
|||||||
params.sourceType = PDFCMS::ColorSpaceType::XYZ;
|
params.sourceType = PDFCMS::ColorSpaceType::XYZ;
|
||||||
|
|
||||||
const PDFLabColorSpace* labColorSpace = static_cast<const PDFLabColorSpace*>(source);
|
const PDFLabColorSpace* labColorSpace = static_cast<const PDFLabColorSpace*>(source);
|
||||||
const PDFColor aMin = labColorSpace->getAMin();
|
const PDFColorComponent aMin = labColorSpace->getAMin();
|
||||||
const PDFColor aMax = labColorSpace->getAMax();
|
const PDFColorComponent aMax = labColorSpace->getAMax();
|
||||||
const PDFColor bMin = labColorSpace->getBMin();
|
const PDFColorComponent bMin = labColorSpace->getBMin();
|
||||||
const PDFColor bMax = labColorSpace->getBMax();
|
const PDFColorComponent bMax = labColorSpace->getBMax();
|
||||||
|
|
||||||
transformedInputColorsVector.resize(input.size(), 0.0f);
|
transformedInputColorsVector.resize(input.size(), 0.0f);
|
||||||
auto it = transformedInputColorsVector.begin();
|
auto it = transformedInputColorsVector.begin();
|
||||||
|
|
||||||
for (auto sourceIt = input.cbegin(); sourceIt != input.cend(); sourceIt = std::next(sourceIt, 3))
|
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 LStar = qBound<PDFColorComponent>(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 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);
|
PDFColorComponent bStar = qBound<PDFColorComponent>(bMin, interpolate(*std::next(sourceIt, 2), 0.0, 1.0, bMin, bMax), bMax);
|
||||||
|
|
||||||
@ -781,8 +808,11 @@ bool PDFAbstractColorSpace::transform(const PDFAbstractColorSpace* source,
|
|||||||
const PDFColorComponent gM = g(M);
|
const PDFColorComponent gM = g(M);
|
||||||
const PDFColorComponent gN = g(N);
|
const PDFColorComponent gN = g(N);
|
||||||
|
|
||||||
|
Q_ASSERT(it != transformedInputColorsVector.end());
|
||||||
*it++ = gL;
|
*it++ = gL;
|
||||||
|
Q_ASSERT(it != transformedInputColorsVector.end());
|
||||||
*it++ = gM;
|
*it++ = gM;
|
||||||
|
Q_ASSERT(it != transformedInputColorsVector.end());
|
||||||
*it++ = gN;
|
*it++ = gN;
|
||||||
}
|
}
|
||||||
Q_ASSERT(it == transformedInputColorsVector.end());
|
Q_ASSERT(it == transformedInputColorsVector.end());
|
||||||
@ -855,6 +885,119 @@ bool PDFAbstractColorSpace::transform(const PDFAbstractColorSpace* source,
|
|||||||
Q_ASSERT(false);
|
Q_ASSERT(false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (target->getColorSpace())
|
||||||
|
{
|
||||||
|
case ColorSpace::DeviceGray:
|
||||||
|
// Output buffer size is the same as target type
|
||||||
|
params.targetType = PDFCMS::ColorSpaceType::DeviceGray;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ColorSpace::DeviceRGB:
|
||||||
|
// Output buffer size is the same as target type
|
||||||
|
params.targetType = PDFCMS::ColorSpaceType::DeviceRGB;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ColorSpace::DeviceCMYK:
|
||||||
|
// Output buffer size is the same as target type
|
||||||
|
params.targetType = PDFCMS::ColorSpaceType::DeviceCMYK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case pdf::PDFAbstractColorSpace::ColorSpace::CalGray:
|
||||||
|
{
|
||||||
|
params.targetType = PDFCMS::ColorSpaceType::XYZ;
|
||||||
|
transformedOutputColorsVector.resize(output.size() * 3, 0.0f);
|
||||||
|
transformedOutput = PDFColorBuffer(transformedOutputColorsVector.data(), transformedOutputColorsVector.size());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case pdf::PDFAbstractColorSpace::ColorSpace::CalRGB:
|
||||||
|
{
|
||||||
|
// Output buffer size is the same as target type
|
||||||
|
params.targetType = PDFCMS::ColorSpaceType::XYZ;
|
||||||
|
transformedOutputColorsVector.resize(output.size(), 0.0f);
|
||||||
|
transformedOutput = PDFColorBuffer(transformedOutputColorsVector.data(), transformedOutputColorsVector.size());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case pdf::PDFAbstractColorSpace::ColorSpace::ICCBased:
|
||||||
|
{
|
||||||
|
const PDFICCBasedColorSpace* iccBasedColorSpace = static_cast<const PDFICCBasedColorSpace*>(target);
|
||||||
|
|
||||||
|
params.targetType = PDFCMS::ColorSpaceType::ICC;
|
||||||
|
params.targetIccId = iccBasedColorSpace->getIccProfileDataChecksum();
|
||||||
|
params.targetIccData = iccBasedColorSpace->getIccProfileData();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
Q_ASSERT(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
params.input = transformedInput;
|
||||||
|
params.output = transformedOutput;
|
||||||
|
|
||||||
|
cms->transformColorSpace(params);
|
||||||
|
|
||||||
|
switch (target->getColorSpace())
|
||||||
|
{
|
||||||
|
case pdf::PDFAbstractColorSpace::ColorSpace::CalGray:
|
||||||
|
{
|
||||||
|
const PDFCalGrayColorSpace* calGrayColorSpace = static_cast<const PDFCalGrayColorSpace*>(source);
|
||||||
|
const PDFColorComponent gamma = 1.0 / calGrayColorSpace->getGamma();
|
||||||
|
|
||||||
|
auto outputIt = output.begin();
|
||||||
|
for (auto transformedOutputIt = transformedOutput.cbegin(); transformedOutputIt != transformedOutput.cend(); transformedOutputIt = std::next(transformedOutputIt, 3))
|
||||||
|
{
|
||||||
|
PDFColor3 XYZ = { };
|
||||||
|
XYZ[0] = *transformedOutputIt;
|
||||||
|
XYZ[1] = *std::next(transformedOutputIt, 1);
|
||||||
|
XYZ[2] = *std::next(transformedOutputIt, 2);
|
||||||
|
|
||||||
|
const PDFColorComponent gray = (XYZ[0] + XYZ[1] + XYZ[2]) * 0.333333333333333;
|
||||||
|
const PDFColorComponent grayWithGamma = std::powf(gray, gamma);
|
||||||
|
*outputIt++ = grayWithGamma;
|
||||||
|
}
|
||||||
|
Q_ASSERT(outputIt == output.cend());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case pdf::PDFAbstractColorSpace::ColorSpace::CalRGB:
|
||||||
|
{
|
||||||
|
const PDFCalRGBColorSpace* calRGBColorSpace = static_cast<const PDFCalRGBColorSpace*>(source);
|
||||||
|
const PDFColor3 gammaForward = calRGBColorSpace->getGamma();
|
||||||
|
const PDFColorComponentMatrix_3x3 matrixForward = calRGBColorSpace->getMatrix();
|
||||||
|
const PDFColor3 gammaInverse = PDFColor3{ 1.0f / gammaForward[0], 1.0f / gammaForward[1], 1.0f / gammaForward[2] };
|
||||||
|
const PDFColorComponentMatrix_3x3 matrixInverse = getInverseMatrix(matrixForward);
|
||||||
|
|
||||||
|
auto outputIt = output.begin();
|
||||||
|
for (auto transformedOutputIt = transformedOutput.cbegin(); transformedOutputIt != transformedOutput.cend(); transformedOutputIt = std::next(transformedOutputIt, 3))
|
||||||
|
{
|
||||||
|
PDFColor3 XYZ = { };
|
||||||
|
XYZ[0] = *transformedOutputIt;
|
||||||
|
XYZ[1] = *std::next(transformedOutputIt, 1);
|
||||||
|
XYZ[2] = *std::next(transformedOutputIt, 2);
|
||||||
|
|
||||||
|
const PDFColor3 RGB = matrixInverse * XYZ;
|
||||||
|
const PDFColor3 RGBwithGamma = colorPowerByFactors(RGB, gammaInverse);
|
||||||
|
|
||||||
|
Q_ASSERT(outputIt != output.end());
|
||||||
|
*outputIt++ = RGBwithGamma[0];
|
||||||
|
Q_ASSERT(outputIt != output.end());
|
||||||
|
*outputIt++ = RGBwithGamma[1];
|
||||||
|
Q_ASSERT(outputIt != output.end());
|
||||||
|
*outputIt++ = RGBwithGamma[2];
|
||||||
|
}
|
||||||
|
Q_ASSERT(outputIt == output.cend());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
PDFColorSpacePointer PDFAbstractColorSpace::createColorSpaceImpl(const PDFDictionary* colorSpaceDictionary,
|
PDFColorSpacePointer PDFAbstractColorSpace::createColorSpaceImpl(const PDFDictionary* colorSpaceDictionary,
|
||||||
|
Reference in New Issue
Block a user