Asynchronous page rendering (compilig of pages)

This commit is contained in:
Jakub Melka 2019-12-14 19:09:34 +01:00
parent 0277a9f059
commit 1f09c83700
16 changed files with 473 additions and 61 deletions

View File

@ -39,6 +39,7 @@ SOURCES += \
sources/pdfaction.cpp \
sources/pdfblendfunction.cpp \
sources/pdfccittfaxdecoder.cpp \
sources/pdfcompiler.cpp \
sources/pdffile.cpp \
sources/pdfitemmodels.cpp \
sources/pdfjbig2decoder.cpp \
@ -76,6 +77,7 @@ HEADERS += \
sources/pdfaction.h \
sources/pdfblendfunction.h \
sources/pdfccittfaxdecoder.h \
sources/pdfcompiler.h \
sources/pdffile.h \
sources/pdfitemmodels.h \
sources/pdfjbig2decoder.h \

View File

@ -0,0 +1,171 @@
// 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 "pdfcompiler.h"
#include "pdfdrawspacecontroller.h"
#include <QtConcurrent/QtConcurrent>
namespace pdf
{
PDFAsynchronousPageCompiler::PDFAsynchronousPageCompiler(PDFDrawWidgetProxy* proxy) :
BaseClass(proxy),
m_proxy(proxy)
{
// TODO: Create setting for this
m_cache.setMaxCost(128 * 1024 * 1024);
}
void PDFAsynchronousPageCompiler::start()
{
switch (m_state)
{
case State::Inactive:
{
m_state = State::Active;
break;
}
case State::Active:
break; // We have nothing to do...
case State::Stopping:
{
// We shouldn't call this function while stopping!
Q_ASSERT(false);
break;
}
}
}
void PDFAsynchronousPageCompiler::stop()
{
switch (m_state)
{
case State::Inactive:
break; // We have nothing to do...
case State::Active:
{
// Stop the engine
m_state = State::Stopping;
for (const auto& taskItem : m_tasks)
{
disconnect(taskItem.second.taskWatcher, &QFutureWatcher<PDFPrecompiledPage>::finished, this, &PDFAsynchronousPageCompiler::onPageCompiled);
taskItem.second.taskWatcher->waitForFinished();
}
m_tasks.clear();
m_cache.clear();
m_state = State::Inactive;
break;
}
case State::Stopping:
{
// We shouldn't call this function while stopping!
Q_ASSERT(false);
break;
}
}
}
void PDFAsynchronousPageCompiler::reset()
{
stop();
start();
}
const PDFPrecompiledPage* PDFAsynchronousPageCompiler::getPrecompiledCache(PDFInteger pageIndex, bool compile)
{
if (m_state != State::Active || !m_proxy->getDocument())
{
// Engine is not active, always return nullptr
return nullptr;
}
const PDFPrecompiledPage* page = m_cache.object(pageIndex);
if (!page && compile && !m_tasks.count(pageIndex))
{
// Compile the page
auto compilePage = [this, pageIndex]() -> PDFPrecompiledPage
{
PDFPrecompiledPage compiledPage;
PDFRenderer renderer(m_proxy->getDocument(), m_proxy->getFontCache(), m_proxy->getOptionalContentActivity(), m_proxy->getFeatures(), m_proxy->getMeshQualitySettings());
renderer.compile(&compiledPage, pageIndex);
return compiledPage;
};
CompileTask& task = m_tasks[pageIndex];
task.taskFuture = QtConcurrent::run(compilePage);
task.taskWatcher = new QFutureWatcher<PDFPrecompiledPage>(this);
task.taskWatcher->setFuture(task.taskFuture);
connect(task.taskWatcher, &QFutureWatcher<PDFPrecompiledPage>::finished, this, &PDFAsynchronousPageCompiler::onPageCompiled);
onPageCompiled();
}
return page;
}
void PDFAsynchronousPageCompiler::onPageCompiled()
{
std::vector<PDFInteger> compiledPages;
// Search all tasks for finished tasks
for (auto it = m_tasks.begin(); it != m_tasks.end();)
{
CompileTask& task = it->second;
if (task.taskWatcher->isFinished())
{
if (m_state == State::Active)
{
// If we are in active state, try to store precompiled page
PDFPrecompiledPage* page = new PDFPrecompiledPage(task.taskWatcher->result());
qint64 memoryConsumptionEstimate = page->getMemoryConsumptionEstimate();
if (m_cache.insert(it->first, page, memoryConsumptionEstimate))
{
compiledPages.push_back(it->first);
}
else
{
// We can't insert page to the cache, because cache size is too small. We will
// emit error string to inform the user, that cache is too small.
QString message = PDFTranslationContext::tr("Precompiled page size is too high (%1 kB). Cache size is %2 kB. Increase the cache size!").arg(memoryConsumptionEstimate / 1024).arg(m_cache.maxCost() / 1024);
emit renderingError(it->first, { PDFRenderError(RenderErrorType::Error, message) });
}
}
task.taskWatcher->deleteLater();
it = m_tasks.erase(it);
}
else
{
// Just increment the counter
++it;
}
}
if (!compiledPages.empty())
{
Q_ASSERT(std::is_sorted(compiledPages.cbegin(), compiledPages.cend()));
emit pageImageChanged(false, compiledPages);
}
}
} // namespace pdf

