Image processing

This commit is contained in:
Jakub Melka 2021-02-24 19:01:52 +01:00
parent d632595710
commit 4b291d6db8
3 changed files with 399 additions and 236 deletions

View File

@ -1,4 +1,4 @@
// Copyright (C) 2019-2020 Jakub Melka // Copyright (C) 2019-2021 Jakub Melka
// //
// This file is part of Pdf4Qt. // This file is part of Pdf4Qt.
// //

View File

@ -1,4 +1,4 @@
// Copyright (C) 2019-2020 Jakub Melka // Copyright (C) 2019-2021 Jakub Melka
// //
// This file is part of Pdf4Qt. // This file is part of Pdf4Qt.
// //

View File

@ -1,4 +1,4 @@
// Copyright (C) 2020 Jakub Melka // Copyright (C) 2020-2021 Jakub Melka
// //
// This file is part of Pdf4Qt. // This file is part of Pdf4Qt.
// //
@ -700,6 +700,12 @@ PDFFloatBitmapWithColorSpace::PDFFloatBitmapWithColorSpace()
} }
PDFFloatBitmapWithColorSpace::PDFFloatBitmapWithColorSpace(size_t width, size_t height, PDFPixelFormat format) :
PDFFloatBitmap(width, height, format)
{
}
PDFFloatBitmapWithColorSpace::PDFFloatBitmapWithColorSpace(size_t width, size_t height, PDFPixelFormat format, PDFColorSpacePointer blendColorSpace) : PDFFloatBitmapWithColorSpace::PDFFloatBitmapWithColorSpace(size_t width, size_t height, PDFPixelFormat format, PDFColorSpacePointer blendColorSpace) :
PDFFloatBitmap(width, height, format), PDFFloatBitmap(width, height, format),
m_colorSpace(blendColorSpace) m_colorSpace(blendColorSpace)
@ -1134,11 +1140,126 @@ PDFFloatBitmapWithColorSpace PDFTransparencyRenderer::getColoredImage(const PDFI
const PDFImageData& softMask = sourceImage.getSoftMaskData(); const PDFImageData& softMask = sourceImage.getSoftMaskData();
PDFColorSpacePointer imageColorSpace = sourceImage.getColorSpace(); PDFColorSpacePointer imageColorSpace = sourceImage.getColorSpace();
const size_t colorComponentCount = imageColorSpace->getColorComponentCount(); size_t colorComponentCount = imageColorSpace->getColorComponentCount();
const bool isCMYK = colorComponentCount == 4; bool isCMYK = colorComponentCount == 4;
const bool useSmoothImageTransformation = m_settings.flags.testFlag(PDFTransparencyRendererSettings::SmoothImageTransformation) && sourceImage.isInterpolated(); const bool useSmoothImageTransformation = m_settings.flags.testFlag(PDFTransparencyRendererSettings::SmoothImageTransformation) && sourceImage.isInterpolated();
if (!imageColorSpace)
{
throw PDFException(PDFTranslationContext::tr("Invalid image color space."));
}
Q_ASSERT(imageData.isValid()); Q_ASSERT(imageData.isValid());
if (imageColorSpace->getColorSpace() == PDFAbstractColorSpace::ColorSpace::Indexed)
{
const PDFIndexedColorSpace* indexedColorSpace = dynamic_cast<const PDFIndexedColorSpace*>(imageColorSpace.data());
imageColorSpace = indexedColorSpace->getBaseColorSpace();
if (!imageColorSpace)
{
throw PDFException(PDFTranslationContext::tr("Invalid base color space of indexed color space."));
}
unsigned int componentCount = imageData.getComponents();
PDFBitReader reader(&imageData.getData(), imageData.getBitsPerComponent());
if (componentCount != colorComponentCount)
{
throw PDFException(PDFTranslationContext::tr("Invalid colors for indexed color space. Color space has %1 colors. Provided color count is %4.").arg(colorComponentCount).arg(componentCount));
}
const unsigned int imageWidth = imageData.getWidth();
const unsigned int imageHeight = imageData.getHeight();
Q_ASSERT(componentCount == 1);
std::vector<PDFColorComponent> colorIndices;
colorIndices.reserve(imageData.getWidth() * imageData.getHeight());
for (unsigned int i = 0; i < imageHeight; ++i)
{
reader.seek(i * imageData.getStride());
for (unsigned int j = 0; j < imageWidth; ++j)
{
PDFBitReader::Value index = reader.read();
colorIndices.push_back(index);
}
}
PDFColorBuffer indicesBuffer(colorIndices.data(), colorIndices.size());
std::vector<PDFColorComponent> transformedColors = indexedColorSpace->transformColorsToBaseColorSpace(indicesBuffer);
colorComponentCount = imageColorSpace->getColorComponentCount();
isCMYK = colorComponentCount == 4;
if (transformedColors.size() != colorComponentCount * imageWidth * imageHeight)
{
throw PDFException(PDFTranslationContext::tr("Conversion of indexed image to base color space failed."));
}
result = PDFFloatBitmapWithColorSpace(imageWidth, imageHeight, PDFPixelFormat::createFormat(uint8_t(colorComponentCount), 0, true, isCMYK, false));
result.makeOpaque();
for (unsigned int i = 0; i < imageHeight; ++i)
{
for (unsigned int j = 0; j < imageWidth; ++j)
{
PDFColorBuffer buffer = result.getPixel(j, i);
size_t pixelIndex = (i * imageWidth + j) * colorComponentCount;
for (size_t k = 0; k < colorComponentCount; ++k)
{
Q_ASSERT(pixelIndex + k < transformedColors.size());
buffer[k] = transformedColors[pixelIndex + k];
}
}
}
switch (imageData.getMaskingType())
{
case PDFImageData::MaskingType::None:
break;
case PDFImageData::MaskingType::SoftMask:
{
PDFFloatBitmap alphaMask = getAlphaMaskFromSoftMask(softMask);
if (alphaMask.getWidth() != result.getWidth() || alphaMask.getHeight() != result.getHeight())
{
// Scale the alpha mask, if it is masked
alphaMask = alphaMask.resize(result.getWidth(), result.getHeight(), useSmoothImageTransformation ? Qt::SmoothTransformation : Qt::FastTransformation);
}
Q_ASSERT(alphaMask.getPixelFormat().getChannelCount() == 2);
Q_ASSERT(alphaMask.getPixelFormat().hasOpacityChannel());
Q_ASSERT(alphaMask.getPixelFormat().hasShapeChannel());
Q_ASSERT(result.getPixelFormat().hasShapeChannel());
Q_ASSERT(result.getPixelFormat().hasOpacityChannel());
const uint8_t sourceShapeChannelIndex = alphaMask.getPixelFormat().getShapeChannelIndex();
const uint8_t sourceOpacityChannelIndex = alphaMask.getPixelFormat().getOpacityChannelIndex();
const uint8_t targetShapeChannelIndex = result.getPixelFormat().getShapeChannelIndex();
const uint8_t targetOpacityChannelIndex = result.getPixelFormat().getOpacityChannelIndex();
for (size_t i = 0; i < imageHeight; ++i)
{
for (unsigned int j = 0; j < imageWidth; ++j)
{
PDFColorBuffer targetBuffer = result.getPixel(j, i);
PDFColorBuffer alphaBuffer = alphaMask.getPixel(j, i);
targetBuffer[targetShapeChannelIndex] = alphaBuffer[sourceShapeChannelIndex];
targetBuffer[targetOpacityChannelIndex] = alphaBuffer[sourceOpacityChannelIndex];
}
}
break;
}
default:
throw PDFRendererException(RenderErrorType::NotImplemented, PDFTranslationContext::tr("Image masking not implemented!"));
}
}
else
{
switch (imageData.getMaskingType()) switch (imageData.getMaskingType())
{ {
case PDFImageData::MaskingType::None: case PDFImageData::MaskingType::None:
@ -1380,11 +1501,53 @@ PDFFloatBitmapWithColorSpace PDFTransparencyRenderer::getColoredImage(const PDFI
throw PDFRendererException(RenderErrorType::NotImplemented, PDFTranslationContext::tr("Image masking not implemented!")); throw PDFRendererException(RenderErrorType::NotImplemented, PDFTranslationContext::tr("Image masking not implemented!"));
} }
} }
/* }
mapovani barev, pripadne prevod do blend color space
vyresit indexed color space // Jakub Melka: We are mapping into draw buffer, so we must use draw buffer pixel format
vyresit aktivni barvy PDFInkMapping inkMapping = m_inkMapper->createMapping(imageColorSpace.data(), getBlendColorSpace().data(), m_drawBuffer.getPixelFormat());
*/ if (!inkMapping.isValid())
{
result.convertToColorSpace(getCMS(), getGraphicState()->getRenderingIntent(), getBlendColorSpace(), this);
inkMapping = m_inkMapper->createMapping(getBlendColorSpace().data(), getBlendColorSpace().data(), m_drawBuffer.getPixelFormat());
}
Q_ASSERT(inkMapping.isValid());
PDFFloatBitmapWithColorSpace finalResult(result.getWidth(), result.getHeight(), m_drawBuffer.getPixelFormat(), getBlendColorSpace());
const uint8_t sourceBufferShapeChannelIndex = result.getPixelFormat().getShapeChannelIndex();
const uint8_t sourceBufferOpacityChannelIndex = result.getPixelFormat().getOpacityChannelIndex();
const uint8_t targetBufferShapeChannelIndex = finalResult.getPixelFormat().getShapeChannelIndex();
const uint8_t targetBufferOpacityChannelIndex = finalResult.getPixelFormat().getOpacityChannelIndex();
for (size_t y = 0; y < result.getHeight(); ++y)
{
for (size_t x = 0; x < result.getWidth(); ++x)
{
PDFColorBuffer sourceBuffer = result.getPixel(x, y);
PDFColorBuffer targetBuffer = finalResult.getPixel(x, y);
for (const PDFInkMapping::Mapping& ink : inkMapping.mapping)
{
switch (ink.type)
{
case pdf::PDFInkMapping::Pass:
targetBuffer[ink.target] = sourceBuffer[ink.source];
break;
default:
Q_ASSERT(false);
break;
}
}
targetBuffer[targetBufferShapeChannelIndex] = sourceBuffer[sourceBufferShapeChannelIndex];
targetBuffer[targetBufferOpacityChannelIndex] = sourceBuffer[sourceBufferOpacityChannelIndex];
}
}
result = qMove(finalResult);
result.setColorActivity(inkMapping.activeChannels);
return result; return result;
} }