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