mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-02-11 17:20:37 +01:00
Issue #123: Clipping
This commit is contained in:
parent
f80b7b059e
commit
7ad04b55ee
@ -84,12 +84,27 @@ private:
|
||||
/// Returns composition operator
|
||||
static BLCompOp getBLCompOp(QPainter::CompositionMode mode);
|
||||
|
||||
void drawPathImpl(const QPainterPath& path, bool enableStroke, bool enableFill);
|
||||
|
||||
void setFillRule(Qt::FillRule fillRule);
|
||||
void updateFont(QFont newFont);
|
||||
void setPathFillMode(PolygonDrawMode mode, QPainterPath& path);
|
||||
void updateClipping(std::optional<QRegion> clipRegion,
|
||||
std::optional<QPainterPath> clipPath,
|
||||
Qt::ClipOperation clipOperation);
|
||||
|
||||
bool isStrokeActive() const { return m_currentPen.style() != Qt::NoPen; }
|
||||
bool isFillActive() const { return m_currentBrush.style() != Qt::NoBrush; }
|
||||
|
||||
enum class ClipMode
|
||||
{
|
||||
NoClip,
|
||||
NotVisible,
|
||||
NeedsResolve
|
||||
};
|
||||
|
||||
ClipMode resolveClipping(const QPainterPath& path) const;
|
||||
|
||||
QImage& m_qtOffscreenBuffer;
|
||||
std::optional<BLContext> m_blContext;
|
||||
std::optional<BLImage> m_blOffscreenBuffer;
|
||||
@ -99,6 +114,13 @@ private:
|
||||
QBrush m_currentBrush;
|
||||
QFont m_currentFont;
|
||||
QRawFont m_currentRawFont;
|
||||
QTransform m_currentTransform;
|
||||
|
||||
bool m_currentIsClipEnabled = false;
|
||||
bool m_clipSingleRect = false;
|
||||
std::optional<QRegion> m_clipRegion;
|
||||
std::optional<QPainterPath> m_clipPath;
|
||||
std::optional<QPainterPath> m_finalClipPath;
|
||||
};
|
||||
|
||||
PDFBLPaintDevice::PDFBLPaintDevice(QImage& offscreenBuffer, bool isMultithreaded) :
|
||||
@ -247,9 +269,7 @@ void PDFBLPaintEngine::updateState(const QPaintEngineState& updatedState)
|
||||
DirtyBackground = 0x0010,
|
||||
DirtyBackgroundMode = 0x0020,
|
||||
DirtyClipRegion = 0x0080,
|
||||
DirtyClipPath = 0x0100,
|
||||
DirtyHints = 0x0200,
|
||||
DirtyClipEnabled = 0x0800,*/
|
||||
DirtyClipPath = 0x0100,*/
|
||||
|
||||
if (updatedState.state().testFlag(QPaintEngine::DirtyPen))
|
||||
{
|
||||
@ -275,6 +295,7 @@ void PDFBLPaintEngine::updateState(const QPaintEngineState& updatedState)
|
||||
|
||||
if (updatedState.state().testFlag(QPaintEngine::DirtyTransform))
|
||||
{
|
||||
m_currentTransform = updatedState.transform();
|
||||
m_blContext->setMatrix(getBLMatrix(updatedState.transform()));
|
||||
}
|
||||
|
||||
@ -282,6 +303,34 @@ void PDFBLPaintEngine::updateState(const QPaintEngineState& updatedState)
|
||||
{
|
||||
updateFont(updatedState.font());
|
||||
}
|
||||
|
||||
if (updatedState.state().testFlag(QPaintEngine::DirtyClipEnabled))
|
||||
{
|
||||
m_currentIsClipEnabled = updatedState.isClipEnabled();
|
||||
}
|
||||
|
||||
if (updatedState.state().testFlag(QPaintEngine::DirtyHints))
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
if (updatedState.state().testAnyFlags(QPaintEngine::DirtyClipPath | QPaintEngine::DirtyClipRegion))
|
||||
{
|
||||
std::optional<QRegion> clipRegion;
|
||||
std::optional<QPainterPath> clipPath;
|
||||
|
||||
if (updatedState.state().testFlag(QPaintEngine::DirtyClipRegion))
|
||||
{
|
||||
clipRegion = updatedState.clipRegion();
|
||||
}
|
||||
|
||||
if (updatedState.state().testFlag(QPaintEngine::DirtyClipPath))
|
||||
{
|
||||
clipPath = updatedState.clipPath();
|
||||
}
|
||||
|
||||
updateClipping(std::move(clipRegion), std::move(clipPath), updatedState.clipOperation());
|
||||
}
|
||||
}
|
||||
|
||||
void PDFBLPaintEngine::drawRects(const QRect* rects, int rectCount)
|
||||
@ -356,70 +405,52 @@ void PDFBLPaintEngine::drawLines(const QLineF* lines, int lineCount)
|
||||
|
||||
void PDFBLPaintEngine::drawEllipse(const QRectF& r)
|
||||
{
|
||||
QPointF c = r.center();
|
||||
BLEllipse blEllipse(c.x(), c.y(), r.width() * 0.5, r.height() * 0.5);
|
||||
|
||||
if (isFillActive())
|
||||
{
|
||||
m_blContext->fillEllipse(blEllipse);
|
||||
}
|
||||
|
||||
if (isStrokeActive())
|
||||
{
|
||||
m_blContext->strokeEllipse(blEllipse);
|
||||
}
|
||||
QPainterPath path;
|
||||
path.addEllipse(r);
|
||||
drawPathImpl(path, true, true);
|
||||
}
|
||||
|
||||
void PDFBLPaintEngine::drawEllipse(const QRect& r)
|
||||
{
|
||||
QPointF c = r.center();
|
||||
BLEllipse blEllipse(c.x(), c.y(), r.width() * 0.5, r.height() * 0.5);
|
||||
|
||||
if (isFillActive())
|
||||
{
|
||||
m_blContext->fillEllipse(blEllipse);
|
||||
}
|
||||
|
||||
if (isStrokeActive())
|
||||
{
|
||||
m_blContext->strokeEllipse(blEllipse);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFBLPaintEngine::setFillRule(Qt::FillRule fillRule)
|
||||
{
|
||||
BLFillRule blFillRule{};
|
||||
|
||||
switch (fillRule)
|
||||
{
|
||||
case Qt::OddEvenFill:
|
||||
blFillRule = BL_FILL_RULE_EVEN_ODD;
|
||||
break;
|
||||
|
||||
case Qt::WindingFill:
|
||||
blFillRule = BL_FILL_RULE_NON_ZERO;
|
||||
break;
|
||||
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
|
||||
m_blContext->setFillRule(blFillRule);
|
||||
QPainterPath path;
|
||||
path.addEllipse(r);
|
||||
drawPathImpl(path, true, true);
|
||||
}
|
||||
|
||||
void PDFBLPaintEngine::drawPath(const QPainterPath& path)
|
||||
{
|
||||
drawPathImpl(path, true, true);
|
||||
}
|
||||
|
||||
void PDFBLPaintEngine::drawPathImpl(const QPainterPath& path, bool enableStroke, bool enableFill)
|
||||
{
|
||||
QPainterPath transformedPath = m_currentTransform.map(path);
|
||||
ClipMode clipMode = resolveClipping(transformedPath);
|
||||
|
||||
switch (clipMode)
|
||||
{
|
||||
case ClipMode::NoClip:
|
||||
// Do as normal
|
||||
break;
|
||||
|
||||
case pdf::PDFBLPaintEngine::ClipMode::NotVisible:
|
||||
// Graphics is not visible
|
||||
return;
|
||||
|
||||
case pdf::PDFBLPaintEngine::ClipMode::NeedsResolve:
|
||||
break;
|
||||
}
|
||||
|
||||
BLPath blPath = getBLPath(path);
|
||||
|
||||
setFillRule(path.fillRule());
|
||||
|
||||
if (isFillActive())
|
||||
if (isFillActive() && enableFill)
|
||||
{
|
||||
m_blContext->fillPath(blPath);
|
||||
}
|
||||
|
||||
if (isStrokeActive())
|
||||
if (isStrokeActive() && enableStroke)
|
||||
{
|
||||
m_blContext->strokePath(blPath);
|
||||
}
|
||||
@ -462,34 +493,9 @@ void PDFBLPaintEngine::drawPolygon(const QPointF* points, int pointCount, Polygo
|
||||
polygon.assign(points, points + pointCount);
|
||||
path.addPolygon(polygon);
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case QPaintEngine::OddEvenMode:
|
||||
path.setFillRule(Qt::OddEvenFill);
|
||||
break;
|
||||
case QPaintEngine::WindingMode:
|
||||
path.setFillRule(Qt::WindingFill);
|
||||
break;
|
||||
case QPaintEngine::ConvexMode:
|
||||
path.setFillRule(Qt::OddEvenFill);
|
||||
break;
|
||||
case QPaintEngine::PolylineMode:
|
||||
path.setFillRule(Qt::OddEvenFill);
|
||||
break;
|
||||
}
|
||||
setPathFillMode(mode, path);
|
||||
|
||||
setFillRule(path.fillRule());
|
||||
BLPath blPath = getBLPath(path);
|
||||
|
||||
if (isFillActive() && mode != QPaintEngine::PolylineMode)
|
||||
{
|
||||
m_blContext->fillPath(blPath);
|
||||
}
|
||||
|
||||
if (isStrokeActive())
|
||||
{
|
||||
m_blContext->strokePath(blPath);
|
||||
}
|
||||
drawPathImpl(path, true, mode != QPaintEngine::PolylineMode);
|
||||
}
|
||||
|
||||
void PDFBLPaintEngine::drawPolygon(const QPoint* points, int pointCount, PolygonDrawMode mode)
|
||||
@ -499,34 +505,9 @@ void PDFBLPaintEngine::drawPolygon(const QPoint* points, int pointCount, Polygon
|
||||
polygon.assign(points, points + pointCount);
|
||||
path.addPolygon(polygon);
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case QPaintEngine::OddEvenMode:
|
||||
path.setFillRule(Qt::OddEvenFill);
|
||||
break;
|
||||
case QPaintEngine::WindingMode:
|
||||
path.setFillRule(Qt::WindingFill);
|
||||
break;
|
||||
case QPaintEngine::ConvexMode:
|
||||
path.setFillRule(Qt::OddEvenFill);
|
||||
break;
|
||||
case QPaintEngine::PolylineMode:
|
||||
path.setFillRule(Qt::OddEvenFill);
|
||||
break;
|
||||
}
|
||||
setPathFillMode(mode, path);
|
||||
|
||||
setFillRule(path.fillRule());
|
||||
BLPath blPath = getBLPath(path);
|
||||
|
||||
if (isFillActive() && mode != QPaintEngine::PolylineMode)
|
||||
{
|
||||
m_blContext->fillPath(blPath);
|
||||
}
|
||||
|
||||
if (isStrokeActive())
|
||||
{
|
||||
m_blContext->strokePath(blPath);
|
||||
}
|
||||
drawPathImpl(path, true, mode != QPaintEngine::PolylineMode);
|
||||
}
|
||||
|
||||
void PDFBLPaintEngine::drawPixmap(const QRectF& r, const QPixmap& pm, const QRectF& sr)
|
||||
@ -932,4 +913,157 @@ BLCompOp PDFBLPaintEngine::getBLCompOp(QPainter::CompositionMode mode)
|
||||
return BL_COMP_OP_SRC_OVER;
|
||||
}
|
||||
|
||||
void PDFBLPaintEngine::setPathFillMode(PolygonDrawMode mode, QPainterPath& path)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case QPaintEngine::OddEvenMode:
|
||||
path.setFillRule(Qt::OddEvenFill);
|
||||
break;
|
||||
case QPaintEngine::WindingMode:
|
||||
path.setFillRule(Qt::WindingFill);
|
||||
break;
|
||||
case QPaintEngine::ConvexMode:
|
||||
path.setFillRule(Qt::OddEvenFill);
|
||||
break;
|
||||
case QPaintEngine::PolylineMode:
|
||||
path.setFillRule(Qt::OddEvenFill);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PDFBLPaintEngine::updateClipping(std::optional<QRegion> clipRegion,
|
||||
std::optional<QPainterPath> clipPath,
|
||||
Qt::ClipOperation clipOperation)
|
||||
{
|
||||
switch (clipOperation)
|
||||
{
|
||||
case Qt::NoClip:
|
||||
m_clipRegion.reset();
|
||||
m_clipPath.reset();
|
||||
m_finalClipPath.reset();
|
||||
m_clipSingleRect = false;
|
||||
m_blContext->restoreClipping();
|
||||
return;
|
||||
|
||||
case Qt::ReplaceClip:
|
||||
{
|
||||
m_clipPath = std::move(clipPath);
|
||||
m_clipRegion = std::move(clipRegion);
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::IntersectClip:
|
||||
{
|
||||
if (m_clipPath.has_value())
|
||||
{
|
||||
if (clipPath.has_value())
|
||||
{
|
||||
*m_clipPath = m_clipPath->intersected(*clipPath);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_clipPath = std::move(clipPath);
|
||||
}
|
||||
|
||||
if (m_clipRegion.has_value())
|
||||
{
|
||||
if (clipRegion.has_value())
|
||||
{
|
||||
*m_clipRegion = m_clipRegion->intersected(*clipRegion);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_clipRegion = std::move(clipRegion);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_clipSingleRect = m_clipRegion.has_value() && !m_clipPath.has_value() && m_clipRegion->rectCount() == 1;
|
||||
|
||||
if (m_clipSingleRect)
|
||||
{
|
||||
QRegion transformedRegion = m_currentTransform.map(m_clipRegion.value());
|
||||
m_blContext->clipToRect(getBLRect(transformedRegion.boundingRect()));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_blContext->restoreClipping();
|
||||
}
|
||||
|
||||
m_finalClipPath = QPainterPath();
|
||||
|
||||
QPainterPath clip1;
|
||||
QPainterPath clip2;
|
||||
|
||||
if (m_clipPath.has_value())
|
||||
{
|
||||
clip1 = m_clipPath.value();
|
||||
}
|
||||
|
||||
if (m_clipRegion.has_value())
|
||||
{
|
||||
clip2.addRegion(m_clipRegion.value());
|
||||
}
|
||||
|
||||
if (!clip1.isEmpty() && !clip2.isEmpty())
|
||||
{
|
||||
m_finalClipPath = clip1.intersected(clip2);
|
||||
}
|
||||
else if (!clip1.isEmpty())
|
||||
{
|
||||
m_finalClipPath = std::move(clip1);
|
||||
}
|
||||
else if (!clip2.isEmpty())
|
||||
{
|
||||
m_finalClipPath = std::move(clip2);
|
||||
}
|
||||
|
||||
m_finalClipPath = m_currentTransform.map(m_finalClipPath.value());
|
||||
}
|
||||
|
||||
PDFBLPaintEngine::ClipMode PDFBLPaintEngine::resolveClipping(const QPainterPath& path) const
|
||||
{
|
||||
if (!m_currentIsClipEnabled || m_clipSingleRect || !m_finalClipPath.has_value() || m_finalClipPath->isEmpty())
|
||||
{
|
||||
return ClipMode::NoClip;
|
||||
}
|
||||
|
||||
QRectF clipRect = m_finalClipPath->controlPointRect();
|
||||
QRectF pathRect = path.controlPointRect();
|
||||
|
||||
if (!pathRect.intersects(clipRect))
|
||||
{
|
||||
return ClipMode::NotVisible;
|
||||
}
|
||||
|
||||
return ClipMode::NeedsResolve;
|
||||
}
|
||||
|
||||
void PDFBLPaintEngine::setFillRule(Qt::FillRule fillRule)
|
||||
{
|
||||
BLFillRule blFillRule{};
|
||||
|
||||
switch (fillRule)
|
||||
{
|
||||
case Qt::OddEvenFill:
|
||||
blFillRule = BL_FILL_RULE_EVEN_ODD;
|
||||
break;
|
||||
|
||||
case Qt::WindingFill:
|
||||
blFillRule = BL_FILL_RULE_NON_ZERO;
|
||||
break;
|
||||
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
|
||||
m_blContext->setFillRule(blFillRule);
|
||||
}
|
||||
|
||||
} // namespace pdf
|
||||
|
Loading…
x
Reference in New Issue
Block a user