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.h \
sources/pdfobjecteditorwidget_impl.h \ sources/pdfobjecteditorwidget_impl.h \
sources/pdfobjectutils.h \ sources/pdfobjectutils.h \
sources/pdfoperationcontrol.h \
sources/pdfoptimizer.h \ sources/pdfoptimizer.h \
sources/pdfoptionalcontent.h \ sources/pdfoptionalcontent.h \
sources/pdfoutline.h \ sources/pdfoutline.h \

View File

@ -218,7 +218,8 @@ QImage PDFAbstractColorSpace::getImage(const PDFImageData& imageData,
const PDFImageData& softMask, const PDFImageData& softMask,
const PDFCMS* cms, const PDFCMS* cms,
RenderingIntent intent, RenderingIntent intent,
PDFRenderErrorReporter* reporter) const PDFRenderErrorReporter* reporter,
const PDFOperationControl* operationControl) const
{ {
if (imageData.isValid()) if (imageData.isValid())
{ {
@ -249,6 +250,12 @@ QImage PDFAbstractColorSpace::getImage(const PDFImageData& imageData,
auto transformPixelLine = [&](unsigned int i) auto transformPixelLine = [&](unsigned int i)
{ {
// Is operation being cancelled?
if (PDFOperationControl::isOperationCancelled(operationControl))
{
return;
}
try try
{ {
PDFBitReader reader(&imageData.getData(), imageData.getBitsPerComponent()); PDFBitReader reader(&imageData.getData(), imageData.getBitsPerComponent());
@ -260,21 +267,27 @@ QImage PDFAbstractColorSpace::getImage(const PDFImageData& imageData,
std::vector<float> inputColors(imageWidth * componentCount, 0.0f); std::vector<float> inputColors(imageWidth * componentCount, 0.0f);
auto itInputColor = inputColors.begin(); auto itInputColor = inputColors.begin();
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())
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();
*itInputColor++ = interpolate(value, 0.0, max, decode[2 * k], decode[2 * k + 1]); *itInputColor++ = interpolate(value, 0.0, max, decode[2 * k], decode[2 * k + 1]);
} }
else }
{ }
*itInputColor++ = value * coefficient; 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;
} }
} }
@ -334,6 +347,12 @@ QImage PDFAbstractColorSpace::getImage(const PDFImageData& imageData,
auto transformPixelLine = [&](unsigned int i) auto transformPixelLine = [&](unsigned int i)
{ {
// Is operation being cancelled?
if (PDFOperationControl::isOperationCancelled(operationControl))
{
return;
}
try try
{ {
PDFBitReader reader(&imageData.getData(), imageData.getBitsPerComponent()); PDFBitReader reader(&imageData.getData(), imageData.getBitsPerComponent());
@ -1872,7 +1891,8 @@ QImage PDFIndexedColorSpace::getImage(const PDFImageData& imageData,
const PDFImageData& softMask, const PDFImageData& softMask,
const PDFCMS* cms, const PDFCMS* cms,
RenderingIntent intent, RenderingIntent intent,
PDFRenderErrorReporter* reporter) const PDFRenderErrorReporter* reporter,
const PDFOperationControl* operationControl) const
{ {
if (imageData.isValid()) if (imageData.isValid())
{ {
@ -1898,6 +1918,12 @@ QImage PDFIndexedColorSpace::getImage(const PDFImageData& imageData,
for (unsigned int i = 0, rowCount = imageData.getHeight(); i < rowCount; ++i) 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()); reader.seek(i * imageData.getStride());
unsigned char* outputLine = image.scanLine(i); 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) 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()); reader.seek(i * imageData.getStride());
unsigned char* outputLine = image.scanLine(i); unsigned char* outputLine = image.scanLine(i);
unsigned char* alphaLine = alphaMask.scanLine(i); unsigned char* alphaLine = alphaMask.scanLine(i);

View File

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

View File

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

View File

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

View File

@ -745,12 +745,14 @@ PDFImage PDFImage::createImage(const PDFDocument* document,
return image; 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; const bool isImageMask = m_imageData.getMaskingType() == PDFImageData::MaskingType::ImageMask;
if (m_colorSpace && !isImageMask) 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) else if (isImageMask)
{ {

View File

@ -20,6 +20,7 @@
#include "pdfobject.h" #include "pdfobject.h"
#include "pdfcolorspaces.h" #include "pdfcolorspaces.h"
#include "pdfoperationcontrol.h"
#include <QByteArray> #include <QByteArray>
@ -73,7 +74,9 @@ public:
PDFRenderErrorReporter* errorReporter); PDFRenderErrorReporter* errorReporter);
/// Returns image transformed from image data and color space /// 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 /// Returns rendering intent of the image
RenderingIntent getRenderingIntent() const { return m_renderingIntent; } 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_fontCache(fontCache),
m_CMS(CMS), m_CMS(CMS),
m_optionalContentActivity(optionalContentActivity), m_optionalContentActivity(optionalContentActivity),
m_operationControl(nullptr),
m_colorSpaceDictionary(nullptr), m_colorSpaceDictionary(nullptr),
m_fontDictionary(nullptr), m_fontDictionary(nullptr),
m_xobjectDictionary(nullptr), m_xobjectDictionary(nullptr),
@ -311,6 +312,12 @@ QList<PDFRenderError> PDFPageContentProcessor::processContents()
for (size_t i = 0; i < count; ++i) 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)); const PDFObject& streamObject = m_document->getObject(array->getItem(i));
if (streamObject.isStream()) if (streamObject.isStream())
{ {
@ -523,7 +530,7 @@ void PDFPageContentProcessor::processContent(const QByteArray& content)
{ {
PDFLexicalAnalyzer parser(content.constBegin(), content.constEnd()); PDFLexicalAnalyzer parser(content.constBegin(), content.constEnd());
while (!parser.isAtEnd()) while (!parser.isAtEnd() && !isProcessingCancelled())
{ {
bool tokenFetched = false; bool tokenFetched = false;
PDFInteger oldParserPosition = parser.pos(); PDFInteger oldParserPosition = parser.pos();
@ -844,7 +851,7 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool
if (!performPathPaintingUsingShading(path, false, true, shadingPattern)) 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 // Now, merge the current path to the mesh clipping path
QPainterPath boundingPath = mesh.getBoundingPath(); QPainterPath boundingPath = mesh.getBoundingPath();
@ -961,7 +968,7 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool
if (!performPathPaintingUsingShading(strokedPath, true, false, shadingPattern)) 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(); QPainterPath boundingPath = mesh.getBoundingPath();
if (boundingPath.isEmpty()) if (boundingPath.isEmpty())
@ -1095,6 +1102,16 @@ void PDFPageContentProcessor::processTillingPatternPainting(const PDFTilingPatte
performClipping(boundingPath, boundingPath.fillRule()); performClipping(boundingPath, boundingPath.fillRule());
processContent(content); 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) void PDFPageContentProcessor::reportRenderErrorOnce(RenderErrorType type, QString message)
{ {
if (!m_onceReportedErrors.count(message)) if (!m_onceReportedErrors.count(message))
@ -2952,24 +2979,27 @@ void PDFPageContentProcessor::paintXObjectImage(const PDFStream* stream)
if (!performOriginalImagePainting(pdfImage)) if (!performOriginalImagePainting(pdfImage))
{ {
QImage image = pdfImage.getImage(m_CMS, this); QImage image = pdfImage.getImage(m_CMS, this, m_operationControl);
if (image.format() == QImage::Format_Alpha8) if (!isProcessingCancelled())
{ {
QSize size = image.size(); if (image.format() == QImage::Format_Alpha8)
QImage unmaskedImage(size, QImage::Format_ARGB32_Premultiplied); {
unmaskedImage.fill(m_graphicState.getFillColor()); QSize size = image.size();
unmaskedImage.setAlphaChannel(image); QImage unmaskedImage(size, QImage::Format_ARGB32_Premultiplied);
image = qMove(unmaskedImage); unmaskedImage.fill(m_graphicState.getFillColor());
} unmaskedImage.setAlphaChannel(image);
image = qMove(unmaskedImage);
}
if (!image.isNull()) if (!image.isNull())
{ {
performImagePainting(image); performImagePainting(image);
} }
else else
{ {
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Can't decode the image.")); throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Can't decode the image."));
}
} }
} }
} }

View File

@ -26,6 +26,7 @@
#include "pdfmeshqualitysettings.h" #include "pdfmeshqualitysettings.h"
#include "pdfblendfunction.h" #include "pdfblendfunction.h"
#include "pdftextlayout.h" #include "pdftextlayout.h"
#include "pdfoperationcontrol.h"
#include <QMatrix> #include <QMatrix>
#include <QPainterPath> #include <QPainterPath>
@ -237,6 +238,15 @@ public:
/// or true, if it is suppressed. /// or true, if it is suppressed.
virtual bool isContentSuppressedByOC(PDFObjectReference ocgOrOcmd); 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: protected:
struct PDFTransparencyGroup struct PDFTransparencyGroup
@ -1009,6 +1019,7 @@ private:
const PDFFontCache* m_fontCache; const PDFFontCache* m_fontCache;
const PDFCMS* m_CMS; const PDFCMS* m_CMS;
const PDFOptionalContentActivity* m_optionalContentActivity; const PDFOptionalContentActivity* m_optionalContentActivity;
const PDFOperationControl* m_operationControl;
const PDFDictionary* m_colorSpaceDictionary; const PDFDictionary* m_colorSpaceDictionary;
const PDFDictionary* m_fontDictionary; const PDFDictionary* m_fontDictionary;
const PDFDictionary* m_xobjectDictionary; const PDFDictionary* m_xobjectDictionary;

View File

@ -608,10 +608,16 @@ ShadingType PDFFunctionShading::getShadingType() const
return ShadingType::Function; 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; PDFMesh mesh;
Q_UNUSED(operationControl);
QMatrix patternSpaceToDeviceSpaceMatrix = getPatternSpaceToDeviceSpaceMatrix(settings); QMatrix patternSpaceToDeviceSpaceMatrix = getPatternSpaceToDeviceSpaceMatrix(settings);
QMatrix domainToDeviceSpaceMatrix = m_domainToTargetTransform * patternSpaceToDeviceSpaceMatrix; QMatrix domainToDeviceSpaceMatrix = m_domainToTargetTransform * patternSpaceToDeviceSpaceMatrix;
QLineF topLine(m_domain.topLeft(), m_domain.topRight()); QLineF topLine(m_domain.topLeft(), m_domain.topRight());
@ -916,10 +922,16 @@ PDFShadingSampler* PDFFunctionShading::createSampler(QMatrix userSpaceToDeviceSp
return new PDFFunctionShadingSampler(this, userSpaceToDeviceSpaceMatrix); 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; PDFMesh mesh;
Q_UNUSED(operationControl);
QMatrix patternSpaceToDeviceSpaceMatrix = getPatternSpaceToDeviceSpaceMatrix(settings); QMatrix patternSpaceToDeviceSpaceMatrix = getPatternSpaceToDeviceSpaceMatrix(settings);
QPointF p1 = patternSpaceToDeviceSpaceMatrix.map(m_startPoint); QPointF p1 = patternSpaceToDeviceSpaceMatrix.map(m_startPoint);
QPointF p2 = patternSpaceToDeviceSpaceMatrix.map(m_endPoint); QPointF p2 = patternSpaceToDeviceSpaceMatrix.map(m_endPoint);
@ -1423,10 +1435,16 @@ ShadingType PDFRadialShading::getShadingType() const
return ShadingType::Radial; 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; PDFMesh mesh;
Q_UNUSED(operationControl);
QMatrix patternSpaceToDeviceSpaceMatrix = getPatternSpaceToDeviceSpaceMatrix(settings); QMatrix patternSpaceToDeviceSpaceMatrix = getPatternSpaceToDeviceSpaceMatrix(settings);
QPointF p1 = patternSpaceToDeviceSpaceMatrix.map(m_startPoint); QPointF p1 = patternSpaceToDeviceSpaceMatrix.map(m_startPoint);
QPointF p2 = patternSpaceToDeviceSpaceMatrix.map(m_endPoint); QPointF p2 = patternSpaceToDeviceSpaceMatrix.map(m_endPoint);
@ -2239,10 +2257,16 @@ bool PDFFreeFormGouradTriangleShading::processTriangles(InitializeFunction initi
return true; 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; PDFMesh mesh;
Q_UNUSED(operationControl);
auto addTriangle = [this, &settings, &mesh, cms, intent, reporter](const VertexData* va, const VertexData* vb, const VertexData* vc) auto addTriangle = [this, &settings, &mesh, cms, intent, reporter](const VertexData* va, const VertexData* vb, const VertexData* vc)
{ {
const uint32_t via = va->index; const uint32_t via = va->index;
@ -2401,10 +2425,16 @@ bool PDFLatticeFormGouradTriangleShading::processTriangles(InitializeFunction in
return true; 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; PDFMesh mesh;
Q_UNUSED(operationControl);
auto addTriangle = [this, &settings, &mesh, cms, intent, reporter](const VertexData* va, const VertexData* vb, const VertexData* vc) auto addTriangle = [this, &settings, &mesh, cms, intent, reporter](const VertexData* va, const VertexData* vb, const VertexData* vc)
{ {
const uint32_t via = va->index; const uint32_t via = va->index;
@ -3071,7 +3101,11 @@ PDFTensorPatches PDFTensorProductPatchShading::createPatches(QMatrix userSpaceTo
return patches; 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; PDFMesh mesh;
@ -3082,7 +3116,7 @@ PDFMesh PDFTensorProductPatchShading::createMesh(const PDFMeshQualitySettings& s
throw PDFException(PDFTranslationContext::tr("Invalid data in tensor product patch shading.")); 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; return mesh;
} }
@ -3228,7 +3262,8 @@ void PDFTensorProductPatchShadingBase::fillMesh(PDFMesh& mesh,
const PDFCMS* cms, const PDFCMS* cms,
RenderingIntent intent, RenderingIntent intent,
PDFRenderErrorReporter* reporter, 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 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 // 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()) while (!unfinishedTriangles.empty())
{ {
// Mesh generation is cancelled
if (PDFOperationControl::isOperationCancelled(operationControl))
{
mesh = PDFMesh();
return;
}
Triangle triangle = unfinishedTriangles.back(); Triangle triangle = unfinishedTriangles.back();
unfinishedTriangles.pop_back(); unfinishedTriangles.pop_back();
@ -3417,12 +3459,13 @@ void PDFTensorProductPatchShadingBase::fillMesh(PDFMesh& mesh,
const PDFTensorPatches& patches, const PDFTensorPatches& patches,
const PDFCMS* cms, const PDFCMS* cms,
RenderingIntent intent, RenderingIntent intent,
PDFRenderErrorReporter* reporter) const PDFRenderErrorReporter* reporter,
const PDFOperationControl* operationControl) const
{ {
const bool fastAlgorithm = patches.size() > 16; const bool fastAlgorithm = patches.size() > 16;
for (const auto& patch : patches) 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 // Create bounding path
@ -3655,7 +3698,11 @@ PDFTensorPatches PDFCoonsPatchShading::createPatches(QMatrix userSpaceToDeviceSp
return patches; 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; PDFMesh mesh;
PDFTensorPatches patches = createPatches(settings.userSpaceToDeviceSpaceMatrix, true); 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.")); 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; return mesh;
} }

View File

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

View File

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

View File

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

View File

@ -228,7 +228,7 @@ void ObjectViewerWidget::updateUi()
pdf::PDFRenderErrorReporterDummy dummyErrorReporter; pdf::PDFRenderErrorReporterDummy dummyErrorReporter;
pdf::PDFImage pdfImage = pdf::PDFImage::createImage(m_document, stream, qMove(colorSpace), false, pdf::RenderingIntent::Perceptual, &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->stackedWidget->setCurrentWidget(ui->imageBrowserPage);
ui->imageBrowser->setPixmap(QPixmap::fromImage(image)); ui->imageBrowser->setPixmap(QPixmap::fromImage(image));