Mesh quality settings

This commit is contained in:
Jakub Melka
2019-09-28 18:26:31 +02:00
parent 9941438e99
commit b09f9eff21
17 changed files with 335 additions and 68 deletions

View File

@ -65,6 +65,7 @@ SOURCES += \
HEADERS += \
sources/pdfitemmodels.h \
sources/pdfmeshqualitysettings.h \
sources/pdfobject.h \
sources/pdfoptionalcontent.h \
sources/pdfparser.h \

View File

@ -552,7 +552,7 @@ 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);
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())
@ -827,4 +827,31 @@ void PDFDrawWidgetProxy::setFeatures(PDFRenderer::Features features)
}
}
void PDFDrawWidgetProxy::setPreferredMeshResolutionRatio(PDFReal ratio)
{
if (m_meshQualitySettings.preferredMeshResolutionRatio != ratio)
{
m_meshQualitySettings.preferredMeshResolutionRatio = ratio;
emit repaintNeeded();
}
}
void PDFDrawWidgetProxy::setMinimalMeshResolutionRatio(PDFReal ratio)
{
if (m_meshQualitySettings.minimalMeshResolutionRatio != ratio)
{
m_meshQualitySettings.minimalMeshResolutionRatio = ratio;
emit repaintNeeded();
}
}
void PDFDrawWidgetProxy::setColorTolerance(PDFReal colorTolerance)
{
if (m_meshQualitySettings.tolerance != colorTolerance)
{
m_meshQualitySettings.tolerance = colorTolerance;
emit repaintNeeded();
}
}
} // namespace pdf

View File

@ -215,6 +215,9 @@ public:
PDFRenderer::Features getFeatures() const;
void setFeatures(PDFRenderer::Features features);
void setPreferredMeshResolutionRatio(PDFReal ratio);
void setMinimalMeshResolutionRatio(PDFReal ratio);
void setColorTolerance(PDFReal colorTolerance);
signals:
void drawSpaceChanged();
@ -332,6 +335,9 @@ private:
/// Renderer features
PDFRenderer::Features m_features;
/// Mesh quality settings
PDFMeshQualitySettings m_meshQualitySettings;
};
} // namespace pdf

View File

@ -0,0 +1,75 @@
// 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 PDFMESHQUALITYSETTINGS_H
#define PDFMESHQUALITYSETTINGS_H
#include "pdfglobal.h"
#include <QMatrix>
namespace pdf
{
struct PDFMeshQualitySettings
{
/// Initializes default resolution
void initResolution();
/// Value used to compute minimal pxiel size resolution (it is multiplied by whole shading area size)
PDFReal minimalMeshResolutionRatio = 0.005;
/// Value used to compute pixel size resolution (it is multiplied by whole shading area size)
PDFReal preferredMeshResolutionRatio = 0.02;
/// Matrix, which transforms user space points (user space is target space of the shading)
/// to the device space of the paint device.
QMatrix userSpaceToDeviceSpaceMatrix;
/// Rectangle in device space coordinate system, onto which is area meshed.
QRectF deviceSpaceMeshingArea;
/// Preferred mesh resolution in device space pixels. Mesh will be created in this
/// resolution, if it is smooth enough (no jumps in colors occurs).
PDFReal preferredMeshResolution = 1.0;
/// Minimal mesh resolution in device space pixels. If jumps in colors occurs (jump
/// is two colors, that differ more than \p color tolerance), then mesh is meshed to
/// minimal mesh resolution.
PDFReal minimalMeshResolution = 1.0;
/// Color tolerance - 1% by default
PDFReal tolerance = 0.01;
/// Test points to determine maximal curvature of the tensor product patch meshes
PDFInteger patchTestPoints = 64;
/// Lower value of the surface curvature meshing resolution mapping. When ratio between
/// current curvature at the center of meshed triangle and maximal curvature is below
/// this value, then prefered mesh resolution is used. If ratio is higher than this value
/// and lower than \p patchResolutionMappingRatioHigh, then target length is linearly mapped.
/// If value is higher, than \p patchResolutionMappingRatioHigh, then minimal mesh resolution
/// is used when generating triangles on the patch.
PDFReal patchResolutionMappingRatioLow = 0.3;
/// Highter value of the surface curvature meshing resolution mapping. \sa patchResolutionMappingRatioLow
PDFReal patchResolutionMappingRatioHigh = 0.9;
};
} // namespace pdf
#endif // PDFMESHQUALITYSETTINGS_H