View File

@ -0,0 +1,92 @@
// 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/>.
#ifndef PDFCOMPILER_H
#define PDFCOMPILER_H
#include "pdfrenderer.h"
#include "pdfpainter.h"
#include <QCache>
#include <QFuture>
#include <QFutureWatcher>
namespace pdf
{
class PDFDrawWidgetProxy;
/// 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
{
Q_OBJECT
private:
using BaseClass = QObject;
public:
explicit PDFAsynchronousPageCompiler(PDFDrawWidgetProxy* proxy);
/// Starts the engine. Call this function only if the engine
/// is stopped.
void start();
/// Stops the engine and all underlying asynchronous tasks. Also
/// clears the cache. Call this function only if engine is active.
void stop();
/// Resets the engine - calls stop and then calls start.
void reset();
enum class State
{
Inactive,
Active,
Stopping
};
/// Tries to retrieve precompiled page from the cache. If page is not found,
/// then nullptr is returned (no exception is thrown). If \p compile is set to true,
/// and page is not found, and compiler is active, then new asynchronous compile
/// task is performed.
/// \param pageIndex Index of page
/// \param compile Compile the page, if it is not found in the cache
const PDFPrecompiledPage* getPrecompiledCache(PDFInteger pageIndex, bool compile);
signals:
void pageImageChanged(bool all, const std::vector<PDFInteger>& pages);
void renderingError(PDFInteger pageIndex, const QList<PDFRenderError>& errors);
private:
void onPageCompiled();
struct CompileTask
{
QFuture<PDFPrecompiledPage> taskFuture;
QFutureWatcher<PDFPrecompiledPage>* taskWatcher = nullptr;
};
PDFDrawWidgetProxy* m_proxy;
State m_state = State::Inactive;
QCache<PDFInteger, PDFPrecompiledPage> m_cache;
std::map<PDFInteger, CompileTask> m_tasks;
};
} // namespace pdf
#endif // PDFCOMPILER_H

View File

