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