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