@ -20,6 +20,7 @@
#include "pdfdrawwidget.h"
#include "pdfrenderer.h"
#include "pdfpainter.h"
#include "pdfcompiler.h"
#include <QPainter>
@ -137,7 +138,6 @@ QSizeF PDFDrawSpaceController::getReferenceBoundingBox() const
void PDFDrawSpaceController::onOptionalContentGroupStateChanged()
{
emit pageImageChanged(true, { });
emit repaintNeeded();
}
void PDFDrawSpaceController::recalculate()
@ -395,12 +395,15 @@ PDFDrawWidgetProxy::PDFDrawWidgetProxy(QObject* parent) :
m_widget(nullptr),
m_horizontalScrollbar(nullptr),
m_verticalScrollbar(nullptr),
m_features(PDFRenderer::getDefaultFeatures())
m_features(PDFRenderer::getDefaultFeatures()),
m_compiler(new PDFAsynchronousPageCompiler(this))
{
m_controller = new PDFDrawSpaceController(this);
connect(m_controller, &PDFDrawSpaceController::drawSpaceChanged, this, &PDFDrawWidgetProxy::update);
connect(m_controller, &PDFDrawSpaceController::repaintNeeded, this, &PDFDrawWidgetProxy::repaintNeeded);
connect(m_controller, &PDFDrawSpaceController::pageImageChanged, this, &PDFDrawWidgetProxy::pageImageChanged);
connect(m_compiler, &PDFAsynchronousPageCompiler::renderingError, this, &PDFDrawWidgetProxy::renderingError);
connect(m_compiler, &PDFAsynchronousPageCompiler::pageImageChanged, this, &PDFDrawWidgetProxy::pageImageChanged);
}
PDFDrawWidgetProxy::~PDFDrawWidgetProxy()
@ -410,7 +413,9 @@ PDFDrawWidgetProxy::~PDFDrawWidgetProxy()
void PDFDrawWidgetProxy::setDocument(const PDFDocument* document, const PDFOptionalContentActivity* optionalContentActivity)
{
m_compiler->stop();
m_controller->setDocument(document, optionalContentActivity);
m_compiler->start();
}
void PDFDrawWidgetProxy::init(PDFWidget* widget)
@ -600,30 +605,53 @@ void PDFDrawWidgetProxy::draw(QPainter* painter, QRect rect)
// Clear the page space by white color
painter->fillRect(placedRect, Qt::white);
/*
PDFRenderer renderer(m_controller->getDocument(), m_controller->getFontCache(), m_controller->getOptionalContentActivity(), m_features, m_meshQualitySettings);
QList<PDFRenderError> errors = renderer.render(painter, placedRect, item.pageIndex);
if (!errors.empty())
const PDFPrecompiledPage* compiledPage = m_compiler->getPrecompiledCache(item.pageIndex, true);
if (compiledPage && compiledPage->isValid())
{
emit renderingError(item.pageIndex, errors);
}*/
QElapsedTimer timer;
timer.start();
PDFPrecompiledPage compiledPage;
PDFRenderer renderer(m_controller->getDocument(), m_controller->getFontCache(), m_controller->getOptionalContentActivity(), m_features, m_meshQualitySettings);
renderer.compile(&compiledPage, item.pageIndex);
if (compiledPage.isValid())
{
const PDFPage* page = m_controller->getDocument()->getCatalog()->getPage(item.pageIndex);
QMatrix matrix = renderer.createPagePointToDevicePointMatrix(page, placedRect);
compiledPage.draw(painter, page->getCropBox(), matrix, m_features);
}
QMatrix matrix = PDFRenderer::createPagePointToDevicePointMatrix(page, placedRect);
compiledPage->draw(painter, page->getCropBox(), matrix, m_features);
const QList<PDFRenderError>& errors = compiledPage.getErrors();
if (!errors.empty())
{
emit renderingError(item.pageIndex, errors);
const qint64 drawTimeNS = timer.nsecsElapsed();
// Draw rendering times
if (m_features.testFlag(PDFRenderer::DisplayTimes))
{
QFont font = m_widget->font();
font.setPointSize(12);
auto formatDrawTime = [](qint64 nanoseconds)
{
PDFReal miliseconds = nanoseconds / 1000000.0;
return QString::number(miliseconds, 'f', 3);
};
QFontMetrics fontMetrics(font);
const int lineSpacing = fontMetrics.lineSpacing();
painter->save();
painter->setPen(Qt::red);
painter->setFont(font);
painter->translate(placedRect.topLeft());
painter->translate(placedRect.width() / 20.0, placedRect.height() / 20.0); // Offset
painter->setBackground(QBrush(Qt::white));
painter->setBackgroundMode(Qt::OpaqueMode);
painter->drawText(0, 0, PDFTranslationContext::tr("Compile time: %1 [ms]").arg(formatDrawTime(compiledPage->getCompilingTimeNS())));
painter->translate(0, lineSpacing);
painter->drawText(0, 0, PDFTranslationContext::tr("Draw time: %1 [ms]").arg(formatDrawTime(drawTimeNS)));
painter->restore();
}
const QList<PDFRenderError>& errors = compiledPage->getErrors();
if (!errors.empty())
{
emit renderingError(item.pageIndex, errors);
}
}
}
}
@ -1000,9 +1028,10 @@ void PDFDrawWidgetProxy::setFeatures(PDFRenderer::Features features)
{
if (m_features != features)
{
m_compiler->stop();
m_features = features;
m_compiler->start();
emit pageImageChanged(true, { });
emit repaintNeeded();
}
}
@ -1010,9 +1039,10 @@ void PDFDrawWidgetProxy::setPreferredMeshResolutionRatio(PDFReal ratio)
{
if (m_meshQualitySettings.preferredMeshResolutionRatio != ratio)
{
m_compiler->stop();
m_meshQualitySettings.preferredMeshResolutionRatio = ratio;
m_compiler->start();
emit pageImageChanged(true, { });
emit repaintNeeded();
}
}
@ -1020,9 +1050,10 @@ void PDFDrawWidgetProxy::setMinimalMeshResolutionRatio(PDFReal ratio)
{
if (m_meshQualitySettings.minimalMeshResolutionRatio != ratio)
{
m_compiler->stop();
m_meshQualitySettings.minimalMeshResolutionRatio = ratio;
m_compiler->start();
emit pageImageChanged(true, { });
emit repaintNeeded();
}
}
@ -1030,9 +1061,10 @@ void PDFDrawWidgetProxy::setColorTolerance(PDFReal colorTolerance)
{
if (m_meshQualitySettings.tolerance != colorTolerance)
{
m_compiler->stop();
m_meshQualitySettings.tolerance = colorTolerance;
m_compiler->start();
emit pageImageChanged(true, { });
emit repaintNeeded();
}
}

