Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qsvggraphics.cpp
Go to the documentation of this file.
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// Qt-Security score:significant reason:default
4
5
8#include "qsvgfont_p.h"
9
10#include <qabstracttextdocumentlayout.h>
11#include <qdebug.h>
12#include <qloggingcategory.h>
13#include <qpainter.h>
14#include <qscopedvaluerollback.h>
15#include <qtextcursor.h>
16#include <qtextdocument.h>
17#include <private/qfixed_p.h>
18
19#include <QElapsedTimer>
20#include <QLoggingCategory>
21
22#include <math.h>
23
24QT_BEGIN_NAMESPACE
25
26Q_LOGGING_CATEGORY(lcSvgDraw, "qt.svg.draw")
27
28#ifndef QT_SVG_MAX_LAYOUT_SIZE
29#define QT_SVG_MAX_LAYOUT_SIZE (qint64(QFIXED_MAX / 2))
30#endif
31
32void QSvgDummyNode::drawCommand(QPainter *, QSvgExtraStates &)
33{
34 qWarning("Dummy node not meant to be drawn");
35}
36
37QSvgEllipse::QSvgEllipse(QSvgNode *parent, const QRectF &rect)
38 : QSvgNode(parent), m_bounds(rect)
39{
40}
41
42QRectF QSvgEllipse::internalFastBounds(QPainter *p, QSvgExtraStates &) const
43{
44 return p->transform().mapRect(m_bounds);
45}
46
47QRectF QSvgEllipse::internalBounds(QPainter *p, QSvgExtraStates &) const
48{
49 QPainterPath path;
50 path.addEllipse(m_bounds);
51 qreal sw = strokeWidth(p);
52 return qFuzzyIsNull(sw) ? p->transform().map(path).boundingRect()
53 : boundsOnStroke(p, path, sw, BoundsMode::Simplistic);
54}
55
56QRectF QSvgEllipse::decoratedInternalBounds(QPainter *p, QSvgExtraStates &) const
57{
58 QPainterPath path;
59 path.addEllipse(m_bounds);
60 qreal sw = strokeWidth(p);
61 QRectF rect = qFuzzyIsNull(sw) ? p->transform().map(path).boundingRect()
62 : boundsOnStroke(p, path, sw, BoundsMode::IncludeMiterLimit);
63 return filterRegion(rect);
64}
65
66void QSvgEllipse::drawCommand(QPainter *p, QSvgExtraStates &)
67{
68 p->drawEllipse(m_bounds);
69}
70
71bool QSvgEllipse::separateFillStroke(const QSvgExtraStates &) const
72{
73 return true;
74}
75
76QSvgImage::QSvgImage(QSvgNode *parent,
77 const QImage &image,
78 const QString &filename,
79 const QRectF &bounds)
80 : QSvgNode(parent)
81 , m_filename(filename)
82 , m_image(image)
83 , m_bounds(bounds)
84{
85 if (m_bounds.width() == 0.0)
86 m_bounds.setWidth(static_cast<qreal>(m_image.width()));
87 if (m_bounds.height() == 0.0)
88 m_bounds.setHeight(static_cast<qreal>(m_image.height()));
89}
90
91void QSvgImage::drawCommand(QPainter *p, QSvgExtraStates &)
92{
93 p->drawImage(m_bounds, m_image);
94}
95
96QSvgLine::QSvgLine(QSvgNode *parent, const QLineF &line)
97 : QSvgNode(parent), m_line(line)
98{
99}
100
101void QSvgLine::drawCommand(QPainter *p, QSvgExtraStates &states)
102{
103 if (p->pen().widthF() != 0) {
104 qreal oldOpacity = p->opacity();
105 p->setOpacity(oldOpacity * states.strokeOpacity);
106 if (m_line.isNull() && p->pen().capStyle() != Qt::FlatCap)
107 p->drawPoint(m_line.p1());
108 else
109 p->drawLine(m_line);
110 p->setOpacity(oldOpacity);
111 }
112 QSvgMarker::drawMarkersForNode(this, p, states);
113}
114
115QSvgPath::QSvgPath(QSvgNode *parent, const QPainterPath &qpath)
116 : QSvgNode(parent), m_path(qpath)
117{
118}
119
120void QSvgPath::drawCommand(QPainter *p, QSvgExtraStates &states)
121{
122 const qreal oldOpacity = p->opacity();
123 const bool drawingInOnePass = !separateFillStroke(states);
124 if (drawingInOnePass)
125 p->setOpacity(oldOpacity * states.fillOpacity);
126 m_path.setFillRule(states.fillRule);
127 if (m_path.boundingRect().isNull() && p->pen().capStyle() != Qt::FlatCap)
128 p->drawPoint(m_path.boundingRect().topLeft());
129 else
130 p->drawPath(m_path);
131 if (!path().isEmpty())
132 QSvgMarker::drawMarkersForNode(this, p, states);
133 if (drawingInOnePass)
134 p->setOpacity(oldOpacity);
135}
136
137bool QSvgPath::separateFillStroke(const QSvgExtraStates &s) const
138{
139 return !qFuzzyCompare(s.fillOpacity, s.strokeOpacity);
140}
141
142QRectF QSvgPath::internalFastBounds(QPainter *p, QSvgExtraStates &) const
143{
144 return p->transform().mapRect(m_path.controlPointRect());
145}
146
147QRectF QSvgPath::internalBounds(QPainter *p, QSvgExtraStates &) const
148{
149 qreal sw = strokeWidth(p);
150 return qFuzzyIsNull(sw) ? p->transform().map(m_path).boundingRect()
151 : boundsOnStroke(p, m_path, sw, BoundsMode::Simplistic);
152}
153
154QRectF QSvgPath::decoratedInternalBounds(QPainter *p, QSvgExtraStates &s) const
155{
156 qreal sw = strokeWidth(p);
157 QRectF rect = qFuzzyIsNull(sw) ? p->transform().map(m_path).boundingRect()
158 : boundsOnStroke(p, m_path, sw, BoundsMode::IncludeMiterLimit);
159 rect |= QSvgMarker::markersBoundsForNode(this, p, s);
160 return filterRegion(rect);
161}
162
163bool QSvgPath::requiresGroupRendering() const
164{
165 return hasAnyMarker();
166}
167
168QSvgPolygon::QSvgPolygon(QSvgNode *parent, const QPolygonF &poly)
169 : QSvgNode(parent), m_poly(poly)
170{
171}
172
173QRectF QSvgPolygon::internalFastBounds(QPainter *p, QSvgExtraStates &) const
174{
175 return p->transform().mapRect(m_poly.boundingRect());
176}
177
178QRectF QSvgPolygon::internalBounds(QPainter *p, QSvgExtraStates &s) const
179{
180 return internalBounds(p, s, BoundsMode::Simplistic);
181}
182
183QRectF QSvgPolygon::decoratedInternalBounds(QPainter *p, QSvgExtraStates &s) const
184{
185 QRectF rect = internalBounds(p, s, BoundsMode::IncludeMiterLimit);
186 rect |= QSvgMarker::markersBoundsForNode(this, p, s);
187 return filterRegion(rect);
188}
189
190bool QSvgPolygon::requiresGroupRendering() const
191{
192 return hasAnyMarker();
193}
194
195QRectF QSvgPolygon::internalBounds(QPainter *p, QSvgExtraStates &, BoundsMode mode) const
196{
197 qreal sw = strokeWidth(p);
198 if (qFuzzyIsNull(sw)) {
199 return p->transform().map(m_poly).boundingRect();
200 } else {
201 QPainterPath path;
202 path.addPolygon(m_poly);
203 return boundsOnStroke(p, path, sw, mode);
204 }
205}
206
207void QSvgPolygon::drawCommand(QPainter *p, QSvgExtraStates &states)
208{
209 if (m_poly.boundingRect().isNull() && p->pen().capStyle() != Qt::FlatCap)
210 p->drawPoint(m_poly.first());
211 else
212 p->drawPolygon(m_poly, states.fillRule);
213 QSvgMarker::drawMarkersForNode(this, p, states);
214}
215
216bool QSvgPolygon::separateFillStroke(const QSvgExtraStates &) const
217{
218 return true;
219}
220
221QSvgPolyline::QSvgPolyline(QSvgNode *parent, const QPolygonF &poly)
222 : QSvgNode(parent), m_poly(poly)
223{
224
225}
226
227void QSvgPolyline::drawCommand(QPainter *p, QSvgExtraStates &states)
228{
229 if (p->brush().style() != Qt::NoBrush) {
230 p->drawPolygon(m_poly, states.fillRule);
231 } else {
232 if (m_poly.boundingRect().isNull() && p->pen().capStyle() != Qt::FlatCap)
233 p->drawPoint(m_poly.first());
234 else
235 p->drawPolyline(m_poly);
236 QSvgMarker::drawMarkersForNode(this, p, states);
237 }
238}
239
240bool QSvgPolyline::separateFillStroke(const QSvgExtraStates &) const
241{
242 return true;
243}
244
245QSvgRect::QSvgRect(QSvgNode *node, const QRectF &rect, qreal rx, qreal ry)
246 : QSvgNode(node),
247 m_rect(rect), m_rx(rx), m_ry(ry)
248{
249}
250
251QRectF QSvgRect::internalFastBounds(QPainter *p, QSvgExtraStates &) const
252{
253 return p->transform().mapRect(m_rect);
254}
255
256QRectF QSvgRect::internalBounds(QPainter *p, QSvgExtraStates &s) const
257{
258 return internalBounds(p, s, BoundsMode::Simplistic);
259}
260
261QRectF QSvgRect::decoratedInternalBounds(QPainter *p, QSvgExtraStates &s) const
262{
263 return filterRegion(internalBounds(p, s, BoundsMode::IncludeMiterLimit));
264}
265
266QRectF QSvgRect::internalBounds(QPainter *p, QSvgExtraStates &, BoundsMode mode) const
267{
268 qreal sw = strokeWidth(p);
269 if (qFuzzyIsNull(sw)) {
270 return p->transform().mapRect(m_rect);
271 } else {
272 QPainterPath path;
273 path.addRect(m_rect);
274 return boundsOnStroke(p, path, sw, mode);
275 }
276}
277
278void QSvgRect::drawCommand(QPainter *p, QSvgExtraStates &)
279{
280 if (m_rx || m_ry)
281 p->drawRoundedRect(m_rect, m_rx, m_ry, Qt::RelativeSize);
282 else
283 p->drawRect(m_rect);
284}
285
286bool QSvgRect::separateFillStroke(const QSvgExtraStates &) const
287{
288 return true;
289}
290
291QSvgTspan * const QSvgText::LINEBREAK = 0;
292
293QSvgText::QSvgText(QSvgNode *parent, const QPointF &coord)
294 : QSvgNode(parent)
295 , m_coord(coord)
296 , m_size(0, 0)
297 , m_type(Text)
298 , m_mode(Default)
299{
300}
301
302QSvgText::~QSvgText()
303{
304 for (int i = 0; i < m_tspans.size(); ++i) {
305 if (m_tspans[i] != LINEBREAK)
306 delete m_tspans[i];
307 }
308}
309
310void QSvgText::setTextArea(const QSizeF &size)
311{
312 m_size = size;
313 m_type = Textarea;
314}
315
316QRectF QSvgText::internalFastBounds(QPainter *p, QSvgExtraStates &) const
317{
318 QFont font = m_style.font ? m_style.font->qfont() : p->font();
319 QFontMetricsF fm(font);
320
321 int charCount = 0;
322 for (int i = 0; i < m_tspans.size(); ++i) {
323 if (m_tspans.at(i) != LINEBREAK)
324 charCount += m_tspans.at(i)->text().size();
325 }
326
327 QRectF approxMaximumBrect(m_coord.x(),
328 m_coord.y(),
329 charCount * fm.averageCharWidth(),
330 -m_tspans.size() * fm.height());
331 return p->transform().mapRect(approxMaximumBrect);
332}
333
334QRectF QSvgText::internalBounds(QPainter *p, QSvgExtraStates &states) const
335{
336 QRectF boundingRect;
337 if (shouldDrawNode(p, states))
338 draw_helper(p, states, &boundingRect);
339 return p->transform().mapRect(boundingRect);
340}
341
342void QSvgText::drawCommand(QPainter *p, QSvgExtraStates &states)
343{
344 draw_helper(p, states);
345}
346
347bool QSvgText::shouldDrawNode(QPainter *p, QSvgExtraStates &) const
348{
349 qsizetype numChars = 0;
350 qreal originalFontSize = p->font().pointSizeF();
351 qreal maxFontSize = originalFontSize;
352 for (const QSvgTspan *span : std::as_const(m_tspans)) {
353 if (span == LINEBREAK)
354 continue;
355
356 numChars += span->text().size();
357
358 QSvgFontStyle *style = static_cast<QSvgFontStyle *>(span->styleProperty(QSvgStyleProperty::FONT));
359 if (style != nullptr && style->qfont().pointSizeF() > maxFontSize)
360 maxFontSize = style->qfont().pointSizeF();
361 }
362
363 QFont font = p->font();
364 font.setPixelSize(maxFontSize);
365 QFontMetricsF fm(font);
366 if (m_tspans.size() * fm.height() >= QT_SVG_MAX_LAYOUT_SIZE) {
367 qCWarning(lcSvgDraw) << "Text element too high to lay out, ignoring";
368 return false;
369 }
370
371 if (numChars * fm.maxWidth() >= QT_SVG_MAX_LAYOUT_SIZE) {
372 qCWarning(lcSvgDraw) << "Text element too wide to lay out, ignoring";
373 return false;
374 }
375
376 return true;
377}
378
379bool QSvgText::separateFillStroke(const QSvgExtraStates &) const
380{
381 return true;
382}
383
384void QSvgText::draw_helper(QPainter *p, QSvgExtraStates &states, QRectF *boundingRect) const
385{
386 const bool isPainting = (boundingRect == nullptr);
387 if (!isPainting || shouldDrawNode(p, states)) {
388 QFont font = p->font();
389 Qt::Alignment alignment = states.textAnchor;
390
391 qreal y = 0;
392 bool initial = true;
393 qreal px = m_coord.x();
394 qreal py = m_coord.y();
395
396 if (m_type == Textarea) {
397 if (alignment == Qt::AlignHCenter)
398 px += m_size.width() / 2;
399 else if (alignment == Qt::AlignRight)
400 px += m_size.width();
401 }
402
403 QRectF bounds;
404 if (m_size.height() != 0)
405 bounds = QRectF(0, py, 1, m_size.height()); // x and width are not used.
406
407 bool appendSpace = false;
408 QStringList paragraphs;
409 QList<QList<QTextLayout::FormatRange> > formatRanges(1);
410 paragraphs.push_back(QString());
411
412 for (int i = 0; i < m_tspans.size(); ++i) {
413 if (m_tspans[i] == LINEBREAK) {
414 if (m_type == Textarea) {
415 if (paragraphs.back().isEmpty()) {
416 font.setPixelSize(font.pointSizeF());
417 font.setHintingPreference(QFont::PreferNoHinting);
418
419 QTextLayout::FormatRange range;
420 range.start = 0;
421 range.length = 1;
422 range.format.setFont(font);
423 formatRanges.back().append(range);
424
425 paragraphs.back().append(QLatin1Char(' '));;
426 }
427 appendSpace = false;
428 paragraphs.push_back(QString());
429 formatRanges.resize(formatRanges.size() + 1);
430 }
431 } else {
432 WhitespaceMode mode = m_tspans[i]->whitespaceMode();
433 m_tspans[i]->applyStyle(p, states);
434
435 font = p->font();
436 font.setPixelSize(font.pointSizeF());
437 font.setHintingPreference(QFont::PreferNoHinting);
438
439 QString newText(m_tspans[i]->text());
440 newText.replace(QLatin1Char('\t'), QLatin1Char(' '));
441 newText.replace(QLatin1Char('\n'), QLatin1Char(' '));
442
443 bool prependSpace = !appendSpace && !m_tspans[i]->isTspan() && (mode == Default) && !paragraphs.back().isEmpty() && newText.startsWith(QLatin1Char(' '));
444 if (appendSpace || prependSpace)
445 paragraphs.back().append(QLatin1Char(' '));
446
447 bool appendSpaceNext = (!m_tspans[i]->isTspan() && (mode == Default) && newText.endsWith(QLatin1Char(' ')));
448
449 if (mode == Default) {
450 newText = newText.simplified();
451 if (newText.isEmpty())
452 appendSpaceNext = false;
453 }
454
455 QTextLayout::FormatRange range;
456 range.start = paragraphs.back().size();
457 range.length = newText.size();
458 range.format.setFont(font);
459 if (p->pen().style() != Qt::NoPen && p->pen().brush() != Qt::NoBrush)
460 range.format.setTextOutline(p->pen());
461 // work around QTBUG-136696: NoBrush fills the foreground with the outline's pen
462 range.format.setForeground(p->brush().style() == Qt::NoBrush
463 ? QColor(Qt::transparent) : p->brush());
464
465 if (appendSpace) {
466 Q_ASSERT(!formatRanges.back().isEmpty());
467 ++formatRanges.back().back().length;
468 } else if (prependSpace) {
469 --range.start;
470 ++range.length;
471 }
472 formatRanges.back().append(range);
473
474 appendSpace = appendSpaceNext;
475 paragraphs.back() += newText;
476
477 m_tspans[i]->revertStyle(p, states);
478 }
479 }
480
481 if (states.svgFont) {
482 // SVG fonts not fully supported...
483 if (!m_glyphsToDraw)
484 m_glyphsToDraw = states.svgFont->toGlyphs(paragraphs.join(QLatin1Char('\n')));
485 if (isPainting) {
486 states.svgFont->draw(p, m_coord, m_glyphsToDraw.value(),
487 p->font().pointSizeF(), states.textAnchor);
488 } else {
489 *boundingRect = states.svgFont->boundingRect(p, m_coord, m_glyphsToDraw.value(),
490 p->font().pointSizeF(), states.textAnchor);
491 }
492 } else {
493 QRectF brect;
494 for (int i = 0; i < paragraphs.size(); ++i) {
495 QTextLayout tl(paragraphs[i]);
496 QTextOption op = tl.textOption();
497 op.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
498 tl.setTextOption(op);
499 tl.setFormats(formatRanges[i]);
500 tl.beginLayout();
501
502 forever {
503 QTextLine line = tl.createLine();
504 if (!line.isValid())
505 break;
506 if (m_size.width() != 0)
507 line.setLineWidth(m_size.width());
508 }
509 tl.endLayout();
510
511 bool endOfBoundsReached = false;
512 for (int i = 0; i < tl.lineCount(); ++i) {
513 QTextLine line = tl.lineAt(i);
514
515 qreal x = 0;
516 if (alignment == Qt::AlignHCenter)
517 x -= 0.5 * line.naturalTextWidth();
518 else if (alignment == Qt::AlignRight)
519 x -= line.naturalTextWidth();
520
521 if (initial && m_type == Text)
522 y -= line.ascent();
523 initial = false;
524
525 line.setPosition(QPointF(x, y));
526 brect |= line.naturalTextRect();
527
528 // Check if the current line fits into the bounding rectangle.
529 if ((m_size.width() != 0 && line.naturalTextWidth() > m_size.width())
530 || (m_size.height() != 0 && y + line.height() > m_size.height())) {
531 // I need to set the bounds height to 'y-epsilon' to avoid drawing the current
532 // line. Since the font is scaled to 100 units, 1 should be a safe epsilon.
533 bounds.setHeight(y - 1);
534 endOfBoundsReached = true;
535 break;
536 }
537
538 y += 1.1 * line.height();
539 }
540 if (isPainting)
541 tl.draw(p, QPointF(px, py), QList<QTextLayout::FormatRange>(), bounds);
542
543 if (endOfBoundsReached)
544 break;
545 }
546 if (boundingRect) {
547 brect.translate(m_coord);
548 if (bounds.height() > 0)
549 brect.setBottom(qMin(brect.bottom(), bounds.bottom()));
550 *boundingRect = brect;
551 }
552 }
553 }
554}
555
556void QSvgText::addText(QStringView text)
557{
558 m_tspans.append(new QSvgTspan(this, false));
559 m_tspans.back()->setWhitespaceMode(m_mode);
560 m_tspans.back()->addText(text);
561}
562
563QSvgUse::QSvgUse(const QPointF &start, QSvgNode *parent, QSvgNode *node)
564 : QSvgNode(parent), m_link(node), m_start(start), m_recursing(false)
565{
566
567}
568
569void QSvgUse::drawCommand(QPainter *p, QSvgExtraStates &states)
570{
571 if (Q_UNLIKELY(!m_link || isDescendantOf(m_link) || m_recursing))
572 return;
573
574 Q_ASSERT(states.nestedUseCount == 0 || states.nestedUseLevel > 0);
575 if (states.nestedUseLevel > 3 && states.nestedUseCount > (256 + states.nestedUseLevel * 2)) {
576 qCDebug(lcSvgDraw, "Too many nested use nodes at #%s!", qPrintable(m_linkId));
577 return;
578 }
579
580 QScopedValueRollback<bool> inUseGuard(states.inUse, true);
581
582 if (!m_start.isNull()) {
583 p->translate(m_start);
584 }
585 if (states.nestedUseLevel > 0)
586 ++states.nestedUseCount;
587 {
588 QScopedValueRollback<int> useLevelGuard(states.nestedUseLevel, states.nestedUseLevel + 1);
589 QScopedValueRollback<bool> recursingGuard(m_recursing, true);
590 m_link->draw(p, states);
591 }
592 if (states.nestedUseLevel == 0)
593 states.nestedUseCount = 0;
594
595 if (!m_start.isNull()) {
596 p->translate(-m_start);
597 }
598}
599
600QSvgNode::Type QSvgDummyNode::type() const
601{
602 return FeUnsupported;
603}
604
605QSvgNode::Type QSvgCircle::type() const
606{
607 return Circle;
608}
609
610QSvgNode::Type QSvgEllipse::type() const
611{
612 return Ellipse;
613}
614
615QSvgNode::Type QSvgImage::type() const
616{
617 return Image;
618}
619
620QSvgNode::Type QSvgLine::type() const
621{
622 return Line;
623}
624
625QSvgNode::Type QSvgPath::type() const
626{
627 return Path;
628}
629
630QSvgNode::Type QSvgPolygon::type() const
631{
632 return Polygon;
633}
634
635QSvgNode::Type QSvgPolyline::type() const
636{
637 return Polyline;
638}
639
640QSvgNode::Type QSvgRect::type() const
641{
642 return Rect;
643}
644
645QSvgNode::Type QSvgText::type() const
646{
647 return m_type;
648}
649
651{
652 return Use;
653}
654
656{
657 return Video;
658}
659
660QRectF QSvgUse::internalBounds(QPainter *p, QSvgExtraStates &states) const
661{
662 QRectF bounds;
663 if (Q_LIKELY(m_link && !isDescendantOf(m_link) && !m_recursing)) {
664 QScopedValueRollback<bool> guard(m_recursing, true);
665 p->translate(m_start);
666 bounds = m_link->bounds(p, states);
667 p->translate(-m_start);
668 }
669 return bounds;
670}
671
672QRectF QSvgUse::decoratedInternalBounds(QPainter *p, QSvgExtraStates &states) const
673{
674 QRectF bounds;
675 if (Q_LIKELY(m_link && !isDescendantOf(m_link) && !m_recursing)) {
676 QScopedValueRollback<bool> guard(m_recursing, true);
677 p->translate(m_start);
678 bounds = m_link->decoratedBounds(p, states);
679 p->translate(-m_start);
680 }
681 return bounds;
682}
683
684QRectF QSvgPolyline::internalFastBounds(QPainter *p, QSvgExtraStates &) const
685{
686 return p->transform().mapRect(m_poly.boundingRect());
687}
688
689QRectF QSvgPolyline::internalBounds(QPainter *p, QSvgExtraStates &s) const
690{
691 return internalBounds(p, s, BoundsMode::Simplistic);
692}
693
694QRectF QSvgPolyline::decoratedInternalBounds(QPainter *p, QSvgExtraStates &s) const
695{
696 QRectF rect = internalBounds(p, s, BoundsMode::IncludeMiterLimit);
697 rect |= QSvgMarker::markersBoundsForNode(this, p, s);
698 return filterRegion(rect);
699}
700
701bool QSvgPolyline::requiresGroupRendering() const
702{
703 return hasAnyMarker();
704}
705
706QRectF QSvgPolyline::internalBounds(QPainter *p, QSvgExtraStates &, BoundsMode mode) const
707{
708 qreal sw = strokeWidth(p);
709 if (qFuzzyIsNull(sw)) {
710 return p->transform().map(m_poly).boundingRect();
711 } else {
712 QPainterPath path;
713 path.addPolygon(m_poly);
714 return boundsOnStroke(p, path, sw, mode);
715 }
716}
717
718QRectF QSvgImage::internalBounds(QPainter *p, QSvgExtraStates &) const
719{
720 return p->transform().mapRect(m_bounds);
721}
722
723QRectF QSvgLine::internalFastBounds(QPainter *p, QSvgExtraStates &) const
724{
725 QPointF p1 = p->transform().map(m_line.p1());
726 QPointF p2 = p->transform().map(m_line.p2());
727 qreal minX = qMin(p1.x(), p2.x());
728 qreal minY = qMin(p1.y(), p2.y());
729 qreal maxX = qMax(p1.x(), p2.x());
730 qreal maxY = qMax(p1.y(), p2.y());
731 return QRectF(minX, minY, maxX - minX, maxY - minY);
732}
733
734QRectF QSvgLine::internalBounds(QPainter *p, QSvgExtraStates &s) const
735{
736 return internalBounds(p, s, BoundsMode::Simplistic);
737}
738
739QRectF QSvgLine::decoratedInternalBounds(QPainter *p, QSvgExtraStates &s) const
740{
741 QRectF rect = internalBounds(p, s, BoundsMode::IncludeMiterLimit);
742 rect |= QSvgMarker::markersBoundsForNode(this, p, s);
743 return filterRegion(rect);
744}
745
746bool QSvgLine::requiresGroupRendering() const
747{
748 return hasAnyMarker();
749}
750
751QRectF QSvgLine::internalBounds(QPainter *p, QSvgExtraStates &s, BoundsMode mode) const
752{
753 qreal sw = strokeWidth(p);
754 if (qFuzzyIsNull(sw)) {
755 return internalFastBounds(p, s);
756 } else {
757 QPainterPath path;
758 path.moveTo(m_line.p1());
759 path.lineTo(m_line.p2());
760 return boundsOnStroke(p, path, sw, mode);
761 }
762}
763
764QT_END_NAMESPACE
friend class QPainter
QRectF decoratedInternalBounds(QPainter *p, QSvgExtraStates &states) const override
void drawCommand(QPainter *p, QSvgExtraStates &states) override
QRectF internalBounds(QPainter *p, QSvgExtraStates &states) const override
QSvgUse(const QPointF &start, QSvgNode *parent, QSvgNode *link)
Type type() const override
Type type() const override
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
#define qPrintable(string)
Definition qstring.h:1683
#define QT_SVG_MAX_LAYOUT_SIZE