mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Create mapped color
This commit is contained in:
@ -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))
|
||||
{
|
||||
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);
|
||||
@ -615,6 +615,8 @@ const PDFFloatBitmap& PDFTransparencyRenderer::endPaint()
|
||||
|
||||
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)
|
||||
@ -635,7 +637,24 @@ void PDFTransparencyRenderer::performClipping(const QPainterPath& path, Qt::Fill
|
||||
|
||||
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)
|
||||
@ -716,6 +735,7 @@ void PDFTransparencyRenderer::performBeginTransparencyGroup(ProcessOrder order,
|
||||
data.immediateBackdrop.makeTransparent();
|
||||
|
||||
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,
|
||||
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;
|
||||
}
|
||||
|
||||
void PDFTransparencyRenderer::invalidateCachedItems()
|
||||
{
|
||||
m_mappedStrokeColor.dirty();
|
||||
m_mappedFillColor.dirty();
|
||||
}
|
||||
|
||||
void PDFTransparencyRenderer::removeInitialBackdrop()
|
||||
{
|
||||
PDFFloatBitmapWithColorSpace* immediateBackdrop = getImmediateBackdrop();
|
||||
@ -835,6 +863,125 @@ bool PDFTransparencyRenderer::isTransparencyGroupKnockout() const
|
||||
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) :
|
||||
m_document(document)
|
||||
{
|
||||
@ -892,6 +1039,7 @@ void PDFInkMapper::createSpotColors(bool activate)
|
||||
SpotColorInfo info;
|
||||
info.name = colorName;
|
||||
info.colorSpace = colorSpacePointer;
|
||||
info.spotColorIndex = uint32_t(m_spotColors.size());
|
||||
m_spotColors.emplace_back(qMove(info));
|
||||
}
|
||||
}
|
||||
@ -913,8 +1061,9 @@ void PDFInkMapper::createSpotColors(bool activate)
|
||||
{
|
||||
SpotColorInfo info;
|
||||
info.name = colorantInfo.name;
|
||||
info.index = uint32_t(i);
|
||||
info.colorSpaceIndex = uint32_t(i);
|
||||
info.colorSpace = colorSpacePointer;
|
||||
info.spotColorIndex = uint32_t(m_spotColors.size());
|
||||
m_spotColors.emplace_back(qMove(info));
|
||||
}
|
||||
}
|
||||
@ -958,4 +1107,83 @@ const PDFInkMapper::SpotColorInfo* PDFInkMapper::getSpotColor(const QByteArray&
|
||||
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
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "pdfcolorspaces.h"
|
||||
#include "pdfpagecontentprocessor.h"
|
||||
#include "pdfconstants.h"
|
||||
#include "pdfutils.h"
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
@ -236,6 +237,29 @@ private:
|
||||
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).
|
||||
class Pdf4QtLIBSHARED_EXPORT PDFInkMapper
|
||||
{
|
||||
@ -245,7 +269,8 @@ public:
|
||||
struct SpotColorInfo
|
||||
{
|
||||
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;
|
||||
bool active = false; ///< Is spot color active?
|
||||
};
|
||||
@ -269,6 +294,17 @@ public:
|
||||
/// \param colorName Color name
|
||||
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:
|
||||
|
||||
const PDFDocument* m_document;
|
||||
@ -352,8 +388,23 @@ private:
|
||||
QPainterPath clipPath; ///< Clipping path in device state coordinates
|
||||
};
|
||||
|
||||
struct PDFMappedColor
|
||||
{
|
||||
PDFColor mappedColor;
|
||||
uint32_t activeChannels = 0;
|
||||
};
|
||||
|
||||
void invalidateCachedItems();
|
||||
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* getImmediateBackdrop();
|
||||
PDFFloatBitmapWithColorSpace* getBackdrop();
|
||||
@ -364,6 +415,12 @@ private:
|
||||
bool isTransparencyGroupIsolated() 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_processColorSpace; ///< Process color space (color space, in which is page graphic's blended)
|
||||
std::unique_ptr<PDFTransparencyGroupGuard> m_pageTransparencyGroupGuard;
|
||||
@ -371,6 +428,8 @@ private:
|
||||
std::stack<PDFTransparencyPainterState> m_painterStateStack;
|
||||
const PDFInkMapper* m_inkMapper;
|
||||
bool m_active;
|
||||
PDFCachedItem<PDFMappedColor> m_mappedStrokeColor;
|
||||
PDFCachedItem<PDFMappedColor> m_mappedFillColor;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
Reference in New Issue
Block a user