mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Soft masks for images
This commit is contained in:
@ -87,7 +87,7 @@ size_t PDFDeviceCMYKColorSpace::getColorComponentCount() const
|
|||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage PDFAbstractColorSpace::getImage(const PDFImageData& imageData) const
|
QImage PDFAbstractColorSpace::getImage(const PDFImageData& imageData, const PDFImageData& softMask) const
|
||||||
{
|
{
|
||||||
if (imageData.isValid())
|
if (imageData.isValid())
|
||||||
{
|
{
|
||||||
@ -107,7 +107,7 @@ QImage PDFAbstractColorSpace::getImage(const PDFImageData& imageData) const
|
|||||||
const std::vector<PDFReal>& decode = imageData.getDecode();
|
const std::vector<PDFReal>& decode = imageData.getDecode();
|
||||||
if (!decode.empty() && decode.size() != componentCount * 2)
|
if (!decode.empty() && decode.size() != componentCount * 2)
|
||||||
{
|
{
|
||||||
throw PDFException(PDFTranslationContext::tr("Invalid size of the decoded array. Expected %1, actual %2.").arg(componentCount * 2).arg(decode.size()));
|
throw PDFException(PDFTranslationContext::tr("Invalid size of the decode array. Expected %1, actual %2.").arg(componentCount * 2).arg(decode.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
PDFBitReader reader(&imageData.getData(), imageData.getBitsPerComponent());
|
PDFBitReader reader(&imageData.getData(), imageData.getBitsPerComponent());
|
||||||
@ -151,6 +151,74 @@ QImage PDFAbstractColorSpace::getImage(const PDFImageData& imageData) const
|
|||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case PDFImageData::MaskingType::SoftMask:
|
||||||
|
{
|
||||||
|
const bool hasMatte = !softMask.getMatte().empty();
|
||||||
|
QImage image(imageData.getWidth(), imageData.getHeight(), hasMatte ? QImage::Format_RGBA8888_Premultiplied : QImage::Format_RGBA8888);
|
||||||
|
image.fill(QColor(Qt::white));
|
||||||
|
|
||||||
|
unsigned int componentCount = imageData.getComponents();
|
||||||
|
if (componentCount != getColorComponentCount())
|
||||||
|
{
|
||||||
|
throw PDFException(PDFTranslationContext::tr("Invalid colors for color space. Color space has %1 colors. Provided color count is %4.").arg(getColorComponentCount()).arg(componentCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<PDFReal>& decode = imageData.getDecode();
|
||||||
|
if (!decode.empty() && decode.size() != componentCount * 2)
|
||||||
|
{
|
||||||
|
throw PDFException(PDFTranslationContext::tr("Invalid size of the decode array. Expected %1, actual %2.").arg(componentCount * 2).arg(decode.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFBitReader reader(&imageData.getData(), imageData.getBitsPerComponent());
|
||||||
|
|
||||||
|
PDFColor color;
|
||||||
|
color.resize(componentCount);
|
||||||
|
|
||||||
|
QImage alphaMask = createAlphaMask(softMask);
|
||||||
|
if (alphaMask.size() != image.size())
|
||||||
|
{
|
||||||
|
// Scale the alpha mask, if it is masked
|
||||||
|
alphaMask = alphaMask.scaled(image.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
const double max = reader.max();
|
||||||
|
const double coefficient = 1.0 / max;
|
||||||
|
for (unsigned int i = 0, rowCount = imageData.getHeight(); i < rowCount; ++i)
|
||||||
|
{
|
||||||
|
reader.seek(i * imageData.getStride());
|
||||||
|
unsigned char* outputLine = image.scanLine(i);
|
||||||
|
unsigned char* alphaLine = alphaMask.scanLine(i);
|
||||||
|
|
||||||
|
for (unsigned int j = 0; j < imageData.getWidth(); ++j)
|
||||||
|
{
|
||||||
|
for (unsigned int k = 0; k < componentCount; ++k)
|
||||||
|
{
|
||||||
|
PDFReal value = reader.read();
|
||||||
|
|
||||||
|
// Interpolate value, if it is not empty
|
||||||
|
if (!decode.empty())
|
||||||
|
{
|
||||||
|
color[k] = interpolate(value, 0.0, max, decode[2 * k], decode[2 * k + 1]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
color[k] = value * coefficient;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor transformedColor = getColor(color);
|
||||||
|
QRgb rgb = transformedColor.rgb();
|
||||||
|
|
||||||
|
*outputLine++ = qRed(rgb);
|
||||||
|
*outputLine++ = qGreen(rgb);
|
||||||
|
*outputLine++ = qBlue(rgb);
|
||||||
|
*outputLine++ = *alphaLine++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
case PDFImageData::MaskingType::ColorKeyMasking:
|
case PDFImageData::MaskingType::ColorKeyMasking:
|
||||||
{
|
{
|
||||||
QImage image(imageData.getWidth(), imageData.getHeight(), QImage::Format_RGBA8888);
|
QImage image(imageData.getWidth(), imageData.getHeight(), QImage::Format_RGBA8888);
|
||||||
@ -247,6 +315,69 @@ QColor PDFAbstractColorSpace::getCheckedColor(const PDFColor& color) const
|
|||||||
return getColor(color);
|
return getColor(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QImage PDFAbstractColorSpace::createAlphaMask(const PDFImageData& softMask)
|
||||||
|
{
|
||||||
|
if (softMask.getMaskingType() != PDFImageData::MaskingType::None)
|
||||||
|
{
|
||||||
|
throw PDFException(PDFTranslationContext::tr("Soft mask can't have masking."));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (softMask.getWidth() < 1 || softMask.getHeight() < 1)
|
||||||
|
{
|
||||||
|
throw PDFException(PDFTranslationContext::tr("Invalid size of soft mask."));
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage image(softMask.getWidth(), softMask.getHeight(), QImage::Format_Alpha8);
|
||||||
|
|
||||||
|
unsigned int componentCount = softMask.getComponents();
|
||||||
|
if (componentCount != 1)
|
||||||
|
{
|
||||||
|
throw PDFException(PDFTranslationContext::tr("Soft mask should have only 1 color component (alpha) instead of %1.").arg(componentCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<PDFReal>& decode = softMask.getDecode();
|
||||||
|
if (!decode.empty() && decode.size() != componentCount * 2)
|
||||||
|
{
|
||||||
|
throw PDFException(PDFTranslationContext::tr("Invalid size of the decode array. Expected %1, actual %2.").arg(componentCount * 2).arg(decode.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFBitReader reader(&softMask.getData(), softMask.getBitsPerComponent());
|
||||||
|
|
||||||
|
PDFColor color;
|
||||||
|
color.resize(componentCount);
|
||||||
|
|
||||||
|
const double max = reader.max();
|
||||||
|
const double coefficient = 1.0 / max;
|
||||||
|
for (unsigned int i = 0, rowCount = softMask.getHeight(); i < rowCount; ++i)
|
||||||
|
{
|
||||||
|
reader.seek(i * softMask.getStride());
|
||||||
|
unsigned char* outputLine = image.scanLine(i);
|
||||||
|
|
||||||
|
for (unsigned int j = 0; j < softMask.getWidth(); ++j)
|
||||||
|
{
|
||||||
|
PDFReal alpha = 0.0;
|
||||||
|
|
||||||
|
PDFReal value = reader.read();
|
||||||
|
|
||||||
|
// Interpolate value, if it is not empty
|
||||||
|
if (!decode.empty())
|
||||||
|
{
|
||||||
|
alpha = interpolate(value, 0.0, max, decode[0], decode[1]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
alpha = value * coefficient;
|
||||||
|
}
|
||||||
|
|
||||||
|
alpha = qBound(0.0, alpha, 1.0);
|
||||||
|
uint8_t alphaCoded = alpha * 255;
|
||||||
|
*outputLine++ = alphaCoded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
PDFColorSpacePointer PDFAbstractColorSpace::createColorSpace(const PDFDictionary* colorSpaceDictionary,
|
PDFColorSpacePointer PDFAbstractColorSpace::createColorSpace(const PDFDictionary* colorSpaceDictionary,
|
||||||
const PDFDocument* document,
|
const PDFDocument* document,
|
||||||
const PDFObject& colorSpace)
|
const PDFObject& colorSpace)
|
||||||
@ -818,46 +949,104 @@ size_t PDFIndexedColorSpace::getColorComponentCount() const
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage PDFIndexedColorSpace::getImage(const PDFImageData& imageData) const
|
QImage PDFIndexedColorSpace::getImage(const PDFImageData& imageData, const PDFImageData& softMask) const
|
||||||
{
|
{
|
||||||
if (imageData.isValid())
|
if (imageData.isValid())
|
||||||
{
|
{
|
||||||
QImage image(imageData.getWidth(), imageData.getHeight(), QImage::Format_RGB888);
|
switch (imageData.getMaskingType())
|
||||||
image.fill(QColor(Qt::white));
|
|
||||||
|
|
||||||
unsigned int componentCount = imageData.getComponents();
|
|
||||||
PDFBitReader reader(&imageData.getData(), imageData.getBitsPerComponent());
|
|
||||||
|
|
||||||
if (componentCount != getColorComponentCount())
|
|
||||||
{
|
{
|
||||||
throw PDFException(PDFTranslationContext::tr("Invalid colors for indexed color space. Color space has %1 colors. Provided color count is %4.").arg(getColorComponentCount()).arg(componentCount));
|
case PDFImageData::MaskingType::None:
|
||||||
}
|
|
||||||
|
|
||||||
Q_ASSERT(componentCount == 1);
|
|
||||||
|
|
||||||
PDFColor color;
|
|
||||||
color.resize(1);
|
|
||||||
|
|
||||||
for (unsigned int i = 0, rowCount = imageData.getHeight(); i < rowCount; ++i)
|
|
||||||
{
|
|
||||||
reader.seek(i * imageData.getStride());
|
|
||||||
unsigned char* outputLine = image.scanLine(i);
|
|
||||||
|
|
||||||
for (unsigned int j = 0; j < imageData.getWidth(); ++j)
|
|
||||||
{
|
{
|
||||||
PDFBitReader::Value index = reader.read();
|
QImage image(imageData.getWidth(), imageData.getHeight(), QImage::Format_RGB888);
|
||||||
color[0] = index;
|
image.fill(QColor(Qt::white));
|
||||||
|
|
||||||
QColor transformedColor = getColor(color);
|
unsigned int componentCount = imageData.getComponents();
|
||||||
QRgb rgb = transformedColor.rgb();
|
PDFBitReader reader(&imageData.getData(), imageData.getBitsPerComponent());
|
||||||
|
|
||||||
*outputLine++ = qRed(rgb);
|
if (componentCount != getColorComponentCount())
|
||||||
*outputLine++ = qGreen(rgb);
|
{
|
||||||
*outputLine++ = qBlue(rgb);
|
throw PDFException(PDFTranslationContext::tr("Invalid colors for indexed color space. Color space has %1 colors. Provided color count is %4.").arg(getColorComponentCount()).arg(componentCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(componentCount == 1);
|
||||||
|
|
||||||
|
PDFColor color;
|
||||||
|
color.resize(1);
|
||||||
|
|
||||||
|
for (unsigned int i = 0, rowCount = imageData.getHeight(); i < rowCount; ++i)
|
||||||
|
{
|
||||||
|
reader.seek(i * imageData.getStride());
|
||||||
|
unsigned char* outputLine = image.scanLine(i);
|
||||||
|
|
||||||
|
for (unsigned int j = 0; j < imageData.getWidth(); ++j)
|
||||||
|
{
|
||||||
|
PDFBitReader::Value index = reader.read();
|
||||||
|
color[0] = index;
|
||||||
|
|
||||||
|
QColor transformedColor = getColor(color);
|
||||||
|
QRgb rgb = transformedColor.rgb();
|
||||||
|
|
||||||
|
*outputLine++ = qRed(rgb);
|
||||||
|
*outputLine++ = qGreen(rgb);
|
||||||
|
*outputLine++ = qBlue(rgb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return image;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return image;
|
case PDFImageData::MaskingType::SoftMask:
|
||||||
|
{
|
||||||
|
const bool hasMatte = !softMask.getMatte().empty();
|
||||||
|
QImage image(imageData.getWidth(), imageData.getHeight(), hasMatte ? QImage::Format_RGBA8888_Premultiplied : QImage::Format_RGBA8888);
|
||||||
|
|
||||||
|
unsigned int componentCount = imageData.getComponents();
|
||||||
|
PDFBitReader reader(&imageData.getData(), imageData.getBitsPerComponent());
|
||||||
|
|
||||||
|
if (componentCount != getColorComponentCount())
|
||||||
|
{
|
||||||
|
throw PDFException(PDFTranslationContext::tr("Invalid colors for indexed color space. Color space has %1 colors. Provided color count is %4.").arg(getColorComponentCount()).arg(componentCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(componentCount == 1);
|
||||||
|
|
||||||
|
PDFColor color;
|
||||||
|
color.resize(1);
|
||||||
|
|
||||||
|
QImage alphaMask = createAlphaMask(softMask);
|
||||||
|
if (alphaMask.size() != image.size())
|
||||||
|
{
|
||||||
|
// Scale the alpha mask, if it is masked
|
||||||
|
alphaMask = alphaMask.scaled(image.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int i = 0, rowCount = imageData.getHeight(); i < rowCount; ++i)
|
||||||
|
{
|
||||||
|
reader.seek(i * imageData.getStride());
|
||||||
|
unsigned char* outputLine = image.scanLine(i);
|
||||||
|
unsigned char* alphaLine = alphaMask.scanLine(i);
|
||||||
|
|
||||||
|
for (unsigned int j = 0; j < imageData.getWidth(); ++j)
|
||||||
|
{
|
||||||
|
PDFBitReader::Value index = reader.read();
|
||||||
|
color[0] = index;
|
||||||
|
|
||||||
|
QColor transformedColor = getColor(color);
|
||||||
|
QRgb rgb = transformedColor.rgb();
|
||||||
|
|
||||||
|
*outputLine++ = qRed(rgb);
|
||||||
|
*outputLine++ = qGreen(rgb);
|
||||||
|
*outputLine++ = qBlue(rgb);
|
||||||
|
*outputLine++ = *alphaLine++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw PDFRendererException(RenderErrorType::NotImplemented, PDFTranslationContext::tr("Image masking not implemented!"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return QImage();
|
return QImage();
|
||||||
|
@ -87,6 +87,7 @@ public:
|
|||||||
ColorKeyMasking, ///< Masking by color key
|
ColorKeyMasking, ///< Masking by color key
|
||||||
Image, ///< Masking by image with alpha mask
|
Image, ///< Masking by image with alpha mask
|
||||||
ImageMask, ///< Masking by 1-bit image (see "ImageMask" entry in image's dictionary), current color from the graphic state is used to paint an image
|
ImageMask, ///< Masking by 1-bit image (see "ImageMask" entry in image's dictionary), current color from the graphic state is used to paint an image
|
||||||
|
SoftMask, ///< Image is masked by soft mask
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit PDFImageData() :
|
explicit PDFImageData() :
|
||||||
@ -108,7 +109,8 @@ public:
|
|||||||
MaskingType maskingType,
|
MaskingType maskingType,
|
||||||
QByteArray data,
|
QByteArray data,
|
||||||
std::vector<PDFInteger>&& colorKeyMask,
|
std::vector<PDFInteger>&& colorKeyMask,
|
||||||
std::vector<PDFReal>&& decode) :
|
std::vector<PDFReal>&& decode,
|
||||||
|
std::vector<PDFReal>&& matte) :
|
||||||
m_components(components),
|
m_components(components),
|
||||||
m_bitsPerComponent(bitsPerComponent),
|
m_bitsPerComponent(bitsPerComponent),
|
||||||
m_width(width),
|
m_width(width),
|
||||||
@ -117,7 +119,8 @@ public:
|
|||||||
m_maskingType(maskingType),
|
m_maskingType(maskingType),
|
||||||
m_data(qMove(data)),
|
m_data(qMove(data)),
|
||||||
m_colorKeyMask(qMove(colorKeyMask)),
|
m_colorKeyMask(qMove(colorKeyMask)),
|
||||||
m_decode(qMove(decode))
|
m_decode(qMove(decode)),
|
||||||
|
m_matte(qMove(matte))
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -131,6 +134,7 @@ public:
|
|||||||
const QByteArray& getData() const { return m_data; }
|
const QByteArray& getData() const { return m_data; }
|
||||||
const std::vector<PDFInteger>& getColorKeyMask() const { return m_colorKeyMask; }
|
const std::vector<PDFInteger>& getColorKeyMask() const { return m_colorKeyMask; }
|
||||||
const std::vector<PDFReal>& getDecode() const { return m_decode; }
|
const std::vector<PDFReal>& getDecode() const { return m_decode; }
|
||||||
|
const std::vector<PDFReal>& getMatte() const { return m_matte; }
|
||||||
|
|
||||||
/// Returns number of color channels
|
/// Returns number of color channels
|
||||||
unsigned int getColorChannels() const { return m_components; }
|
unsigned int getColorChannels() const { return m_components; }
|
||||||
@ -162,6 +166,10 @@ private:
|
|||||||
/// then contains n pairs of numbers, where n is number of color components. If ImageMask
|
/// then contains n pairs of numbers, where n is number of color components. If ImageMask
|
||||||
/// in the image dictionary is true, then decode array should be [0 1] or [1 0].
|
/// in the image dictionary is true, then decode array should be [0 1] or [1 0].
|
||||||
std::vector<PDFReal> m_decode;
|
std::vector<PDFReal> m_decode;
|
||||||
|
|
||||||
|
/// Matte color (color, agains which is image preblended, when using soft masking
|
||||||
|
/// image (defined for soft masks).
|
||||||
|
std::vector<PDFReal> m_matte;
|
||||||
};
|
};
|
||||||
|
|
||||||
using PDFColor3 = std::array<PDFColorComponent, 3>;
|
using PDFColor3 = std::array<PDFColorComponent, 3>;
|
||||||
@ -211,13 +219,17 @@ public:
|
|||||||
virtual QColor getDefaultColor() const = 0;
|
virtual QColor getDefaultColor() const = 0;
|
||||||
virtual QColor getColor(const PDFColor& color) const = 0;
|
virtual QColor getColor(const PDFColor& color) const = 0;
|
||||||
virtual size_t getColorComponentCount() const = 0;
|
virtual size_t getColorComponentCount() const = 0;
|
||||||
virtual QImage getImage(const PDFImageData& imageData) const;
|
virtual QImage getImage(const PDFImageData& imageData, const PDFImageData& softMask) const;
|
||||||
virtual const PDFPatternColorSpace* asPatternColorSpace() const { return nullptr; }
|
virtual const PDFPatternColorSpace* asPatternColorSpace() const { return nullptr; }
|
||||||
|
|
||||||
/// Checks, if number of color components is OK, and if yes, converts them to the QColor value.
|
/// Checks, if number of color components is OK, and if yes, converts them to the QColor value.
|
||||||
/// If they are not OK, exception is thrown.
|
/// If they are not OK, exception is thrown.
|
||||||
QColor getCheckedColor(const PDFColor& color) const;
|
QColor getCheckedColor(const PDFColor& color) const;
|
||||||
|
|
||||||
|
/// Creates alpha mask from soft image data. Exception is thrown, if something fails.
|
||||||
|
/// \param softMask Soft mask
|
||||||
|
static QImage createAlphaMask(const PDFImageData& softMask);
|
||||||
|
|
||||||
/// Parses the desired color space. If desired color space is not found, then exception is thrown.
|
/// Parses the desired color space. If desired color space is not found, then exception is thrown.
|
||||||
/// If everything is OK, then shared pointer to the new color space is returned.
|
/// If everything is OK, then shared pointer to the new color space is returned.
|
||||||
/// \param colorSpaceDictionary Dictionary containing color spaces of the page
|
/// \param colorSpaceDictionary Dictionary containing color spaces of the page
|
||||||
@ -496,7 +508,7 @@ public:
|
|||||||
virtual QColor getDefaultColor() const override;
|
virtual QColor getDefaultColor() const override;
|
||||||
virtual QColor getColor(const PDFColor& color) const override;
|
virtual QColor getColor(const PDFColor& color) const override;
|
||||||
virtual size_t getColorComponentCount() const override;
|
virtual size_t getColorComponentCount() const override;
|
||||||
virtual QImage getImage(const PDFImageData& imageData) const override;
|
virtual QImage getImage(const PDFImageData& imageData, const PDFImageData& softMask) const override;
|
||||||
|
|
||||||
/// Creates indexed color space from provided values.
|
/// Creates indexed color space from provided values.
|
||||||
/// \param colorSpaceDictionary Color space dictionary
|
/// \param colorSpaceDictionary Color space dictionary
|
||||||
|
@ -45,7 +45,7 @@ struct PDFJPEGDCTSource
|
|||||||
int startByte = 0;
|
int startByte = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
PDFImage PDFImage::createImage(const PDFDocument* document, const PDFStream* stream, PDFColorSpacePointer colorSpace, PDFRenderErrorReporter* errorReporter)
|
PDFImage PDFImage::createImage(const PDFDocument* document, const PDFStream* stream, PDFColorSpacePointer colorSpace, bool isSoftMask, PDFRenderErrorReporter* errorReporter)
|
||||||
{
|
{
|
||||||
PDFImage image;
|
PDFImage image;
|
||||||
image.m_colorSpace = colorSpace;
|
image.m_colorSpace = colorSpace;
|
||||||
@ -59,10 +59,9 @@ PDFImage PDFImage::createImage(const PDFDocument* document, const PDFStream* str
|
|||||||
throw PDFException(PDFTranslationContext::tr("Image has not data."));
|
throw PDFException(PDFTranslationContext::tr("Image has not data."));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement SMask
|
|
||||||
// TODO: Implement SMaskInData
|
// TODO: Implement SMaskInData
|
||||||
|
|
||||||
for (const char* notImplementedKey : { "SMask", "SMaskInData" })
|
for (const char* notImplementedKey : { "SMaskInData" })
|
||||||
{
|
{
|
||||||
if (dictionary->hasKey(notImplementedKey))
|
if (dictionary->hasKey(notImplementedKey))
|
||||||
{
|
{
|
||||||
@ -74,6 +73,17 @@ PDFImage PDFImage::createImage(const PDFDocument* document, const PDFStream* str
|
|||||||
std::vector<PDFInteger> mask;
|
std::vector<PDFInteger> mask;
|
||||||
std::vector<PDFReal> decode = loader.readNumberArrayFromDictionary(dictionary, "Decode");
|
std::vector<PDFReal> decode = loader.readNumberArrayFromDictionary(dictionary, "Decode");
|
||||||
bool imageMask = loader.readBooleanFromDictionary(dictionary, "ImageMask", false);
|
bool imageMask = loader.readBooleanFromDictionary(dictionary, "ImageMask", false);
|
||||||
|
std::vector<PDFReal> matte = loader.readNumberArrayFromDictionary(dictionary, "Matte");
|
||||||
|
|
||||||
|
if (isSoftMask && (imageMask || dictionary->hasKey("Mask") || dictionary->hasKey("SMask")))
|
||||||
|
{
|
||||||
|
throw PDFRendererException(RenderErrorType::NotImplemented, PDFTranslationContext::tr("Soft mask image can't have mask / soft mask itself."));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isSoftMask && !matte.empty())
|
||||||
|
{
|
||||||
|
throw PDFRendererException(RenderErrorType::NotImplemented, PDFTranslationContext::tr("Regular image can't have Matte entry (used for soft masks)."));
|
||||||
|
}
|
||||||
|
|
||||||
// Fill Mask
|
// Fill Mask
|
||||||
if (dictionary->hasKey("Mask"))
|
if (dictionary->hasKey("Mask"))
|
||||||
@ -91,6 +101,22 @@ PDFImage PDFImage::createImage(const PDFDocument* document, const PDFStream* str
|
|||||||
throw PDFRendererException(RenderErrorType::NotImplemented, PDFTranslationContext::tr("Mask image is not implemented."));
|
throw PDFRendererException(RenderErrorType::NotImplemented, PDFTranslationContext::tr("Mask image is not implemented."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (dictionary->hasKey("SMask"))
|
||||||
|
{
|
||||||
|
// Parse soft mask image
|
||||||
|
const PDFObject& softMaskObject = document->getObject(dictionary->get("SMask"));
|
||||||
|
|
||||||
|
if (softMaskObject.isStream())
|
||||||
|
{
|
||||||
|
PDFImage softMaskImage = createImage(document, softMaskObject.getStream(), PDFColorSpacePointer(new PDFDeviceGrayColorSpace()), true, errorReporter);
|
||||||
|
maskingType = PDFImageData::MaskingType::SoftMask;
|
||||||
|
image.m_softMask = qMove(softMaskImage.m_imageData);
|
||||||
|
}
|
||||||
|
else if (!softMaskObject.isNull())
|
||||||
|
{
|
||||||
|
throw PDFRendererException(RenderErrorType::NotImplemented, PDFTranslationContext::tr("Invalid soft mask object."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (imageMask)
|
if (imageMask)
|
||||||
{
|
{
|
||||||
@ -262,7 +288,7 @@ PDFImage PDFImage::createImage(const PDFDocument* document, const PDFStream* str
|
|||||||
}
|
}
|
||||||
|
|
||||||
jpeg_finish_decompress(&codec);
|
jpeg_finish_decompress(&codec);
|
||||||
image.m_imageData = PDFImageData(components, bitsPerComponent, width, height, rowStride, maskingType, qMove(buffer), qMove(mask), qMove(decode));
|
image.m_imageData = PDFImageData(components, bitsPerComponent, width, height, rowStride, maskingType, qMove(buffer), qMove(mask), qMove(decode), qMove(matte));
|
||||||
}
|
}
|
||||||
|
|
||||||
jpeg_destroy_decompress(&codec);
|
jpeg_destroy_decompress(&codec);
|
||||||
@ -428,7 +454,7 @@ PDFImage PDFImage::createImage(const PDFDocument* document, const PDFStream* str
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
image.m_imageData = PDFImageData(components, bitsPerComponent, width, height, stride, maskingType, qMove(imageDataBuffer), qMove(mask), qMove(decode));
|
image.m_imageData = PDFImageData(components, bitsPerComponent, width, height, stride, maskingType, qMove(imageDataBuffer), qMove(mask), qMove(decode), qMove(matte));
|
||||||
valid = image.m_imageData.isValid();
|
valid = image.m_imageData.isValid();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -472,7 +498,7 @@ PDFImage PDFImage::createImage(const PDFDocument* document, const PDFStream* str
|
|||||||
{
|
{
|
||||||
throw PDFRendererException(RenderErrorType::NotImplemented, PDFTranslationContext::tr("Not implemented image filter 'JBIG2Decode'."));
|
throw PDFRendererException(RenderErrorType::NotImplemented, PDFTranslationContext::tr("Not implemented image filter 'JBIG2Decode'."));
|
||||||
}
|
}
|
||||||
else if (colorSpace)
|
else if (colorSpace || isSoftMask)
|
||||||
{
|
{
|
||||||
// We treat data as binary maybe compressed stream (for example by Flate/LZW method), but data can also be not compressed.
|
// We treat data as binary maybe compressed stream (for example by Flate/LZW method), but data can also be not compressed.
|
||||||
const unsigned int components = static_cast<unsigned int>(colorSpace->getColorComponentCount());
|
const unsigned int components = static_cast<unsigned int>(colorSpace->getColorComponentCount());
|
||||||
@ -494,7 +520,7 @@ PDFImage PDFImage::createImage(const PDFDocument* document, const PDFStream* str
|
|||||||
const unsigned int stride = (components * bitsPerComponent * width + 7) / 8;
|
const unsigned int stride = (components * bitsPerComponent * width + 7) / 8;
|
||||||
|
|
||||||
QByteArray imageDataBuffer = document->getDecodedStream(stream);
|
QByteArray imageDataBuffer = document->getDecodedStream(stream);
|
||||||
image.m_imageData = PDFImageData(components, bitsPerComponent, width, height, stride, maskingType, qMove(imageDataBuffer), qMove(mask), qMove(decode));
|
image.m_imageData = PDFImageData(components, bitsPerComponent, width, height, stride, maskingType, qMove(imageDataBuffer), qMove(mask), qMove(decode), qMove(matte));
|
||||||
}
|
}
|
||||||
else if (imageMask)
|
else if (imageMask)
|
||||||
{
|
{
|
||||||
@ -519,7 +545,7 @@ PDFImage PDFImage::createImage(const PDFDocument* document, const PDFStream* str
|
|||||||
const unsigned int stride = (width + 7) / 8;
|
const unsigned int stride = (width + 7) / 8;
|
||||||
|
|
||||||
QByteArray imageDataBuffer = document->getDecodedStream(stream);
|
QByteArray imageDataBuffer = document->getDecodedStream(stream);
|
||||||
image.m_imageData = PDFImageData(1, bitsPerComponent, width, height, stride, maskingType, qMove(imageDataBuffer), qMove(mask), qMove(decode));
|
image.m_imageData = PDFImageData(1, bitsPerComponent, width, height, stride, maskingType, qMove(imageDataBuffer), qMove(mask), qMove(decode), qMove(matte));
|
||||||
}
|
}
|
||||||
|
|
||||||
return image;
|
return image;
|
||||||
@ -530,7 +556,7 @@ QImage PDFImage::getImage() const
|
|||||||
const bool isImageMask = m_imageData.getMaskingType() == PDFImageData::MaskingType::ImageMask;
|
const bool isImageMask = m_imageData.getMaskingType() == PDFImageData::MaskingType::ImageMask;
|
||||||
if (m_colorSpace && !isImageMask)
|
if (m_colorSpace && !isImageMask)
|
||||||
{
|
{
|
||||||
return m_colorSpace->getImage(m_imageData);
|
return m_colorSpace->getImage(m_imageData, m_softMask);
|
||||||
}
|
}
|
||||||
else if (isImageMask)
|
else if (isImageMask)
|
||||||
{
|
{
|
||||||
|
@ -38,8 +38,9 @@ public:
|
|||||||
/// \param document Document
|
/// \param document Document
|
||||||
/// \param stream Stream with image
|
/// \param stream Stream with image
|
||||||
/// \param colorSpace Color space of the image
|
/// \param colorSpace Color space of the image
|
||||||
|
/// \param isSoftMask Is it a soft mask image?
|
||||||
/// \param errorReporter Error reporter for reporting errors (or warnings)
|
/// \param errorReporter Error reporter for reporting errors (or warnings)
|
||||||
static PDFImage createImage(const PDFDocument* document, const PDFStream* stream, PDFColorSpacePointer colorSpace, PDFRenderErrorReporter* errorReporter);
|
static PDFImage createImage(const PDFDocument* document, const PDFStream* stream, PDFColorSpacePointer colorSpace, bool isSoftMask, PDFRenderErrorReporter* errorReporter);
|
||||||
|
|
||||||
/// Returns image transformed from image data and color space
|
/// Returns image transformed from image data and color space
|
||||||
QImage getImage() const;
|
QImage getImage() const;
|
||||||
@ -48,6 +49,7 @@ private:
|
|||||||
PDFImage() = default;
|
PDFImage() = default;
|
||||||
|
|
||||||
PDFImageData m_imageData;
|
PDFImageData m_imageData;
|
||||||
|
PDFImageData m_softMask;
|
||||||
PDFColorSpacePointer m_colorSpace;
|
PDFColorSpacePointer m_colorSpace;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2663,7 +2663,7 @@ void PDFPageContentProcessor::paintXObjectImage(const PDFStream* stream)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PDFImage pdfImage = PDFImage::createImage(m_document, stream, qMove(colorSpace), this);
|
PDFImage pdfImage = PDFImage::createImage(m_document, stream, qMove(colorSpace), false, this);
|
||||||
QImage image = pdfImage.getImage();
|
QImage image = pdfImage.getImage();
|
||||||
|
|
||||||
if (image.format() == QImage::Format_Alpha8)
|
if (image.format() == QImage::Format_Alpha8)
|
||||||
|
Reference in New Issue
Block a user