1 | // Copyright (C) 2016 The Qt Company Ltd. |
---|---|
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qsvgnode_p.h" |
5 | #include "qsvgtinydocument_p.h" |
6 | |
7 | #include <QLoggingCategory> |
8 | #include<QElapsedTimer> |
9 | #include <QtGui/qimageiohandler.h> |
10 | |
11 | #include "qdebug.h" |
12 | #include "qstack.h" |
13 | |
14 | #include <QtGui/private/qoutlinemapper_p.h> |
15 | |
16 | QT_BEGIN_NAMESPACE |
17 | |
18 | Q_DECLARE_LOGGING_CATEGORY(lcSvgDraw); |
19 | |
20 | Q_LOGGING_CATEGORY(lcSvgTiming, "qt.svg.timing") |
21 | |
22 | #if !defined(QT_SVG_SIZE_LIMIT) |
23 | # define QT_SVG_SIZE_LIMIT QT_RASTER_COORD_LIMIT |
24 | #endif |
25 | |
26 | QSvgNode::QSvgNode(QSvgNode *parent) |
27 | : m_parent(parent), |
28 | m_visible(true), |
29 | m_displayMode(BlockMode) |
30 | { |
31 | } |
32 | |
33 | QSvgNode::~QSvgNode() |
34 | { |
35 | |
36 | } |
37 | |
38 | void QSvgNode::draw(QPainter *p, QSvgExtraStates &states) |
39 | { |
40 | #ifndef QT_NO_DEBUG |
41 | QElapsedTimer qtSvgTimer; qtSvgTimer.start(); |
42 | #endif |
43 | |
44 | if (shouldDrawNode(p, states)) { |
45 | applyStyle(p, states); |
46 | QSvgNode *maskNode = this->hasMask() ? document()->namedNode(id: this->maskId()) : nullptr; |
47 | QSvgFilterContainer *filterNode = this->hasFilter() ? static_cast<QSvgFilterContainer*>(document()->namedNode(id: this->filterId())) |
48 | : nullptr; |
49 | if (filterNode && filterNode->type() == QSvgNode::Filter && filterNode->supported()) { |
50 | QTransform xf = p->transform(); |
51 | p->resetTransform(); |
52 | QRectF localRect = internalBounds(p, states); |
53 | p->setTransform(transform: xf); |
54 | QRectF boundsRect = xf.mapRect(filterNode->filterRegion(itemBounds: localRect)); |
55 | QImage proxy = drawIntoBuffer(p, states, boundsRect: boundsRect.toRect()); |
56 | proxy = filterNode->applyFilter(buffer: proxy, p, bounds: localRect); |
57 | if (maskNode && maskNode->type() == QSvgNode::Mask) { |
58 | boundsRect = QRectF(proxy.offset(), proxy.size()); |
59 | localRect = p->transform().inverted().mapRect(boundsRect); |
60 | QImage mask = static_cast<QSvgMask*>(maskNode)->createMask(p, states, localRect, globalRect: &boundsRect); |
61 | applyMaskToBuffer(proxy: &proxy, mask); |
62 | } |
63 | applyBufferToCanvas(p, proxy); |
64 | |
65 | } else if (maskNode && maskNode->type() == QSvgNode::Mask) { |
66 | QRectF boundsRect; |
67 | QImage mask = static_cast<QSvgMask*>(maskNode)->createMask(p, states, targetNode: this, globalRect: &boundsRect); |
68 | drawWithMask(p, states, mask, boundsRect: boundsRect.toRect()); |
69 | } else if (!qFuzzyCompare(p1: p->opacity(), p2: 1.0) && requiresGroupRendering()) { |
70 | QTransform xf = p->transform(); |
71 | p->resetTransform(); |
72 | |
73 | QRectF localRect = decoratedInternalBounds(p, states); |
74 | // adding safety border needed because of the antialiazing effects |
75 | QRectF boundsRect = xf.mapRect(localRect); |
76 | const int deltaX = boundsRect.width() * 0.1; |
77 | const int deltaY = boundsRect.height() * 0.1; |
78 | boundsRect = boundsRect.adjusted(xp1: -deltaX, yp1: -deltaY, xp2: deltaX, yp2: deltaY); |
79 | |
80 | p->setTransform(transform: xf); |
81 | |
82 | QImage proxy = drawIntoBuffer(p, states, boundsRect: boundsRect.toAlignedRect()); |
83 | applyBufferToCanvas(p, proxy); |
84 | } else { |
85 | if (separateFillStroke()) |
86 | fillThenStroke(p, states); |
87 | else |
88 | drawCommand(p, states); |
89 | |
90 | } |
91 | revertStyle(p, states); |
92 | } |
93 | |
94 | #ifndef QT_NO_DEBUG |
95 | if (Q_UNLIKELY(lcSvgTiming().isDebugEnabled())) |
96 | qCDebug(lcSvgTiming) << "Drawing"<< typeName() << "took"<< (qtSvgTimer.nsecsElapsed() / 1000000.0f) << "ms"; |
97 | #endif |
98 | } |
99 | |
100 | void QSvgNode::fillThenStroke(QPainter *p, QSvgExtraStates &states) |
101 | { |
102 | qreal oldOpacity = p->opacity(); |
103 | if (p->brush().style() != Qt::NoBrush) { |
104 | QPen oldPen = p->pen(); |
105 | p->setPen(Qt::NoPen); |
106 | p->setOpacity(oldOpacity * states.fillOpacity); |
107 | |
108 | drawCommand(p, states); |
109 | |
110 | p->setPen(oldPen); |
111 | } |
112 | if (p->pen() != Qt::NoPen && p->pen().brush() != Qt::NoBrush && p->pen().widthF() != 0) { |
113 | QBrush oldBrush = p->brush(); |
114 | p->setOpacity(oldOpacity * states.strokeOpacity); |
115 | p->setBrush(Qt::NoBrush); |
116 | |
117 | drawCommand(p, states); |
118 | |
119 | p->setBrush(oldBrush); |
120 | } |
121 | p->setOpacity(oldOpacity); |
122 | } |
123 | |
124 | void QSvgNode::drawWithMask(QPainter *p, QSvgExtraStates &states, const QImage &mask, const QRect &boundsRect) |
125 | { |
126 | QImage proxy = drawIntoBuffer(p, states, boundsRect); |
127 | if (proxy.isNull()) |
128 | return; |
129 | applyMaskToBuffer(proxy: &proxy, mask); |
130 | |
131 | p->save(); |
132 | p->resetTransform(); |
133 | p->drawImage(r: boundsRect, image: proxy); |
134 | p->restore(); |
135 | } |
136 | |
137 | QImage QSvgNode::drawIntoBuffer(QPainter *p, QSvgExtraStates &states, const QRect &boundsRect) |
138 | { |
139 | QImage proxy; |
140 | if (!QImageIOHandler::allocateImage(size: boundsRect.size(), format: QImage::Format_ARGB32_Premultiplied, image: &proxy)) { |
141 | qCWarning(lcSvgDraw) << "The requested buffer size is too big, ignoring"; |
142 | return proxy; |
143 | } |
144 | proxy.setOffset(boundsRect.topLeft()); |
145 | proxy.fill(color: Qt::transparent); |
146 | QPainter proxyPainter(&proxy); |
147 | proxyPainter.setPen(p->pen()); |
148 | proxyPainter.setBrush(p->brush()); |
149 | proxyPainter.setFont(p->font()); |
150 | proxyPainter.translate(offset: -boundsRect.topLeft()); |
151 | proxyPainter.setTransform(transform: p->transform(), combine: true); |
152 | proxyPainter.setRenderHints(hints: p->renderHints()); |
153 | if (separateFillStroke()) |
154 | fillThenStroke(p: &proxyPainter, states); |
155 | else |
156 | drawCommand(p: &proxyPainter, states); |
157 | return proxy; |
158 | } |
159 | |
160 | void QSvgNode::applyMaskToBuffer(QImage *proxy, QImage mask) const |
161 | { |
162 | QPainter proxyPainter(proxy); |
163 | proxyPainter.setCompositionMode(QPainter::CompositionMode_DestinationOut); |
164 | proxyPainter.resetTransform(); |
165 | proxyPainter.drawImage(r: QRect(0, 0, mask.width(), mask.height()), image: mask); |
166 | } |
167 | |
168 | void QSvgNode::applyBufferToCanvas(QPainter *p, QImage proxy) const |
169 | { |
170 | QTransform xf = p->transform(); |
171 | p->resetTransform(); |
172 | p->drawImage(r: QRect(proxy.offset(), proxy.size()), image: proxy); |
173 | p->setTransform(transform: xf); |
174 | } |
175 | |
176 | bool QSvgNode::isDescendantOf(const QSvgNode *parent) const |
177 | { |
178 | const QSvgNode *n = this; |
179 | while (n) { |
180 | if (n == parent) |
181 | return true; |
182 | n = n->m_parent; |
183 | } |
184 | return false; |
185 | } |
186 | |
187 | void QSvgNode::appendStyleProperty(QSvgStyleProperty *prop, const QString &id) |
188 | { |
189 | //qDebug()<<"appending "<<prop->type()<< " ("<< id <<") "<<"to "<<this<<this->type(); |
190 | QSvgTinyDocument *doc; |
191 | switch (prop->type()) { |
192 | case QSvgStyleProperty::QUALITY: |
193 | m_style.quality = static_cast<QSvgQualityStyle*>(prop); |
194 | break; |
195 | case QSvgStyleProperty::FILL: |
196 | m_style.fill = static_cast<QSvgFillStyle*>(prop); |
197 | break; |
198 | case QSvgStyleProperty::VIEWPORT_FILL: |
199 | m_style.viewportFill = static_cast<QSvgViewportFillStyle*>(prop); |
200 | break; |
201 | case QSvgStyleProperty::FONT: |
202 | m_style.font = static_cast<QSvgFontStyle*>(prop); |
203 | break; |
204 | case QSvgStyleProperty::STROKE: |
205 | m_style.stroke = static_cast<QSvgStrokeStyle*>(prop); |
206 | break; |
207 | case QSvgStyleProperty::SOLID_COLOR: |
208 | m_style.solidColor = static_cast<QSvgSolidColorStyle*>(prop); |
209 | doc = document(); |
210 | if (doc && !id.isEmpty()) |
211 | doc->addNamedStyle(id, style: m_style.solidColor); |
212 | break; |
213 | case QSvgStyleProperty::GRADIENT: |
214 | m_style.gradient = static_cast<QSvgGradientStyle*>(prop); |
215 | doc = document(); |
216 | if (doc && !id.isEmpty()) |
217 | doc->addNamedStyle(id, style: m_style.gradient); |
218 | break; |
219 | case QSvgStyleProperty::PATTERN: |
220 | m_style.pattern = static_cast<QSvgPatternStyle*>(prop); |
221 | doc = document(); |
222 | if (doc && !id.isEmpty()) |
223 | doc->addNamedStyle(id, style: m_style.pattern); |
224 | break; |
225 | case QSvgStyleProperty::TRANSFORM: |
226 | m_style.transform = static_cast<QSvgTransformStyle*>(prop); |
227 | break; |
228 | case QSvgStyleProperty::ANIMATE_COLOR: |
229 | m_style.animateColors.append( |
230 | t: static_cast<QSvgAnimateColor*>(prop)); |
231 | break; |
232 | case QSvgStyleProperty::ANIMATE_TRANSFORM: |
233 | m_style.animateTransforms.append( |
234 | t: static_cast<QSvgAnimateTransform*>(prop)); |
235 | break; |
236 | case QSvgStyleProperty::OPACITY: |
237 | m_style.opacity = static_cast<QSvgOpacityStyle*>(prop); |
238 | break; |
239 | case QSvgStyleProperty::COMP_OP: |
240 | m_style.compop = static_cast<QSvgCompOpStyle*>(prop); |
241 | break; |
242 | default: |
243 | qDebug(msg: "QSvgNode: Trying to append unknown property!"); |
244 | break; |
245 | } |
246 | } |
247 | |
248 | void QSvgNode::applyStyle(QPainter *p, QSvgExtraStates &states) const |
249 | { |
250 | m_style.apply(p, node: this, states); |
251 | } |
252 | |
253 | /*! |
254 | \internal |
255 | |
256 | Apply the styles of all parents to the painter and the states. |
257 | The styles are applied from the top level node to the current node. |
258 | This function can be used to set the correct style for a node |
259 | if it's draw function is triggered out of the ordinary draw context, |
260 | for example the mask node, that is cross-referenced. |
261 | */ |
262 | void QSvgNode::applyStyleRecursive(QPainter *p, QSvgExtraStates &states) const |
263 | { |
264 | if (parent()) |
265 | parent()->applyStyleRecursive(p, states); |
266 | applyStyle(p, states); |
267 | } |
268 | |
269 | void QSvgNode::revertStyle(QPainter *p, QSvgExtraStates &states) const |
270 | { |
271 | m_style.revert(p, states); |
272 | } |
273 | |
274 | void QSvgNode::revertStyleRecursive(QPainter *p, QSvgExtraStates &states) const |
275 | { |
276 | revertStyle(p, states); |
277 | if (parent()) |
278 | parent()->revertStyleRecursive(p, states); |
279 | } |
280 | |
281 | QSvgStyleProperty * QSvgNode::styleProperty(QSvgStyleProperty::Type type) const |
282 | { |
283 | const QSvgNode *node = this; |
284 | while (node) { |
285 | switch (type) { |
286 | case QSvgStyleProperty::QUALITY: |
287 | if (node->m_style.quality) |
288 | return node->m_style.quality; |
289 | break; |
290 | case QSvgStyleProperty::FILL: |
291 | if (node->m_style.fill) |
292 | return node->m_style.fill; |
293 | break; |
294 | case QSvgStyleProperty::VIEWPORT_FILL: |
295 | if (m_style.viewportFill) |
296 | return node->m_style.viewportFill; |
297 | break; |
298 | case QSvgStyleProperty::FONT: |
299 | if (node->m_style.font) |
300 | return node->m_style.font; |
301 | break; |
302 | case QSvgStyleProperty::STROKE: |
303 | if (node->m_style.stroke) |
304 | return node->m_style.stroke; |
305 | break; |
306 | case QSvgStyleProperty::SOLID_COLOR: |
307 | if (node->m_style.solidColor) |
308 | return node->m_style.solidColor; |
309 | break; |
310 | case QSvgStyleProperty::GRADIENT: |
311 | if (node->m_style.gradient) |
312 | return node->m_style.gradient; |
313 | break; |
314 | case QSvgStyleProperty::PATTERN: |
315 | if (node->m_style.pattern) |
316 | return node->m_style.pattern; |
317 | break; |
318 | case QSvgStyleProperty::TRANSFORM: |
319 | if (node->m_style.transform) |
320 | return node->m_style.transform; |
321 | break; |
322 | case QSvgStyleProperty::ANIMATE_COLOR: |
323 | if (!node->m_style.animateColors.isEmpty()) |
324 | return node->m_style.animateColors.first(); |
325 | break; |
326 | case QSvgStyleProperty::ANIMATE_TRANSFORM: |
327 | if (!node->m_style.animateTransforms.isEmpty()) |
328 | return node->m_style.animateTransforms.first(); |
329 | break; |
330 | case QSvgStyleProperty::OPACITY: |
331 | if (node->m_style.opacity) |
332 | return node->m_style.opacity; |
333 | break; |
334 | case QSvgStyleProperty::COMP_OP: |
335 | if (node->m_style.compop) |
336 | return node->m_style.compop; |
337 | break; |
338 | default: |
339 | break; |
340 | } |
341 | node = node->parent(); |
342 | } |
343 | |
344 | return 0; |
345 | } |
346 | |
347 | QSvgPaintStyleProperty * QSvgNode::styleProperty(const QString &id) const |
348 | { |
349 | QString rid = id; |
350 | if (rid.startsWith(c: QLatin1Char('#'))) |
351 | rid.remove(i: 0, len: 1); |
352 | QSvgTinyDocument *doc = document(); |
353 | return doc ? doc->namedStyle(id: rid) : 0; |
354 | } |
355 | |
356 | QRectF QSvgNode::internalFastBounds(QPainter *p, QSvgExtraStates &states) const |
357 | { |
358 | return internalBounds(p, states); |
359 | } |
360 | |
361 | QRectF QSvgNode::internalBounds(QPainter *, QSvgExtraStates &) const |
362 | { |
363 | return QRectF(0, 0, 0, 0); |
364 | } |
365 | |
366 | QRectF QSvgNode::bounds() const |
367 | { |
368 | if (!m_cachedBounds.isEmpty()) |
369 | return m_cachedBounds; |
370 | |
371 | QImage dummy(1, 1, QImage::Format_RGB32); |
372 | QPainter p(&dummy); |
373 | initPainter(p: &p); |
374 | QSvgExtraStates states; |
375 | |
376 | if (parent()) |
377 | parent()->applyStyleRecursive(p: &p, states); |
378 | p.setWorldTransform(matrix: QTransform()); |
379 | m_cachedBounds = bounds(p: &p, states); |
380 | if (parent()) // always revert the style to not store old transformations |
381 | parent()->revertStyleRecursive(p: &p, states); |
382 | return m_cachedBounds; |
383 | } |
384 | |
385 | QSvgTinyDocument * QSvgNode::document() const |
386 | { |
387 | QSvgTinyDocument *doc = nullptr; |
388 | QSvgNode *node = const_cast<QSvgNode*>(this); |
389 | while (node && node->type() != QSvgNode::Doc) { |
390 | node = node->parent(); |
391 | } |
392 | doc = static_cast<QSvgTinyDocument*>(node); |
393 | |
394 | return doc; |
395 | } |
396 | |
397 | QString QSvgNode::typeName() const |
398 | { |
399 | switch (type()) { |
400 | case Doc: return QStringLiteral("svg"); |
401 | case Group: return QStringLiteral("g"); |
402 | case Defs: return QStringLiteral("defs"); |
403 | case Switch: return QStringLiteral("switch"); |
404 | case Animation: return QStringLiteral("animation"); |
405 | case Circle: return QStringLiteral("circle"); |
406 | case Ellipse: return QStringLiteral("ellipse"); |
407 | case Image: return QStringLiteral("image"); |
408 | case Line: return QStringLiteral("line"); |
409 | case Path: return QStringLiteral("path"); |
410 | case Polygon: return QStringLiteral("polygon"); |
411 | case Polyline: return QStringLiteral("polyline"); |
412 | case Rect: return QStringLiteral("rect"); |
413 | case Text: return QStringLiteral("text"); |
414 | case Textarea: return QStringLiteral("textarea"); |
415 | case Tspan: return QStringLiteral("tspan"); |
416 | case Use: return QStringLiteral("use"); |
417 | case Video: return QStringLiteral("video"); |
418 | case Mask: return QStringLiteral("mask"); |
419 | case Symbol: return QStringLiteral("symbol"); |
420 | case Marker: return QStringLiteral("marker"); |
421 | case Pattern: return QStringLiteral("pattern"); |
422 | case Filter: return QStringLiteral("filter"); |
423 | case FeMerge: return QStringLiteral("feMerge"); |
424 | case FeMergenode: return QStringLiteral("feMergeNode"); |
425 | case FeColormatrix: return QStringLiteral("feColorMatrix"); |
426 | case FeGaussianblur: return QStringLiteral("feGaussianBlur"); |
427 | case FeOffset: return QStringLiteral("feOffset"); |
428 | case FeComposite: return QStringLiteral("feComposite"); |
429 | case FeFlood: return QStringLiteral("feFlood"); |
430 | case FeUnsupported: return QStringLiteral("feUnsupported"); |
431 | } |
432 | return QStringLiteral("unknown"); |
433 | } |
434 | |
435 | void QSvgNode::setRequiredFeatures(const QStringList &lst) |
436 | { |
437 | m_requiredFeatures = lst; |
438 | } |
439 | |
440 | const QStringList & QSvgNode::requiredFeatures() const |
441 | { |
442 | return m_requiredFeatures; |
443 | } |
444 | |
445 | void QSvgNode::setRequiredExtensions(const QStringList &lst) |
446 | { |
447 | m_requiredExtensions = lst; |
448 | } |
449 | |
450 | const QStringList & QSvgNode::requiredExtensions() const |
451 | { |
452 | return m_requiredExtensions; |
453 | } |
454 | |
455 | void QSvgNode::setRequiredLanguages(const QStringList &lst) |
456 | { |
457 | m_requiredLanguages = lst; |
458 | } |
459 | |
460 | const QStringList & QSvgNode::requiredLanguages() const |
461 | { |
462 | return m_requiredLanguages; |
463 | } |
464 | |
465 | void QSvgNode::setRequiredFormats(const QStringList &lst) |
466 | { |
467 | m_requiredFormats = lst; |
468 | } |
469 | |
470 | const QStringList & QSvgNode::requiredFormats() const |
471 | { |
472 | return m_requiredFormats; |
473 | } |
474 | |
475 | void QSvgNode::setRequiredFonts(const QStringList &lst) |
476 | { |
477 | m_requiredFonts = lst; |
478 | } |
479 | |
480 | const QStringList & QSvgNode::requiredFonts() const |
481 | { |
482 | return m_requiredFonts; |
483 | } |
484 | |
485 | void QSvgNode::setVisible(bool visible) |
486 | { |
487 | //propagate visibility change of true to the parent |
488 | //not propagating false is just a small performance |
489 | //degradation since we'll iterate over children without |
490 | //drawing any of them |
491 | if (m_parent && visible && !m_parent->isVisible()) |
492 | m_parent->setVisible(true); |
493 | |
494 | m_visible = visible; |
495 | } |
496 | |
497 | QRectF QSvgNode::bounds(QPainter *p, QSvgExtraStates &states) const |
498 | { |
499 | applyStyle(p, states); |
500 | QRectF rect = internalBounds(p, states); |
501 | revertStyle(p, states); |
502 | return rect; |
503 | } |
504 | |
505 | QRectF QSvgNode::decoratedInternalBounds(QPainter *p, QSvgExtraStates &states) const |
506 | { |
507 | return filterRegion(bounds: internalBounds(p, states)); |
508 | } |
509 | |
510 | QRectF QSvgNode::decoratedBounds(QPainter *p, QSvgExtraStates &states) const |
511 | { |
512 | applyStyle(p, states); |
513 | QRectF rect = decoratedInternalBounds(p, states); |
514 | revertStyle(p, states); |
515 | return rect; |
516 | } |
517 | |
518 | void QSvgNode::setNodeId(const QString &i) |
519 | { |
520 | m_id = i; |
521 | } |
522 | |
523 | void QSvgNode::setXmlClass(const QString &str) |
524 | { |
525 | m_class = str; |
526 | } |
527 | |
528 | QString QSvgNode::maskId() const |
529 | { |
530 | return m_maskId; |
531 | } |
532 | |
533 | void QSvgNode::setMaskId(const QString &str) |
534 | { |
535 | m_maskId = str; |
536 | } |
537 | |
538 | bool QSvgNode::hasMask() const |
539 | { |
540 | if (document()->options().testFlag(flag: QtSvg::Tiny12FeaturesOnly)) |
541 | return false; |
542 | return !m_maskId.isEmpty(); |
543 | } |
544 | |
545 | QString QSvgNode::filterId() const |
546 | { |
547 | return m_filterId; |
548 | } |
549 | |
550 | void QSvgNode::setFilterId(const QString &str) |
551 | { |
552 | m_filterId = str; |
553 | } |
554 | |
555 | bool QSvgNode::hasFilter() const |
556 | { |
557 | if (document()->options().testFlag(flag: QtSvg::Tiny12FeaturesOnly)) |
558 | return false; |
559 | return !m_filterId.isEmpty(); |
560 | } |
561 | |
562 | QString QSvgNode::markerStartId() const |
563 | { |
564 | return m_markerStartId; |
565 | } |
566 | |
567 | void QSvgNode::setMarkerStartId(const QString &str) |
568 | { |
569 | m_markerStartId = str; |
570 | } |
571 | |
572 | bool QSvgNode::hasMarkerStart() const |
573 | { |
574 | if (document()->options().testFlag(flag: QtSvg::Tiny12FeaturesOnly)) |
575 | return false; |
576 | return !m_markerStartId.isEmpty(); |
577 | } |
578 | |
579 | QString QSvgNode::markerMidId() const |
580 | { |
581 | return m_markerMidId; |
582 | } |
583 | |
584 | void QSvgNode::setMarkerMidId(const QString &str) |
585 | { |
586 | m_markerMidId = str; |
587 | } |
588 | |
589 | bool QSvgNode::hasMarkerMid() const |
590 | { |
591 | if (document()->options().testFlag(flag: QtSvg::Tiny12FeaturesOnly)) |
592 | return false; |
593 | return !m_markerMidId.isEmpty(); |
594 | } |
595 | |
596 | QString QSvgNode::markerEndId() const |
597 | { |
598 | return m_markerEndId; |
599 | } |
600 | |
601 | void QSvgNode::setMarkerEndId(const QString &str) |
602 | { |
603 | m_markerEndId = str; |
604 | } |
605 | |
606 | bool QSvgNode::hasMarkerEnd() const |
607 | { |
608 | if (document()->options().testFlag(flag: QtSvg::Tiny12FeaturesOnly)) |
609 | return false; |
610 | return !m_markerEndId.isEmpty(); |
611 | } |
612 | |
613 | bool QSvgNode::hasAnyMarker() const |
614 | { |
615 | if (document()->options().testFlag(flag: QtSvg::Tiny12FeaturesOnly)) |
616 | return false; |
617 | return hasMarkerStart() || hasMarkerMid() || hasMarkerEnd(); |
618 | } |
619 | |
620 | bool QSvgNode::requiresGroupRendering() const |
621 | { |
622 | return false; |
623 | } |
624 | |
625 | void QSvgNode::setDisplayMode(DisplayMode mode) |
626 | { |
627 | m_displayMode = mode; |
628 | } |
629 | |
630 | QSvgNode::DisplayMode QSvgNode::displayMode() const |
631 | { |
632 | return m_displayMode; |
633 | } |
634 | |
635 | qreal QSvgNode::strokeWidth(QPainter *p) |
636 | { |
637 | const QPen &pen = p->pen(); |
638 | if (pen.style() == Qt::NoPen || pen.brush().style() == Qt::NoBrush || pen.isCosmetic()) |
639 | return 0; |
640 | return pen.widthF(); |
641 | } |
642 | |
643 | void QSvgNode::initPainter(QPainter *p) |
644 | { |
645 | QPen pen(Qt::NoBrush, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin); |
646 | pen.setMiterLimit(4); |
647 | p->setPen(pen); |
648 | p->setBrush(Qt::black); |
649 | p->setRenderHint(hint: QPainter::Antialiasing); |
650 | p->setRenderHint(hint: QPainter::SmoothPixmapTransform); |
651 | QFont font(p->font()); |
652 | if (font.pointSize() < 0 && font.pixelSize() > 0) { |
653 | font.setPointSizeF(font.pixelSize() * 72.0 / p->device()->logicalDpiY()); |
654 | p->setFont(font); |
655 | } |
656 | } |
657 | |
658 | QRectF QSvgNode::boundsOnStroke(QPainter *p, const QPainterPath &path, |
659 | qreal width, BoundsMode mode) |
660 | { |
661 | QPainterPathStroker stroker; |
662 | stroker.setWidth(width); |
663 | if (mode == BoundsMode::IncludeMiterLimit) { |
664 | stroker.setJoinStyle(p->pen().joinStyle()); |
665 | stroker.setMiterLimit(p->pen().miterLimit()); |
666 | } |
667 | QPainterPath stroke = stroker.createStroke(path); |
668 | return p->transform().map(p: stroke).boundingRect(); |
669 | } |
670 | |
671 | bool QSvgNode::shouldDrawNode(QPainter *p, QSvgExtraStates &states) const |
672 | { |
673 | if (m_displayMode == DisplayMode::NoneMode) |
674 | return false; |
675 | |
676 | if (document() && document()->options().testFlag(flag: QtSvg::AssumeTrustedSource)) |
677 | return true; |
678 | |
679 | QRectF brect = internalFastBounds(p, states); |
680 | if (brect.width() <= QT_SVG_SIZE_LIMIT && brect.height() <= QT_SVG_SIZE_LIMIT) { |
681 | return true; |
682 | } else { |
683 | qCWarning(lcSvgDraw) << "Shape of type"<< type() << "ignored because it will take too long to rasterize (bounding rect="<< brect << ")." |
684 | << "Enable AssumeTrustedSource in QSvgHandler or set QT_SVG_DEFAULT_OPTIONS=2 to disable this check."; |
685 | return false; |
686 | } |
687 | } |
688 | |
689 | QRectF QSvgNode::filterRegion(QRectF bounds) const |
690 | { |
691 | QSvgFilterContainer *filterNode = hasFilter() |
692 | ? static_cast<QSvgFilterContainer*>(document()->namedNode(id: filterId())) |
693 | : nullptr; |
694 | |
695 | if (filterNode && filterNode->type() == QSvgNode::Filter && filterNode->supported()) |
696 | return filterNode->filterRegion(itemBounds: bounds); |
697 | |
698 | return bounds; |
699 | } |
700 | |
701 | QT_END_NAMESPACE |
702 |
Definitions
- lcSvgTiming
- QSvgNode
- ~QSvgNode
- draw
- fillThenStroke
- drawWithMask
- drawIntoBuffer
- applyMaskToBuffer
- applyBufferToCanvas
- isDescendantOf
- appendStyleProperty
- applyStyle
- applyStyleRecursive
- revertStyle
- revertStyleRecursive
- styleProperty
- styleProperty
- internalFastBounds
- internalBounds
- bounds
- document
- typeName
- setRequiredFeatures
- requiredFeatures
- setRequiredExtensions
- requiredExtensions
- setRequiredLanguages
- requiredLanguages
- setRequiredFormats
- requiredFormats
- setRequiredFonts
- requiredFonts
- setVisible
- bounds
- decoratedInternalBounds
- decoratedBounds
- setNodeId
- setXmlClass
- maskId
- setMaskId
- hasMask
- filterId
- setFilterId
- hasFilter
- markerStartId
- setMarkerStartId
- hasMarkerStart
- markerMidId
- setMarkerMidId
- hasMarkerMid
- markerEndId
- setMarkerEndId
- hasMarkerEnd
- hasAnyMarker
- requiresGroupRendering
- setDisplayMode
- displayMode
- strokeWidth
- initPainter
- boundsOnStroke
- shouldDrawNode
Start learning QML with our Intro Training
Find out more