View File

@ -34,6 +34,7 @@ namespace pdf
{
class PDFWidget;
class IDrawWidget;
class PDFAsynchronousPageCompiler;
/// This class controls draw space - page layout. Pages are divided into blocks
/// each block can contain one or multiple pages. Units are in milimeters.
@ -255,7 +256,12 @@ public:
static constexpr PDFReal ZOOM_STEP = 1.2;
const PDFDocument* getDocument() const { return m_controller->getDocument(); }
const PDFFontCache* getFontCache() const { return m_controller->getFontCache(); }
const PDFOptionalContentActivity* getOptionalContentActivity() const { return m_controller->getOptionalContentActivity(); }
PDFRenderer::Features getFeatures() const;
const PDFMeshQualitySettings& getMeshQualitySettings() const { return m_meshQualitySettings; }
void setFeatures(PDFRenderer::Features features);
void setPreferredMeshResolutionRatio(PDFReal ratio);
void setMinimalMeshResolutionRatio(PDFReal ratio);
@ -384,6 +390,9 @@ private:
/// Mesh quality settings
PDFMeshQualitySettings m_meshQualitySettings;
/// Page compiler
PDFAsynchronousPageCompiler* m_compiler;
};
} // namespace pdf

View File

@ -51,6 +51,7 @@ PDFWidget::PDFWidget(RendererEngine engine, int samplesCount, QWidget* parent) :
m_proxy->init(this);
connect(m_proxy, &PDFDrawWidgetProxy::renderingError, this, &PDFWidget::onRenderingError);
connect(m_proxy, &PDFDrawWidgetProxy::repaintNeeded, m_drawWidget->getWidget(), QOverload<>::of(&QWidget::update));
connect(m_proxy, &PDFDrawWidgetProxy::pageImageChanged, this, &PDFWidget::onPageImageChanged);
}
PDFWidget::~PDFWidget()
@ -111,6 +112,28 @@ void PDFWidget::onRenderingError(PDFInteger pageIndex, const QList<PDFRenderErro
emit pageRenderingErrorsChanged(pageIndex, errors.size());
}
void PDFWidget::onPageImageChanged(bool all, const std::vector<PDFInteger>& pages)
{
if (all)
{
m_drawWidget->getWidget()->update();
}
else
{
std::vector<PDFInteger> currentPages = m_drawWidget->getCurrentPages();
Q_ASSERT(std::is_sorted(pages.cbegin(), pages.cend()));
for (PDFInteger pageIndex : currentPages)
{
if (std::binary_search(pages.cbegin(), pages.cend(), pageIndex))
{
m_drawWidget->getWidget()->update();
return;
}
}
}
}
IDrawWidget* PDFWidget::createDrawWidget(RendererEngine rendererEngine, int samplesCount)
{
switch (rendererEngine)

View File

@ -80,6 +80,7 @@ signals:
private:
void onRenderingError(PDFInteger pageIndex, const QList<PDFRenderError>& errors);
void onPageImageChanged(bool all, const std::vector<PDFInteger>& pages);
IDrawWidget* createDrawWidget(RendererEngine rendererEngine, int samplesCount);

View File

@ -613,6 +613,42 @@ void PDFPrecompiledPage::finalize(qint64 compilingTimeNS, QList<PDFRenderError>
{
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();
}
}
} // namespace pdf