View File

@ -184,7 +184,8 @@ PDFPageContentProcessor::PDFPageContentProcessor(const PDFPage* page,
const PDFDocument* document,
const PDFFontCache* fontCache,
const PDFOptionalContentActivity* optionalContentActivity,
QMatrix pagePointToDevicePointMatrix) :
QMatrix pagePointToDevicePointMatrix,
const PDFMeshQualitySettings& meshQualitySettings) :
m_page(page),
m_document(document),
m_fontCache(fontCache),
@ -201,7 +202,8 @@ PDFPageContentProcessor::PDFPageContentProcessor(const PDFPage* page,
m_drawingUncoloredTilingPatternState(0),
m_isWarningColorOperatorsInUncoloredTilingPatternReported(false),
m_patternBaseMatrix(pagePointToDevicePointMatrix),
m_pagePointToDevicePointMatrix(pagePointToDevicePointMatrix)
m_pagePointToDevicePointMatrix(pagePointToDevicePointMatrix),
m_meshQualitySettings(meshQualitySettings)
{
Q_ASSERT(page);
Q_ASSERT(document);
@ -628,10 +630,10 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool
}
// We must create a mesh and then draw pattern
PDFMeshQualitySettings settings;
PDFMeshQualitySettings settings = m_meshQualitySettings;
settings.deviceSpaceMeshingArea = getPageBoundingRectDeviceSpace();
settings.userSpaceToDeviceSpaceMatrix = getPatternBaseMatrix();
settings.initDefaultResolution();
settings.initResolution();
PDFMesh mesh = shadingPattern->createMesh(settings);
@ -717,10 +719,10 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool
}
// We must create a mesh and then draw pattern
PDFMeshQualitySettings settings;
PDFMeshQualitySettings settings = m_meshQualitySettings;
settings.deviceSpaceMeshingArea = getPageBoundingRectDeviceSpace();
settings.userSpaceToDeviceSpaceMatrix = getPatternBaseMatrix();
settings.initDefaultResolution();
settings.initResolution();
PDFMesh mesh = shadingPattern->createMesh(settings);

View File

@ -23,6 +23,7 @@
#include "pdfparser.h"
#include "pdffont.h"
#include "pdfutils.h"
#include "pdfmeshqualitysettings.h"
#include <QMatrix>
#include <QPainterPath>
@ -48,7 +49,8 @@ public:
const PDFDocument* document,
const PDFFontCache* fontCache,
const PDFOptionalContentActivity* optionalContentActivity,
QMatrix pagePointToDevicePointMatrix);
QMatrix pagePointToDevicePointMatrix,
const PDFMeshQualitySettings& meshQualitySettings);
virtual ~PDFPageContentProcessor();
enum class Operator
@ -814,6 +816,9 @@ private:
/// Bounding rectangle of pages media box in device space coordinates. If drawing rotation
/// is zero, then it corresponds to the scaled media box of the page.
QRectF m_pageBoundingRectDeviceSpace;
/// Mesh quality settings
PDFMeshQualitySettings m_meshQualitySettings;
};
} // namespace pdf

View File

