PDF4QT/PdfForQtLib/sources/pdfpainter.cpp

657 lines
22 KiB
C++
Raw Normal View History

2019-02-24 17:48:37 +01:00
// Copyright (C) 2019 Jakub Melka
//
// This file is part of PdfForQt.
//
// PdfForQt 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
// (at your option) any later version.
//
// PdfForQt 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 PDFForQt. If not, see <https://www.gnu.org/licenses/>.
#include "pdfpainter.h"
2019-08-31 14:37:18 +02:00
#include "pdfpattern.h"
2019-02-24 17:48:37 +01:00
#include <QPainter>
namespace pdf
{
2019-12-14 14:39:43 +01:00
PDFPainterBase::PDFPainterBase(PDFRenderer::Features features,
const PDFPage* page,
const PDFDocument* document,
const PDFFontCache* fontCache,
2019-12-25 17:56:17 +01:00
const PDFCMS* cms,
2019-12-14 14:39:43 +01:00
const PDFOptionalContentActivity* optionalContentActivity,
QMatrix pagePointToDevicePointMatrix,
const PDFMeshQualitySettings& meshQualitySettings) :
2019-12-25 17:56:17 +01:00
BaseClass(page, document, fontCache, cms, optionalContentActivity, pagePointToDevicePointMatrix, meshQualitySettings),
2019-12-14 14:39:43 +01:00
m_features(features)
{
}
void PDFPainterBase::performUpdateGraphicsState(const PDFPageContentProcessorState& state)
{
const PDFPageContentProcessorState::StateFlags flags = state.getStateFlags();
// If current transformation matrix has changed, then update it
if (flags.testFlag(PDFPageContentProcessorState::StateCurrentTransformationMatrix))
{
setWorldMatrix(getCurrentWorldMatrix());
}
if (flags.testFlag(PDFPageContentProcessorState::StateStrokeColor) ||
flags.testFlag(PDFPageContentProcessorState::StateLineWidth) ||
flags.testFlag(PDFPageContentProcessorState::StateLineCapStyle) ||
flags.testFlag(PDFPageContentProcessorState::StateLineJoinStyle) ||
flags.testFlag(PDFPageContentProcessorState::StateMitterLimit) ||
flags.testFlag(PDFPageContentProcessorState::StateLineDashPattern) ||
flags.testFlag(PDFPageContentProcessorState::StateAlphaStroking))
{
m_currentPen.dirty();
}
if (flags.testFlag(PDFPageContentProcessorState::StateFillColor) ||
flags.testFlag(PDFPageContentProcessorState::StateAlphaFilling))
{
m_currentBrush.dirty();
}
// If current blend mode has changed, then update it
if (flags.testFlag(PDFPageContentProcessorState::StateBlendMode))
{
// Try to simulate transparency groups. Use only first composition mode,
// outside the transparency groups (so we are on pages main transparency
// groups).
const BlendMode blendMode = state.getBlendMode();
if (canSetBlendMode(blendMode))
{
if (!PDFBlendModeInfo::isSupportedByQt(blendMode))
{
reportRenderErrorOnce(RenderErrorType::NotSupported, PDFTranslationContext::tr("Blend mode '%1' not supported.").arg(PDFBlendModeInfo::getBlendModeName(blendMode)));
}
const QPainter::CompositionMode compositionMode = PDFBlendModeInfo::getCompositionModeFromBlendMode(blendMode);
setCompositionMode(compositionMode);
}
else if (blendMode != BlendMode::Normal && blendMode != BlendMode::Compatible)
{
reportRenderErrorOnce(RenderErrorType::NotSupported, PDFTranslationContext::tr("Blend mode '%1' is in transparency group, which is not supported.").arg(PDFBlendModeInfo::getBlendModeName(blendMode)));
}
}
BaseClass::performUpdateGraphicsState(state);
}
bool PDFPainterBase::isContentSuppressedByOC(PDFObjectReference ocgOrOcmd)
{
if (m_features.testFlag(PDFRenderer::IgnoreOptionalContent))
{
return false;
}
return PDFPageContentProcessor::isContentSuppressedByOC(ocgOrOcmd);
}
QPen PDFPainterBase::getCurrentPenImpl() const
{
const PDFPageContentProcessorState* graphicState = getGraphicState();
QColor color = graphicState->getStrokeColor();
if (color.isValid())
{
color.setAlphaF(getEffectiveStrokingAlpha());
const PDFReal lineWidth = graphicState->getLineWidth();
Qt::PenCapStyle penCapStyle = graphicState->getLineCapStyle();
Qt::PenJoinStyle penJoinStyle = graphicState->getLineJoinStyle();
const PDFLineDashPattern& lineDashPattern = graphicState->getLineDashPattern();
const PDFReal mitterLimit = graphicState->getMitterLimit();
QPen pen(color);
pen.setWidthF(lineWidth);
pen.setCapStyle(penCapStyle);
pen.setJoinStyle(penJoinStyle);
pen.setMiterLimit(mitterLimit);
if (lineDashPattern.isSolid())
{
pen.setStyle(Qt::SolidLine);
}
else
{
pen.setStyle(Qt::CustomDashLine);
pen.setDashPattern(QVector<PDFReal>::fromStdVector(lineDashPattern.getDashArray()));
pen.setDashOffset(lineDashPattern.getDashOffset());
}
return pen;
}
else
{
return QPen(Qt::NoPen);
}
}
QBrush PDFPainterBase::getCurrentBrushImpl() const
{
const PDFPageContentProcessorState* graphicState = getGraphicState();
QColor color = graphicState->getFillColor();
if (color.isValid())
{
color.setAlphaF(getEffectiveFillingAlpha());
return QBrush(color, Qt::SolidPattern);
}
else
{
return QBrush(Qt::NoBrush);
}
}
PDFReal PDFPainterBase::getEffectiveStrokingAlpha() const
{
PDFReal alpha = getGraphicState()->getAlphaStroking();
auto it = m_transparencyGroupDataStack.crbegin();
auto itEnd = m_transparencyGroupDataStack.crend();
for (; it != itEnd; ++it)
{
const PDFTransparencyGroupPainterData& transparencyGroup = *it;
alpha *= transparencyGroup.alphaStroke;
if (transparencyGroup.group.isolated)
{
break;
}
}
return alpha;
}
PDFReal PDFPainterBase::getEffectiveFillingAlpha() const
{
PDFReal alpha = getGraphicState()->getAlphaFilling();
auto it = m_transparencyGroupDataStack.crbegin();
auto itEnd = m_transparencyGroupDataStack.crend();
for (; it != itEnd; ++it)
{
const PDFTransparencyGroupPainterData& transparencyGroup = *it;
alpha *= transparencyGroup.alphaFill;
if (transparencyGroup.group.isolated)
{
break;
}
}
return alpha;
}
bool PDFPainterBase::canSetBlendMode(BlendMode mode) const
{
// We will assume, that we can set blend mode, when
// all other blend modes on transparency stack are normal,
// or compatible. It should work.
Q_UNUSED(mode);
return std::all_of(m_transparencyGroupDataStack.cbegin(), m_transparencyGroupDataStack.cend(), [](const PDFTransparencyGroupPainterData& group) { return group.blendMode == BlendMode::Normal || group.blendMode == BlendMode::Compatible; });
}
void PDFPainterBase::performBeginTransparencyGroup(ProcessOrder order, const PDFTransparencyGroup& transparencyGroup)
{
if (order == ProcessOrder::BeforeOperation)
{
PDFTransparencyGroupPainterData data;
data.group = transparencyGroup;
data.alphaFill = getGraphicState()->getAlphaFilling();
data.alphaStroke = getGraphicState()->getAlphaStroking();
data.blendMode = getGraphicState()->getBlendMode();
m_transparencyGroupDataStack.emplace_back(qMove(data));
}
}
void PDFPainterBase::performEndTransparencyGroup(ProcessOrder order, const PDFTransparencyGroup& transparencyGroup)
{
Q_UNUSED(transparencyGroup);
if (order == ProcessOrder::AfterOperation)
{
m_transparencyGroupDataStack.pop_back();
}
}
PDFPainter::PDFPainter(QPainter* painter,
PDFRenderer::Features features,
QMatrix pagePointToDevicePointMatrix,
const PDFPage* page,
const PDFDocument* document,
const PDFFontCache* fontCache,
2019-12-25 17:56:17 +01:00
const PDFCMS* cms,
2019-09-28 18:26:31 +02:00
const PDFOptionalContentActivity* optionalContentActivity,
const PDFMeshQualitySettings& meshQualitySettings) :
2019-12-25 17:56:17 +01:00
BaseClass(features, page, document, fontCache, cms, optionalContentActivity, pagePointToDevicePointMatrix, meshQualitySettings),
2019-12-14 14:39:43 +01:00
m_painter(painter)
2019-02-24 17:48:37 +01:00
{
Q_ASSERT(painter);
Q_ASSERT(pagePointToDevicePointMatrix.isInvertible());
m_painter->save();
if (features.testFlag(PDFRenderer::ClipToCropBox))
{
QRectF cropBox = page->getCropBox();
if (cropBox.isValid())
{
QPainterPath path;
path.addPolygon(pagePointToDevicePointMatrix.map(cropBox));
m_painter->setClipPath(path, Qt::IntersectClip);
}
}
m_painter->setRenderHint(QPainter::SmoothPixmapTransform, features.testFlag(PDFRenderer::SmoothImages));
2019-02-24 17:48:37 +01:00
}
PDFPainter::~PDFPainter()
{
m_painter->restore();
}
2019-05-04 18:22:40 +02:00
void PDFPainter::performPathPainting(const QPainterPath& path, bool stroke, bool fill, bool text, Qt::FillRule fillRule)
2019-02-24 17:48:37 +01:00
{
2019-08-31 15:55:59 +02:00
Q_ASSERT(stroke || fill);
2019-08-31 14:37:18 +02:00
2019-05-04 18:22:40 +02:00
// Set antialiasing
2019-12-14 14:39:43 +01:00
const bool antialiasing = (text && hasFeature(PDFRenderer::TextAntialiasing)) || (!text && hasFeature(PDFRenderer::Antialiasing));
2019-05-04 18:22:40 +02:00
m_painter->setRenderHint(QPainter::Antialiasing, antialiasing);
2019-02-24 17:48:37 +01:00
if (stroke)
{
m_painter->setPen(getCurrentPen());
}
else
{
m_painter->setPen(Qt::NoPen);
}
if (fill)
{
m_painter->setBrush(getCurrentBrush());
}
else
{
m_painter->setBrush(Qt::NoBrush);
}
Q_ASSERT(path.fillRule() == fillRule);
m_painter->drawPath(path);
}
void PDFPainter::performClipping(const QPainterPath& path, Qt::FillRule fillRule)
{
Q_ASSERT(path.fillRule() == fillRule);
2019-03-17 16:12:36 +01:00
m_painter->setClipPath(path, Qt::IntersectClip);
2019-02-24 17:48:37 +01:00
}
2019-05-07 18:21:22 +02:00
void PDFPainter::performImagePainting(const QImage& image)
{
if (isContentSuppressed())
{
// Content is suppressed, do not paint anything
return;
}
2019-05-07 18:21:22 +02:00
m_painter->save();
2019-07-06 16:27:36 +02:00
QImage adjustedImage = image;
2019-12-14 14:39:43 +01:00
if (hasFeature(PDFRenderer::SmoothImages))
2019-07-06 16:27:36 +02:00
{
// Test, if we can use smooth images. We can use them under following conditions:
// 1) Transformed rectangle is not skewed or deformed (so vectors (0, 1) and (1, 0) are orthogonal)
// 2) Image enlargement is not too big (so we doesn't run out of memory)
QMatrix matrix = m_painter->worldMatrix();
QLineF mappedWidthVector = matrix.map(QLineF(0, 0, 1, 0));
QLineF mappedHeightVector = matrix.map(QLineF(0, 0, 0, 1));
qreal angle = mappedWidthVector.angleTo(mappedHeightVector);
if (qFuzzyCompare(angle, 90.0))
{
// Image is not skewed, so we test enlargement factor
const int newWidth = mappedWidthVector.length();
const int newHeight = mappedHeightVector.length();
const int newPixels = newWidth * newHeight;
const int oldPixels = image.width() * image.height();
if (newPixels < oldPixels * 8)
{
QSize size = adjustedImage.size();
QSize adjustedImageSize = size.scaled(newWidth, newHeight, Qt::KeepAspectRatio);
adjustedImage = adjustedImage.scaled(adjustedImageSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
}
}
QMatrix imageTransform(1.0 / adjustedImage.width(), 0, 0, 1.0 / adjustedImage.height(), 0, 0);
2019-05-07 18:21:22 +02:00
QMatrix worldMatrix = imageTransform * m_painter->worldMatrix();
// Because Qt uses opposite axis direction than PDF, then we must transform the y-axis
// to the opposite (so the image is then unchanged)
2019-07-06 16:27:36 +02:00
worldMatrix.translate(0, adjustedImage.height());
2019-05-07 18:21:22 +02:00
worldMatrix.scale(1, -1);
m_painter->setWorldMatrix(worldMatrix);
2019-07-06 16:27:36 +02:00
m_painter->drawImage(0, 0, adjustedImage);
2019-05-07 18:21:22 +02:00
m_painter->restore();
}
2019-08-31 15:55:59 +02:00
void PDFPainter::performMeshPainting(const PDFMesh& mesh)
{
m_painter->save();
m_painter->setWorldMatrix(QMatrix());
2019-09-29 18:09:09 +02:00
mesh.paint(m_painter, getEffectiveFillingAlpha());
2019-08-31 15:55:59 +02:00
m_painter->restore();
}
2019-12-14 14:39:43 +01:00
void PDFPainter::performSaveGraphicState(ProcessOrder order)
2019-02-24 17:48:37 +01:00
{
2019-12-14 14:39:43 +01:00
if (order == ProcessOrder::AfterOperation)
2019-02-24 17:48:37 +01:00
{
2019-12-14 14:39:43 +01:00
m_painter->save();
2019-02-24 17:48:37 +01:00
}
2019-12-14 14:39:43 +01:00
}
2019-02-24 17:48:37 +01:00
2019-12-14 14:39:43 +01:00
void PDFPainter::performRestoreGraphicState(ProcessOrder order)
{
if (order == ProcessOrder::BeforeOperation)
2019-02-24 17:48:37 +01:00
{
2019-12-14 14:39:43 +01:00
m_painter->restore();
2019-02-24 17:48:37 +01:00
}
2019-12-14 14:39:43 +01:00
}
2019-12-14 14:39:43 +01:00
void PDFPainter::setWorldMatrix(const QMatrix& matrix)
{
m_painter->setWorldMatrix(matrix, false);
}
2019-09-29 18:09:09 +02:00
2019-12-14 14:39:43 +01:00
void PDFPainter::setCompositionMode(QPainter::CompositionMode mode)
{
m_painter->setCompositionMode(mode);
}
2019-09-29 15:44:35 +02:00
2019-12-14 14:39:43 +01:00
void PDFPrecompiledPageGenerator::performPathPainting(const QPainterPath& path, bool stroke, bool fill, bool text, Qt::FillRule fillRule)
{
Q_ASSERT(stroke || fill);
Q_ASSERT(path.fillRule() == fillRule);
2019-09-29 15:44:35 +02:00
2019-12-14 14:39:43 +01:00
QPen pen = stroke ? getCurrentPen() : QPen(Qt::NoPen);
QBrush brush = fill ? getCurrentBrush() : QBrush(Qt::NoBrush);
m_precompiledPage->addPath(qMove(pen), qMove(brush), path, text);
2019-02-24 17:48:37 +01:00
}
2019-12-14 14:39:43 +01:00
void PDFPrecompiledPageGenerator::performClipping(const QPainterPath& path, Qt::FillRule fillRule)
2019-02-24 17:48:37 +01:00
{
2019-12-14 14:39:43 +01:00
Q_ASSERT(path.fillRule() == fillRule);
m_precompiledPage->addClip(path);
2019-02-24 17:48:37 +01:00
}
2019-12-14 14:39:43 +01:00
void PDFPrecompiledPageGenerator::performImagePainting(const QImage& image)
2019-02-24 17:48:37 +01:00
{
2019-12-14 14:39:43 +01:00
if (isContentSuppressed())
2019-02-24 17:48:37 +01:00
{
2019-12-14 14:39:43 +01:00
// Content is suppressed, do not paint anything
return;
2019-02-24 17:48:37 +01:00
}
2019-12-14 14:39:43 +01:00
m_precompiledPage->addImage(image);
2019-02-24 17:48:37 +01:00
}
2019-12-14 14:39:43 +01:00
void PDFPrecompiledPageGenerator::performMeshPainting(const PDFMesh& mesh)
2019-09-29 18:09:09 +02:00
{
2019-12-14 14:39:43 +01:00
m_precompiledPage->addMesh(mesh, getEffectiveFillingAlpha());
2019-09-29 18:09:09 +02:00
}
2019-12-14 14:39:43 +01:00
void PDFPrecompiledPageGenerator::performSaveGraphicState(PDFPageContentProcessor::ProcessOrder order)
2019-09-29 18:09:09 +02:00
{
if (order == ProcessOrder::AfterOperation)
{
2019-12-14 14:39:43 +01:00
m_precompiledPage->addSaveGraphicState();
2019-09-29 18:09:09 +02:00
}
}
2019-12-14 14:39:43 +01:00
void PDFPrecompiledPageGenerator::performRestoreGraphicState(PDFPageContentProcessor::ProcessOrder order)
2019-07-06 15:55:37 +02:00
{
2019-12-14 14:39:43 +01:00
if (order == ProcessOrder::BeforeOperation)
2019-07-06 15:55:37 +02:00
{
2019-12-14 14:39:43 +01:00
m_precompiledPage->addRestoreGraphicState();
2019-07-06 15:55:37 +02:00
}
2019-12-14 14:39:43 +01:00
}
2019-07-06 15:55:37 +02:00
2019-12-14 14:39:43 +01:00
void PDFPrecompiledPageGenerator::setWorldMatrix(const QMatrix& matrix)
{
m_precompiledPage->addSetWorldMatrix(matrix);
2019-07-06 15:55:37 +02:00
}
2019-12-14 14:39:43 +01:00
void PDFPrecompiledPageGenerator::setCompositionMode(QPainter::CompositionMode mode)
2019-02-24 17:48:37 +01:00
{
2019-12-14 14:39:43 +01:00
m_precompiledPage->addSetCompositionMode(mode);
}
2019-02-24 17:48:37 +01:00
2019-12-14 14:39:43 +01:00
void PDFPrecompiledPage::draw(QPainter* painter, const QRectF& cropBox, const QMatrix& pagePointToDevicePointMatrix, PDFRenderer::Features features) const
{
Q_ASSERT(painter);
Q_ASSERT(pagePointToDevicePointMatrix.isInvertible());
2019-02-24 17:48:37 +01:00
2019-12-14 14:39:43 +01:00
painter->save();
2019-02-24 17:48:37 +01:00
2019-12-14 14:39:43 +01:00
if (features.testFlag(PDFRenderer::ClipToCropBox))
{
if (cropBox.isValid())
2019-02-24 17:48:37 +01:00
{
2019-12-14 14:39:43 +01:00
QPainterPath path;
path.addPolygon(pagePointToDevicePointMatrix.map(cropBox));
painter->setClipPath(path, Qt::IntersectClip);
2019-02-24 17:48:37 +01:00
}
}
2019-12-14 14:39:43 +01:00
painter->setRenderHint(QPainter::SmoothPixmapTransform, features.testFlag(PDFRenderer::SmoothImages));
// Process all instructions
for (const Instruction& instruction : m_instructions)
2019-02-24 17:48:37 +01:00
{
2019-12-14 14:39:43 +01:00
switch (instruction.type)
{
case InstructionType::DrawPath:
{
const PathPaintData& data = m_paths[instruction.dataIndex];
// Set antialiasing
const bool antialiasing = (data.isText && features.testFlag(PDFRenderer::TextAntialiasing)) || (!data.isText && features.testFlag(PDFRenderer::Antialiasing));
painter->setRenderHint(QPainter::Antialiasing, antialiasing);
painter->setPen(data.pen);
painter->setBrush(data.brush);
painter->drawPath(data.path);
break;
}
case InstructionType::DrawImage:
{
const ImageData& data = m_images[instruction.dataIndex];
const QImage& image = data.image;
painter->save();
QMatrix imageTransform(1.0 / image.width(), 0, 0, 1.0 / image.height(), 0, 0);
QMatrix worldMatrix = imageTransform * painter->worldMatrix();
// Jakub Melka: Because Qt uses opposite axis direction than PDF, then we must transform the y-axis
// to the opposite (so the image is then unchanged)
worldMatrix.translate(0, image.height());
worldMatrix.scale(1, -1);
painter->setWorldMatrix(worldMatrix);
painter->drawImage(0, 0, image);
painter->restore();
break;
}
case InstructionType::DrawMesh:
{
const MeshPaintData& data = m_meshes[instruction.dataIndex];
painter->save();
painter->setWorldMatrix(pagePointToDevicePointMatrix);
data.mesh.paint(painter, data.alpha);
painter->restore();
break;
}
case InstructionType::Clip:
{
painter->setClipPath(m_clips[instruction.dataIndex].clipPath, Qt::IntersectClip);
break;
}
case InstructionType::SaveGraphicState:
{
painter->save();
break;
}
case InstructionType::RestoreGraphicState:
{
painter->restore();
break;
}
case InstructionType::SetWorldMatrix:
{
painter->setWorldMatrix(m_matrices[instruction.dataIndex] * pagePointToDevicePointMatrix);
break;
}
case InstructionType::SetCompositionMode:
{
painter->setCompositionMode(m_compositionModes[instruction.dataIndex]);
break;
}
default:
{
Q_ASSERT(false);
break;
}
}
2019-02-24 17:48:37 +01:00
}
2019-12-14 14:39:43 +01:00
painter->restore();
2019-02-24 17:48:37 +01:00
}
2019-12-14 14:39:43 +01:00
void PDFPrecompiledPage::addPath(QPen pen, QBrush brush, QPainterPath path, bool isText)
2019-02-24 17:48:37 +01:00
{
2019-12-14 14:39:43 +01:00
m_instructions.emplace_back(InstructionType::DrawPath, m_paths.size());
m_paths.emplace_back(qMove(pen), qMove(brush), qMove(path), isText);
2019-02-24 17:48:37 +01:00
}
2019-12-14 14:39:43 +01:00
void PDFPrecompiledPage::addClip(QPainterPath path)
2019-09-29 18:09:09 +02:00
{
2019-12-14 14:39:43 +01:00
m_instructions.emplace_back(InstructionType::Clip, m_clips.size());
m_clips.emplace_back(qMove(path));
}
2019-09-29 18:09:09 +02:00
2019-12-14 14:39:43 +01:00
void PDFPrecompiledPage::addImage(QImage image)
{
// Convert the image into format Format_ARGB32_Premultiplied for fast drawing.
// If this format is used, then no image conversion is performed while drawing.
if (image.format() != QImage::Format_ARGB32_Premultiplied)
2019-09-29 18:09:09 +02:00
{
2019-12-14 14:39:43 +01:00
image.convertTo(QImage::Format_ARGB32_Premultiplied);
2019-09-29 18:09:09 +02:00
}
2019-12-14 14:39:43 +01:00
m_instructions.emplace_back(InstructionType::DrawImage, m_images.size());
m_images.emplace_back(qMove(image));
2019-09-29 18:09:09 +02:00
}
2019-12-14 14:39:43 +01:00
void PDFPrecompiledPage::addMesh(PDFMesh mesh, PDFReal alpha)
2019-09-29 18:09:09 +02:00
{
2019-12-14 14:39:43 +01:00
m_instructions.emplace_back(InstructionType::DrawMesh, m_meshes.size());
m_meshes.emplace_back(qMove(mesh), alpha);
}
2019-09-29 18:09:09 +02:00
2019-12-14 14:39:43 +01:00
void PDFPrecompiledPage::addSetWorldMatrix(const QMatrix& matrix)
{
m_instructions.emplace_back(InstructionType::SetWorldMatrix, m_matrices.size());
m_matrices.push_back(matrix);
}
2019-09-29 18:09:09 +02:00
2019-12-14 14:39:43 +01:00
void PDFPrecompiledPage::addSetCompositionMode(QPainter::CompositionMode compositionMode)
{
m_instructions.emplace_back(InstructionType::SetCompositionMode, m_compositionModes.size());
m_compositionModes.push_back(compositionMode);
2019-09-29 18:09:09 +02:00
}
2019-12-14 14:39:43 +01:00
void PDFPrecompiledPage::optimize()
2019-09-29 18:09:09 +02:00
{
2019-12-14 14:39:43 +01:00
m_instructions.shrink_to_fit();
m_paths.shrink_to_fit();
m_clips.shrink_to_fit();
m_images.shrink_to_fit();
m_meshes.shrink_to_fit();
m_matrices.shrink_to_fit();
m_compositionModes.shrink_to_fit();
}
2019-09-29 18:09:09 +02:00
2019-12-14 14:39:43 +01:00
void PDFPrecompiledPage::finalize(qint64 compilingTimeNS, QList<PDFRenderError> errors)
{
m_compilingTimeNS = compilingTimeNS;
m_errors = qMove(errors);
// Determine memory consumption
m_memoryConsumptionEstimate = sizeof(*this);
m_memoryConsumptionEstimate += sizeof(Instruction) * m_instructions.capacity();
m_memoryConsumptionEstimate += sizeof(PathPaintData) * m_paths.capacity();
m_memoryConsumptionEstimate += sizeof(ClipData) * m_clips.capacity();
m_memoryConsumptionEstimate += sizeof(ImageData) * m_images.capacity();
m_memoryConsumptionEstimate += sizeof(MeshPaintData) * m_meshes.capacity();
m_memoryConsumptionEstimate += sizeof(QMatrix) * m_matrices.capacity();
m_memoryConsumptionEstimate += sizeof(QPainter::CompositionMode) * m_compositionModes.capacity();
m_memoryConsumptionEstimate += sizeof(PDFRenderError) * m_errors.size();
auto calculateQPathMemoryConsumption = [](const QPainterPath& path)
{
return sizeof(QPainterPath::Element) * path.capacity();
};
for (const PathPaintData& data : m_paths)
{
// Texts are shared from the font
if (!data.isText)
{
m_memoryConsumptionEstimate += calculateQPathMemoryConsumption(data.path);
}
}
for (const ClipData& data : m_clips)
{
m_memoryConsumptionEstimate += calculateQPathMemoryConsumption(data.clipPath);
}
for (const ImageData& data : m_images)
{
m_memoryConsumptionEstimate += data.image.sizeInBytes();
}
for (const MeshPaintData& data : m_meshes)
{
m_memoryConsumptionEstimate += data.mesh.getMemoryConsumptionEstimate();
}
2019-09-29 18:09:09 +02:00
}
2019-02-24 17:48:37 +01:00
} // namespace pdf