40void QSvgNode::draw(QPainter *p, QSvgExtraStates &states)
43 QElapsedTimer qtSvgTimer; qtSvgTimer.start();
46 if (shouldDrawNode(p, states)) {
47 applyStyle(p, states);
48 applyAnimatedStyle(p, states);
49 QSvgNode *maskNode =
this->hasMask() ? document()->namedNode(
this->maskId()) :
nullptr;
50 QSvgFilterContainer *filterNode =
this->hasFilter() ?
static_cast<QSvgFilterContainer*>(document()->namedNode(
this->filterId()))
52 if (filterNode && filterNode->type() == QSvgNode::Filter && filterNode->supported()) {
53 QTransform xf = p->transform();
55 QRectF localRect = internalBounds(p, states);
57 QRectF boundsRect = xf.mapRect(filterNode->filterRegion(localRect));
58 QImage proxy = drawIntoBuffer(p, states, boundsRect.toRect());
59 proxy = filterNode->applyFilter(proxy, p, localRect);
60 if (maskNode && maskNode->type() == QSvgNode::Mask) {
61 boundsRect = QRectF(proxy.offset(), proxy.size());
62 localRect = p->transform().inverted().mapRect(boundsRect);
63 QImage mask =
static_cast<QSvgMask*>(maskNode)->createMask(p, states, localRect, &boundsRect);
64 applyMaskToBuffer(&proxy, mask);
66 applyBufferToCanvas(p, proxy);
68 }
else if (maskNode && maskNode->type() == QSvgNode::Mask) {
70 QImage mask =
static_cast<QSvgMask*>(maskNode)->createMask(p, states,
this, &boundsRect);
71 drawWithMask(p, states, mask, boundsRect.toRect());
72 }
else if (!qFuzzyCompare(p->opacity(), qreal(1.0)) && requiresGroupRendering()) {
73 QTransform xf = p->transform();
76 QRectF localRect = decoratedInternalBounds(p, states);
78 QRectF boundsRect = xf.mapRect(localRect);
79 const int deltaX = boundsRect.width() * 0.1;
80 const int deltaY = boundsRect.height() * 0.1;
81 boundsRect = boundsRect.adjusted(-deltaX, -deltaY, deltaX, deltaY);
85 QImage proxy = drawIntoBuffer(p, states, boundsRect.toAlignedRect());
86 applyBufferToCanvas(p, proxy);
88 if (separateFillStroke(states))
89 fillThenStroke(p, states);
91 drawCommand(p, states);
94 revertAnimatedStyle(p ,states);
95 revertStyle(p, states);
99 if (Q_UNLIKELY(lcSvgTiming().isDebugEnabled()))
100 qCDebug(lcSvgTiming) <<
"Drawing" << typeName() <<
"took" << (qtSvgTimer.nsecsElapsed() / 1000000.0f) <<
"ms";
104void QSvgNode::fillThenStroke(QPainter *p, QSvgExtraStates &states)
106 qreal oldOpacity = p->opacity();
107 if (p->brush().style() != Qt::NoBrush) {
108 QPen oldPen = p->pen();
109 p->setPen(Qt::NoPen);
110 p->setOpacity(oldOpacity * states.fillOpacity);
112 drawCommand(p, states);
116 if (p->pen() != Qt::NoPen && p->pen().brush() != Qt::NoBrush && p->pen().widthF() != 0) {
117 QBrush oldBrush = p->brush();
118 p->setOpacity(oldOpacity * states.strokeOpacity);
119 p->setBrush(Qt::NoBrush);
121 drawCommand(p, states);
123 p->setBrush(oldBrush);
125 p->setOpacity(oldOpacity);
128void QSvgNode::drawWithMask(QPainter *p, QSvgExtraStates &states,
const QImage &mask,
const QRect &boundsRect)
130 QImage proxy = drawIntoBuffer(p, states, boundsRect);
133 applyMaskToBuffer(&proxy, mask);
137 p->drawImage(boundsRect, proxy);
141QImage QSvgNode::drawIntoBuffer(QPainter *p, QSvgExtraStates &states,
const QRect &boundsRect)
144 if (!QImageIOHandler::allocateImage(boundsRect.size(), QImage::Format_ARGB32_Premultiplied, &proxy)) {
145 qCWarning(lcSvgDraw) <<
"The requested buffer size is too big, ignoring";
148 proxy.setOffset(boundsRect.topLeft());
149 proxy.fill(Qt::transparent);
150 QPainter proxyPainter(&proxy);
151 proxyPainter.setPen(p->pen());
152 proxyPainter.setBrush(p->brush());
153 proxyPainter.setFont(p->font());
154 proxyPainter.translate(-boundsRect.topLeft());
155 proxyPainter.setTransform(p->transform(),
true);
156 proxyPainter.setRenderHints(p->renderHints());
157 if (separateFillStroke(states))
158 fillThenStroke(&proxyPainter, states);
160 drawCommand(&proxyPainter, states);
164void QSvgNode::applyMaskToBuffer(QImage *proxy, QImage mask)
const
166 QPainter proxyPainter(proxy);
167 proxyPainter.setCompositionMode(QPainter::CompositionMode_DestinationOut);
168 proxyPainter.resetTransform();
169 proxyPainter.drawImage(QRect(0, 0, mask.width(), mask.height()), mask);
191void QSvgNode::appendStyleProperty(QSvgStyleProperty *prop,
const QString &id)
195 switch (prop->type()) {
196 case QSvgStyleProperty::QUALITY:
197 m_style.quality =
static_cast<QSvgQualityStyle*>(prop);
199 case QSvgStyleProperty::FILL:
200 m_style.fill =
static_cast<QSvgFillStyle*>(prop);
202 case QSvgStyleProperty::VIEWPORT_FILL:
203 m_style.viewportFill =
static_cast<QSvgViewportFillStyle*>(prop);
205 case QSvgStyleProperty::FONT:
206 m_style.font =
static_cast<QSvgFontStyle*>(prop);
208 case QSvgStyleProperty::STROKE:
209 m_style.stroke =
static_cast<QSvgStrokeStyle*>(prop);
211 case QSvgStyleProperty::SOLID_COLOR:
212 m_style.solidColor =
static_cast<QSvgSolidColorStyle*>(prop);
214 if (doc && !id.isEmpty())
215 doc->addNamedStyle(id, m_style.solidColor);
217 case QSvgStyleProperty::GRADIENT:
218 m_style.gradient =
static_cast<QSvgGradientStyle*>(prop);
220 if (doc && !id.isEmpty())
221 doc->addNamedStyle(id, m_style.gradient);
223 case QSvgStyleProperty::PATTERN:
224 m_style.pattern =
static_cast<QSvgPatternStyle*>(prop);
226 if (doc && !id.isEmpty())
227 doc->addNamedStyle(id, m_style.pattern);
229 case QSvgStyleProperty::TRANSFORM:
230 m_style.transform =
static_cast<QSvgTransformStyle*>(prop);
232 case QSvgStyleProperty::OPACITY:
233 m_style.opacity =
static_cast<QSvgOpacityStyle*>(prop);
235 case QSvgStyleProperty::COMP_OP:
236 m_style.compop =
static_cast<QSvgCompOpStyle*>(prop);
238 case QSvgStyleProperty::OFFSET:
239 m_style.offset =
static_cast<QSvgOffsetStyle*>(prop);
242 qDebug(
"QSvgNode: Trying to append unknown property!");
292QSvgStyleProperty * QSvgNode::styleProperty(QSvgStyleProperty::Type type)
const
294 const QSvgNode *node =
this;
297 case QSvgStyleProperty::QUALITY:
298 if (node->m_style.quality)
299 return node->m_style.quality;
301 case QSvgStyleProperty::FILL:
302 if (node->m_style.fill)
303 return node->m_style.fill;
305 case QSvgStyleProperty::VIEWPORT_FILL:
306 if (m_style.viewportFill)
307 return node->m_style.viewportFill;
309 case QSvgStyleProperty::FONT:
310 if (node->m_style.font)
311 return node->m_style.font;
313 case QSvgStyleProperty::STROKE:
314 if (node->m_style.stroke)
315 return node->m_style.stroke;
317 case QSvgStyleProperty::SOLID_COLOR:
318 if (node->m_style.solidColor)
319 return node->m_style.solidColor;
321 case QSvgStyleProperty::GRADIENT:
322 if (node->m_style.gradient)
323 return node->m_style.gradient;
325 case QSvgStyleProperty::PATTERN:
326 if (node->m_style.pattern)
327 return node->m_style.pattern;
329 case QSvgStyleProperty::TRANSFORM:
330 if (node->m_style.transform)
331 return node->m_style.transform;
333 case QSvgStyleProperty::OPACITY:
334 if (node->m_style.opacity)
335 return node->m_style.opacity;
337 case QSvgStyleProperty::COMP_OP:
338 if (node->m_style.compop)
339 return node->m_style.compop;
344 node = node->parent();
368QRectF QSvgNode::bounds()
const
370 if (!m_cachedBounds.isEmpty())
371 return m_cachedBounds;
373 QImage dummy(1, 1, QImage::Format_RGB32);
376 QSvgExtraStates states;
379 parent()->applyStyleRecursive(&p, states);
380 p.setWorldTransform(QTransform());
381 m_cachedBounds = bounds(&p, states);
383 parent()->revertStyleRecursive(&p, states);
384 return m_cachedBounds;
647void QSvgNode::initPainter(QPainter *p)
649 QPen pen(Qt::NoBrush, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin);
650 pen.setMiterLimit(4);
652 p->setBrush(Qt::black);
653 p->setRenderHint(QPainter::Antialiasing);
654 p->setRenderHint(QPainter::SmoothPixmapTransform);
655 QFont font(p->font());
656 if (font.pointSize() < 0 && font.pixelSize() > 0) {
657 font.setPointSizeF(font.pixelSize() * 72.0 / p->device()->logicalDpiY());
662QRectF QSvgNode::boundsOnStroke(QPainter *p,
const QPainterPath &path,
663 qreal width, BoundsMode mode)
665 QPainterPathStroker stroker;
666 stroker.setWidth(width);
667 if (mode == BoundsMode::IncludeMiterLimit) {
668 stroker.setJoinStyle(p->pen().joinStyle());
669 stroker.setMiterLimit(p->pen().miterLimit());
671 QPainterPath stroke = stroker.createStroke(path);
672 return p->transform().map(stroke).boundingRect();