@ -29,8 +29,9 @@ PDFPainter::PDFPainter(QPainter* painter,
const PDFPage* page,
const PDFDocument* document,
const PDFFontCache* fontCache,
const PDFOptionalContentActivity* optionalContentActivity) :
PDFPageContentProcessor(page, document, fontCache, optionalContentActivity, pagePointToDevicePointMatrix),
const PDFOptionalContentActivity* optionalContentActivity,
const PDFMeshQualitySettings& meshQualitySettings) :
PDFPageContentProcessor(page, document, fontCache, optionalContentActivity, pagePointToDevicePointMatrix, meshQualitySettings),
m_painter(painter),
m_features(features)
{

View File

@ -41,13 +41,15 @@ public:
/// \param document Document owning the page
/// \param fontCache Font cache
/// \param optionalContentActivity Activity of optional content
/// \param meshQualitySettings Mesh quality settings
explicit PDFPainter(QPainter* painter,
PDFRenderer::Features features,
QMatrix pagePointToDevicePointMatrix,
const PDFPage* page,
const PDFDocument* document,
const PDFFontCache* fontCache,
const PDFOptionalContentActivity* optionalContentActivity);
const PDFOptionalContentActivity* optionalContentActivity,
const PDFMeshQualitySettings& meshQualitySettings);
virtual ~PDFPainter() override;
protected:

View File

