Create mapped color

This commit is contained in:
Jakub Melka
2021-02-05 19:45:58 +01:00
parent 975bdba6f6
commit 3fdbcfb4c5
2 changed files with 291 additions and 4 deletions

View File

@ -499,7 +499,7 @@ void PDFFloatBitmapWithColorSpace::convertToColorSpace(const PDFCMS* cms,
if (!PDFAbstractColorSpace::transform(m_colorSpace.data(), targetColorSpace.data(), cms, intent, sourceProcessColors.getPixels(), targetProcessColors.getPixels(), reporter)) if (!PDFAbstractColorSpace::transform(m_colorSpace.data(), targetColorSpace.data(), cms, intent, sourceProcessColors.getPixels(), targetProcessColors.getPixels(), reporter))
{ {
reporter->reportRenderError(RenderErrorType::Error, PDFTranslationContext::tr("Transformation between blending color spaces failed.")); reporter->reportRenderError(RenderErrorType::Error, PDFTranslationContext::tr("Transformation between blending color space failed."));
} }
PDFFloatBitmapWithColorSpace temporary(getWidth(), getHeight(), newFormat, targetColorSpace); PDFFloatBitmapWithColorSpace temporary(getWidth(), getHeight(), newFormat, targetColorSpace);
@ -615,6 +615,8 @@ const PDFFloatBitmap& PDFTransparencyRenderer::endPaint()
void PDFTransparencyRenderer::performPathPainting(const QPainterPath& path, bool stroke, bool fill, bool text, Qt::FillRule fillRule) void PDFTransparencyRenderer::performPathPainting(const QPainterPath& path, bool stroke, bool fill, bool text, Qt::FillRule fillRule)
{ {
auto mappedStrokeColor = getMappedStrokeColor();
auto mappedFillColor = getMappedFillColor();
} }
void PDFTransparencyRenderer::performClipping(const QPainterPath& path, Qt::FillRule fillRule) void PDFTransparencyRenderer::performClipping(const QPainterPath& path, Qt::FillRule fillRule)
@ -635,7 +637,24 @@ void PDFTransparencyRenderer::performClipping(const QPainterPath& path, Qt::Fill
void PDFTransparencyRenderer::performUpdateGraphicsState(const PDFPageContentProcessorState& state) void PDFTransparencyRenderer::performUpdateGraphicsState(const PDFPageContentProcessorState& state)
{ {
Q_UNUSED(state); PDFPageContentProcessorState::StateFlags stateFlags = state.getStateFlags();
const bool colorTransformAffected = stateFlags.testFlag(PDFPageContentProcessorState::StateRenderingIntent) ||
stateFlags.testFlag(PDFPageContentProcessorState::StateBlackPointCompensation);
if (colorTransformAffected ||
stateFlags.testFlag(PDFPageContentProcessorState::StateStrokeColor) ||
stateFlags.testFlag(PDFPageContentProcessorState::StateStrokeColorSpace))
{
m_mappedStrokeColor.dirty();
}
if (colorTransformAffected ||
stateFlags.testFlag(PDFPageContentProcessorState::StateFillColor) ||
stateFlags.testFlag(PDFPageContentProcessorState::StateFillColorSpace))
{
m_mappedFillColor.dirty();
}
} }
void PDFTransparencyRenderer::performSaveGraphicState(ProcessOrder order) void PDFTransparencyRenderer::performSaveGraphicState(ProcessOrder order)
@ -716,6 +735,7 @@ void PDFTransparencyRenderer::performBeginTransparencyGroup(ProcessOrder order,
data.immediateBackdrop.makeTransparent(); data.immediateBackdrop.makeTransparent();
m_transparencyGroupDataStack.emplace_back(qMove(data)); m_transparencyGroupDataStack.emplace_back(qMove(data));
invalidateCachedItems();
} }
} }
@ -736,6 +756,8 @@ void PDFTransparencyRenderer::performEndTransparencyGroup(ProcessOrder order, co
PDFFloatBitmap::blend(sourceData.immediateBackdrop, targetData.immediateBackdrop, *getBackdrop(), *getInitialBackdrop(), sourceData.softMask, PDFFloatBitmap::blend(sourceData.immediateBackdrop, targetData.immediateBackdrop, *getBackdrop(), *getInitialBackdrop(), sourceData.softMask,
sourceData.alphaIsShape, sourceData.alphaFill, BlendMode::Normal, targetData.group.knockout, 0xFFFF, PDFFloatBitmap::OverprintMode::NoOveprint); sourceData.alphaIsShape, sourceData.alphaFill, BlendMode::Normal, targetData.group.knockout, 0xFFFF, PDFFloatBitmap::OverprintMode::NoOveprint);
invalidateCachedItems();
} }
} }
@ -759,6 +781,12 @@ PDFReal PDFTransparencyRenderer::getOpacityFilling() const
return !getGraphicState()->getAlphaIsShape() ? getGraphicState()->getAlphaFilling() : 1.0; return !getGraphicState()->getAlphaIsShape() ? getGraphicState()->getAlphaFilling() : 1.0;
} }
void PDFTransparencyRenderer::invalidateCachedItems()
{
m_mappedStrokeColor.dirty();
m_mappedFillColor.dirty();
}
void PDFTransparencyRenderer::removeInitialBackdrop() void PDFTransparencyRenderer::removeInitialBackdrop()
{ {
PDFFloatBitmapWithColorSpace* immediateBackdrop = getImmediateBackdrop(); PDFFloatBitmapWithColorSpace* immediateBackdrop = getImmediateBackdrop();
@ -835,6 +863,125 @@ bool PDFTransparencyRenderer::isTransparencyGroupKnockout() const
return m_transparencyGroupDataStack.back().group.knockout; return m_transparencyGroupDataStack.back().group.knockout;
} }
const PDFTransparencyRenderer::PDFMappedColor& PDFTransparencyRenderer::getMappedStrokeColor()
{
return m_mappedStrokeColor.get(this, &PDFTransparencyRenderer::getMappedStrokeColorImpl);
}
const PDFTransparencyRenderer::PDFMappedColor& PDFTransparencyRenderer::getMappedFillColor()
{
return m_mappedFillColor.get(this, &PDFTransparencyRenderer::getMappedFillColorImpl);
}
void PDFTransparencyRenderer::fillMappedColorUsingMapping(const PDFPixelFormat pixelFormat,
PDFMappedColor& result,
const PDFInkMapping& inkMapping,
const PDFColor& sourceColor)
{
result.mappedColor.resize(pixelFormat.getColorChannelCount());
// Zero the color
for (size_t i = 0; i < pixelFormat.getColorChannelCount(); ++i)
{
result.mappedColor[i] = 0.0f;
}
for (const PDFInkMapping::Mapping& ink : inkMapping.mapping)
{
// Sanity check of source color
if (ink.source >= sourceColor.size())
{
reportRenderError(RenderErrorType::Error, PDFTranslationContext::tr("Invalid source ink index %1.").arg(ink.source));
continue;
}
// Sanity check of target color
if (ink.target >= result.mappedColor.size())
{
reportRenderError(RenderErrorType::Error, PDFTranslationContext::tr("Invalid target ink index %1.").arg(ink.target));
continue;
}
switch (ink.type)
{
case pdf::PDFInkMapping::Pass:
result.mappedColor[ink.target] = sourceColor[ink.source];
break;
default:
Q_ASSERT(false);
break;
}
}
result.activeChannels = inkMapping.activeChannels;
}
PDFTransparencyRenderer::PDFMappedColor PDFTransparencyRenderer::createMappedColor(const PDFColor& sourceColor, const PDFAbstractColorSpace* sourceColorSpace)
{
PDFMappedColor result;
const PDFAbstractColorSpace* targetColorSpace = getBlendColorSpace().data();
const PDFPixelFormat pixelFormat = getImmediateBackdrop()->getPixelFormat();
Q_ASSERT(targetColorSpace->equals(getImmediateBackdrop()->getColorSpace().data()));
PDFInkMapping inkMapping = m_inkMapper->createMapping(sourceColorSpace, targetColorSpace, pixelFormat);
if (inkMapping.isValid())
{
fillMappedColorUsingMapping(pixelFormat, result, inkMapping, sourceColor);
}
else
{
// Jakub Melka: We must convert color form source color space to target color space
std::vector<PDFColorComponent> sourceColorVector(sourceColor.size(), 0.0f);
for (size_t i = 0; i < sourceColorVector.size(); ++i)
{
sourceColorVector[i] = sourceColor[i];
}
PDFColorBuffer sourceColorBuffer(sourceColorVector.data(), sourceColorVector.size());
std::vector<PDFColorComponent> targetColorVector(pixelFormat.getProcessColorChannelCount(), 0.0f);
PDFColorBuffer targetColorBuffer(targetColorVector.data(), targetColorVector.size());
if (!PDFAbstractColorSpace::transform(sourceColorSpace, targetColorSpace, getCMS(), getGraphicState()->getRenderingIntent(), sourceColorBuffer, targetColorBuffer, this))
{
reportRenderError(RenderErrorType::Error, PDFTranslationContext::tr("Transformation from source color space to target blending color space failed."));
}
PDFColor adjustedSourceColor;
adjustedSourceColor.resize(targetColorBuffer.size());
for (size_t i = 0; i < targetColorBuffer.size(); ++i)
{
adjustedSourceColor[i] = targetColorBuffer[i];
}
inkMapping = m_inkMapper->createMapping(targetColorSpace, targetColorSpace, pixelFormat);
Q_ASSERT(inkMapping.isValid());
fillMappedColorUsingMapping(pixelFormat, result, inkMapping, adjustedSourceColor);
}
return result;
}
PDFTransparencyRenderer::PDFMappedColor PDFTransparencyRenderer::getMappedStrokeColorImpl()
{
const PDFAbstractColorSpace* sourceColorSpace = getGraphicState()->getStrokeColorSpace();
const PDFColor& sourceColor = getGraphicState()->getStrokeColorOriginal();
return createMappedColor(sourceColor, sourceColorSpace);
}
PDFTransparencyRenderer::PDFMappedColor PDFTransparencyRenderer::getMappedFillColorImpl()
{
const PDFAbstractColorSpace* sourceColorSpace = getGraphicState()->getFillColorSpace();
const PDFColor& sourceColor = getGraphicState()->getFillColorOriginal();
return createMappedColor(sourceColor, sourceColorSpace);
}
PDFInkMapper::PDFInkMapper(const PDFDocument* document) : PDFInkMapper::PDFInkMapper(const PDFDocument* document) :
m_document(document) m_document(document)
{ {
@ -892,6 +1039,7 @@ void PDFInkMapper::createSpotColors(bool activate)
SpotColorInfo info; SpotColorInfo info;
info.name = colorName; info.name = colorName;
info.colorSpace = colorSpacePointer; info.colorSpace = colorSpacePointer;
info.spotColorIndex = uint32_t(m_spotColors.size());
m_spotColors.emplace_back(qMove(info)); m_spotColors.emplace_back(qMove(info));
} }
} }
@ -913,8 +1061,9 @@ void PDFInkMapper::createSpotColors(bool activate)
{ {
SpotColorInfo info; SpotColorInfo info;
info.name = colorantInfo.name; info.name = colorantInfo.name;
info.index = uint32_t(i); info.colorSpaceIndex = uint32_t(i);
info.colorSpace = colorSpacePointer; info.colorSpace = colorSpacePointer;
info.spotColorIndex = uint32_t(m_spotColors.size());
m_spotColors.emplace_back(qMove(info)); m_spotColors.emplace_back(qMove(info));
} }
} }
@ -958,4 +1107,83 @@ const PDFInkMapper::SpotColorInfo* PDFInkMapper::getSpotColor(const QByteArray&
return nullptr; return nullptr;
} }
PDFInkMapping PDFInkMapper::createMapping(const PDFAbstractColorSpace* sourceColorSpace,
const PDFAbstractColorSpace* targetColorSpace,
PDFPixelFormat targetPixelFormat) const
{
PDFInkMapping mapping;
Q_ASSERT(targetColorSpace->getColorComponentCount() == targetPixelFormat.getProcessColorChannelCount());
if (sourceColorSpace->equals(targetColorSpace))
{
Q_ASSERT(sourceColorSpace->getColorComponentCount() == targetColorSpace->getColorComponentCount());
uint8_t colorCount = uint8_t(targetColorSpace->getColorComponentCount());
mapping.mapping.reserve(colorCount);
for (uint8_t i = 0; i < colorCount; ++i)
{
mapping.map(i, i);
}
}
else
{
switch (sourceColorSpace->getColorSpace())
{
case PDFAbstractColorSpace::ColorSpace::Separation:
{
const PDFSeparationColorSpace* separationColorSpace = dynamic_cast<const PDFSeparationColorSpace*>(sourceColorSpace);
if (separationColorSpace->isAll())
{
// Map this color to all device colors
uint32_t colorCount = static_cast<uint32_t>(targetColorSpace->getColorComponentCount());
mapping.mapping.reserve(colorCount);
for (size_t i = 0; i < colorCount; ++i)
{
mapping.map(0, uint8_t(i));
}
}
else if (!separationColorSpace->isNone() && !separationColorSpace->getColorName().isEmpty())
{
const QByteArray& colorName = separationColorSpace->getColorName();
const SpotColorInfo* info = getSpotColor(colorName);
if (info && info->active && targetPixelFormat.hasSpotColors() && info->spotColorIndex < targetPixelFormat.getSpotColorChannelCount())
{
mapping.map(0, uint8_t(targetPixelFormat.getSpotColorChannelIndexStart() + info->spotColorIndex));
}
}
break;
}
case PDFAbstractColorSpace::ColorSpace::DeviceN:
{
const PDFDeviceNColorSpace* deviceNColorSpace = dynamic_cast<const PDFDeviceNColorSpace*>(sourceColorSpace);
if (!deviceNColorSpace->isNone())
{
const PDFDeviceNColorSpace::Colorants& colorants = deviceNColorSpace->getColorants();
for (size_t i = 0; i < colorants.size(); ++i)
{
const PDFDeviceNColorSpace::ColorantInfo& colorantInfo = colorants[i];
const SpotColorInfo* info = getSpotColor(colorantInfo.name);
if (info && info->active && targetPixelFormat.hasSpotColors() && info->spotColorIndex < targetPixelFormat.getSpotColorChannelCount())
{
mapping.map(uint8_t(i), uint8_t(targetColorSpace->getColorComponentCount() + info->spotColorIndex));
}
}
}
break;
}
}
}
return mapping;
}
} // namespace pdf } // namespace pdf

