mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Overprint
This commit is contained in:
@ -84,25 +84,27 @@ bool PDFBlendModeInfo::isSeparable(BlendMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case BlendMode::Normal:
|
||||
case BlendMode::Multiply:
|
||||
case BlendMode::Screen:
|
||||
case BlendMode::Overlay:
|
||||
case BlendMode::Darken:
|
||||
case BlendMode::Lighten:
|
||||
case BlendMode::ColorDodge:
|
||||
case BlendMode::ColorBurn:
|
||||
case BlendMode::HardLight:
|
||||
case BlendMode::SoftLight:
|
||||
case BlendMode::Difference:
|
||||
case BlendMode::Exclusion:
|
||||
case BlendMode::Compatible:
|
||||
case BlendMode::Normal:
|
||||
case BlendMode::Multiply:
|
||||
case BlendMode::Screen:
|
||||
case BlendMode::Overlay:
|
||||
case BlendMode::Darken:
|
||||
case BlendMode::Lighten:
|
||||
case BlendMode::ColorDodge:
|
||||
case BlendMode::ColorBurn:
|
||||
case BlendMode::HardLight:
|
||||
case BlendMode::SoftLight:
|
||||
case BlendMode::Difference:
|
||||
case BlendMode::Exclusion:
|
||||
case BlendMode::Compatible:
|
||||
case BlendMode::Overprint_SelectBackdrop:
|
||||
case BlendMode::Overprint_SelectNonZeroSourceOrBackdrop:
|
||||
return true;
|
||||
|
||||
case BlendMode::Hue:
|
||||
case BlendMode::Saturation:
|
||||
case BlendMode::Color:
|
||||
case BlendMode::Luminosity:
|
||||
case BlendMode::Hue:
|
||||
case BlendMode::Saturation:
|
||||
case BlendMode::Color:
|
||||
case BlendMode::Luminosity:
|
||||
return false;
|
||||
|
||||
default:
|
||||
@ -339,6 +341,19 @@ PDFColorComponent PDFBlendFunction::blend(BlendMode mode, PDFColorComponent Cb,
|
||||
case BlendMode::Exclusion:
|
||||
return Cb + Cs - 2.0f * Cb * Cs;
|
||||
|
||||
case BlendMode::Overprint_SelectBackdrop:
|
||||
return Cb;
|
||||
|
||||
case BlendMode::Overprint_SelectNonZeroSourceOrBackdrop:
|
||||
{
|
||||
if (qFuzzyIsNull(Cs))
|
||||
{
|
||||
return Cb;
|
||||
}
|
||||
|
||||
return Cs;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
|
@ -51,6 +51,10 @@ enum class BlendMode
|
||||
// to normal. It should be recognized for sake of compatibility.
|
||||
Compatible, ///< Equals to normal
|
||||
|
||||
// Special blend modes for handling overprint. Used only internally.
|
||||
Overprint_SelectBackdrop,
|
||||
Overprint_SelectNonZeroSourceOrBackdrop,
|
||||
|
||||
// Invalid blending mode - for internal purposes only
|
||||
Invalid
|
||||
};
|
||||
|
@ -44,6 +44,7 @@ class PDFRenderErrorReporter;
|
||||
using PDFColor = PDFFlatArray<PDFColorComponent, 4>;
|
||||
using PDFColorSpacePointer = QSharedPointer<PDFAbstractColorSpace>;
|
||||
using PDFColorBuffer = PDFBuffer<PDFColorComponent>;
|
||||
using PDFConstColorBuffer = PDFBuffer<const PDFColorComponent>;
|
||||
|
||||
static constexpr const int COLOR_SPACE_MAX_LEVEL_OF_RECURSION = 12;
|
||||
|
||||
|
@ -47,6 +47,12 @@ PDFColorBuffer PDFFloatBitmap::getPixel(size_t x, size_t y)
|
||||
return PDFColorBuffer(m_data.data() + index, m_pixelSize);
|
||||
}
|
||||
|
||||
PDFConstColorBuffer PDFFloatBitmap::getPixel(size_t x, size_t y) const
|
||||
{
|
||||
const size_t index = getPixelIndex(x, y);
|
||||
return PDFConstColorBuffer(m_data.data() + index, m_pixelSize);
|
||||
}
|
||||
|
||||
PDFColorBuffer PDFFloatBitmap::getPixels()
|
||||
{
|
||||
return PDFColorBuffer(m_data.data(), m_data.size());
|
||||
@ -130,7 +136,9 @@ void PDFFloatBitmap::blend(const PDFFloatBitmap& source,
|
||||
PDFFloatBitmap& softMask,
|
||||
bool alphaIsShape,
|
||||
PDFColorComponent constantAlpha,
|
||||
BlendMode mode)
|
||||
BlendMode mode,
|
||||
uint32_t activeColorChannels,
|
||||
OverprintMode overprintMode)
|
||||
{
|
||||
Q_ASSERT(source.getWidth() == target.getWidth());
|
||||
Q_ASSERT(source.getHeight() == target.getHeight());
|
||||
@ -146,16 +154,104 @@ void PDFFloatBitmap::blend(const PDFFloatBitmap& source,
|
||||
const uint8_t opacityChannel = pixelFormat.getOpacityChannelIndex();
|
||||
const uint8_t colorChannelStart = pixelFormat.getColorChannelIndexStart();
|
||||
const uint8_t colorChannelEnd = pixelFormat.getColorChannelIndexEnd();
|
||||
const uint8_t processColorChannelStart = pixelFormat.getProcessColorChannelIndexStart();
|
||||
const uint8_t processColorChannelEnd = pixelFormat.getProcessColorChannelIndexEnd();
|
||||
const uint8_t spotColorChannelStart = pixelFormat.getSpotColorChannelIndexStart();
|
||||
const uint8_t spotColorChannelEnd = pixelFormat.getSpotColorChannelIndexEnd();
|
||||
std::vector<PDFColorComponent> B_i(source.getPixelSize(), 0.0f);
|
||||
std::vector<BlendMode> channelBlendModes(source.getPixelSize(), mode);
|
||||
|
||||
// For blending spot colors, only white preserving blend modes are possible.
|
||||
// If this is not the case, revert spot color blend mode to normal blending.
|
||||
// See 11.7.4.2 of PDF 2.0 specification.
|
||||
if (pixelFormat.hasSpotColors() && !PDFBlendModeInfo::isWhitePreserving(mode))
|
||||
{
|
||||
auto itBegin = std::next(channelBlendModes.begin(), spotColorChannelStart);
|
||||
auto itEnd = std::next(channelBlendModes.begin(), spotColorChannelEnd);
|
||||
std::fill(itBegin, itEnd, BlendMode::Normal);
|
||||
}
|
||||
|
||||
// Handle overprint mode for normal blend mode. We do not support
|
||||
// oveprinting for other blend modes, than normal.
|
||||
|
||||
switch (overprintMode)
|
||||
{
|
||||
case OverprintMode::NoOveprint:
|
||||
break;
|
||||
|
||||
case OverprintMode::Overprint_Mode_0:
|
||||
{
|
||||
// Select source color, if channel is active,
|
||||
// otherwise select backdrop color.
|
||||
for (uint8_t colorChannelIndex = colorChannelStart; colorChannelIndex < colorChannelEnd; ++colorChannelIndex)
|
||||
{
|
||||
uint32_t flag = (static_cast<uint32_t>(1)) << colorChannelIndex;
|
||||
if (channelBlendModes[colorChannelIndex] == BlendMode::Normal && !(activeColorChannels & flag))
|
||||
{
|
||||
// Color channel is inactive
|
||||
channelBlendModes[colorChannelIndex] = BlendMode::Overprint_SelectBackdrop;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case OverprintMode::Overprint_Mode_1:
|
||||
{
|
||||
// For process colors, select source color, if it is nonzero,
|
||||
// otherwise select backdrop. If process color channel is inactive,
|
||||
// select backdrop.
|
||||
if (pixelFormat.hasProcessColors() && mode == BlendMode::Normal)
|
||||
{
|
||||
for (uint8_t colorChannelIndex = processColorChannelStart; colorChannelIndex < processColorChannelEnd; ++colorChannelIndex)
|
||||
{
|
||||
uint32_t flag = (static_cast<uint32_t>(1)) << colorChannelIndex;
|
||||
if (!(activeColorChannels & flag))
|
||||
{
|
||||
// Color channel is inactive
|
||||
channelBlendModes[colorChannelIndex] = BlendMode::Overprint_SelectBackdrop;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Color channel is active, but select source color only, if it is nonzero
|
||||
channelBlendModes[colorChannelIndex] = BlendMode::Overprint_SelectNonZeroSourceOrBackdrop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pixelFormat.hasSpotColors())
|
||||
{
|
||||
// For spot colors, select backdrop, if channel is inactive,
|
||||
// otherwise select source color.
|
||||
for (uint8_t colorChannelIndex = spotColorChannelStart; colorChannelIndex < spotColorChannelEnd; ++colorChannelIndex)
|
||||
{
|
||||
uint32_t flag = (static_cast<uint32_t>(1)) << colorChannelIndex;
|
||||
if (channelBlendModes[colorChannelIndex] == BlendMode::Normal && !(activeColorChannels & flag))
|
||||
{
|
||||
// Color channel is inactive
|
||||
channelBlendModes[colorChannelIndex] = BlendMode::Overprint_SelectBackdrop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t x = 0; x < width; ++x)
|
||||
{
|
||||
for (size_t y = 0; y < height; ++y)
|
||||
{
|
||||
PDFColorBuffer sourceColor = source.getPixel(x, y);
|
||||
PDFConstColorBuffer sourceColor = source.getPixel(x, y);
|
||||
PDFColorBuffer targetColor = target.getPixel(x, y);
|
||||
PDFColorBuffer backdropColor = backdrop.getPixel(x, y);
|
||||
PDFColorBuffer initialBackdropColor = initialBackdrop.getPixel(x, y);
|
||||
PDFConstColorBuffer backdropColor = backdrop.getPixel(x, y);
|
||||
PDFConstColorBuffer initialBackdropColor = initialBackdrop.getPixel(x, y);
|
||||
PDFColorBuffer alphaColorBuffer = softMask.getPixel(x, y);
|
||||
|
||||
const PDFColorComponent softMaskValue = alphaColorBuffer[0];
|
||||
@ -199,40 +295,33 @@ void PDFFloatBitmap::blend(const PDFFloatBitmap& source,
|
||||
{
|
||||
for (uint8_t i = pixelFormat.getProcessColorChannelIndexStart(); i < pixelFormat.getProcessColorChannelIndexEnd(); ++i)
|
||||
{
|
||||
B_i[i] = PDFBlendFunction::blend(mode, backdropColor[i], sourceColor[i]);
|
||||
B_i[i] = PDFBlendFunction::blend(channelBlendModes[i], backdropColor[i], sourceColor[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (uint8_t i = pixelFormat.getProcessColorChannelIndexStart(); i < pixelFormat.getProcessColorChannelIndexEnd(); ++i)
|
||||
{
|
||||
B_i[i] = 1.0f - PDFBlendFunction::blend(mode, 1.0f - backdropColor[i], 1.0f - sourceColor[i]);
|
||||
B_i[i] = 1.0f - PDFBlendFunction::blend(channelBlendModes[i], 1.0f - backdropColor[i], 1.0f - sourceColor[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pixelFormat.hasSpotColors())
|
||||
{
|
||||
// Blend mode for spot colors must be white-preserving,
|
||||
// see 11.7.4.2 of PDF 2.0 specification
|
||||
BlendMode spotBlendMode = mode;
|
||||
if (!PDFBlendModeInfo::isWhitePreserving(mode))
|
||||
{
|
||||
spotBlendMode = BlendMode::Normal;
|
||||
}
|
||||
|
||||
if (!isSpotColorSubtractive)
|
||||
{
|
||||
for (uint8_t i = pixelFormat.getSpotColorChannelIndexStart(); i < pixelFormat.getSpotColorChannelIndexEnd(); ++i)
|
||||
{
|
||||
B_i[i] = PDFBlendFunction::blend(spotBlendMode, backdropColor[i], sourceColor[i]);
|
||||
B_i[i] = PDFBlendFunction::blend(channelBlendModes[i], backdropColor[i], sourceColor[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (uint8_t i = pixelFormat.getSpotColorChannelIndexStart(); i < pixelFormat.getSpotColorChannelIndexEnd(); ++i)
|
||||
{
|
||||
B_i[i] = 1.0f - PDFBlendFunction::blend(spotBlendMode, 1.0f - backdropColor[i], 1.0f - sourceColor[i]);
|
||||
B_i[i] = 1.0f - PDFBlendFunction::blend(channelBlendModes[i], 1.0f - backdropColor[i], 1.0f - sourceColor[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -305,14 +394,14 @@ void PDFFloatBitmap::blend(const PDFFloatBitmap& source,
|
||||
{
|
||||
for (uint8_t i = pixelFormat.getSpotColorChannelIndexStart(); i < pixelFormat.getSpotColorChannelIndexEnd(); ++i)
|
||||
{
|
||||
B_i[i] = PDFBlendFunction::blend(BlendMode::Normal, backdropColor[i], sourceColor[i]);
|
||||
B_i[i] = PDFBlendFunction::blend(channelBlendModes[i], backdropColor[i], sourceColor[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (uint8_t i = pixelFormat.getSpotColorChannelIndexStart(); i < pixelFormat.getSpotColorChannelIndexEnd(); ++i)
|
||||
{
|
||||
B_i[i] = 1.0f - PDFBlendFunction::blend(BlendMode::Normal, 1.0f - backdropColor[i], 1.0f - sourceColor[i]);
|
||||
B_i[i] = 1.0f - PDFBlendFunction::blend(channelBlendModes[i], 1.0f - backdropColor[i], 1.0f - sourceColor[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -532,6 +621,8 @@ void PDFTransparencyRenderer::performBeginTransparencyGroup(ProcessOrder order,
|
||||
|
||||
void PDFTransparencyRenderer::performEndTransparencyGroup(ProcessOrder order, const PDFTransparencyGroup& transparencyGroup)
|
||||
{
|
||||
Q_UNUSED(transparencyGroup);
|
||||
|
||||
if (order == ProcessOrder::AfterOperation)
|
||||
{
|
||||
// "Unblend" the initial backdrop from immediate backdrop, according to 11.4.8
|
||||
@ -543,7 +634,8 @@ void PDFTransparencyRenderer::performEndTransparencyGroup(ProcessOrder order, co
|
||||
PDFTransparencyGroupPainterData& targetData = m_transparencyGroupDataStack.back();
|
||||
sourceData.immediateBackdrop.convertToColorSpace(getCMS(), targetData.renderingIntent, targetData.blendColorSpace, this);
|
||||
|
||||
PDFFloatBitmap::blend(sourceData, targetData, *getBackdrop(), *getInitialBackdrop(), sourceData.softMask, sourceData.alphaIsShape, sourceData.alphaFill, BlendMode::Normal);
|
||||
PDFFloatBitmap::blend(sourceData.immediateBackdrop, targetData.immediateBackdrop, *getBackdrop(), *getInitialBackdrop(), sourceData.softMask,
|
||||
sourceData.alphaIsShape, sourceData.alphaFill, BlendMode::Normal, 0xFFFF, PDFFloatBitmap::OverprintMode::NoOveprint);
|
||||
}
|
||||
}
|
||||
|
||||
@ -573,7 +665,7 @@ void PDFTransparencyRenderer::removeInitialBackdrop()
|
||||
|
||||
if (!qFuzzyIsNull(alpha_g_n))
|
||||
{
|
||||
for (const uint8_t i = colorChannelIndexStart; i < colorChannelIndexEnd; ++i)
|
||||
for (uint8_t i = colorChannelIndexStart; i < colorChannelIndexEnd; ++i)
|
||||
{
|
||||
const PDFColorComponent C_0 = initialBackdropColorBuffer[i];
|
||||
const PDFColorComponent C_n = immediateBackdropColorBuffer[i];
|
||||
|
@ -134,6 +134,9 @@ public:
|
||||
/// Returns buffer with pixel channels
|
||||
PDFColorBuffer getPixel(size_t x, size_t y);
|
||||
|
||||
/// Returns constant buffer with pixel channels
|
||||
PDFConstColorBuffer getPixel(size_t x, size_t y) const;
|
||||
|
||||
/// Returns buffer with all pixels
|
||||
PDFColorBuffer getPixels();
|
||||
|
||||
@ -164,9 +167,19 @@ public:
|
||||
/// Extract process colors into another bitmap
|
||||
PDFFloatBitmap extractProcessColors();
|
||||
|
||||
enum class OverprintMode
|
||||
{
|
||||
NoOveprint, ///< No oveprint performed
|
||||
Overprint_Mode_0, ///< Overprint performed (either backdrop or source color is selected)
|
||||
Overprint_Mode_1, ///< Overprint performed (only nonzero source color is selected, otherwise backdrop)
|
||||
};
|
||||
|
||||
/// Performs bitmap blending, pixel format of source and target must be the same.
|
||||
/// Blending algorithm uses the one from chapter 11.4.8 in the PDF 2.0 specification.
|
||||
/// Bitmap size must be equal for all three bitmaps (source, target and soft mask).
|
||||
/// Oveprinting is also handled. You can specify a mask with active color channels.
|
||||
/// If n-th bit in \p activeColorChannels variable is 1, then color channel is active;
|
||||
/// otherwise backdrop color is selected (if overprint is active).
|
||||
/// \param source Source bitmap
|
||||
/// \param target Target bitmap
|
||||
/// \param backdrop Backdrop
|
||||
@ -175,14 +188,18 @@ public:
|
||||
/// \param alphaIsShape Both soft mask and constant alpha are shapes and not opacity?
|
||||
/// \param constantAlpha Constant alpha, can mean shape or opacity
|
||||
/// \param mode Blend mode
|
||||
void blend(const PDFFloatBitmap& source,
|
||||
PDFFloatBitmap& target,
|
||||
const PDFFloatBitmap& backdrop,
|
||||
const PDFFloatBitmap& initialBackdrop,
|
||||
PDFFloatBitmap& softMask,
|
||||
bool alphaIsShape,
|
||||
PDFColorComponent constantAlpha,
|
||||
BlendMode mode);
|
||||
/// \param activeColorChannels Active color channels
|
||||
/// \param overprintMode Overprint mode
|
||||
static void blend(const PDFFloatBitmap& source,
|
||||
PDFFloatBitmap& target,
|
||||
const PDFFloatBitmap& backdrop,
|
||||
const PDFFloatBitmap& initialBackdrop,
|
||||
PDFFloatBitmap& softMask,
|
||||
bool alphaIsShape,
|
||||
PDFColorComponent constantAlpha,
|
||||
BlendMode mode,
|
||||
uint32_t activeColorChannels,
|
||||
OverprintMode overprintMode);
|
||||
|
||||
private:
|
||||
void fillChannel(size_t channel, PDFColorComponent value);
|
||||
|
Reference in New Issue
Block a user