Issue #10: Ability to cancel the operation

This commit is contained in:
Jakub Melka
2022-02-04 20:03:23 +01:00
parent 284f0c4db8
commit 49cab7937a
15 changed files with 302 additions and 61 deletions

View File

@ -142,6 +142,7 @@ HEADERS += \
sources/pdfobjecteditorwidget.h \
sources/pdfobjecteditorwidget_impl.h \
sources/pdfobjectutils.h \
sources/pdfoperationcontrol.h \
sources/pdfoptimizer.h \
sources/pdfoptionalcontent.h \
sources/pdfoutline.h \

View File

@ -218,7 +218,8 @@ QImage PDFAbstractColorSpace::getImage(const PDFImageData& imageData,
const PDFImageData& softMask,
const PDFCMS* cms,
RenderingIntent intent,
PDFRenderErrorReporter* reporter) const
PDFRenderErrorReporter* reporter,
const PDFOperationControl* operationControl) const
{
if (imageData.isValid())
{
@ -249,6 +250,12 @@ QImage PDFAbstractColorSpace::getImage(const PDFImageData& imageData,
auto transformPixelLine = [&](unsigned int i)
{
// Is operation being cancelled?
if (PDFOperationControl::isOperationCancelled(operationControl))
{
return;
}
try
{
PDFBitReader reader(&imageData.getData(), imageData.getBitsPerComponent());
@ -260,23 +267,29 @@ QImage PDFAbstractColorSpace::getImage(const PDFImageData& imageData,
std::vector<float> inputColors(imageWidth * componentCount, 0.0f);
auto itInputColor = inputColors.begin();
if (!decode.empty())
{
// Interpolate value
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())
{
*itInputColor++ = interpolate(value, 0.0, max, decode[2 * k], decode[2 * k + 1]);
}
}
}
else
{
// Just read all values and multiply them with coefficient
const unsigned int pixelCount = imageData.getWidth() * componentCount;
for (unsigned int j = 0; j < pixelCount; ++j)
{
PDFReal value = reader.read();
*itInputColor++ = value * coefficient;
}
}
}
fillRGBBuffer(inputColors, outputLine, intent, cms, reporter);
}
@ -334,6 +347,12 @@ QImage PDFAbstractColorSpace::getImage(const PDFImageData& imageData,
auto transformPixelLine = [&](unsigned int i)
{
// Is operation being cancelled?
if (PDFOperationControl::isOperationCancelled(operationControl))
{
return;
}
try
{
PDFBitReader reader(&imageData.getData(), imageData.getBitsPerComponent());
@ -1872,7 +1891,8 @@ QImage PDFIndexedColorSpace::getImage(const PDFImageData& imageData,
const PDFImageData& softMask,
const PDFCMS* cms,
RenderingIntent intent,
PDFRenderErrorReporter* reporter) const
PDFRenderErrorReporter* reporter,
const PDFOperationControl* operationControl) const
{
if (imageData.isValid())
{
@ -1898,6 +1918,12 @@ QImage PDFIndexedColorSpace::getImage(const PDFImageData& imageData,
for (unsigned int i = 0, rowCount = imageData.getHeight(); i < rowCount; ++i)
{
// Is operation being cancelled?
if (PDFOperationControl::isOperationCancelled(operationControl))
{
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Operation cancelled!"));
}
reader.seek(i * imageData.getStride());
unsigned char* outputLine = image.scanLine(i);
@ -1945,6 +1971,12 @@ QImage PDFIndexedColorSpace::getImage(const PDFImageData& imageData,
for (unsigned int i = 0, rowCount = imageData.getHeight(); i < rowCount; ++i)
{
// Is operation being cancelled?
if (PDFOperationControl::isOperationCancelled(operationControl))
{
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Operation cancelled!"));
}
reader.seek(i * imageData.getStride());
unsigned char* outputLine = image.scanLine(i);
unsigned char* alphaLine = alphaMask.scanLine(i);

View File

@ -21,6 +21,7 @@
#include "pdfflatarray.h"
#include "pdffunction.h"
#include "pdfutils.h"
#include "pdfoperationcontrol.h"
#include <QColor>
#include <QImage>
@ -365,11 +366,13 @@ public:
/// \param cms Color management system
/// \param intent Rendering intent
/// \param reporter Error reporter
/// \param operationControl Operation control
virtual QImage getImage(const PDFImageData& imageData,
const PDFImageData& softMask,
const PDFCMS* cms,
RenderingIntent intent,
PDFRenderErrorReporter* reporter) const;
PDFRenderErrorReporter* reporter,
const PDFOperationControl* operationControl) const;
/// Fills RGB buffer using colors from \p colors. Colors are transformed
/// by this color space (or color management system is used). Buffer
@ -755,7 +758,8 @@ public:
const PDFImageData& softMask,
const PDFCMS* cms,
RenderingIntent intent,
PDFRenderErrorReporter* reporter) const override;
PDFRenderErrorReporter* reporter,
const PDFOperationControl* operationControl) const override;
/// Creates indexed color space from provided values.
/// \param colorSpaceDictionary Color space dictionary

View File

@ -64,11 +64,12 @@ void PDFAsynchronousPageCompilerWorkerThread::run()
auto proxy = m_compiler->getProxy();
proxy->getFontCache()->setCacheShrinkEnabled(this, false);
auto compilePage = [proxy](PDFAsynchronousPageCompiler::CompileTask& task) -> PDFPrecompiledPage
auto compilePage = [this, proxy](PDFAsynchronousPageCompiler::CompileTask& task) -> PDFPrecompiledPage
{
PDFPrecompiledPage compiledPage;
PDFCMSPointer cms = proxy->getCMSManager()->getCurrentCMS();
PDFRenderer renderer(proxy->getDocument(), proxy->getFontCache(), cms.data(), proxy->getOptionalContentActivity(), proxy->getFeatures(), proxy->getMeshQualitySettings());
renderer.setOperationControl(m_compiler);
renderer.compile(&task.precompiledPage, task.pageIndex);
task.finished = true;
return compiledPage;
@ -122,6 +123,11 @@ PDFAsynchronousPageCompiler::~PDFAsynchronousPageCompiler()
stop(true);
}
bool PDFAsynchronousPageCompiler::isOperationCancelled() const
{
return m_state == State::Stopping;
}
void PDFAsynchronousPageCompiler::start()
{
switch (m_state)

View File

@ -54,7 +54,7 @@ private:
/// Asynchronous page compiler compiles pages asynchronously, and stores them in the
/// cache. Cache size can be set. This object is designed to cooperate with
/// draw widget proxy.
class PDFAsynchronousPageCompiler : public QObject
class PDFAsynchronousPageCompiler : public QObject, public PDFOperationControl
{
Q_OBJECT
@ -106,6 +106,9 @@ public:
/// \param compile Compile the page, if it is not found in the cache
const PDFPrecompiledPage* getCompiledPage(PDFInteger pageIndex, bool compile);
/// Is operation being cancelled?
virtual bool isOperationCancelled() const override;
signals:
void pageImageChanged(bool all, const std::vector<pdf::PDFInteger>& pages);
void renderingError(pdf::PDFInteger pageIndex, const QList<pdf::PDFRenderError>& errors);

View File

@ -745,12 +745,14 @@ PDFImage PDFImage::createImage(const PDFDocument* document,
return image;
}
QImage PDFImage::getImage(const PDFCMS* cms, PDFRenderErrorReporter* reporter) const
QImage PDFImage::getImage(const PDFCMS* cms,
PDFRenderErrorReporter* reporter,
const PDFOperationControl* operationControl) const
{
const bool isImageMask = m_imageData.getMaskingType() == PDFImageData::MaskingType::ImageMask;
if (m_colorSpace && !isImageMask)
{
return m_colorSpace->getImage(m_imageData, m_softMask, cms, m_renderingIntent, reporter);
return m_colorSpace->getImage(m_imageData, m_softMask, cms, m_renderingIntent, reporter, operationControl);
}
else if (isImageMask)
{

View File

@ -20,6 +20,7 @@
#include "pdfobject.h"
#include "pdfcolorspaces.h"
#include "pdfoperationcontrol.h"
#include <QByteArray>
@ -73,7 +74,9 @@ public:
PDFRenderErrorReporter* errorReporter);
/// Returns image transformed from image data and color space
QImage getImage(const PDFCMS* cms, PDFRenderErrorReporter* reporter) const;
QImage getImage(const PDFCMS* cms,
PDFRenderErrorReporter* reporter,
const PDFOperationControl* operationControl) const;
/// Returns rendering intent of the image
RenderingIntent getRenderingIntent() const { return m_renderingIntent; }

View File

@ -0,0 +1,51 @@
// Copyright (C) 2022 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#ifndef PDFOPERATIONCONTROL_H
#define PDFOPERATIONCONTROL_H
#include "pdfglobal.h"
namespace pdf
{
/// Operation controller. This interface can be used for
/// long operation interruption - so operation is cancelled
/// immediately.
class PDFOperationControl
{
public:
constexpr PDFOperationControl() = default;
virtual ~PDFOperationControl() = default;
/// Returns true, if operation is cancelled and
/// processed data should be abandoned. It is safe
/// to call this function in another thread, implementators
/// of this interface must ensure thread safety.
/// If this function returns true, it must return true until
/// all operations are stopped and correctly handled.
virtual bool isOperationCancelled() const = 0;
static inline bool isOperationCancelled(const PDFOperationControl* operationControl)
{
return operationControl && operationControl->isOperationCancelled();
}
};
}
#endif // PDFOPERATIONCONTROL_H

View File

@ -232,6 +232,7 @@ PDFPageContentProcessor::PDFPageContentProcessor(const PDFPage* page,
m_fontCache(fontCache),
m_CMS(CMS),
m_optionalContentActivity(optionalContentActivity),
m_operationControl(nullptr),
m_colorSpaceDictionary(nullptr),
m_fontDictionary(nullptr),
m_xobjectDictionary(nullptr),
@ -311,6 +312,12 @@ QList<PDFRenderError> PDFPageContentProcessor::processContents()
for (size_t i = 0; i < count; ++i)
{
if (isProcessingCancelled())
{
// Break, if processing is being cancelled
break;
}
const PDFObject& streamObject = m_document->getObject(array->getItem(i));
if (streamObject.isStream())
{
@ -523,7 +530,7 @@ void PDFPageContentProcessor::processContent(const QByteArray& content)
{
PDFLexicalAnalyzer parser(content.constBegin(), content.constEnd());
while (!parser.isAtEnd())
while (!parser.isAtEnd() && !isProcessingCancelled())
{
bool tokenFetched = false;
PDFInteger oldParserPosition = parser.pos();
@ -844,7 +851,7 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool
if (!performPathPaintingUsingShading(path, false, true, shadingPattern))
{
PDFMesh mesh = shadingPattern->createMesh(settings, m_CMS, m_graphicState.getRenderingIntent(), this);
PDFMesh mesh = shadingPattern->createMesh(settings, m_CMS, m_graphicState.getRenderingIntent(), this, m_operationControl);
// Now, merge the current path to the mesh clipping path
QPainterPath boundingPath = mesh.getBoundingPath();
@ -961,7 +968,7 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool
if (!performPathPaintingUsingShading(strokedPath, true, false, shadingPattern))
{
PDFMesh mesh = shadingPattern->createMesh(settings, m_CMS, m_graphicState.getRenderingIntent(), this);
PDFMesh mesh = shadingPattern->createMesh(settings, m_CMS, m_graphicState.getRenderingIntent(), this, m_operationControl);
QPainterPath boundingPath = mesh.getBoundingPath();
if (boundingPath.isEmpty())
@ -1095,6 +1102,16 @@ void PDFPageContentProcessor::processTillingPatternPainting(const PDFTilingPatte
performClipping(boundingPath, boundingPath.fillRule());
processContent(content);
if (isProcessingCancelled())
{
break;
}
}
if (isProcessingCancelled())
{
break;
}
}
}
@ -1831,6 +1848,16 @@ void PDFPageContentProcessor::finishMarkedContent()
}
}
void PDFPageContentProcessor::setOperationControl(const PDFOperationControl* newOperationControl)
{
m_operationControl = newOperationControl;
}
bool PDFPageContentProcessor::isProcessingCancelled() const
{
return m_operationControl && m_operationControl->isOperationCancelled();
}
void PDFPageContentProcessor::reportRenderErrorOnce(RenderErrorType type, QString message)
{
if (!m_onceReportedErrors.count(message))
@ -2952,8 +2979,10 @@ void PDFPageContentProcessor::paintXObjectImage(const PDFStream* stream)
if (!performOriginalImagePainting(pdfImage))
{
QImage image = pdfImage.getImage(m_CMS, this);
QImage image = pdfImage.getImage(m_CMS, this, m_operationControl);
if (!isProcessingCancelled())
{
if (image.format() == QImage::Format_Alpha8)
{
QSize size = image.size();
@ -2972,6 +3001,7 @@ void PDFPageContentProcessor::paintXObjectImage(const PDFStream* stream)
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Can't decode the image."));
}
}
}
}
void PDFPageContentProcessor::reportWarningAboutColorOperatorsInUTP()

View File

@ -26,6 +26,7 @@
#include "pdfmeshqualitysettings.h"
#include "pdfblendfunction.h"
#include "pdftextlayout.h"
#include "pdfoperationcontrol.h"
#include <QMatrix>
#include <QPainterPath>
@ -237,6 +238,15 @@ public:
/// or true, if it is suppressed.
virtual bool isContentSuppressedByOC(PDFObjectReference ocgOrOcmd);
/// Sets operation control object which can decide, if operation should
/// be cancelled. If this is the case, page content processor stops
/// processing page contents.
/// \param newOperationControl Operation control object
void setOperationControl(const PDFOperationControl* newOperationControl);
/// Returns true, if page content processing is being cancelled
bool isProcessingCancelled() const;
protected:
struct PDFTransparencyGroup
@ -1009,6 +1019,7 @@ private:
const PDFFontCache* m_fontCache;
const PDFCMS* m_CMS;
const PDFOptionalContentActivity* m_optionalContentActivity;
const PDFOperationControl* m_operationControl;
const PDFDictionary* m_colorSpaceDictionary;
const PDFDictionary* m_fontDictionary;
const PDFDictionary* m_xobjectDictionary;

View File

@ -608,10 +608,16 @@ ShadingType PDFFunctionShading::getShadingType() const
return ShadingType::Function;
}
PDFMesh PDFFunctionShading::createMesh(const PDFMeshQualitySettings& settings, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const
PDFMesh PDFFunctionShading::createMesh(const PDFMeshQualitySettings& settings,
const PDFCMS* cms,
RenderingIntent intent,
PDFRenderErrorReporter* reporter,
const PDFOperationControl* operationControl) const
{
PDFMesh mesh;
Q_UNUSED(operationControl);
QMatrix patternSpaceToDeviceSpaceMatrix = getPatternSpaceToDeviceSpaceMatrix(settings);
QMatrix domainToDeviceSpaceMatrix = m_domainToTargetTransform * patternSpaceToDeviceSpaceMatrix;
QLineF topLine(m_domain.topLeft(), m_domain.topRight());
@ -916,10 +922,16 @@ PDFShadingSampler* PDFFunctionShading::createSampler(QMatrix userSpaceToDeviceSp
return new PDFFunctionShadingSampler(this, userSpaceToDeviceSpaceMatrix);
}
PDFMesh PDFAxialShading::createMesh(const PDFMeshQualitySettings& settings, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const
PDFMesh PDFAxialShading::createMesh(const PDFMeshQualitySettings& settings,
const PDFCMS* cms,
RenderingIntent intent,
PDFRenderErrorReporter* reporter,
const PDFOperationControl* operationControl) const
{
PDFMesh mesh;
Q_UNUSED(operationControl);
QMatrix patternSpaceToDeviceSpaceMatrix = getPatternSpaceToDeviceSpaceMatrix(settings);
QPointF p1 = patternSpaceToDeviceSpaceMatrix.map(m_startPoint);
QPointF p2 = patternSpaceToDeviceSpaceMatrix.map(m_endPoint);
@ -1423,10 +1435,16 @@ ShadingType PDFRadialShading::getShadingType() const
return ShadingType::Radial;
}
PDFMesh PDFRadialShading::createMesh(const PDFMeshQualitySettings& settings, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const
PDFMesh PDFRadialShading::createMesh(const PDFMeshQualitySettings& settings,
const PDFCMS* cms,
RenderingIntent intent,
PDFRenderErrorReporter* reporter,
const PDFOperationControl* operationControl) const
{
PDFMesh mesh;
Q_UNUSED(operationControl);
QMatrix patternSpaceToDeviceSpaceMatrix = getPatternSpaceToDeviceSpaceMatrix(settings);
QPointF p1 = patternSpaceToDeviceSpaceMatrix.map(m_startPoint);
QPointF p2 = patternSpaceToDeviceSpaceMatrix.map(m_endPoint);
@ -2239,10 +2257,16 @@ bool PDFFreeFormGouradTriangleShading::processTriangles(InitializeFunction initi
return true;
}
PDFMesh PDFFreeFormGouradTriangleShading::createMesh(const PDFMeshQualitySettings& settings, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const
PDFMesh PDFFreeFormGouradTriangleShading::createMesh(const PDFMeshQualitySettings& settings,
const PDFCMS* cms,
RenderingIntent intent,
PDFRenderErrorReporter* reporter,
const PDFOperationControl* operationControl) const
{
PDFMesh mesh;
Q_UNUSED(operationControl);
auto addTriangle = [this, &settings, &mesh, cms, intent, reporter](const VertexData* va, const VertexData* vb, const VertexData* vc)
{
const uint32_t via = va->index;
@ -2401,10 +2425,16 @@ bool PDFLatticeFormGouradTriangleShading::processTriangles(InitializeFunction in
return true;
}
PDFMesh PDFLatticeFormGouradTriangleShading::createMesh(const PDFMeshQualitySettings& settings, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const
PDFMesh PDFLatticeFormGouradTriangleShading::createMesh(const PDFMeshQualitySettings& settings,
const PDFCMS* cms,
RenderingIntent intent,
PDFRenderErrorReporter* reporter,
const PDFOperationControl* operationControl) const
{
PDFMesh mesh;
Q_UNUSED(operationControl);
auto addTriangle = [this, &settings, &mesh, cms, intent, reporter](const VertexData* va, const VertexData* vb, const VertexData* vc)
{
const uint32_t via = va->index;
@ -3071,7 +3101,11 @@ PDFTensorPatches PDFTensorProductPatchShading::createPatches(QMatrix userSpaceTo
return patches;
}
PDFMesh PDFTensorProductPatchShading::createMesh(const PDFMeshQualitySettings& settings, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const
PDFMesh PDFTensorProductPatchShading::createMesh(const PDFMeshQualitySettings& settings,
const PDFCMS* cms,
RenderingIntent intent,
PDFRenderErrorReporter* reporter,
const PDFOperationControl* operationControl) const
{
PDFMesh mesh;
@ -3082,7 +3116,7 @@ PDFMesh PDFTensorProductPatchShading::createMesh(const PDFMeshQualitySettings& s
throw PDFException(PDFTranslationContext::tr("Invalid data in tensor product patch shading."));
}
fillMesh(mesh, getPatternSpaceToDeviceSpaceMatrix(settings.userSpaceToDeviceSpaceMatrix), settings, patches, cms, intent, reporter);
fillMesh(mesh, getPatternSpaceToDeviceSpaceMatrix(settings.userSpaceToDeviceSpaceMatrix), settings, patches, cms, intent, reporter, operationControl);
return mesh;
}
@ -3228,7 +3262,8 @@ void PDFTensorProductPatchShadingBase::fillMesh(PDFMesh& mesh,
const PDFCMS* cms,
RenderingIntent intent,
PDFRenderErrorReporter* reporter,
bool fastAlgorithm) const
bool fastAlgorithm,
const PDFOperationControl* operationControl) const
{
// We implement algorithm similar to Ruppert's algorithm (see https://en.wikipedia.org/wiki/Ruppert%27s_algorithm), but
// we do not need a mesh for FEM calculation, so we do not care about quality of the triangles (we can have triangles with
@ -3301,6 +3336,13 @@ void PDFTensorProductPatchShadingBase::fillMesh(PDFMesh& mesh,
while (!unfinishedTriangles.empty())
{
// Mesh generation is cancelled
if (PDFOperationControl::isOperationCancelled(operationControl))
{
mesh = PDFMesh();
return;
}
Triangle triangle = unfinishedTriangles.back();
unfinishedTriangles.pop_back();
@ -3417,12 +3459,13 @@ void PDFTensorProductPatchShadingBase::fillMesh(PDFMesh& mesh,
const PDFTensorPatches& patches,
const PDFCMS* cms,
RenderingIntent intent,
PDFRenderErrorReporter* reporter) const
PDFRenderErrorReporter* reporter,
const PDFOperationControl* operationControl) const
{
const bool fastAlgorithm = patches.size() > 16;
for (const auto& patch : patches)
{
fillMesh(mesh, settings, patch, cms, intent, reporter, fastAlgorithm);
fillMesh(mesh, settings, patch, cms, intent, reporter, fastAlgorithm, operationControl);
}
// Create bounding path
@ -3655,7 +3698,11 @@ PDFTensorPatches PDFCoonsPatchShading::createPatches(QMatrix userSpaceToDeviceSp
return patches;
}
PDFMesh PDFCoonsPatchShading::createMesh(const PDFMeshQualitySettings& settings, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const
PDFMesh PDFCoonsPatchShading::createMesh(const PDFMeshQualitySettings& settings,
const PDFCMS* cms,
RenderingIntent intent,
PDFRenderErrorReporter* reporter,
const PDFOperationControl* operationControl) const
{
PDFMesh mesh;
PDFTensorPatches patches = createPatches(settings.userSpaceToDeviceSpaceMatrix, true);
@ -3665,7 +3712,7 @@ PDFMesh PDFCoonsPatchShading::createMesh(const PDFMeshQualitySettings& settings,
throw PDFException(PDFTranslationContext::tr("Invalid data in coons patch shading."));
}
fillMesh(mesh, getPatternSpaceToDeviceSpaceMatrix(settings), settings, patches, cms, intent, reporter);
fillMesh(mesh, getPatternSpaceToDeviceSpaceMatrix(settings), settings, patches, cms, intent, reporter, operationControl);
return mesh;
}

View File

@ -311,7 +311,11 @@ public:
/// \param cms Color management system
/// \param intent Rendering intent
/// \param reporter Error reporter
virtual PDFMesh createMesh(const PDFMeshQualitySettings& settings, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const = 0;
/// \param operationControl Operation control
virtual PDFMesh createMesh(const PDFMeshQualitySettings& settings,
const PDFCMS* cms, RenderingIntent intent,
PDFRenderErrorReporter* reporter,
const PDFOperationControl* operationControl) const = 0;
/// Returns patterns graphic state. This state must be applied before
/// the shading pattern is painted to the target device.
@ -386,7 +390,11 @@ public:
explicit PDFFunctionShading() = default;
virtual ShadingType getShadingType() const override;
virtual PDFMesh createMesh(const PDFMeshQualitySettings& settings, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const override;
virtual PDFMesh createMesh(const PDFMeshQualitySettings& settings,
const PDFCMS* cms,
RenderingIntent intent,
PDFRenderErrorReporter* reporter,
const PDFOperationControl* operationControl) const override;
virtual PDFShadingSampler* createSampler(QMatrix userSpaceToDeviceSpaceMatrix) const override;
const QRectF& getDomain() const { return m_domain; }
@ -407,7 +415,11 @@ public:
explicit PDFAxialShading() = default;
virtual ShadingType getShadingType() const override;
virtual PDFMesh createMesh(const PDFMeshQualitySettings& settings, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const override;
virtual PDFMesh createMesh(const PDFMeshQualitySettings& settings,
const PDFCMS* cms,
RenderingIntent intent,
PDFRenderErrorReporter* reporter,
const PDFOperationControl* operationControl) const override;
virtual PDFShadingSampler* createSampler(QMatrix userSpaceToDeviceSpaceMatrix) const override;
private:
@ -420,7 +432,11 @@ public:
explicit PDFRadialShading() = default;
virtual ShadingType getShadingType() const override;
virtual PDFMesh createMesh(const PDFMeshQualitySettings& settings, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const override;
virtual PDFMesh createMesh(const PDFMeshQualitySettings& settings,
const PDFCMS* cms,
RenderingIntent intent,
PDFRenderErrorReporter* reporter,
const PDFOperationControl* operationControl) const override;
virtual PDFShadingSampler* createSampler(QMatrix userSpaceToDeviceSpaceMatrix) const override;
PDFReal getR0() const { return m_r0; }
@ -480,7 +496,11 @@ public:
explicit PDFFreeFormGouradTriangleShading() = default;
virtual ShadingType getShadingType() const override;
virtual PDFMesh createMesh(const PDFMeshQualitySettings& settings, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const override;
virtual PDFMesh createMesh(const PDFMeshQualitySettings& settings,
const PDFCMS* cms,
RenderingIntent intent,
PDFRenderErrorReporter* reporter,
const PDFOperationControl* operationControl) const override;
virtual PDFShadingSampler* createSampler(QMatrix userSpaceToDeviceSpaceMatrix) const override;
private:
@ -509,7 +529,11 @@ public:
explicit PDFLatticeFormGouradTriangleShading() = default;
virtual ShadingType getShadingType() const override;
virtual PDFMesh createMesh(const PDFMeshQualitySettings& settings, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const override;
virtual PDFMesh createMesh(const PDFMeshQualitySettings& settings,
const PDFCMS* cms,
RenderingIntent intent,
PDFRenderErrorReporter* reporter,
const PDFOperationControl* operationControl) const override;
virtual PDFShadingSampler* createSampler(QMatrix userSpaceToDeviceSpaceMatrix) const override;
private:
@ -675,8 +699,8 @@ public:
protected:
struct Triangle;
void fillMesh(PDFMesh& mesh, const PDFMeshQualitySettings& settings, const PDFTensorPatch& patch, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter, bool fastAlgorithm) const;
void fillMesh(PDFMesh& mesh, const QMatrix& patternSpaceToDeviceSpaceMatrix, const PDFMeshQualitySettings& settings, const PDFTensorPatches& patches, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const;
void fillMesh(PDFMesh& mesh, const PDFMeshQualitySettings& settings, const PDFTensorPatch& patch, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter, bool fastAlgorithm, const PDFOperationControl* operationControl) const;
void fillMesh(PDFMesh& mesh, const QMatrix& patternSpaceToDeviceSpaceMatrix, const PDFMeshQualitySettings& settings, const PDFTensorPatches& patches, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter, const PDFOperationControl* operationControl) const;
static void addTriangle(std::vector<Triangle>& triangles, const PDFTensorPatch& patch, std::array<QPointF, 3> uvCoordinates);
private:
@ -689,7 +713,11 @@ public:
explicit PDFCoonsPatchShading() = default;
virtual ShadingType getShadingType() const override;
virtual PDFMesh createMesh(const PDFMeshQualitySettings& settings, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const override;
virtual PDFMesh createMesh(const PDFMeshQualitySettings& settings,
const PDFCMS* cms,
RenderingIntent intent,
PDFRenderErrorReporter* reporter,
const PDFOperationControl* operationControl) const override;
virtual PDFTensorPatches createPatches(QMatrix userSpaceToDeviceSpaceMatrix, bool transformColor) const override;
private:
@ -702,7 +730,11 @@ public:
explicit PDFTensorProductPatchShading() = default;
virtual ShadingType getShadingType() const override;
virtual PDFMesh createMesh(const PDFMeshQualitySettings& settings, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const override;
virtual PDFMesh createMesh(const PDFMeshQualitySettings& settings,
const PDFCMS* cms,
RenderingIntent intent,
PDFRenderErrorReporter* reporter,
const PDFOperationControl* operationControl) const override;
virtual PDFTensorPatches createPatches(QMatrix userSpaceToDeviceSpaceMatrix, bool transformColor) const override;
private:

View File

@ -43,6 +43,7 @@ PDFRenderer::PDFRenderer(const PDFDocument* document,
m_fontCache(fontCache),
m_cms(cms),
m_optionalContentActivity(optionalContentActivity),
m_operationControl(nullptr),
m_features(features),
m_meshQualitySettings(meshQualitySettings)
{
@ -107,6 +108,16 @@ QMatrix PDFRenderer::createMediaBoxToDevicePointMatrix(const QRectF& mediaBox,
return matrix;
}
const PDFOperationControl* PDFRenderer::getOperationControl() const
{
return m_operationControl;
}
void PDFRenderer::setOperationControl(const PDFOperationControl* newOperationControl)
{
m_operationControl = newOperationControl;
}
QList<PDFRenderError> PDFRenderer::render(QPainter* painter, const QRectF& rectangle, size_t pageIndex) const
{
const PDFCatalog* catalog = m_document->getCatalog();
@ -122,6 +133,7 @@ QList<PDFRenderError> PDFRenderer::render(QPainter* painter, const QRectF& recta
QMatrix matrix = createPagePointToDevicePointMatrix(page, rectangle);
PDFPainter processor(painter, m_features, matrix, page, m_document, m_fontCache, m_cms, m_optionalContentActivity, m_meshQualitySettings);
processor.setOperationControl(m_operationControl);
return processor.processContents();
}
@ -138,6 +150,7 @@ QList<PDFRenderError> PDFRenderer::render(QPainter* painter, const QMatrix& matr
Q_ASSERT(page);
PDFPainter processor(painter, m_features, matrix, page, m_document, m_fontCache, m_cms, m_optionalContentActivity, m_meshQualitySettings);
processor.setOperationControl(m_operationControl);
return processor.processContents();
}
@ -158,6 +171,7 @@ void PDFRenderer::compile(PDFPrecompiledPage* precompiledPage, size_t pageIndex)
timer.start();
PDFPrecompiledPageGenerator generator(precompiledPage, m_features, page, m_document, m_fontCache, m_cms, m_optionalContentActivity, m_meshQualitySettings);
generator.setOperationControl(m_operationControl);
QList<PDFRenderError> errors = generator.processContents();
if (m_features.testFlag(InvertColors))

View File

@ -20,6 +20,7 @@
#include "pdfpage.h"
#include "pdfexception.h"
#include "pdfoperationcontrol.h"
#include "pdfmeshqualitysettings.h"
#include <QMutex>
@ -113,11 +114,15 @@ public:
/// Returns default renderer features
static constexpr Features getDefaultFeatures() { return Features(Antialiasing | TextAntialiasing | ClipToCropBox | DisplayAnnotations); }
const PDFOperationControl* getOperationControl() const;
void setOperationControl(const PDFOperationControl* newOperationControl);
private:
const PDFDocument* m_document;
const PDFFontCache* m_fontCache;
const PDFCMS* m_cms;
const PDFOptionalContentActivity* m_optionalContentActivity;
const PDFOperationControl* m_operationControl;
Features m_features;
PDFMeshQualitySettings m_meshQualitySettings;
};

View File

@ -228,7 +228,7 @@ void ObjectViewerWidget::updateUi()
pdf::PDFRenderErrorReporterDummy dummyErrorReporter;
pdf::PDFImage pdfImage = pdf::PDFImage::createImage(m_document, stream, qMove(colorSpace), false, pdf::RenderingIntent::Perceptual, &dummyErrorReporter);
QImage image = pdfImage.getImage(m_cms, &dummyErrorReporter);
QImage image = pdfImage.getImage(m_cms, &dummyErrorReporter, nullptr);
ui->stackedWidget->setCurrentWidget(ui->imageBrowserPage);
ui->imageBrowser->setPixmap(QPixmap::fromImage(image));