@ -903,7 +903,7 @@ PDFMesh PDFAxialShading::createMesh(const PDFMeshQualitySettings& settings) cons
{
// We will skip this coordinate, if both of meshing criteria have been met:
// 1) Color difference is small (lesser than tolerance)
// 2) Distance from previous and next point is less than preffered meshing resolution OR colors are equal
// 2) Distance from previous and next point is less than preferred meshing resolution OR colors are equal
if (it != coloredCoordinates.cbegin() && std::next(it) != coloredCoordinates.cend())
{
@ -1083,15 +1083,12 @@ QPointF PDFMesh::getTriangleCenter(const PDFMesh::Triangle& triangle) const
return (m_vertices[triangle.v1] + m_vertices[triangle.v2] + m_vertices[triangle.v3]) / 3.0;
}
void PDFMeshQualitySettings::initDefaultResolution()
void PDFMeshQualitySettings::initResolution()
{
// We will take 0.5% percent of device space meshing area as minimal resolution (it is ~1.5 mm for
// A4 page) and default resolution 4x number of that.
Q_ASSERT(deviceSpaceMeshingArea.isValid());
PDFReal size = qMax(deviceSpaceMeshingArea.width(), deviceSpaceMeshingArea.height());
minimalMeshResolution = size * 0.005;
preferredMeshResolution = minimalMeshResolution * 4;
minimalMeshResolution = size * minimalMeshResolutionRatio;
preferredMeshResolution = size * qMax(preferredMeshResolutionRatio, minimalMeshResolutionRatio);
}
ShadingType PDFRadialShading::getShadingType() const
@ -1272,7 +1269,7 @@ PDFMesh PDFRadialShading::createMesh(const PDFMeshQualitySettings& settings) con
{
// We will skip this coordinate, if both of meshing criteria have been met:
// 1) Color difference is small (lesser than tolerance)
// 2) Distance from previous and next point is less than preffered meshing resolution OR colors are equal
// 2) Distance from previous and next point is less than preferred meshing resolution OR colors are equal
if (it != coloredCoordinates.cbegin() && std::next(it) != coloredCoordinates.cend())
{
@ -2610,6 +2607,4 @@ PDFMesh PDFCoonsPatchShading::createMesh(const PDFMeshQualitySettings& settings)
return mesh;
}
// TODO: Implement settings of meshing in the settings dialog
} // namespace pdf

View File

@ -21,6 +21,7 @@
#include "pdfobject.h"
#include "pdffunction.h"
#include "pdfcolorspaces.h"
#include "pdfmeshqualitysettings.h"
#include <QMatrix>
@ -53,45 +54,6 @@ enum class ShadingType
TensorProductPatchMesh = 7
};
struct PDFMeshQualitySettings
{
/// Initializes default resolution
void initDefaultResolution();
/// Matrix, which transforms user space points (user space is target space of the shading)
/// to the device space of the paint device.
QMatrix userSpaceToDeviceSpaceMatrix;
/// Rectangle in device space coordinate system, onto which is area meshed.
QRectF deviceSpaceMeshingArea;
/// Preferred mesh resolution in device space pixels. Mesh will be created in this
/// resolution, if it is smooth enough (no jumps in colors occurs).
PDFReal preferredMeshResolution = 1.0;
/// Minimal mesh resolution in device space pixels. If jumps in colors occurs (jump
/// is two colors, that differ more than \p color tolerance), then mesh is meshed to
/// minimal mesh resolution.
PDFReal minimalMeshResolution = 1.0;
/// Color tolerance - 1% by default
PDFReal tolerance = 0.01;
/// Test points to determine maximal curvature of the tensor product patch meshes
PDFInteger patchTestPoints = 64;
/// Lower value of the surface curvature meshing resolution mapping. When ratio between
/// current curvature at the center of meshed triangle and maximal curvature is below
/// this value, then prefered mesh resolution is used. If ratio is higher than this value
/// and lower than \p patchResolutionMappingRatioHigh, then target length is linearly mapped.
/// If value is higher, than \p patchResolutionMappingRatioHigh, then minimal mesh resolution
/// is used when generating triangles on the patch.
PDFReal patchResolutionMappingRatioLow = 0.3;
/// Highter value of the surface curvature meshing resolution mapping. \sa patchResolutionMappingRatioLow
PDFReal patchResolutionMappingRatioHigh = 0.9;
};
/// Mesh consisting of triangles
class PDFMesh
{

View File

@ -22,11 +22,16 @@
namespace pdf
{
PDFRenderer::PDFRenderer(const PDFDocument* document, const PDFFontCache* fontCache, const PDFOptionalContentActivity* optionalContentActivity, Features features) :
PDFRenderer::PDFRenderer(const PDFDocument* document,
const PDFFontCache* fontCache,
const PDFOptionalContentActivity* optionalContentActivity,
Features features,
const PDFMeshQualitySettings& meshQualitySettings) :
m_document(document),
m_fontCache(fontCache),
m_optionalContentActivity(optionalContentActivity),
m_features(features)
m_features(features),
m_meshQualitySettings(meshQualitySettings)
{
Q_ASSERT(document);
}
@ -88,7 +93,7 @@ QList<PDFRenderError> PDFRenderer::render(QPainter* painter, const QRectF& recta
}
}
PDFPainter processor(painter, m_features, matrix, page, m_document, m_fontCache, m_optionalContentActivity);
PDFPainter processor(painter, m_features, matrix, page, m_document, m_fontCache, m_optionalContentActivity, m_meshQualitySettings);
return processor.processContents();
}
@ -106,7 +111,7 @@ QList<PDFRenderError> PDFRenderer::render(QPainter* painter, const QMatrix& matr
const PDFPage* page = catalog->getPage(pageIndex);
Q_ASSERT(page);
PDFPainter processor(painter, m_features, matrix, page, m_document, m_fontCache, m_optionalContentActivity);
PDFPainter processor(painter, m_features, matrix, page, m_document, m_fontCache, m_optionalContentActivity, m_meshQualitySettings);
return processor.processContents();
}

View File

@ -20,6 +20,7 @@
#include "pdfpage.h"
#include "pdfexception.h"
#include "pdfmeshqualitysettings.h"
class QPainter;
@ -44,7 +45,7 @@ public:
Q_DECLARE_FLAGS(Features, Feature)
explicit PDFRenderer(const PDFDocument* document, const PDFFontCache* fontCache, const PDFOptionalContentActivity* optionalContentActivity, Features features);
explicit PDFRenderer(const PDFDocument* document, const PDFFontCache* fontCache, const PDFOptionalContentActivity* optionalContentActivity, Features features, const PDFMeshQualitySettings& meshQualitySettings);
/// Paints desired page onto the painter. Page is painted in the rectangle using best-fit method.
/// If the page doesn't exist, then error is returned. No exception is thrown. Rendering errors
@ -67,6 +68,7 @@ private:
const PDFFontCache* m_fontCache;
const PDFOptionalContentActivity* m_optionalContentActivity;
Features m_features;
PDFMeshQualitySettings m_meshQualitySettings;
};