mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-01-27 23:59:23 +01:00
Axial shading sampling
This commit is contained in:
parent
5b8b1de2db
commit
d9c6c5e2b4
@ -359,6 +359,19 @@ void PDFPageContentProcessor::performPathPainting(const QPainterPath& path, bool
|
||||
Q_UNUSED(fillRule);
|
||||
}
|
||||
|
||||
bool PDFPageContentProcessor::performPathPaintingUsingShading(const QPainterPath& path, bool stroke, bool fill, const PDFShadingPattern* shadingPattern)
|
||||
{
|
||||
Q_UNUSED(path);
|
||||
Q_UNUSED(shadingPattern);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void PDFPageContentProcessor::performFinishPathPainting()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PDFPageContentProcessor::performClipping(const QPainterPath& path, Qt::FillRule fillRule)
|
||||
{
|
||||
Q_UNUSED(path);
|
||||
@ -820,21 +833,24 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool
|
||||
settings.userSpaceToDeviceSpaceMatrix = getPatternBaseMatrix();
|
||||
settings.initResolution();
|
||||
|
||||
PDFMesh mesh = shadingPattern->createMesh(settings, m_CMS, m_graphicState.getRenderingIntent(), this);
|
||||
|
||||
// Now, merge the current path to the mesh clipping path
|
||||
QPainterPath boundingPath = mesh.getBoundingPath();
|
||||
if (boundingPath.isEmpty())
|
||||
if (!performPathPaintingUsingShading(path, false, true, shadingPattern))
|
||||
{
|
||||
boundingPath = getCurrentWorldMatrix().map(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
boundingPath = boundingPath.intersected(path);
|
||||
}
|
||||
mesh.setBoundingPath(boundingPath);
|
||||
PDFMesh mesh = shadingPattern->createMesh(settings, m_CMS, m_graphicState.getRenderingIntent(), this);
|
||||
|
||||
performMeshPainting(mesh);
|
||||
// Now, merge the current path to the mesh clipping path
|
||||
QPainterPath boundingPath = mesh.getBoundingPath();
|
||||
if (boundingPath.isEmpty())
|
||||
{
|
||||
boundingPath = getCurrentWorldMatrix().map(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
boundingPath = boundingPath.intersected(path);
|
||||
}
|
||||
mesh.setBoundingPath(boundingPath);
|
||||
|
||||
performMeshPainting(mesh);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -917,8 +933,6 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool
|
||||
settings.userSpaceToDeviceSpaceMatrix = getPatternBaseMatrix();
|
||||
settings.initResolution();
|
||||
|
||||
PDFMesh mesh = shadingPattern->createMesh(settings, m_CMS, m_graphicState.getRenderingIntent(), this);
|
||||
|
||||
// We must stroke the path.
|
||||
QPainterPathStroker stroker;
|
||||
stroker.setCapStyle(m_graphicState.getLineCapStyle());
|
||||
@ -934,18 +948,23 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool
|
||||
}
|
||||
QPainterPath strokedPath = stroker.createStroke(path);
|
||||
|
||||
QPainterPath boundingPath = mesh.getBoundingPath();
|
||||
if (boundingPath.isEmpty())
|
||||
if (!performPathPaintingUsingShading(strokedPath, true, false, shadingPattern))
|
||||
{
|
||||
boundingPath = getCurrentWorldMatrix().map(strokedPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
boundingPath = boundingPath.intersected(strokedPath);
|
||||
}
|
||||
mesh.setBoundingPath(boundingPath);
|
||||
PDFMesh mesh = shadingPattern->createMesh(settings, m_CMS, m_graphicState.getRenderingIntent(), this);
|
||||
|
||||
performMeshPainting(mesh);
|
||||
QPainterPath boundingPath = mesh.getBoundingPath();
|
||||
if (boundingPath.isEmpty())
|
||||
{
|
||||
boundingPath = getCurrentWorldMatrix().map(strokedPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
boundingPath = boundingPath.intersected(strokedPath);
|
||||
}
|
||||
mesh.setBoundingPath(boundingPath);
|
||||
|
||||
performMeshPainting(mesh);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -971,6 +990,8 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool
|
||||
{
|
||||
performPathPainting(path, stroke, fill, text, fillRule);
|
||||
}
|
||||
|
||||
performFinishPathPainting();
|
||||
}
|
||||
|
||||
void PDFPageContentProcessor::processTillingPatternPainting(const PDFTilingPattern* tilingPattern,
|
||||
|
@ -41,6 +41,7 @@ class PDFCMS;
|
||||
class PDFMesh;
|
||||
class PDFImage;
|
||||
class PDFTilingPattern;
|
||||
class PDFShadingPattern;
|
||||
class PDFOptionalContentActivity;
|
||||
|
||||
static constexpr const char* PDF_RESOURCE_EXTGSTATE = "ExtGState";
|
||||
@ -505,6 +506,17 @@ protected:
|
||||
/// \param fillRule Fill rule used in the fill mode
|
||||
virtual void performPathPainting(const QPainterPath& path, bool stroke, bool fill, bool text, Qt::FillRule fillRule);
|
||||
|
||||
/// This function is used, when we want to implement custom fill using shading. If path is successfully
|
||||
/// filled by shading, then true should be returned.
|
||||
/// \param path Path to be filled
|
||||
/// \param stroke Is path actually stroked?
|
||||
/// \param fill Is path actually filled?
|
||||
/// \param shadingPattern Shading pattern
|
||||
virtual bool performPathPaintingUsingShading(const QPainterPath& path, bool stroke, bool fill, const PDFShadingPattern* shadingPattern);
|
||||
|
||||
/// This function is called after path paintig is finished
|
||||
virtual void performFinishPathPainting();
|
||||
|
||||
/// This function has to be implemented in the client drawing implementation, it should
|
||||
/// clip along the path (intersect with current clipping path).
|
||||
virtual void performClipping(const QPainterPath& path, Qt::FillRule fillRule);
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "pdfutils.h"
|
||||
#include "pdfcolorspaces.h"
|
||||
#include "pdfexecutionpolicy.h"
|
||||
#include "pdfconstants.h"
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
@ -34,11 +35,23 @@ PatternType PDFShadingPattern::getType() const
|
||||
return PatternType::Shading;
|
||||
}
|
||||
|
||||
const PDFAbstractColorSpace* PDFShadingPattern::getColorSpace() const
|
||||
{
|
||||
return m_colorSpace.data();
|
||||
}
|
||||
|
||||
QMatrix PDFShadingPattern::getPatternSpaceToDeviceSpaceMatrix(const PDFMeshQualitySettings& settings) const
|
||||
{
|
||||
return m_matrix * settings.userSpaceToDeviceSpaceMatrix;
|
||||
}
|
||||
|
||||
PDFShadingSampler* PDFShadingPattern::createSampler(QMatrix userSpaceToDeviceSpaceMatrix) const
|
||||
{
|
||||
Q_UNUSED(userSpaceToDeviceSpaceMatrix);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ShadingType PDFAxialShading::getShadingType() const
|
||||
{
|
||||
return ShadingType::Axial;
|
||||
@ -171,6 +184,7 @@ PDFPatternPtr PDFPattern::createShadingPattern(const PDFDictionary* colorSpaceDi
|
||||
}
|
||||
|
||||
QColor backgroundColor;
|
||||
PDFColor originalBackgroundColor;
|
||||
if (!ignoreBackgroundColor)
|
||||
{
|
||||
std::vector<PDFReal> backgroundColorValues = loader.readNumberArrayFromDictionary(shadingDictionary, "Background");
|
||||
@ -178,6 +192,12 @@ PDFPatternPtr PDFPattern::createShadingPattern(const PDFDictionary* colorSpaceDi
|
||||
{
|
||||
backgroundColor = colorSpace->getCheckedColor(PDFAbstractColorSpace::convertToColor(backgroundColorValues), cms, intent, reporter);
|
||||
}
|
||||
|
||||
originalBackgroundColor.resize(backgroundColorValues.size());
|
||||
for (size_t i = 0; i < backgroundColorValues.size(); ++i)
|
||||
{
|
||||
originalBackgroundColor[i] = backgroundColorValues[i];
|
||||
}
|
||||
}
|
||||
QRectF boundingBox = loader.readRectangle(shadingDictionary->get("BBox"), QRectF());
|
||||
bool antialias = loader.readBooleanFromDictionary(shadingDictionary, "AntiAlias", false);
|
||||
@ -240,6 +260,7 @@ PDFPatternPtr PDFPattern::createShadingPattern(const PDFDictionary* colorSpaceDi
|
||||
// Load items for function shading
|
||||
functionShading->m_antiAlias = antialias;
|
||||
functionShading->m_backgroundColor = backgroundColor;
|
||||
functionShading->m_originalBackgroundColor = qMove(originalBackgroundColor);
|
||||
functionShading->m_colorSpace = colorSpace;
|
||||
functionShading->m_boundingBox = boundingBox;
|
||||
functionShading->m_domain = QRectF(functionDomain[0], functionDomain[2], functionDomain[1] - functionDomain[0], functionDomain[3] - functionDomain[2]);
|
||||
@ -281,6 +302,7 @@ PDFPatternPtr PDFPattern::createShadingPattern(const PDFDictionary* colorSpaceDi
|
||||
// Load items for axial shading
|
||||
axialShading->m_antiAlias = antialias;
|
||||
axialShading->m_backgroundColor = backgroundColor;
|
||||
axialShading->m_originalBackgroundColor = qMove(originalBackgroundColor);
|
||||
axialShading->m_colorSpace = colorSpace;
|
||||
axialShading->m_boundingBox = boundingBox;
|
||||
axialShading->m_domainStart = domain[0];
|
||||
@ -331,6 +353,7 @@ PDFPatternPtr PDFPattern::createShadingPattern(const PDFDictionary* colorSpaceDi
|
||||
// Load items for axial shading
|
||||
radialShading->m_antiAlias = antialias;
|
||||
radialShading->m_backgroundColor = backgroundColor;
|
||||
radialShading->m_originalBackgroundColor = qMove(originalBackgroundColor);
|
||||
radialShading->m_colorSpace = colorSpace;
|
||||
radialShading->m_boundingBox = boundingBox;
|
||||
radialShading->m_domainStart = domain[0];
|
||||
@ -422,6 +445,7 @@ PDFPatternPtr PDFPattern::createShadingPattern(const PDFDictionary* colorSpaceDi
|
||||
|
||||
type4567Shading->m_antiAlias = antialias;
|
||||
type4567Shading->m_backgroundColor = backgroundColor;
|
||||
type4567Shading->m_originalBackgroundColor = qMove(originalBackgroundColor);
|
||||
type4567Shading->m_colorSpace = colorSpace;
|
||||
type4567Shading->m_matrix = matrix;
|
||||
type4567Shading->m_patternGraphicState = patternGraphicState;
|
||||
@ -1011,6 +1035,158 @@ PDFMesh PDFAxialShading::createMesh(const PDFMeshQualitySettings& settings, cons
|
||||
return mesh;
|
||||
}
|
||||
|
||||
class PDFAxialShadingSampler : public PDFShadingSampler
|
||||
{
|
||||
public:
|
||||
PDFAxialShadingSampler(const PDFAxialShading* axialShadingPattern, QMatrix userSpaceToDeviceSpaceMatrix) :
|
||||
PDFShadingSampler(axialShadingPattern),
|
||||
m_axialShadingPattern(axialShadingPattern),
|
||||
m_xStart(0.0),
|
||||
m_xEnd(0.0),
|
||||
m_tAtStart(0.0),
|
||||
m_tAtEnd(0.0),
|
||||
m_tMin(0.0),
|
||||
m_tMax(0.0)
|
||||
{
|
||||
QMatrix patternSpaceToDeviceSpace = axialShadingPattern->getMatrix() * userSpaceToDeviceSpaceMatrix;
|
||||
|
||||
QPointF p1 = patternSpaceToDeviceSpace.map(axialShadingPattern->getStartPoint());
|
||||
QPointF p2 = patternSpaceToDeviceSpace.map(axialShadingPattern->getEndPoint());
|
||||
|
||||
// Strategy: for simplification, we rotate the line clockwise so we will
|
||||
// get the shading axis equal to the x-axis. Then we will determine the shading
|
||||
// area and create mesh according the settings.
|
||||
QLineF line(p1, p2);
|
||||
const double angle = line.angleTo(QLineF(0, 0, 1, 0));
|
||||
|
||||
// Matrix p1p2LCS is local coordinate system of line p1-p2. It transforms
|
||||
// points on the line to the global coordinate system. So, point (0, 0) will
|
||||
// map onto p1 and point (length(p1-p2), 0) will map onto p2.
|
||||
QMatrix p1p2LCS;
|
||||
p1p2LCS.translate(p1.x(), p1.y());
|
||||
p1p2LCS.rotate(angle);
|
||||
QMatrix p1p2GCS = p1p2LCS.inverted();
|
||||
|
||||
QPointF p1m = p1p2GCS.map(p1);
|
||||
QPointF p2m = p1p2GCS.map(p2);
|
||||
|
||||
Q_ASSERT(isZero(p1m.y()));
|
||||
Q_ASSERT(isZero(p2m.y()));
|
||||
Q_ASSERT(p1m.x() <= p2m.x());
|
||||
|
||||
m_xStart = p1m.x();
|
||||
m_xEnd = p2m.x();
|
||||
|
||||
m_tAtStart = axialShadingPattern->getDomainStart();
|
||||
m_tAtEnd = axialShadingPattern->getDomainEnd();
|
||||
m_tMin = qMin(m_tAtStart, m_tAtEnd);
|
||||
m_tMax = qMax(m_tAtStart, m_tAtEnd);
|
||||
}
|
||||
|
||||
virtual bool sample(const QPointF& devicePoint, PDFColorBuffer outputBuffer, int limit) const override
|
||||
{
|
||||
Q_UNUSED(limit);
|
||||
|
||||
if (!m_pattern->getColorSpace() || m_pattern->getColorSpace()->getColorComponentCount() != outputBuffer.size())
|
||||
{
|
||||
// Invalid color space, or invalid color buffer
|
||||
return false;
|
||||
}
|
||||
|
||||
QPointF mappedPoint = m_p1p2GCS.map(devicePoint);
|
||||
const PDFReal x = mappedPoint.x();
|
||||
|
||||
PDFReal t = m_tAtStart;
|
||||
|
||||
if (x < m_xStart)
|
||||
{
|
||||
if (!m_axialShadingPattern->isExtendStart())
|
||||
{
|
||||
return fillBackgroundColor(outputBuffer);
|
||||
}
|
||||
|
||||
t = m_tAtStart;
|
||||
}
|
||||
else if (x > m_xEnd)
|
||||
{
|
||||
if (!m_axialShadingPattern->isExtendEnd())
|
||||
{
|
||||
return fillBackgroundColor(outputBuffer);
|
||||
}
|
||||
|
||||
t = m_tAtEnd;
|
||||
}
|
||||
else
|
||||
{
|
||||
t = interpolate(x, m_xStart, m_xEnd, m_tAtStart, m_tAtEnd);
|
||||
t = qBound(m_tMin, t, m_tMax);
|
||||
}
|
||||
|
||||
const auto& functions = m_axialShadingPattern->getFunctions();
|
||||
std::array<PDFReal, PDF_MAX_COLOR_COMPONENTS> colorBuffer = { };
|
||||
|
||||
if (colorBuffer.size() < outputBuffer.size())
|
||||
{
|
||||
// Jakub Melka: Too much colors - we cant process it
|
||||
return false;
|
||||
}
|
||||
|
||||
if (functions.size() == 1)
|
||||
{
|
||||
Q_ASSERT(outputBuffer.size() <= colorBuffer.size());
|
||||
PDFFunction::FunctionResult result = functions.front()->apply(&t, &t + 1, colorBuffer.data(), colorBuffer.data() + outputBuffer.size());
|
||||
|
||||
if (!result)
|
||||
{
|
||||
// Function call failed
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (functions.size() != outputBuffer.size())
|
||||
{
|
||||
// Invalid number of functions
|
||||
return false;
|
||||
}
|
||||
|
||||
Q_ASSERT(outputBuffer.size() <= colorBuffer.size());
|
||||
for (size_t i = 0, count = outputBuffer.size(); i < count; ++i)
|
||||
{
|
||||
PDFFunction::FunctionResult result = functions[i]->apply(&t, &t + 1, colorBuffer.data() + i, colorBuffer.data() + i + 1);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
// Function call failed
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0, count = outputBuffer.size(); i < count; ++i)
|
||||
{
|
||||
outputBuffer[i] = colorBuffer[i];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const PDFAxialShading* m_axialShadingPattern;
|
||||
QMatrix m_p1p2GCS;
|
||||
PDFReal m_xStart;
|
||||
PDFReal m_xEnd;
|
||||
PDFReal m_tAtStart;
|
||||
PDFReal m_tAtEnd;
|
||||
PDFReal m_tMin;
|
||||
PDFReal m_tMax;
|
||||
};
|
||||
|
||||
PDFShadingSampler* PDFAxialShading::createSampler(QMatrix userSpaceToDeviceSpaceMatrix) const
|
||||
{
|
||||
return new PDFAxialShadingSampler(this, userSpaceToDeviceSpaceMatrix);
|
||||
}
|
||||
|
||||
void PDFMesh::paint(QPainter* painter, PDFReal alpha) const
|
||||
{
|
||||
if (m_triangles.empty())
|
||||
@ -2649,4 +2825,21 @@ PDFMesh PDFCoonsPatchShading::createMesh(const PDFMeshQualitySettings& settings,
|
||||
return mesh;
|
||||
}
|
||||
|
||||
bool PDFShadingSampler::fillBackgroundColor(PDFColorBuffer outputBuffer) const
|
||||
{
|
||||
const auto& originalBackgroundColor = m_pattern->getOriginalBackgroundColor();
|
||||
|
||||
if (originalBackgroundColor.size() == outputBuffer.size())
|
||||
{
|
||||
for (size_t i = 0; i < outputBuffer.size(); ++i)
|
||||
{
|
||||
outputBuffer[i] = originalBackgroundColor[i];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace pdf
|
||||
|
@ -264,6 +264,34 @@ private:
|
||||
QByteArray m_content;
|
||||
};
|
||||
|
||||
/// Compute color of sample points from shading pattern. Some sampler implementation
|
||||
/// uses numerical algorithms (such as Newton-Raphson method for type 6/7 shading), so
|
||||
/// calculation can be very slow for some types of shadings.
|
||||
class PDFShadingSampler
|
||||
{
|
||||
public:
|
||||
explicit inline PDFShadingSampler(const PDFShadingPattern* pattern) :
|
||||
m_pattern(pattern)
|
||||
{
|
||||
|
||||
}
|
||||
virtual ~PDFShadingSampler() = default;
|
||||
|
||||
/// Try to compute color of the point in device space coordinates in the shading. If color
|
||||
/// can't be computed, then false is returned, otherwise true is returned.
|
||||
/// \param devicePoint Point in device space coordinates
|
||||
/// \param outputBuffer Color output buffer (where computed color is stored)
|
||||
/// \param limit Maximal number of the steps of numerical calculation algorithms (for type 6/7 shading only)
|
||||
virtual bool sample(const QPointF& devicePoint, PDFColorBuffer outputBuffer, int limit) const = 0;
|
||||
|
||||
/// Fill background color to the output buffer. If the background color is not filled,
|
||||
/// or is invalid, then false is returned, otherwise true is returned.
|
||||
bool fillBackgroundColor(PDFColorBuffer outputBuffer) const;
|
||||
|
||||
protected:
|
||||
const PDFShadingPattern* m_pattern;
|
||||
};
|
||||
|
||||
/// Shading pattern - smooth color distribution along the pattern's space
|
||||
class PDFShadingPattern : public PDFPattern
|
||||
{
|
||||
@ -295,18 +323,28 @@ public:
|
||||
/// If pattern has not background color, then invalid color is returned.
|
||||
const QColor& getBackgroundColor() const { return m_backgroundColor; }
|
||||
|
||||
/// Returns original background color (in color space of the shading pattern)
|
||||
const PDFColor& getOriginalBackgroundColor() const { return m_originalBackgroundColor; }
|
||||
|
||||
/// Returns true, if shading pattern should be anti-aliased
|
||||
bool isAntialiasing() const { return m_antiAlias; }
|
||||
|
||||
/// Returns matrix transforming pattern space to device space
|
||||
QMatrix getPatternSpaceToDeviceSpaceMatrix(const PDFMeshQualitySettings& settings) const;
|
||||
|
||||
/// Create sampler which can compute shading colors in device space coordinates. If sampler can't
|
||||
/// be created (or shading is invalid), then nullptr is returned.
|
||||
/// \param userSpaceToDeviceSpaceMatrix Matrix, which transforms user space points
|
||||
/// (user space is target space of the shading) to the device space of the paint device.
|
||||
virtual PDFShadingSampler* createSampler(QMatrix userSpaceToDeviceSpaceMatrix) const;
|
||||
|
||||
protected:
|
||||
friend class PDFPattern;
|
||||
|
||||
PDFObject m_patternGraphicState;
|
||||
PDFColorSpacePointer m_colorSpace;
|
||||
QColor m_backgroundColor;
|
||||
PDFColor m_originalBackgroundColor;
|
||||
bool m_antiAlias = false;
|
||||
};
|
||||
|
||||
@ -315,6 +353,14 @@ class PDFSingleDimensionShading : public PDFShadingPattern
|
||||
public:
|
||||
explicit PDFSingleDimensionShading() = default;
|
||||
|
||||
const std::vector<PDFFunctionPtr>& getFunctions() const { return m_functions; }
|
||||
const QPointF& getStartPoint() const { return m_startPoint; }
|
||||
const QPointF& getEndPoint() const { return m_endPoint; }
|
||||
PDFReal getDomainStart() const { return m_domainStart; }
|
||||
PDFReal getDomainEnd() const { return m_domainEnd; }
|
||||
bool isExtendStart() const { return m_extendStart; }
|
||||
bool isExtendEnd() const { return m_extendEnd; }
|
||||
|
||||
protected:
|
||||
friend class PDFPattern;
|
||||
|
||||
@ -350,6 +396,7 @@ public:
|
||||
|
||||
virtual ShadingType getShadingType() const override;
|
||||
virtual PDFMesh createMesh(const PDFMeshQualitySettings& settings, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const override;
|
||||
virtual PDFShadingSampler* createSampler(QMatrix userSpaceToDeviceSpaceMatrix) const;
|
||||
|
||||
private:
|
||||
friend class PDFPattern;
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "pdfcms.h"
|
||||
#include "pdfexecutionpolicy.h"
|
||||
#include "pdfimage.h"
|
||||
#include "pdfpattern.h"
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
@ -1831,6 +1832,98 @@ void PDFTransparencyRenderer::performPathPainting(const QPainterPath& path, bool
|
||||
flushDrawBuffer();
|
||||
}
|
||||
|
||||
bool PDFTransparencyRenderer::performPathPaintingUsingShading(const QPainterPath& path, bool stroke, bool fill, const PDFShadingPattern* shadingPattern)
|
||||
{
|
||||
if (path.isEmpty())
|
||||
{
|
||||
// Path is empty
|
||||
return true;
|
||||
}
|
||||
|
||||
QMatrix worldMatrix = getCurrentWorldMatrix();
|
||||
QPainterPath worldPath = worldMatrix.map(path);
|
||||
QRect fillRect = getActualFillRect(worldPath.controlPointRect());
|
||||
|
||||
if (fillRect.isEmpty())
|
||||
{
|
||||
// Jakub Melka: nothing to draw, rectangle is empty
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<PDFShadingSampler> sampler(shadingPattern->createSampler(getPatternBaseMatrix()));
|
||||
if (!sampler)
|
||||
{
|
||||
// Can't create sampler - this is error
|
||||
reportRenderError(RenderErrorType::Error, PDFTranslationContext::tr("Cannot create shading sampler."));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Now, we have a sampler, so we create a texture, which we will later use
|
||||
// as color source.
|
||||
const PDFAbstractColorSpace* colorSpace = shadingPattern->getColorSpace();
|
||||
const size_t shadingColorComponentCount = colorSpace->getColorComponentCount();
|
||||
PDFFloatBitmapWithColorSpace texture(fillRect.width() + 1, fillRect.height() + 1, PDFPixelFormat::createFormat(shadingColorComponentCount, 0, true, shadingColorComponentCount == 4, false), colorSpace);
|
||||
QPointF offset = fillRect.topLeft();
|
||||
|
||||
const PDFPixelFormat texturePixelFormat = texture.getPixelFormat();
|
||||
const uint8_t textureShapeChannel = texturePixelFormat.getShapeChannelIndex();
|
||||
const uint8_t textureOpacityChannel = texturePixelFormat.getOpacityChannelIndex();
|
||||
|
||||
if (fillRect.width() > fillRect.height())
|
||||
{
|
||||
// Columns
|
||||
PDFIntegerRange<int> range(fillRect.left(), fillRect.right() + 1);
|
||||
auto processEntry = [&, this](int x)
|
||||
{
|
||||
for (int y = fillRect.top(); y <= fillRect.bottom(); ++y)
|
||||
{
|
||||
PDFColorBuffer buffer = texture.getPixel(x, y);
|
||||
bool isSampled = sampler->sample(QPointF(x, y) + offset, buffer.resized(shadingColorComponentCount), m_settings.shadingAlgorithmLimit);
|
||||
const PDFColorComponent textureSampleShape = isSampled ? 1.0f : 0.0f;
|
||||
buffer[textureShapeChannel] = textureSampleShape;
|
||||
buffer[textureOpacityChannel] = textureSampleShape;
|
||||
}
|
||||
};
|
||||
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Content, range.begin(), range.end(), processEntry);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rows
|
||||
PDFIntegerRange<int> range(fillRect.top(), fillRect.bottom() + 1);
|
||||
auto processEntry = [&, this](int y)
|
||||
{
|
||||
for (int x = fillRect.left(); x <= fillRect.right(); ++x)
|
||||
{
|
||||
PDFColorBuffer buffer = texture.getPixel(x, y);
|
||||
bool isSampled = sampler->sample(QPointF(x, y) + offset, buffer.resized(shadingColorComponentCount), m_settings.shadingAlgorithmLimit);
|
||||
const PDFColorComponent textureSampleShape = isSampled ? 1.0f : 0.0f;
|
||||
buffer[textureShapeChannel] = textureSampleShape;
|
||||
buffer[textureOpacityChannel] = textureSampleShape;
|
||||
}
|
||||
};
|
||||
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Content, range.begin(), range.end(), processEntry);
|
||||
}
|
||||
|
||||
PDFPainterPathSampler clipSampler(m_painterStateStack.top().clipPath, m_settings.samplesCount, 1.0f, fillRect, m_settings.flags.testFlag(PDFTransparencyRendererSettings::PrecisePathSampler));
|
||||
PDFPainterPathSampler pathSampler(worldPath, m_settings.samplesCount, 0.0f, fillRect, m_settings.flags.testFlag(PDFTransparencyRendererSettings::PrecisePathSampler));
|
||||
|
||||
for (int x = fillRect.left(); x <= fillRect.right(); ++x)
|
||||
{
|
||||
for (int y = fillRect.top(); y <= fillRect.bottom(); ++y)
|
||||
{
|
||||
/* performPixelSampling(shapeStroking, opacityStroking, shapeChannel, opacityChannel, colorChannelStart, colorChannelEnd, x, y, strokeColor, clipSampler, pathSampler);*/
|
||||
}
|
||||
}
|
||||
|
||||
m_drawBuffer.modify(fillRect, fill, stroke);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PDFTransparencyRenderer::performFinishPathPainting()
|
||||
{
|
||||
flushDrawBuffer();
|
||||
}
|
||||
|
||||
void PDFTransparencyRenderer::performClipping(const QPainterPath& path, Qt::FillRule fillRule)
|
||||
{
|
||||
Q_UNUSED(fillRule);
|
||||
|
@ -510,6 +510,10 @@ struct PDFTransparencyRendererSettings
|
||||
/// multithreaded painting is performed.
|
||||
int multithreadingPathSampleThreshold = 128;
|
||||
|
||||
/// Maximal number of steps performed in numerical algorithm
|
||||
/// used when some shadings are being sampled.
|
||||
int shadingAlgorithmLimit = 64;
|
||||
|
||||
enum Flag
|
||||
{
|
||||
None = 0x0000,
|
||||
@ -596,6 +600,8 @@ public:
|
||||
QImage toImage(bool use16Bit, bool usePaper = false, PDFRGB paperColor = PDFRGB()) const;
|
||||
|
||||
virtual void performPathPainting(const QPainterPath& path, bool stroke, bool fill, bool text, Qt::FillRule fillRule) override;
|
||||
virtual bool performPathPaintingUsingShading(const QPainterPath& path, bool stroke, bool fill, const PDFShadingPattern* shadingPattern);
|
||||
virtual void performFinishPathPainting();
|
||||
virtual void performClipping(const QPainterPath& path, Qt::FillRule fillRule) override;
|
||||
virtual void performUpdateGraphicsState(const PDFPageContentProcessorState& state) override;
|
||||
virtual void performSaveGraphicState(ProcessOrder order) override;
|
||||
|
@ -551,6 +551,12 @@ public:
|
||||
|
||||
size_t size() const { return m_end - m_begin; }
|
||||
|
||||
PDFBuffer resized(size_t newSize) const
|
||||
{
|
||||
Q_ASSERT(newSize <= size());
|
||||
return PDFBuffer(m_begin, newSize);
|
||||
}
|
||||
|
||||
private:
|
||||
value_ptr m_begin;
|
||||
value_ptr m_end;
|
||||
|
@ -282,7 +282,7 @@ OutputPreviewDialog::RenderedImage OutputPreviewDialog::renderPage(const pdf::PD
|
||||
&m_inkMapperForRendering, settings, pagePointToDevicePoint);
|
||||
|
||||
renderer.beginPaint(imageSize);
|
||||
renderer.processContents();
|
||||
result.errors = renderer.processContents();
|
||||
renderer.endPaint();
|
||||
|
||||
QImage image = renderer.toImage(false, true, paperColor);
|
||||
|
@ -62,6 +62,7 @@ private:
|
||||
struct RenderedImage
|
||||
{
|
||||
QImage image;
|
||||
QList<pdf::PDFRenderError> errors;
|
||||
};
|
||||
|
||||
void updatePageImage();
|
||||
|
Loading…
x
Reference in New Issue
Block a user