View File

@ -22,6 +22,7 @@
#include "pdfcolorspaces.h" #include "pdfcolorspaces.h"
#include "pdfpagecontentprocessor.h" #include "pdfpagecontentprocessor.h"
#include "pdfconstants.h" #include "pdfconstants.h"
#include "pdfutils.h"
namespace pdf namespace pdf
{ {
@ -236,6 +237,29 @@ private:
PDFColorSpacePointer m_colorSpace; PDFColorSpacePointer m_colorSpace;
}; };
/// Ink mapping
struct PDFInkMapping
{
inline bool isValid() const { return !mapping.empty(); }
inline void reserve(size_t size) { mapping.reserve(size); }
inline void map(uint8_t source, uint8_t target) { mapping.emplace_back(Mapping{ source, target, Pass}); activeChannels |= 1 << target; }
enum Type
{
Pass
};
struct Mapping
{
uint8_t source = 0;
uint8_t target = 0;
Type type = Pass;
};
std::vector<Mapping> mapping;
uint32_t activeChannels = 0;
};
/// Ink mapper for mapping device inks (device colors) and spot inks (spot colors). /// Ink mapper for mapping device inks (device colors) and spot inks (spot colors).
class Pdf4QtLIBSHARED_EXPORT PDFInkMapper class Pdf4QtLIBSHARED_EXPORT PDFInkMapper
{ {
@ -245,7 +269,8 @@ public:
struct SpotColorInfo struct SpotColorInfo
{ {
QByteArray name; QByteArray name;
uint32_t index = 0; ///< Index into DeviceN color space (index of colorant) uint32_t spotColorIndex = 0; ///< Index of this spot color
uint32_t colorSpaceIndex = 0; ///< Index into DeviceN color space (index of colorant)
PDFColorSpacePointer colorSpace; PDFColorSpacePointer colorSpace;
bool active = false; ///< Is spot color active? bool active = false; ///< Is spot color active?
}; };
@ -269,6 +294,17 @@ public:
/// \param colorName Color name /// \param colorName Color name
const SpotColorInfo* getSpotColor(const QByteArray& colorName) const; const SpotColorInfo* getSpotColor(const QByteArray& colorName) const;
/// Creates color mapping from source color space to the target color space.
/// If mapping cannot be created, then invalid mapping is returned. Target
/// color space must be blending color space and must correspond to active
/// blending space, if used when painting.
/// \param sourceColorSpace Source color space
/// \param targetColorSpace Target color space
/// \param targetPixelFormat
PDFInkMapping createMapping(const PDFAbstractColorSpace* sourceColorSpace,
const PDFAbstractColorSpace* targetColorSpace,
PDFPixelFormat targetPixelFormat) const;
private: private:
const PDFDocument* m_document; const PDFDocument* m_document;
@ -352,8 +388,23 @@ private:
QPainterPath clipPath; ///< Clipping path in device state coordinates QPainterPath clipPath; ///< Clipping path in device state coordinates
}; };
struct PDFMappedColor
{
PDFColor mappedColor;
uint32_t activeChannels = 0;
};
void invalidateCachedItems();
void removeInitialBackdrop(); void removeInitialBackdrop();
void fillMappedColorUsingMapping(const PDFPixelFormat pixelFormat,
PDFMappedColor& result,
const PDFInkMapping& inkMapping,
const PDFColor& sourceColor);
PDFMappedColor createMappedColor(const PDFColor& sourceColor,
const PDFAbstractColorSpace* sourceColorSpace);
PDFFloatBitmapWithColorSpace* getInitialBackdrop(); PDFFloatBitmapWithColorSpace* getInitialBackdrop();
PDFFloatBitmapWithColorSpace* getImmediateBackdrop(); PDFFloatBitmapWithColorSpace* getImmediateBackdrop();
PDFFloatBitmapWithColorSpace* getBackdrop(); PDFFloatBitmapWithColorSpace* getBackdrop();
@ -364,6 +415,12 @@ private:
bool isTransparencyGroupIsolated() const; bool isTransparencyGroupIsolated() const;
bool isTransparencyGroupKnockout() const; bool isTransparencyGroupKnockout() const;
const PDFMappedColor& getMappedStrokeColor();
const PDFMappedColor& getMappedFillColor();
PDFMappedColor getMappedStrokeColorImpl();
PDFMappedColor getMappedFillColorImpl();
PDFColorSpacePointer m_deviceColorSpace; ///< Device color space (color space for final result) PDFColorSpacePointer m_deviceColorSpace; ///< Device color space (color space for final result)
PDFColorSpacePointer m_processColorSpace; ///< Process color space (color space, in which is page graphic's blended) PDFColorSpacePointer m_processColorSpace; ///< Process color space (color space, in which is page graphic's blended)
std::unique_ptr<PDFTransparencyGroupGuard> m_pageTransparencyGroupGuard; std::unique_ptr<PDFTransparencyGroupGuard> m_pageTransparencyGroupGuard;
@ -371,6 +428,8 @@ private:
std::stack<PDFTransparencyPainterState> m_painterStateStack; std::stack<PDFTransparencyPainterState> m_painterStateStack;
const PDFInkMapper* m_inkMapper; const PDFInkMapper* m_inkMapper;
bool m_active; bool m_active;
PDFCachedItem<PDFMappedColor> m_mappedStrokeColor;
PDFCachedItem<PDFMappedColor> m_mappedFillColor;
}; };
} // namespace pdf } // namespace pdf