View File

@ -139,6 +139,12 @@ private:
class PDFPrecompiledPage
{
public:
explicit inline PDFPrecompiledPage() = default;
inline PDFPrecompiledPage(const PDFPrecompiledPage&) = default;
inline PDFPrecompiledPage(PDFPrecompiledPage&&) = default;
inline PDFPrecompiledPage& operator=(const PDFPrecompiledPage&) = default;
inline PDFPrecompiledPage& operator=(PDFPrecompiledPage&&) = default;
enum class InstructionType
{
@ -200,6 +206,9 @@ public:
/// Returns true, if page is valid (i.e. has nonzero instruction count)
bool isValid() const { return !m_instructions.empty(); }
/// Returns memory consumption estimate
qint64 getMemoryConsumptionEstimate() const { return m_memoryConsumptionEstimate; }
private:
struct PathPaintData
{
@ -258,6 +267,7 @@ private:
};
qint64 m_compilingTimeNS = 0;
qint64 m_memoryConsumptionEstimate = 0;
std::vector<Instruction> m_instructions;
std::vector<PathPaintData> m_paths;
std::vector<ClipData> m_clips;

View File

@ -1087,6 +1087,16 @@ QPointF PDFMesh::getTriangleCenter(const PDFMesh::Triangle& triangle) const
return (m_vertices[triangle.v1] + m_vertices[triangle.v2] + m_vertices[triangle.v3]) / 3.0;
}
qint64 PDFMesh::getMemoryConsumptionEstimate() const
{
qint64 memoryConsumption = sizeof(*this);
memoryConsumption += sizeof(QPointF) * m_vertices.capacity();
memoryConsumption += sizeof(Triangle) * m_triangles.capacity();
memoryConsumption += sizeof(QPainterPath::Element) * m_boundingPath.capacity();
memoryConsumption += sizeof(QPainterPath::Element) * m_backgroundPath.capacity();
return memoryConsumption;
}
void PDFMeshQualitySettings::initResolution()
{
Q_ASSERT(deviceSpaceMeshingArea.isValid());

View File

@ -140,6 +140,9 @@ public:
/// Returns true, if mesh is empty
bool isEmpty() const { return m_vertices.empty(); }
/// Returns estimate of number of bytes, which this mesh occupies in memory
qint64 getMemoryConsumptionEstimate() const;
private:
std::vector<QPointF> m_vertices;
std::vector<Triangle> m_triangles;

View File

@ -38,7 +38,7 @@ PDFRenderer::PDFRenderer(const PDFDocument* document,
Q_ASSERT(document);
}
QMatrix PDFRenderer::createPagePointToDevicePointMatrix(const PDFPage* page, const QRectF& rectangle) const
QMatrix PDFRenderer::createPagePointToDevicePointMatrix(const PDFPage* page, const QRectF& rectangle)
{
QRectF mediaBox = page->getRotatedMediaBox();

View File

@ -42,6 +42,7 @@ public:
SmoothImages = 0x0004, ///< Adjust images to the device space using smooth transformation (slower, but better image quality)
IgnoreOptionalContent = 0x0008, ///< Ignore optional content (so all is drawn ignoring settings of optional content)
ClipToCropBox = 0x0010, ///< Clip page content to crop box (items outside crop box will not be visible)
DisplayTimes = 0x0020, ///< Display page compile/draw time
};
Q_DECLARE_FLAGS(Features, Feature)
@ -72,7 +73,7 @@ public:
/// from page's media box to the target rectangle.
/// \param page Page, for which we want to create matrix
/// \param rectangle Page rectangle, to which is page media box transformed
QMatrix createPagePointToDevicePointMatrix(const PDFPage* page, const QRectF& rectangle) const;
static QMatrix createPagePointToDevicePointMatrix(const PDFPage* page, const QRectF& rectangle);
/// Returns default renderer features
static constexpr Features getDefaultFeatures() { return Antialiasing | TextAntialiasing | ClipToCropBox; }

View File

@ -80,6 +80,8 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget *parent) :
ui->actionOpen->setShortcut(QKeySequence::Open);
ui->actionClose->setShortcut(QKeySequence::Close);
ui->actionQuit->setShortcut(QKeySequence::Quit);
ui->actionZoom_In->setShortcut(QKeySequence::ZoomIn);
ui->actionZoom_Out->setShortcut(QKeySequence::ZoomOut);
connect(ui->actionOpen, &QAction::triggered, this, &PDFViewerMainWindow::onActionOpenTriggered);
connect(ui->actionClose, &QAction::triggered, this, &PDFViewerMainWindow::onActionCloseTriggered);
@ -184,6 +186,7 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget *parent) :
updatePageLayoutActions();
updateUI(true);
onViewerSettingsChanged();
}
PDFViewerMainWindow::~PDFViewerMainWindow()

