41void QSvgNode::draw(QPainter *p, QSvgExtraStates &states)
44 QElapsedTimer qtSvgTimer; qtSvgTimer.start();
47 if (shouldDrawNode(p, states)) {
48 quint8 remainingDepth = states.trustedSource ? states.remainingNestedNodes
49 : states.remainingNestedNodes - 1;
50 QScopedValueRollback<quint8> nestedNodesGuard(states.remainingNestedNodes, remainingDepth);
51 if (states.remainingNestedNodes == 0) {
52 qCWarning(lcSvgDraw) <<
"Too many nested nodes at" <<
qPrintable(typeName())
53 <<
"exceeding max nested limit of" << QtSvg::renderingMaxNestedNodes <<
"."
54 <<
"Enable AssumeTrustedSource in QSvgHandler or set QT_SVG_DEFAULT_OPTIONS=2 to disable this check.";
58 applyStyle(p, states);
59 applyAnimatedStyle(p, states);
60 QSvgNode *maskNode =
this->hasMask() ? document()->namedNode(
this->maskId()) :
nullptr;
61 QSvgFilterContainer *filterNode =
this->hasFilter() ?
static_cast<QSvgFilterContainer*>(document()->namedNode(
this->filterId()))
63 if (filterNode && filterNode->type() == QSvgNode::Filter && filterNode->supported()) {
64 QTransform xf = p->transform();
66 QRectF localRect = internalBounds(p, states);
68 QRectF boundsRect = xf.mapRect(filterNode->filterRegion(localRect));
69 QImage proxy = drawIntoBuffer(p, states, boundsRect.toRect());
70 proxy = filterNode->applyFilter(proxy, p, localRect);
71 if (maskNode && maskNode->type() == QSvgNode::Mask) {
72 boundsRect = QRectF(proxy.offset(), proxy.size());
73 localRect = p->transform().inverted().mapRect(boundsRect);
74 QImage mask =
static_cast<QSvgMask*>(maskNode)->createMask(p, states, localRect, &boundsRect);
75 applyMaskToBuffer(&proxy, mask);
77 applyBufferToCanvas(p, proxy);
79 }
else if (maskNode && maskNode->type() == QSvgNode::Mask) {
81 QImage mask =
static_cast<QSvgMask*>(maskNode)->createMask(p, states,
this, &boundsRect);
82 drawWithMask(p, states, mask, boundsRect.toRect());
83 }
else if (!qFuzzyCompare(p->opacity(), qreal(1.0)) && requiresGroupRendering()) {
84 QTransform xf = p->transform();
87 QRectF localRect = decoratedInternalBounds(p, states);
89 QRectF boundsRect = xf.mapRect(localRect);
90 const int deltaX = boundsRect.width() * 0.1;
91 const int deltaY = boundsRect.height() * 0.1;
92 boundsRect = boundsRect.adjusted(-deltaX, -deltaY, deltaX, deltaY);
96 QImage proxy = drawIntoBuffer(p, states, boundsRect.toAlignedRect());
97 applyBufferToCanvas(p, proxy);
99 if (separateFillStroke(p, states))
100 fillThenStroke(p, states);
102 drawCommand(p, states);
105 revertAnimatedStyle(p ,states);
106 revertStyle(p, states);
110 if (Q_UNLIKELY(lcSvgTiming().isDebugEnabled()))
111 qCDebug(lcSvgTiming) <<
"Drawing" << typeName() <<
"took" << (qtSvgTimer.nsecsElapsed() / 1000000.0f) <<
"ms";
115void QSvgNode::fillThenStroke(QPainter *p, QSvgExtraStates &states)
117 qreal oldOpacity = p->opacity();
118 if (p->brush().style() != Qt::NoBrush) {
119 QPen oldPen = p->pen();
120 p->setPen(Qt::NoPen);
121 p->setOpacity(oldOpacity * states.fillOpacity);
123 drawCommand(p, states);
127 if (p->pen() != Qt::NoPen && p->pen().brush() != Qt::NoBrush && p->pen().widthF() != 0) {
128 QBrush oldBrush = p->brush();
129 p->setOpacity(oldOpacity * states.strokeOpacity);
130 p->setBrush(Qt::NoBrush);
132 drawCommand(p, states);
134 p->setBrush(oldBrush);
136 p->setOpacity(oldOpacity);
152QImage QSvgNode::drawIntoBuffer(QPainter *p, QSvgExtraStates &states,
const QRect &boundsRect)
155 if (!QImageIOHandler::allocateImage(boundsRect.size(), QImage::Format_ARGB32_Premultiplied, &proxy)) {
156 qCWarning(lcSvgDraw) <<
"The requested buffer size is too big, ignoring";
159 proxy.setOffset(boundsRect.topLeft());
160 proxy.fill(Qt::transparent);
161 QPainter proxyPainter(&proxy);
162 proxyPainter.setPen(p->pen());
163 proxyPainter.setBrush(p->brush());
164 proxyPainter.setFont(p->font());
165 proxyPainter.translate(-boundsRect.topLeft());
166 proxyPainter.setTransform(p->transform(),
true);
167 proxyPainter.setRenderHints(p->renderHints());
168 if (separateFillStroke(p, states))
169 fillThenStroke(&proxyPainter, states);
171 drawCommand(&proxyPainter, states);
202void QSvgNode::appendStyleProperty(QSvgStyleProperty *prop)
204 switch (prop->type()) {
205 case QSvgStyleProperty::QUALITY:
206 m_style.quality =
static_cast<QSvgQualityStyle*>(prop);
208 case QSvgStyleProperty::FILL:
209 m_style.fill =
static_cast<QSvgFillStyle*>(prop);
211 case QSvgStyleProperty::VIEWPORT_FILL:
212 m_style.viewportFill =
static_cast<QSvgViewportFillStyle*>(prop);
214 case QSvgStyleProperty::FONT:
215 m_style.font =
static_cast<QSvgFontStyle*>(prop);
217 case QSvgStyleProperty::STROKE:
218 m_style.stroke =
static_cast<QSvgStrokeStyle*>(prop);
220 case QSvgStyleProperty::TRANSFORM:
221 m_style.transform =
static_cast<QSvgTransformStyle*>(prop);
223 case QSvgStyleProperty::OPACITY:
224 m_style.opacity =
static_cast<QSvgOpacityStyle*>(prop);
226 case QSvgStyleProperty::COMP_OP:
227 m_style.compop =
static_cast<QSvgCompOpStyle*>(prop);
229 case QSvgStyleProperty::OFFSET:
230 m_style.offset =
static_cast<QSvgOffsetStyle*>(prop);
233 qDebug(
"QSvgNode: Trying to append unknown property!");
283QSvgStyleProperty * QSvgNode::styleProperty(QSvgStyleProperty::Type type)
const
285 const QSvgNode *node =
this;
288 case QSvgStyleProperty::QUALITY:
289 if (node->m_style.quality)
290 return node->m_style.quality;
292 case QSvgStyleProperty::FILL:
293 if (node->m_style.fill)
294 return node->m_style.fill;
296 case QSvgStyleProperty::VIEWPORT_FILL:
297 if (m_style.viewportFill)
298 return node->m_style.viewportFill;
300 case QSvgStyleProperty::FONT:
301 if (node->m_style.font)
302 return node->m_style.font;
304 case QSvgStyleProperty::STROKE:
305 if (node->m_style.stroke)
306 return node->m_style.stroke;
308 case QSvgStyleProperty::TRANSFORM:
309 if (node->m_style.transform)
310 return node->m_style.transform;
312 case QSvgStyleProperty::OPACITY:
313 if (node->m_style.opacity)
314 return node->m_style.opacity;
316 case QSvgStyleProperty::COMP_OP:
317 if (node->m_style.compop)
318 return node->m_style.compop;
323 node = node->parent();
339QRectF QSvgNode::bounds()
const
341 if (!m_cachedBounds.isEmpty())
342 return m_cachedBounds;
344 QImage dummy(1, 1, QImage::Format_RGB32);
347 QSvgExtraStates states;
350 parent()->applyStyleRecursive(&p, states);
351 p.setWorldTransform(QTransform());
352 m_cachedBounds = bounds(&p, states);
354 parent()->revertStyleRecursive(&p, states);
355 return m_cachedBounds;
618void QSvgNode::initPainter(QPainter *p)
620 QPen pen(Qt::NoBrush, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin);
621 pen.setMiterLimit(4);
623 p->setBrush(Qt::black);
624 p->setRenderHint(QPainter::Antialiasing);
625 p->setRenderHint(QPainter::SmoothPixmapTransform);
626 QFont font(p->font());
627 if (font.pointSize() < 0 && font.pixelSize() > 0) {
628 font.setPointSizeF(font.pixelSize() * 72.0 / p->device()->logicalDpiY());
633QRectF QSvgNode::boundsOnStroke(QPainter *p,
const QPainterPath &path,
634 qreal width, BoundsMode mode)
636 QPainterPathStroker stroker;
637 stroker.setWidth(width);
638 if (mode == BoundsMode::IncludeMiterLimit) {
639 stroker.setJoinStyle(p->pen().joinStyle());
640 stroker.setMiterLimit(p->pen().miterLimit());
642 QPainterPath stroke = stroker.createStroke(path);
643 return p->transform().map(stroke).boundingRect();