summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>2023-04-12 13:21:54 +0200
committerEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>2023-04-17 13:29:44 +0200
commitd130c74f4f54cddcd5baa7d8dc4c5b68772345c2 (patch)
tree4755e76fd92e5a32da7de1128f9e103aeea06337 /src
parent659e6bf8ca14ab96acdb5d1b384e8e15ee7b0491 (diff)
downloadqtsvg-d130c74f4f54cddcd5baa7d8dc4c5b68772345c2.tar.gz
Don't rasterize gigantic shapes
We set a limit for the bounding rect of shapes to avoid stalling applications for several minutes while rendering unreasonably large shapes. The limit can be disabled using an environment variable. [ChangeLog][Performance] Unreasonably large shapes are now ignored in SVG files to avoid stalling the application. The check can be disabled by setting QT_SVG_DISABLE_SIZE_LIMIT=1 in the environment. Pick-to: 6.2 6.5 Fixes: QTBUG-111850 Change-Id: Id8154049c96a565aad237ee007da0ee879446448 Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/svg/qsvggraphics.cpp454
-rw-r--r--src/svg/qsvggraphics_p.h8
-rw-r--r--src/svg/qsvgnode.cpp31
-rw-r--r--src/svg/qsvgnode_p.h3
4 files changed, 302 insertions, 194 deletions
diff --git a/src/svg/qsvggraphics.cpp b/src/svg/qsvggraphics.cpp
index facb190..ac8051d 100644
--- a/src/svg/qsvggraphics.cpp
+++ b/src/svg/qsvggraphics.cpp
@@ -32,7 +32,7 @@ Q_LOGGING_CATEGORY(lcSvgTiming, "qt.svg.timing")
qCDebug(lcSvgTiming) << "Drawing" << TYPE << "took" << (qtSvgTimer.nsecsElapsed() / 1000000.0f) << "ms";
#define QT_SVG_DRAW_SHAPE(command) \
- qreal oldOpacity = p->opacity(); \
+ { qreal oldOpacity = p->opacity(); \
QBrush oldBrush = p->brush(); \
QPen oldPen = p->pen(); \
p->setPen(Qt::NoPen); \
@@ -45,7 +45,7 @@ Q_LOGGING_CATEGORY(lcSvgTiming, "qt.svg.timing")
command; \
p->setBrush(oldBrush); \
} \
- p->setOpacity(oldOpacity);
+ p->setOpacity(oldOpacity); }
void QSvgAnimation::draw(QPainter *, QSvgExtraStates &)
@@ -66,6 +66,10 @@ QSvgEllipse::QSvgEllipse(QSvgNode *parent, const QRectF &rect)
{
}
+QRectF QSvgEllipse::fastBounds(QPainter *p, QSvgExtraStates &) const
+{
+ return p->transform().mapRect(m_bounds);
+}
QRectF QSvgEllipse::bounds(QPainter *p, QSvgExtraStates &) const
{
@@ -79,7 +83,8 @@ void QSvgEllipse::draw(QPainter *p, QSvgExtraStates &states)
{
QT_SVG_TIMING_ENTER
applyStyle(p, states);
- QT_SVG_DRAW_SHAPE(p->drawEllipse(m_bounds));
+ if (shouldDrawNode(p, states))
+ QT_SVG_DRAW_SHAPE(p->drawEllipse(m_bounds));
revertStyle(p, states);
QT_SVG_TIMING_EXIT("Ellipse")
}
@@ -93,11 +98,13 @@ void QSvgArc::draw(QPainter *p, QSvgExtraStates &states)
{
QT_SVG_TIMING_ENTER
applyStyle(p, states);
- if (p->pen().widthF() != 0) {
- qreal oldOpacity = p->opacity();
- p->setOpacity(oldOpacity * states.strokeOpacity);
- p->drawPath(m_path);
- p->setOpacity(oldOpacity);
+ if (shouldDrawNode(p, states)) {
+ if (p->pen().widthF() != 0) {
+ qreal oldOpacity = p->opacity();
+ p->setOpacity(oldOpacity * states.strokeOpacity);
+ p->drawPath(m_path);
+ p->setOpacity(oldOpacity);
+ }
}
revertStyle(p, states);
QT_SVG_TIMING_EXIT("Arc")
@@ -117,9 +124,11 @@ QSvgImage::QSvgImage(QSvgNode *parent, const QImage &image,
void QSvgImage::draw(QPainter *p, QSvgExtraStates &states)
{
QT_SVG_TIMING_ENTER
- applyStyle(p, states);
- p->drawImage(m_bounds, m_image);
- revertStyle(p, states);
+ if (shouldDrawNode(p, states)) {
+ applyStyle(p, states);
+ p->drawImage(m_bounds, m_image);
+ revertStyle(p, states);
+ }
QT_SVG_TIMING_EXIT("Image")
}
@@ -134,11 +143,13 @@ void QSvgLine::draw(QPainter *p, QSvgExtraStates &states)
{
QT_SVG_TIMING_ENTER
applyStyle(p, states);
- if (p->pen().widthF() != 0) {
- qreal oldOpacity = p->opacity();
- p->setOpacity(oldOpacity * states.strokeOpacity);
- p->drawLine(m_line);
- p->setOpacity(oldOpacity);
+ if (shouldDrawNode(p, states)) {
+ if (p->pen().widthF() != 0) {
+ qreal oldOpacity = p->opacity();
+ p->setOpacity(oldOpacity * states.strokeOpacity);
+ p->drawLine(m_line);
+ p->setOpacity(oldOpacity);
+ }
}
revertStyle(p, states);
QT_SVG_TIMING_EXIT("Line")
@@ -153,12 +164,19 @@ void QSvgPath::draw(QPainter *p, QSvgExtraStates &states)
{
QT_SVG_TIMING_ENTER
applyStyle(p, states);
- m_path.setFillRule(states.fillRule);
- QT_SVG_DRAW_SHAPE(p->drawPath(m_path));
+ if (shouldDrawNode(p, states)) {
+ m_path.setFillRule(states.fillRule);
+ QT_SVG_DRAW_SHAPE(p->drawPath(m_path));
+ }
revertStyle(p, states);
QT_SVG_TIMING_EXIT("Path")
}
+QRectF QSvgPath::fastBounds(QPainter *p, QSvgExtraStates &) const
+{
+ return p->transform().mapRect(m_path.controlPointRect());
+}
+
QRectF QSvgPath::bounds(QPainter *p, QSvgExtraStates &) const
{
qreal sw = strokeWidth(p);
@@ -171,6 +189,11 @@ QSvgPolygon::QSvgPolygon(QSvgNode *parent, const QPolygonF &poly)
{
}
+QRectF QSvgPolygon::fastBounds(QPainter *p, QSvgExtraStates &) const
+{
+ return p->transform().mapRect(m_poly.boundingRect());
+}
+
QRectF QSvgPolygon::bounds(QPainter *p, QSvgExtraStates &) const
{
qreal sw = strokeWidth(p);
@@ -187,7 +210,8 @@ void QSvgPolygon::draw(QPainter *p, QSvgExtraStates &states)
{
QT_SVG_TIMING_ENTER
applyStyle(p, states);
- QT_SVG_DRAW_SHAPE(p->drawPolygon(m_poly, states.fillRule));
+ if (shouldDrawNode(p, states))
+ QT_SVG_DRAW_SHAPE(p->drawPolygon(m_poly, states.fillRule));
revertStyle(p, states);
QT_SVG_TIMING_EXIT("Polygon")
}
@@ -203,19 +227,21 @@ void QSvgPolyline::draw(QPainter *p, QSvgExtraStates &states)
{
QT_SVG_TIMING_ENTER
applyStyle(p, states);
- qreal oldOpacity = p->opacity();
- if (p->brush().style() != Qt::NoBrush) {
- QPen save = p->pen();
- p->setPen(QPen(Qt::NoPen));
- p->setOpacity(oldOpacity * states.fillOpacity);
- p->drawPolygon(m_poly, states.fillRule);
- p->setPen(save);
- }
- if (p->pen().widthF() != 0) {
- p->setOpacity(oldOpacity * states.strokeOpacity);
- p->drawPolyline(m_poly);
+ if (shouldDrawNode(p, states)) {
+ qreal oldOpacity = p->opacity();
+ if (p->brush().style() != Qt::NoBrush) {
+ QPen save = p->pen();
+ p->setPen(QPen(Qt::NoPen));
+ p->setOpacity(oldOpacity * states.fillOpacity);
+ p->drawPolygon(m_poly, states.fillRule);
+ p->setPen(save);
+ }
+ if (p->pen().widthF() != 0) {
+ p->setOpacity(oldOpacity * states.strokeOpacity);
+ p->drawPolyline(m_poly);
+ }
+ p->setOpacity(oldOpacity);
}
- p->setOpacity(oldOpacity);
revertStyle(p, states);
QT_SVG_TIMING_EXIT("Polyline")
}
@@ -226,6 +252,11 @@ QSvgRect::QSvgRect(QSvgNode *node, const QRectF &rect, int rx, int ry)
{
}
+QRectF QSvgRect::fastBounds(QPainter *p, QSvgExtraStates &) const
+{
+ return p->transform().mapRect(m_rect);
+}
+
QRectF QSvgRect::bounds(QPainter *p, QSvgExtraStates &) const
{
qreal sw = strokeWidth(p);
@@ -242,10 +273,12 @@ void QSvgRect::draw(QPainter *p, QSvgExtraStates &states)
{
QT_SVG_TIMING_ENTER
applyStyle(p, states);
- if (m_rx || m_ry) {
- QT_SVG_DRAW_SHAPE(p->drawRoundedRect(m_rect, m_rx, m_ry, Qt::RelativeSize));
- } else {
- QT_SVG_DRAW_SHAPE(p->drawRect(m_rect));
+ if (shouldDrawNode(p, states)) {
+ if (m_rx || m_ry) {
+ QT_SVG_DRAW_SHAPE(p->drawRoundedRect(m_rect, m_rx, m_ry, Qt::RelativeSize));
+ } else {
+ QT_SVG_DRAW_SHAPE(p->drawRect(m_rect));
+ }
}
revertStyle(p, states);
QT_SVG_TIMING_EXIT("Rect")
@@ -276,6 +309,22 @@ void QSvgText::setTextArea(const QSizeF &size)
m_type = TEXTAREA;
}
+QRectF QSvgText::fastBounds(QPainter *p, QSvgExtraStates &) const
+{
+ QFont font = p->font();
+ QFontMetricsF fm(font);
+
+ int charCount = 0;
+ for (int i = 0; i < m_tspans.size(); ++i)
+ charCount += m_tspans.at(i)->text().size();
+
+ QRectF approxMaximumBrect(m_coord.x(),
+ m_coord.y(),
+ charCount * fm.averageCharWidth(),
+ m_tspans.size() * fm.height());
+ return p->transform().mapRect(approxMaximumBrect);
+}
+
QRectF QSvgText::bounds(QPainter *p, QSvgExtraStates &states) const
{
QRectF boundingRect;
@@ -294,176 +343,178 @@ void QSvgText::draw_helper(QPainter *p, QSvgExtraStates &states, QRectF *boundin
{
const bool isPainting = (boundingRect == nullptr);
applyStyle(p, states);
- qreal oldOpacity = p->opacity();
- p->setOpacity(oldOpacity * states.fillOpacity);
-
- // Force the font to have a size of 100 pixels to avoid truncation problems
- // when the font is very small.
- qreal scale = 100.0 / p->font().pointSizeF();
- Qt::Alignment alignment = states.textAnchor;
-
- QTransform oldTransform = p->worldTransform();
- p->scale(1 / scale, 1 / scale);
-
- qreal y = 0;
- bool initial = true;
- qreal px = m_coord.x() * scale;
- qreal py = m_coord.y() * scale;
- QSizeF scaledSize = m_size * scale;
-
- if (m_type == TEXTAREA) {
- if (alignment == Qt::AlignHCenter)
- px += scaledSize.width() / 2;
- else if (alignment == Qt::AlignRight)
- px += scaledSize.width();
- }
-
- QRectF bounds;
- if (m_size.height() != 0)
- bounds = QRectF(0, py, 1, scaledSize.height()); // x and width are not used.
+ if (!isPainting || shouldDrawNode(p, states)) {
+ qreal oldOpacity = p->opacity();
+ p->setOpacity(oldOpacity * states.fillOpacity);
- bool appendSpace = false;
- QList<QString> paragraphs;
- QList<QList<QTextLayout::FormatRange> > formatRanges(1);
- paragraphs.push_back(QString());
+ // Force the font to have a size of 100 pixels to avoid truncation problems
+ // when the font is very small.
+ qreal scale = 100.0 / p->font().pointSizeF();
+ Qt::Alignment alignment = states.textAnchor;
+
+ QTransform oldTransform = p->worldTransform();
+ p->scale(1 / scale, 1 / scale);
+
+ qreal y = 0;
+ bool initial = true;
+ qreal px = m_coord.x() * scale;
+ qreal py = m_coord.y() * scale;
+ QSizeF scaledSize = m_size * scale;
+
+ if (m_type == TEXTAREA) {
+ if (alignment == Qt::AlignHCenter)
+ px += scaledSize.width() / 2;
+ else if (alignment == Qt::AlignRight)
+ px += scaledSize.width();
+ }
- for (int i = 0; i < m_tspans.size(); ++i) {
- if (m_tspans[i] == LINEBREAK) {
- if (m_type == TEXTAREA) {
- if (paragraphs.back().isEmpty()) {
- QFont font = p->font();
- font.setPixelSize(font.pointSizeF() * scale);
-
- QTextLayout::FormatRange range;
- range.start = 0;
- range.length = 1;
- range.format.setFont(font);
- formatRanges.back().append(range);
-
- paragraphs.back().append(QLatin1Char(' '));;
+ QRectF bounds;
+ if (m_size.height() != 0)
+ bounds = QRectF(0, py, 1, scaledSize.height()); // x and width are not used.
+
+ bool appendSpace = false;
+ QList<QString> paragraphs;
+ QList<QList<QTextLayout::FormatRange> > formatRanges(1);
+ paragraphs.push_back(QString());
+
+ for (int i = 0; i < m_tspans.size(); ++i) {
+ if (m_tspans[i] == LINEBREAK) {
+ if (m_type == TEXTAREA) {
+ if (paragraphs.back().isEmpty()) {
+ QFont font = p->font();
+ font.setPixelSize(font.pointSizeF() * scale);
+
+ QTextLayout::FormatRange range;
+ range.start = 0;
+ range.length = 1;
+ range.format.setFont(font);
+ formatRanges.back().append(range);
+
+ paragraphs.back().append(QLatin1Char(' '));;
+ }
+ appendSpace = false;
+ paragraphs.push_back(QString());
+ formatRanges.resize(formatRanges.size() + 1);
}
- appendSpace = false;
- paragraphs.push_back(QString());
- formatRanges.resize(formatRanges.size() + 1);
- }
- } else {
- WhitespaceMode mode = m_tspans[i]->whitespaceMode();
- m_tspans[i]->applyStyle(p, states);
+ } else {
+ WhitespaceMode mode = m_tspans[i]->whitespaceMode();
+ m_tspans[i]->applyStyle(p, states);
- QFont font = p->font();
- font.setPixelSize(font.pointSizeF() * scale);
+ QFont font = p->font();
+ font.setPixelSize(font.pointSizeF() * scale);
- QString newText(m_tspans[i]->text());
- newText.replace(QLatin1Char('\t'), QLatin1Char(' '));
- newText.replace(QLatin1Char('\n'), QLatin1Char(' '));
+ QString newText(m_tspans[i]->text());
+ newText.replace(QLatin1Char('\t'), QLatin1Char(' '));
+ newText.replace(QLatin1Char('\n'), QLatin1Char(' '));
- bool prependSpace = !appendSpace && !m_tspans[i]->isTspan() && (mode == Default) && !paragraphs.back().isEmpty() && newText.startsWith(QLatin1Char(' '));
- if (appendSpace || prependSpace)
- paragraphs.back().append(QLatin1Char(' '));
+ bool prependSpace = !appendSpace && !m_tspans[i]->isTspan() && (mode == Default) && !paragraphs.back().isEmpty() && newText.startsWith(QLatin1Char(' '));
+ if (appendSpace || prependSpace)
+ paragraphs.back().append(QLatin1Char(' '));
- bool appendSpaceNext = (!m_tspans[i]->isTspan() && (mode == Default) && newText.endsWith(QLatin1Char(' ')));
+ bool appendSpaceNext = (!m_tspans[i]->isTspan() && (mode == Default) && newText.endsWith(QLatin1Char(' ')));
- if (mode == Default) {
- newText = newText.simplified();
- if (newText.isEmpty())
- appendSpaceNext = false;
- }
+ if (mode == Default) {
+ newText = newText.simplified();
+ if (newText.isEmpty())
+ appendSpaceNext = false;
+ }
- QTextLayout::FormatRange range;
- range.start = paragraphs.back().size();
- range.length = newText.size();
- range.format.setFont(font);
- range.format.setTextOutline(p->pen());
- range.format.setForeground(p->brush());
-
- if (appendSpace) {
- Q_ASSERT(!formatRanges.back().isEmpty());
- ++formatRanges.back().back().length;
- } else if (prependSpace) {
- --range.start;
- ++range.length;
- }
- formatRanges.back().append(range);
+ QTextLayout::FormatRange range;
+ range.start = paragraphs.back().size();
+ range.length = newText.size();
+ range.format.setFont(font);
+ range.format.setTextOutline(p->pen());
+ range.format.setForeground(p->brush());
+
+ if (appendSpace) {
+ Q_ASSERT(!formatRanges.back().isEmpty());
+ ++formatRanges.back().back().length;
+ } else if (prependSpace) {
+ --range.start;
+ ++range.length;
+ }
+ formatRanges.back().append(range);
- appendSpace = appendSpaceNext;
- paragraphs.back() += newText;
+ appendSpace = appendSpaceNext;
+ paragraphs.back() += newText;
- m_tspans[i]->revertStyle(p, states);
+ m_tspans[i]->revertStyle(p, states);
+ }
}
- }
- if (states.svgFont) {
- // SVG fonts not fully supported...
- QString text = paragraphs.front();
- for (int i = 1; i < paragraphs.size(); ++i) {
- text.append(QLatin1Char('\n'));
- text.append(paragraphs[i]);
- }
- states.svgFont->draw(p, m_coord * scale, text, p->font().pointSizeF() * scale, states.textAnchor);
- } else {
- QRectF brect;
- for (int i = 0; i < paragraphs.size(); ++i) {
- QTextLayout tl(paragraphs[i]);
- QTextOption op = tl.textOption();
- op.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
- tl.setTextOption(op);
- tl.setFormats(formatRanges[i]);
- tl.beginLayout();
-
- forever {
- QTextLine line = tl.createLine();
- if (!line.isValid())
- break;
- if (m_size.width() != 0)
- line.setLineWidth(scaledSize.width());
+ if (states.svgFont) {
+ // SVG fonts not fully supported...
+ QString text = paragraphs.front();
+ for (int i = 1; i < paragraphs.size(); ++i) {
+ text.append(QLatin1Char('\n'));
+ text.append(paragraphs[i]);
}
- tl.endLayout();
-
- bool endOfBoundsReached = false;
- for (int i = 0; i < tl.lineCount(); ++i) {
- QTextLine line = tl.lineAt(i);
-
- qreal x = 0;
- if (alignment == Qt::AlignHCenter)
- x -= 0.5 * line.naturalTextWidth();
- else if (alignment == Qt::AlignRight)
- x -= line.naturalTextWidth();
-
- if (initial && m_type == TEXT)
- y -= line.ascent();
- initial = false;
-
- line.setPosition(QPointF(x, y));
- brect |= line.naturalTextRect();
-
- // Check if the current line fits into the bounding rectangle.
- if ((m_size.width() != 0 && line.naturalTextWidth() > scaledSize.width())
- || (m_size.height() != 0 && y + line.height() > scaledSize.height())) {
- // I need to set the bounds height to 'y-epsilon' to avoid drawing the current
- // line. Since the font is scaled to 100 units, 1 should be a safe epsilon.
- bounds.setHeight(y - 1);
- endOfBoundsReached = true;
- break;
+ states.svgFont->draw(p, m_coord * scale, text, p->font().pointSizeF() * scale, states.textAnchor);
+ } else {
+ QRectF brect;
+ for (int i = 0; i < paragraphs.size(); ++i) {
+ QTextLayout tl(paragraphs[i]);
+ QTextOption op = tl.textOption();
+ op.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
+ tl.setTextOption(op);
+ tl.setFormats(formatRanges[i]);
+ tl.beginLayout();
+
+ forever {
+ QTextLine line = tl.createLine();
+ if (!line.isValid())
+ break;
+ if (m_size.width() != 0)
+ line.setLineWidth(scaledSize.width());
+ }
+ tl.endLayout();
+
+ bool endOfBoundsReached = false;
+ for (int i = 0; i < tl.lineCount(); ++i) {
+ QTextLine line = tl.lineAt(i);
+
+ qreal x = 0;
+ if (alignment == Qt::AlignHCenter)
+ x -= 0.5 * line.naturalTextWidth();
+ else if (alignment == Qt::AlignRight)
+ x -= line.naturalTextWidth();
+
+ if (initial && m_type == TEXT)
+ y -= line.ascent();
+ initial = false;
+
+ line.setPosition(QPointF(x, y));
+ brect |= line.naturalTextRect();
+
+ // Check if the current line fits into the bounding rectangle.
+ if ((m_size.width() != 0 && line.naturalTextWidth() > scaledSize.width())
+ || (m_size.height() != 0 && y + line.height() > scaledSize.height())) {
+ // I need to set the bounds height to 'y-epsilon' to avoid drawing the current
+ // line. Since the font is scaled to 100 units, 1 should be a safe epsilon.
+ bounds.setHeight(y - 1);
+ endOfBoundsReached = true;
+ break;
+ }
+
+ y += 1.1 * line.height();
}
+ if (isPainting)
+ tl.draw(p, QPointF(px, py), QList<QTextLayout::FormatRange>(), bounds);
- y += 1.1 * line.height();
+ if (endOfBoundsReached)
+ break;
+ }
+ if (boundingRect) {
+ brect.translate(m_coord * scale);
+ if (bounds.height() > 0)
+ brect.setBottom(qMin(brect.bottom(), bounds.bottom()));
+ *boundingRect = QTransform::fromScale(1 / scale, 1 / scale).mapRect(brect);
}
- if (isPainting)
- tl.draw(p, QPointF(px, py), QList<QTextLayout::FormatRange>(), bounds);
-
- if (endOfBoundsReached)
- break;
- }
- if (boundingRect) {
- brect.translate(m_coord * scale);
- if (bounds.height() > 0)
- brect.setBottom(qMin(brect.bottom(), bounds.bottom()));
- *boundingRect = QTransform::fromScale(1 / scale, 1 / scale).mapRect(brect);
}
- }
- p->setWorldTransform(oldTransform, false);
- p->setOpacity(oldOpacity);
+ p->setWorldTransform(oldTransform, false);
+ p->setOpacity(oldOpacity);
+ }
revertStyle(p, states);
}
@@ -599,6 +650,11 @@ QRectF QSvgUse::bounds(QPainter *p, QSvgExtraStates &states) const
return bounds;
}
+QRectF QSvgPolyline::fastBounds(QPainter *p, QSvgExtraStates &) const
+{
+ return p->transform().mapRect(m_poly.boundingRect());
+}
+
QRectF QSvgPolyline::bounds(QPainter *p, QSvgExtraStates &) const
{
qreal sw = strokeWidth(p);
@@ -611,6 +667,11 @@ QRectF QSvgPolyline::bounds(QPainter *p, QSvgExtraStates &) const
}
}
+QRectF QSvgArc::fastBounds(QPainter *p, QSvgExtraStates &) const
+{
+ return p->transform().mapRect(m_path.controlPointRect());
+}
+
QRectF QSvgArc::bounds(QPainter *p, QSvgExtraStates &) const
{
qreal sw = strokeWidth(p);
@@ -623,17 +684,22 @@ QRectF QSvgImage::bounds(QPainter *p, QSvgExtraStates &) const
return p->transform().mapRect(m_bounds);
}
-QRectF QSvgLine::bounds(QPainter *p, QSvgExtraStates &) const
+QRectF QSvgLine::fastBounds(QPainter *p, QSvgExtraStates &) const
+{
+ QPointF p1 = p->transform().map(m_line.p1());
+ QPointF p2 = p->transform().map(m_line.p2());
+ qreal minX = qMin(p1.x(), p2.x());
+ qreal minY = qMin(p1.y(), p2.y());
+ qreal maxX = qMax(p1.x(), p2.x());
+ qreal maxY = qMax(p1.y(), p2.y());
+ return QRectF(minX, minY, maxX - minX, maxY - minY);
+}
+
+QRectF QSvgLine::bounds(QPainter *p, QSvgExtraStates &s) const
{
qreal sw = strokeWidth(p);
if (qFuzzyIsNull(sw)) {
- QPointF p1 = p->transform().map(m_line.p1());
- QPointF p2 = p->transform().map(m_line.p2());
- qreal minX = qMin(p1.x(), p2.x());
- qreal minY = qMin(p1.y(), p2.y());
- qreal maxX = qMax(p1.x(), p2.x());
- qreal maxY = qMax(p1.y(), p2.y());
- return QRectF(minX, minY, maxX - minX, maxY - minY);
+ return fastBounds(p, s);
} else {
QPainterPath path;
path.moveTo(m_line.p1());
diff --git a/src/svg/qsvggraphics_p.h b/src/svg/qsvggraphics_p.h
index 7c956e8..96be929 100644
--- a/src/svg/qsvggraphics_p.h
+++ b/src/svg/qsvggraphics_p.h
@@ -41,6 +41,7 @@ public:
QSvgArc(QSvgNode *parent, const QPainterPath &path);
void draw(QPainter *p, QSvgExtraStates &states) override;
Type type() const override;
+ QRectF fastBounds(QPainter *p, QSvgExtraStates &states) const override;
QRectF bounds(QPainter *p, QSvgExtraStates &states) const override;
private:
QPainterPath m_path;
@@ -52,6 +53,7 @@ public:
QSvgEllipse(QSvgNode *parent, const QRectF &rect);
void draw(QPainter *p, QSvgExtraStates &states) override;
Type type() const override;
+ QRectF fastBounds(QPainter *p, QSvgExtraStates &states) const override;
QRectF bounds(QPainter *p, QSvgExtraStates &states) const override;
private:
QRectF m_bounds;
@@ -83,6 +85,7 @@ public:
QSvgLine(QSvgNode *parent, const QLineF &line);
void draw(QPainter *p, QSvgExtraStates &states) override;
Type type() const override;
+ QRectF fastBounds(QPainter *p, QSvgExtraStates &states) const override;
QRectF bounds(QPainter *p, QSvgExtraStates &states) const override;
private:
QLineF m_line;
@@ -94,6 +97,7 @@ public:
QSvgPath(QSvgNode *parent, const QPainterPath &qpath);
void draw(QPainter *p, QSvgExtraStates &states) override;
Type type() const override;
+ QRectF fastBounds(QPainter *p, QSvgExtraStates &states) const override;
QRectF bounds(QPainter *p, QSvgExtraStates &states) const override;
QPainterPath *qpath() {
@@ -109,6 +113,7 @@ public:
QSvgPolygon(QSvgNode *parent, const QPolygonF &poly);
void draw(QPainter *p, QSvgExtraStates &states) override;
Type type() const override;
+ QRectF fastBounds(QPainter *p, QSvgExtraStates &states) const override;
QRectF bounds(QPainter *p, QSvgExtraStates &states) const override;
private:
QPolygonF m_poly;
@@ -120,6 +125,7 @@ public:
QSvgPolyline(QSvgNode *parent, const QPolygonF &poly);
void draw(QPainter *p, QSvgExtraStates &states) override;
Type type() const override;
+ QRectF fastBounds(QPainter *p, QSvgExtraStates &states) const override;
QRectF bounds(QPainter *p, QSvgExtraStates &states) const override;
private:
QPolygonF m_poly;
@@ -131,6 +137,7 @@ public:
QSvgRect(QSvgNode *paren, const QRectF &rect, int rx=0, int ry=0);
Type type() const override;
void draw(QPainter *p, QSvgExtraStates &states) override;
+ QRectF fastBounds(QPainter *p, QSvgExtraStates &states) const override;
QRectF bounds(QPainter *p, QSvgExtraStates &states) const override;
private:
QRectF m_rect;
@@ -160,6 +167,7 @@ public:
void addLineBreak() {m_tspans.append(LINEBREAK);}
void setWhitespaceMode(WhitespaceMode mode) {m_mode = mode;}
+ QRectF fastBounds(QPainter *p, QSvgExtraStates &states) const override;
QRectF bounds(QPainter *p, QSvgExtraStates &states) const override;
private:
diff --git a/src/svg/qsvgnode.cpp b/src/svg/qsvgnode.cpp
index 62c118e..31e5338 100644
--- a/src/svg/qsvgnode.cpp
+++ b/src/svg/qsvgnode.cpp
@@ -4,11 +4,21 @@
#include "qsvgnode_p.h"
#include "qsvgtinydocument_p.h"
+#include <QLoggingCategory>
+
#include "qdebug.h"
#include "qstack.h"
+#include <QtGui/private/qoutlinemapper_p.h>
+
+#if !defined(QT_SVG_SIZE_LIMIT)
+# define QT_SVG_SIZE_LIMIT QT_RASTER_COORD_LIMIT
+#endif
+
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcSvgDraw)
+
QSvgNode::QSvgNode(QSvgNode *parent)
: m_parent(parent),
m_visible(true),
@@ -167,6 +177,11 @@ QSvgFillStyleProperty * QSvgNode::styleProperty(const QString &id) const
return doc ? doc->namedStyle(rid) : 0;
}
+QRectF QSvgNode::fastBounds(QPainter *p, QSvgExtraStates &states) const
+{
+ return bounds(p, states);
+}
+
QRectF QSvgNode::bounds(QPainter *, QSvgExtraStates &) const
{
return QRectF(0, 0, 0, 0);
@@ -311,4 +326,20 @@ qreal QSvgNode::strokeWidth(QPainter *p)
return pen.widthF();
}
+bool QSvgNode::shouldDrawNode(QPainter *p, QSvgExtraStates &states) const
+{
+ static bool alwaysDraw = qEnvironmentVariableIntValue("QT_SVG_DISABLE_SIZE_LIMIT");
+ if (alwaysDraw)
+ return true;
+
+ QRectF brect = fastBounds(p, states);
+ if (brect.width() <= QT_SVG_SIZE_LIMIT && brect.height() <= QT_SVG_SIZE_LIMIT) {
+ return true;
+ } else {
+ qCWarning(lcSvgDraw) << "Shape of type" << type() << "ignored because it will take too long to rasterize (bounding rect=" << brect << ")."
+ << "Set QT_SVG_DISABLE_SIZE_LIMIT=1 to disable this check.";
+ return false;
+ }
+}
+
QT_END_NAMESPACE
diff --git a/src/svg/qsvgnode_p.h b/src/svg/qsvgnode_p.h
index d8c1ccd..df9ef8a 100644
--- a/src/svg/qsvgnode_p.h
+++ b/src/svg/qsvgnode_p.h
@@ -88,6 +88,7 @@ public:
QSvgTinyDocument *document() const;
virtual Type type() const =0;
+ virtual QRectF fastBounds(QPainter *p, QSvgExtraStates &states) const;
virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const;
virtual QRectF transformedBounds(QPainter *p, QSvgExtraStates &states) const;
QRectF transformedBounds() const;
@@ -118,6 +119,8 @@ public:
QString xmlClass() const;
void setXmlClass(const QString &str);
+
+ bool shouldDrawNode(QPainter *p, QSvgExtraStates &states) const;
protected:
mutable QSvgStyle m_style;