45void QSvgNode::draw(QPainter *p, QSvgExtraStates &states)
48 QElapsedTimer qtSvgTimer; qtSvgTimer.start();
51 if (shouldDrawNode(p, states)) {
52 quint8 remainingDepth = states.trustedSource ? states.remainingNestedNodes
53 : states.remainingNestedNodes - 1;
54 QScopedValueRollback<quint8> nestedNodesGuard(states.remainingNestedNodes, remainingDepth);
55 if (states.remainingNestedNodes == 0) {
56 qCWarning(lcSvgDraw).noquote() <<
"Too many nested nodes at" << typeName()
57 <<
"exceeding max nested limit of" << QtSvg::renderingMaxNestedNodes <<
"."
58 <<
"Enable AssumeTrustedSource in QSvgHandler or set QT_SVG_DEFAULT_OPTIONS=2 to disable this check.";
62 applyStyle(p, states);
63 applyAnimatedStyle(p, states);
64 QSvgNode *maskNode =
this->hasMask() ? states.doc()->namedNode(
this->maskId()) :
nullptr;
65 QSvgFilterContainer *filterNode =
this->hasFilter()
66 ?
static_cast<QSvgFilterContainer *>(states.doc()->namedNode(
this->filterId()))
68 if (filterNode && filterNode->type() == QSvgNode::Filter && filterNode->supported()) {
69 QTransform xf = p->transform();
71 QRectF localRect = internalBounds(p, states);
73 QRectF boundsRect = xf.mapRect(filterNode->filterRegion(localRect));
74 QImage proxy = drawIntoBuffer(p, states, boundsRect.toRect());
75 proxy = filterNode->applyFilter(proxy, p, localRect);
76 if (maskNode && maskNode->type() == QSvgNode::Mask) {
77 boundsRect = QRectF(proxy.offset(), proxy.size());
78 localRect = p->transform().inverted().mapRect(boundsRect);
79 QImage mask =
static_cast<QSvgMask*>(maskNode)->createMask(p, states, localRect, &boundsRect);
80 applyMaskToBuffer(&proxy, mask);
82 applyBufferToCanvas(p, proxy);
84 }
else if (maskNode && maskNode->type() == QSvgNode::Mask) {
86 QImage mask =
static_cast<QSvgMask*>(maskNode)->createMask(p, states,
this, &boundsRect);
87 drawWithMask(p, states, mask, boundsRect.toRect());
88 }
else if (!qFuzzyCompare(p->opacity(), qreal(1.0)) && requiresGroupRendering()) {
89 QTransform xf = p->transform();
92 QRectF localRect = decoratedInternalBounds(p, states);
94 QRectF boundsRect = xf.mapRect(localRect);
95 const int deltaX = boundsRect.width() * 0.1;
96 const int deltaY = boundsRect.height() * 0.1;
97 boundsRect = boundsRect.adjusted(-deltaX, -deltaY, deltaX, deltaY);
101 QImage proxy = drawIntoBuffer(p, states, boundsRect.toAlignedRect());
102 applyBufferToCanvas(p, proxy);
104 if (separateFillStroke(p, states))
105 fillThenStroke(p, states);
107 drawCommand(p, states);
110 revertAnimatedStyle(p ,states);
111 revertStyle(p, states);
115 if (Q_UNLIKELY(lcSvgTiming().isDebugEnabled()))
116 qCDebug(lcSvgTiming) <<
"Drawing" << typeName() <<
"took" << (qtSvgTimer.nsecsElapsed() / 1000000.0f) <<
"ms";
120void QSvgNode::fillThenStroke(QPainter *p, QSvgExtraStates &states)
122 qreal oldOpacity = p->opacity();
123 if (p->brush().style() != Qt::NoBrush) {
124 QPen oldPen = p->pen();
125 p->setPen(Qt::NoPen);
126 p->setOpacity(oldOpacity * states.fillOpacity);
128 drawCommand(p, states);
132 if (p->pen() != Qt::NoPen && p->pen().brush() != Qt::NoBrush && p->pen().widthF() != 0) {
133 QBrush oldBrush = p->brush();
134 p->setOpacity(oldOpacity * states.strokeOpacity);
135 p->setBrush(Qt::NoBrush);
137 drawCommand(p, states);
139 p->setBrush(oldBrush);
141 p->setOpacity(oldOpacity);
157QImage QSvgNode::drawIntoBuffer(QPainter *p, QSvgExtraStates &states,
const QRect &boundsRect)
160 if (!QImageIOHandler::allocateImage(boundsRect.size(), QImage::Format_ARGB32_Premultiplied, &proxy)) {
161 qCWarning(lcSvgDraw) <<
"The requested buffer size is too big, ignoring";
164 proxy.setOffset(boundsRect.topLeft());
165 proxy.fill(Qt::transparent);
166 QPainter proxyPainter(&proxy);
167 proxyPainter.setPen(p->pen());
168 proxyPainter.setBrush(p->brush());
169 proxyPainter.setFont(p->font());
170 proxyPainter.translate(-boundsRect.topLeft());
171 proxyPainter.setTransform(p->transform(),
true);
172 proxyPainter.setRenderHints(p->renderHints());
173 if (separateFillStroke(p, states))
174 fillThenStroke(&proxyPainter, states);
176 drawCommand(&proxyPainter, states);
289QRectF QSvgNode::bounds(
const QSvgDocument *doc)
const
291 if (!m_cachedBounds.isEmpty())
292 return m_cachedBounds;
294 QImage dummy(1, 1, QImage::Format_RGB32);
297 QSvgExtraStates states(doc);
300 parent()->applyStyleRecursive(&p, states);
301 p.setWorldTransform(QTransform());
302 m_cachedBounds = bounds(&p, states);
304 parent()->revertStyleRecursive(&p, states);
305 return m_cachedBounds;
545void QSvgNode::initPainter(QPainter *p)
547 QPen pen(Qt::NoBrush, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin);
548 pen.setMiterLimit(4);
550 p->setBrush(Qt::black);
551 p->setRenderHint(QPainter::Antialiasing);
552 p->setRenderHint(QPainter::SmoothPixmapTransform);
553 QFont font(p->font());
554 if (font.pointSize() < 0 && font.pixelSize() > 0) {
555 font.setPointSizeF(font.pixelSize() * 72.0 / p->device()->logicalDpiY());
560QRectF QSvgNode::boundsOnStroke(QPainter *p,
const QPainterPath &path,
561 qreal width, BoundsMode mode)
563 QPainterPathStroker stroker;
564 stroker.setWidth(width);
565 if (mode == BoundsMode::IncludeMiterLimit) {
566 stroker.setJoinStyle(p->pen().joinStyle());
567 stroker.setMiterLimit(p->pen().miterLimit());
569 QPainterPath stroke = stroker.createStroke(path);
570 return p->transform().map(stroke).boundingRect();