// 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 .
#ifndef PDFPATTERN_H
#define PDFPATTERN_H
#include "pdfobject.h"
#include "pdffunction.h"
#include "pdfcolorspaces.h"
#include
#include
namespace pdf
{
class PDFPattern;
class PDFShadingPattern;
using PDFPatternPtr = std::shared_ptr;
enum class PatternType
{
Invalid = 0,
Tiling = 1,
Shading = 2,
};
enum class ShadingType
{
Invalid = 0,
Function = 1,
Axial = 2,
Radial = 3,
FreeFormGouradTriangle = 4,
LatticeFormGouradTriangle = 5,
CoonsPatchMesh = 6,
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;
};
/// Mesh consisting of triangles
class PDFMesh
{
public:
explicit PDFMesh() = default;
struct Triangle
{
uint32_t v1 = 0;
uint32_t v2 = 0;
uint32_t v3 = 0;
QRgb color;
};
/// Adds vertex. Returns index of added vertex.
/// \param vertex Vertex to be added
/// \returns Index of the added vertex
inline uint32_t addVertex(const QPointF& vertex) { const size_t index = m_vertices.size(); m_vertices.emplace_back(vertex); return static_cast(index); }
/// Adds triangle. Returns index of added triangle.
/// \param triangle Triangle to be added
/// \returns Index of the added vertex
inline uint32_t addTriangle(const Triangle& triangle) { const size_t index = m_triangles.size(); m_triangles.emplace_back(triangle); return static_cast(index); }
/// Adds quad. Vertices are in clockwise order (so, we have edges v1-v2, v2-v3, v3-v4, v4-v1).
/// \param v1 First vertex (for example, topleft)
/// \param v2 Second vertex (for example, topright)
/// \param v3 Third vertex (for example, bottomright)
/// \param v4 Fourth vertex (for example, bottomleft)
/// \param color Color of the quad.
inline void addQuad(uint32_t v1, uint32_t v2, uint32_t v3, uint32_t v4, QRgb color) { addTriangle({v1, v2, v3, color}); addTriangle({ v1, v3, v4, color}); }
/// Paints the mesh on the painter
/// \param painter Painter, onto which is mesh drawn
void paint(QPainter* painter) const;
/// Transforms the mesh according to the matrix transform
/// \param matrix Matrix transform to be performed
void transform(const QMatrix& matrix);
/// Reserves memory for meshing - both number of vertices and triangles.
/// Use this function, if number of vertices and triangles is known.
/// \param vertexCount Vertex count
/// \param triangleCount Triangle count
void reserve(size_t vertexCount, size_t triangleCount) { m_vertices.reserve(vertexCount); m_triangles.reserve(triangleCount); }
const QPainterPath& getBoundingPath() const { return m_boundingPath; }
void setBoundingPath(const QPainterPath& path) { m_boundingPath = path; }
/// Sets the vertex array to the mesh
/// \param vertices New vertex array
void setVertices(std::vector&& vertices) { m_vertices = qMove(vertices); }
/// Sets the triangle array to the mesh
/// \param triangles New triangle array
void setTriangles(std::vector&& triangles) { m_triangles = qMove(triangles); }
/// Returns vertex at given index
/// \param index Index of the vertex
const QPointF& getVertex(size_t index) const { return m_vertices[index]; }
/// Returns triangle center. Triangles vertice indices must be valid.
/// \param triangle Triangle
QPointF getTriangleCenter(const Triangle& triangle) const;
/// Sets the background path. In order to draw background properly, the background
/// color must be set to a valid color.
/// \param path Background path
void setBackgroundPath(QPainterPath path) { m_backgroundPath = qMove(path); }
/// Sets the background color (background path is then painted with this color, if it is not
/// empty), if color is invalid, it turns off background painting.
/// \param backgroundColor Background color
void setBackgroundColor(QColor backgroundColor) { m_backgroundColor = backgroundColor; }
private:
std::vector m_vertices;
std::vector m_triangles;
QPainterPath m_boundingPath;
QPainterPath m_backgroundPath;
QColor m_backgroundColor;
};
/// Represents tiling/shading pattern
class PDFPattern
{
public:
explicit PDFPattern() = default;
virtual ~PDFPattern() = default;
virtual PatternType getType() const = 0;
virtual const PDFShadingPattern* getShadingPattern() const = 0;
/// Returns bounding box in the shadings target coordinate system (not in
/// pattern coordinate system).
const QRectF& getBoundingBox() const { return m_boundingBox; }
/// Returns transformation matrix from pattern space to the default
/// target space.
const QMatrix& getMatrix() const { return m_matrix; }
/// Create pattern from the object. If error occurs, exception is thrown
/// \param colorSpaceDictionary Color space dictionary
/// \param document Document, owning the pdf object
/// \param object Object defining the pattern
static PDFPatternPtr createPattern(const PDFDictionary* colorSpaceDictionary, const PDFDocument* document, const PDFObject& object);
/// Create shading pattern from the object. If error occurs, exception is thrown
/// \param colorSpaceDictionary Color space dictionary
/// \param document Document, owning the pdf object
/// \param object Object defining the shading
/// \param matrix Matrix converting reference coordinate system to the device coordinate system
/// \param patternGraphicState Pattern graphic state
/// \param ignoreBackgroundColor If set, then ignores background color, even if it is present
static PDFPatternPtr createShadingPattern(const PDFDictionary* colorSpaceDictionary,
const PDFDocument* document,
const PDFObject& shadingObject,
const QMatrix& matrix,
const PDFObject& patternGraphicState,
bool ignoreBackgroundColor);
protected:
QRectF m_boundingBox;
QMatrix m_matrix;
};
/// Shading pattern - smooth color distribution along the pattern's space
class PDFShadingPattern : public PDFPattern
{
public:
explicit PDFShadingPattern() = default;
virtual PatternType getType() const override;
virtual ShadingType getShadingType() const = 0;
virtual const PDFShadingPattern* getShadingPattern() const override { return this; }
/// Creates a colored mesh using settings. Mesh is generated in device space
/// coordinate system. You must transform the mesh, if you want to
/// use it in another coordinate system.
/// \param settings Meshing settings
virtual PDFMesh createMesh(const PDFMeshQualitySettings& settings) const = 0;
/// Returns patterns graphic state. This state must be applied before
/// the shading pattern is painted to the target device.
const PDFObject& getPatternGraphicState() const { return m_patternGraphicState; }
/// Returns color space of the pattern.
const PDFAbstractColorSpace* getColorSpace() const;
/// Returns patterns background color (if pattern has background color).
/// If pattern has not background color, then invalid color is returned.
const QColor& getBackgroundColor() const { return m_backgroundColor; }
/// Returns true, if shading pattern should be anti-aliased
bool isAntialiasing() const { return m_antiAlias; }
protected:
friend class PDFPattern;
PDFObject m_patternGraphicState;
PDFColorSpacePointer m_colorSpace;
QColor m_backgroundColor;
bool m_antiAlias = false;
};
class PDFSingleDimensionShading : public PDFShadingPattern
{
public:
explicit PDFSingleDimensionShading() = default;
protected:
friend class PDFPattern;
std::vector m_functions;
QPointF m_startPoint;
QPointF m_endPoint;
PDFReal m_domainStart = 0.0;
PDFReal m_domainEnd = 1.0;
bool m_extendStart = false;
bool m_extendEnd = false;
};
class PDFFunctionShading : public PDFShadingPattern
{
public:
explicit PDFFunctionShading() = default;
virtual ShadingType getShadingType() const override;
virtual PDFMesh createMesh(const PDFMeshQualitySettings& settings) const override;
private:
friend class PDFPattern;
QRectF m_domain; ///< Domain of the color function
QMatrix m_domainToTargetTransform; ///< Transformation mapping from domain to shading coordinate space
std::vector m_functions; ///< Color functions
};
class PDFAxialShading : public PDFSingleDimensionShading
{
public:
explicit PDFAxialShading() = default;
virtual ShadingType getShadingType() const override;
virtual PDFMesh createMesh(const PDFMeshQualitySettings& settings) const override;
private:
friend class PDFPattern;
};
class PDFRadialShading : public PDFSingleDimensionShading
{
public:
explicit PDFRadialShading() = default;
virtual ShadingType getShadingType() const override;
virtual PDFMesh createMesh(const PDFMeshQualitySettings& settings) const override;
private:
friend class PDFPattern;
PDFReal m_r0 = 0.0;
PDFReal m_r1 = 0.0;
};
} // namespace pdf
#endif // PDFPATTERN_H