View File

@ -140,6 +140,7 @@ void PDFViewerSettingsDialog::loadData()
ui->smoothPicturesCheckBox->setChecked(m_settings.m_features.testFlag(pdf::PDFRenderer::SmoothImages));
ui->ignoreOptionalContentCheckBox->setChecked(m_settings.m_features.testFlag(pdf::PDFRenderer::IgnoreOptionalContent));
ui->clipToCropBoxCheckBox->setChecked(m_settings.m_features.testFlag(pdf::PDFRenderer::ClipToCropBox));
ui->displayTimeCheckBox->setChecked(m_settings.m_features.testFlag(pdf::PDFRenderer::DisplayTimes));
// Shading
ui->preferredMeshResolutionEdit->setValue(m_settings.m_preferredMeshResolutionRatio);
@ -192,6 +193,10 @@ void PDFViewerSettingsDialog::saveData()
{
m_settings.m_features.setFlag(pdf::PDFRenderer::ClipToCropBox, ui->clipToCropBoxCheckBox->isChecked());
}
else if (sender == ui->displayTimeCheckBox)
{
m_settings.m_features.setFlag(pdf::PDFRenderer::DisplayTimes, ui->displayTimeCheckBox->isChecked());
}
else if (sender == ui->preferredMeshResolutionEdit)
{
m_settings.m_preferredMeshResolutionRatio = ui->preferredMeshResolutionEdit->value();

View File

@ -26,7 +26,7 @@
<item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>3</number>
<number>1</number>
</property>
<widget class="QWidget" name="enginePage">
<layout class="QVBoxLayout" name="enginePageLayout">
@ -136,29 +136,8 @@
<layout class="QVBoxLayout" name="renderingSettingsGroupBoxLayout">
<item>
<layout class="QGridLayout" name="renderingSettingsGridLayout">
<item row="2" column="0">
<widget class="QLabel" name="smoothPicturesLabel">
<property name="text">
<string>Smooth pictures</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="ignoreOptionalContentLabel">
<property name="text">
<string>Ignore optional content</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="textAntialiasingLabel">
<property name="text">
<string>Text antialiasing</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="antialiasingCheckBox">
<item row="3" column="1">
<widget class="QCheckBox" name="ignoreOptionalContentCheckBox">
<property name="text">
<string>Enable</string>
</property>
@ -171,10 +150,10 @@
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="clipToCropBoxLabel">
<item row="2" column="1">
<widget class="QCheckBox" name="smoothPicturesCheckBox">
<property name="text">
<string>Clip to crop box</string>
<string>Enable</string>
</property>
</widget>
</item>
@ -185,17 +164,31 @@
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="smoothPicturesCheckBox">
<item row="0" column="1">
<widget class="QCheckBox" name="antialiasingCheckBox">
<property name="text">
<string>Enable</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="ignoreOptionalContentCheckBox">
<item row="4" column="0">
<widget class="QLabel" name="clipToCropBoxLabel">
<property name="text">
<string>Enable</string>
<string>Clip to crop box</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="ignoreOptionalContentLabel">
<property name="text">
<string>Ignore optional content</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="smoothPicturesLabel">
<property name="text">
<string>Smooth pictures</string>
</property>
</widget>
</item>
@ -206,12 +199,33 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="textAntialiasingLabel">
<property name="text">
<string>Text antialiasing</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="displayTimeLabel">
<property name="text">
<string>Display page compile/draw time</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="displayTimeCheckBox">
<property name="text">
<string>Enabled</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="renderingInfoLabel">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Rendering settings defines, how rendering engine should handle the page content, and appearance of displayed graphics. &lt;span style=&quot; font-weight:600;&quot;&gt;Antialiasing&lt;/span&gt; turns on antialiasing of painted shapes, such as rectangles, vector graphics, lines, but it did not affects text (characters printed on the screen). &lt;span style=&quot; font-weight:600;&quot;&gt;Text antialiasing &lt;/span&gt;turns on antialiasing of painted characters on the screen, but not any other items. Both &lt;span style=&quot; font-weight:600;&quot;&gt;Antialiasing &lt;/span&gt;and &lt;span style=&quot; font-weight:600;&quot;&gt;Text antialiasing &lt;/span&gt;affects only software renderer. If you are using hardware rendering engine, such as OpenGL rendering engine, this doesn't do anything, because OpenGL engine renders the pictures using MSAA antialiasing (if turned on).&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Smooth pictures&lt;/span&gt; transforms pictures to device space coordinates using smooth image transformation, which usually leads to better image quality. When this is turned off, then default fast transformation is used, and quality of the image is lower, if the source DPI and device DPI is different.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Ignore optional content &lt;/span&gt;ignores all optional content settings and paints everything in the content stream. &lt;span style=&quot; font-weight:600;&quot;&gt;Clip to crop box&lt;/span&gt; clips the drawing rectangle to the crop box of the page, which is usually smaller, than whole page. The graphics outside of the crop box is not drawn (for example, it can contain marks for printer etc.).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Rendering settings defines, how rendering engine should handle the page content, and appearance of displayed graphics. &lt;span style=&quot; font-weight:600;&quot;&gt;Antialiasing&lt;/span&gt; turns on antialiasing of painted shapes, such as rectangles, vector graphics, lines, but it did not affects text (characters printed on the screen). &lt;span style=&quot; font-weight:600;&quot;&gt;Text antialiasing &lt;/span&gt;turns on antialiasing of painted characters on the screen, but not any other items. Both &lt;span style=&quot; font-weight:600;&quot;&gt;Antialiasing &lt;/span&gt;and &lt;span style=&quot; font-weight:600;&quot;&gt;Text antialiasing &lt;/span&gt;affects only software renderer. If you are using hardware rendering engine, such as OpenGL rendering engine, this doesn't do anything, because OpenGL engine renders the pictures using MSAA antialiasing (if turned on).&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Smooth pictures&lt;/span&gt; transforms pictures to device space coordinates using smooth image transformation, which usually leads to better image quality. When this is turned off, then default fast transformation is used, and quality of the image is lower, if the source DPI and device DPI is different.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Ignore optional content &lt;/span&gt;ignores all optional content settings and paints everything in the content stream. &lt;span style=&quot; font-weight:600;&quot;&gt;Clip to crop box&lt;/span&gt; clips the drawing rectangle to the crop box of the page, which is usually smaller, than whole page. The graphics outside of the crop box is not drawn (for example, it can contain marks for printer etc.). &lt;span style=&quot; font-weight:600;&quot;&gt;Display page compile/draw time &lt;/span&gt;is used mainly for debugging purposes, it displays page compile time (compiled page is stored in the cache) and draw time (when the renderer draws compiled page contents to the output device).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>