diff options
author | Hatem ElKharashy <hatem.elkharashy@qt.io> | 2023-07-27 13:27:33 +0300 |
---|---|---|
committer | Hatem ElKharashy <hatem.elkharashy@qt.io> | 2023-12-05 14:48:37 +0200 |
commit | 6afb54cdf09da8610ce500ff384f84399c3e84d4 (patch) | |
tree | 2decc9f4f159575db0b0a52153f0b0a6bcc939be /src | |
parent | 7b0b31e9a03575b16fc05b116f68b48fa853fb62 (diff) |
Add Pattern element to QSvg
Added the <pattern> element to QtSvg.
Task-number: QTBUG-115544
Change-Id: I5b15bc41ba95cc77558ce61f27df8aed958b1289
Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
Diffstat (limited to 'src')
-rw-r--r-- | src/svg/qsvghandler.cpp | 154 | ||||
-rw-r--r-- | src/svg/qsvghelper_p.h | 14 | ||||
-rw-r--r-- | src/svg/qsvgnode.cpp | 10 | ||||
-rw-r--r-- | src/svg/qsvgstructure.cpp | 148 | ||||
-rw-r--r-- | src/svg/qsvgstructure_p.h | 22 | ||||
-rw-r--r-- | src/svg/qsvgstyle.cpp | 29 | ||||
-rw-r--r-- | src/svg/qsvgstyle_p.h | 26 |
7 files changed, 391 insertions, 12 deletions
diff --git a/src/svg/qsvghandler.cpp b/src/svg/qsvghandler.cpp index f92be2c..f6cee4c 100644 --- a/src/svg/qsvghandler.cpp +++ b/src/svg/qsvghandler.cpp @@ -1042,7 +1042,8 @@ static void parseBrush(QSvgNode *node, QString value = attributes.fill.mid(3, attributes.fill.size() - 3).toString(); QSvgStyleProperty *style = styleFromUrl(node, value); if (style) { - if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT) + if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT + || style->type() == QSvgStyleProperty::PATTERN) prop->setFillStyle(reinterpret_cast<QSvgPaintStyleProperty *>(style)); } else { QString id = idFromUrl(value); @@ -1211,8 +1212,9 @@ static void parsePen(QSvgNode *node, QString value = attributes.stroke.mid(3, attributes.stroke.size() - 3).toString(); QSvgStyleProperty *style = styleFromUrl(node, value); if (style) { - if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT) - prop->setStyle(reinterpret_cast<QSvgPaintStyleProperty *>(style)); + if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT + || style->type() == QSvgStyleProperty::PATTERN) + prop->setStyle(reinterpret_cast<QSvgPaintStyleProperty *>(style)); } else { QString id = idFromUrl(value); prop->setPaintStyleId(id); @@ -3668,6 +3670,108 @@ static QSvgNode *createSwitchNode(QSvgNode *parent, return node; } +static QSvgNode *createPatternNode(QSvgNode *parent, + const QXmlStreamAttributes &attributes, + QSvgHandler *handler) +{ + const QStringView x = attributes.value(QLatin1String("x")); + const QStringView y = attributes.value(QLatin1String("y")); + const QStringView width = attributes.value(QLatin1String("width")); + const QStringView height = attributes.value(QLatin1String("height")); + const QStringView patternUnits = attributes.value(QLatin1String("patternUnits")); + const QStringView patternContentUnits = attributes.value(QLatin1String("patternContentUnits")); + const QStringView patternTransform = attributes.value(QLatin1String("patternTransform")); + + QSvg::UnitTypes nPatternUnits = patternUnits.contains(QLatin1String("userSpaceOnUse")) ? + QSvg::UnitTypes::userSpaceOnUse : QSvg::UnitTypes::objectBoundingBox; + + QSvg::UnitTypes nPatternContentUnits = patternContentUnits.contains(QLatin1String("objectBoundingBox")) ? + QSvg::UnitTypes::objectBoundingBox : QSvg::UnitTypes::userSpaceOnUse; + + QString viewBoxStr = attributes.value(QLatin1String("viewBox")).toString(); + + bool ok = false; + QSvgHandler::LengthType type; + + qreal nx = parseLength(x.toString(), &type, handler, &ok); + nx = convertToPixels(nx, true, type); + if (!ok) + nx = 0.0; + else if (type == QSvgHandler::LT_PERCENT && nPatternUnits == QSvg::UnitTypes::userSpaceOnUse) + nx = (nx / 100.) * handler->document()->viewBox().width(); + else if (type == QSvgHandler::LT_PERCENT) + nx = nx / 100.; + + qreal ny = parseLength(y.toString(), &type, handler, &ok); + ny = convertToPixels(ny, true, type); + if (!ok) + ny = 0.0; + else if (type == QSvgHandler::LT_PERCENT && nPatternUnits == QSvg::UnitTypes::userSpaceOnUse) + ny = (ny / 100.) * handler->document()->viewBox().height(); + else if (type == QSvgHandler::LT_PERCENT) + ny = ny / 100.; + + qreal nwidth = parseLength(width.toString(), &type, handler, &ok); + nwidth = convertToPixels(nwidth, true, type); + if (!ok) + nwidth = 0.0; + else if (type == QSvgHandler::LT_PERCENT && nPatternUnits == QSvg::UnitTypes::userSpaceOnUse) + nwidth = (nwidth / 100.) * handler->document()->viewBox().width(); + else if (type == QSvgHandler::LT_PERCENT) + nwidth = nwidth / 100.; + + qreal nheight = parseLength(height.toString(), &type, handler, &ok); + nheight = convertToPixels(nheight, true, type); + if (!ok) + nheight = 0.0; + else if (type == QSvgHandler::LT_PERCENT && nPatternUnits == QSvg::UnitTypes::userSpaceOnUse) + nheight = (nheight / 100.) * handler->document()->viewBox().height(); + else if (type == QSvgHandler::LT_PERCENT) + nheight = nheight / 100.; + + + QStringList viewBoxValues; + QRectF viewBox; + if (!viewBoxStr.isEmpty()) { + viewBoxStr = viewBoxStr.replace(QLatin1Char(' '), QLatin1Char(',')); + viewBoxStr = viewBoxStr.replace(QLatin1Char('\r'), QLatin1Char(',')); + viewBoxStr = viewBoxStr.replace(QLatin1Char('\n'), QLatin1Char(',')); + viewBoxStr = viewBoxStr.replace(QLatin1Char('\t'), QLatin1Char(',')); + viewBoxValues = viewBoxStr.split(QLatin1Char(','), Qt::SkipEmptyParts); + } + if (viewBoxValues.size() == 4) { + QString xStr = viewBoxValues.at(0).trimmed(); + QString yStr = viewBoxValues.at(1).trimmed(); + QString widthStr = viewBoxValues.at(2).trimmed(); + QString heightStr = viewBoxValues.at(3).trimmed(); + + qreal x = convertToNumber(xStr, handler); + qreal y = convertToNumber(yStr, handler); + qreal w = convertToNumber(widthStr, handler); + qreal h = convertToNumber(heightStr, handler); + + if (w > 0 && h > 0) + viewBox.setRect(x, y, w, h); + } + + QTransform matrix; + if (!patternTransform.isEmpty()) + matrix = parseTransformationMatrix(patternTransform); + + QRectF bounds(nx, ny, nwidth, nheight); + if (bounds.isEmpty()) + return nullptr; + + QSvgRectF patternRectF(bounds, nPatternUnits, nPatternUnits, nPatternUnits, nPatternUnits); + QSvgPattern *node = new QSvgPattern(parent, patternRectF, viewBox, nPatternContentUnits, matrix); + + // Create a style node for the Pattern. + QSvgPatternStyle *prop = new QSvgPatternStyle(node); + node->appendStyleProperty(prop, someId(attributes)); + + return node; +} + static bool parseTbreakNode(QSvgNode *parent, const QXmlStreamAttributes &, QSvgHandler *) @@ -3807,6 +3911,9 @@ static FactoryMethod findGroupFactory(const QString &name, QSvg::FeatureSet feat if (ref == QLatin1String("witch")) return createSwitchNode; if (ref == QLatin1String("ymbol")) return createSymbolNode; break; + case 'p': + if (ref == QLatin1String("attern") && featureSet != QSvg::FeatureSet::StaticTiny1_2) return createPatternNode; + break; default: break; } @@ -4010,14 +4117,40 @@ void QSvgHandler::init() parse(); } -static bool detectCycles(const QSvgNode *node, QList<const QSvgUse *> active = {}) +static bool detectPatternCycles(const QSvgNode *node, QList<const QSvgNode *> active = {}) +{ + QSvgFillStyle *fillStyle = static_cast<QSvgFillStyle*> + (node->styleProperty(QSvgStyleProperty::FILL)); + if (fillStyle && fillStyle->style() && fillStyle->style()->type() == QSvgStyleProperty::PATTERN) { + QSvgPatternStyle *patternStyle = static_cast<QSvgPatternStyle *>(fillStyle->style()); + if (active.contains(patternStyle->patternNode())) + return true; + } + + QSvgStrokeStyle *strokeStyle = static_cast<QSvgStrokeStyle*> + (node->styleProperty(QSvgStyleProperty::STROKE)); + if (strokeStyle && strokeStyle->style() && strokeStyle->style()->type() == QSvgStyleProperty::PATTERN) { + QSvgPatternStyle *patternStyle = static_cast<QSvgPatternStyle *>(strokeStyle->style()); + if (active.contains(patternStyle->patternNode())) + return true; + } + + return false; +} + +static bool detectCycles(const QSvgNode *node, QList<const QSvgNode *> active = {}) { if (Q_UNLIKELY(!node)) return false; switch (node->type()) { case QSvgNode::Doc: case QSvgNode::Group: + case QSvgNode::Defs: + case QSvgNode::Pattern: { + if (node->type() == QSvgNode::Pattern) + active.append(node); + auto *g = static_cast<const QSvgStructureNode*>(node); for (auto *r : g->renderers()) { if (detectCycles(r, active)) @@ -4039,6 +4172,17 @@ static bool detectCycles(const QSvgNode *node, QList<const QSvgUse *> active = { } } break; + case QSvgNode::Rect: + case QSvgNode::Ellipse: + case QSvgNode::Circle: + case QSvgNode::Line: + case QSvgNode::Path: + case QSvgNode::Polygon: + case QSvgNode::Polyline: + case QSvgNode::Tspan: + if (detectPatternCycles(node, active)) + return true; + break; default: break; } @@ -4148,6 +4292,7 @@ bool QSvgHandler::startElement(const QString &localName, case QSvgNode::Mask: case QSvgNode::Symbol: case QSvgNode::Marker: + case QSvgNode::Pattern: { QSvgStructureNode *group = static_cast<QSvgStructureNode*>(m_nodes.top()); @@ -4184,6 +4329,7 @@ bool QSvgHandler::startElement(const QString &localName, case QSvgNode::Mask: case QSvgNode::Symbol: case QSvgNode::Marker: + case QSvgNode::Pattern: { if (node->type() == QSvgNode::Tspan) { const QByteArray msg = QByteArrayLiteral("\'tspan\' element in wrong context."); diff --git a/src/svg/qsvghelper_p.h b/src/svg/qsvghelper_p.h index c06cfee..ea314b2 100644 --- a/src/svg/qsvghelper_p.h +++ b/src/svg/qsvghelper_p.h @@ -78,6 +78,20 @@ public: return result; } + QPointF translationRelativeToBoundingBox(const QRectF &boundingBox) const { + QPointF result; + + if (m_unitX == QSvg::UnitTypes::objectBoundingBox) + result.setX(x() * boundingBox.width()); + else + result.setX(x()); + if (m_unitY == QSvg::UnitTypes::objectBoundingBox) + result.setY(y() * boundingBox.height()); + else + result.setY(y()); + return result; + } + QSvg::UnitTypes unitX() const {return m_unitX;} QSvg::UnitTypes unitY() const {return m_unitY;} QSvg::UnitTypes unitW() const {return m_unitW;} diff --git a/src/svg/qsvgnode.cpp b/src/svg/qsvgnode.cpp index 85ccf1a..56df53d 100644 --- a/src/svg/qsvgnode.cpp +++ b/src/svg/qsvgnode.cpp @@ -183,6 +183,12 @@ void QSvgNode::appendStyleProperty(QSvgStyleProperty *prop, const QString &id) if (doc && !id.isEmpty()) doc->addNamedStyle(id, m_style.gradient); break; + case QSvgStyleProperty::PATTERN: + m_style.pattern = static_cast<QSvgPatternStyle*>(prop); + doc = document(); + if (doc && !id.isEmpty()) + doc->addNamedStyle(id, m_style.pattern); + break; case QSvgStyleProperty::TRANSFORM: m_style.transform = static_cast<QSvgTransformStyle*>(prop); break; @@ -248,6 +254,10 @@ QSvgStyleProperty * QSvgNode::styleProperty(QSvgStyleProperty::Type type) const if (node->m_style.gradient) return node->m_style.gradient; break; + case QSvgStyleProperty::PATTERN: + if (node->m_style.pattern) + return node->m_style.pattern; + break; case QSvgStyleProperty::TRANSFORM: if (node->m_style.transform) return node->m_style.transform; diff --git a/src/svg/qsvgstructure.cpp b/src/svg/qsvgstructure.cpp index fd3958f..3c7a1e2 100644 --- a/src/svg/qsvgstructure.cpp +++ b/src/svg/qsvgstructure.cpp @@ -14,6 +14,8 @@ #include <QLoggingCategory> #include <qscopedvaluerollback.h> +#include <QtGui/qimageiohandler.h> +#include <QLoggingCategory> QT_BEGIN_NAMESPACE @@ -729,4 +731,150 @@ QSvgNode::Type QSvgMask::type() const return Mask; } +QSvgPattern::QSvgPattern(QSvgNode *parent, QSvgRectF bounds, QRectF viewBox, + QSvg::UnitTypes contentUnits, QTransform transform) + : QSvgStructureNode(parent), + m_rect(bounds), + m_viewBox(viewBox), + m_contentUnits(contentUnits), + m_transform(transform) + +{ + +} + +static QImage& defaultPattern() +{ + static QImage checkerPattern; + + if (checkerPattern.isNull()) { + checkerPattern = QImage(QSize(8, 8), QImage::Format_ARGB32); + QPainter p(&checkerPattern); + p.fillRect(QRect(0, 0, 4, 4), QColorConstants::Svg::white); + p.fillRect(QRect(4, 0, 4, 4), QColorConstants::Svg::black); + p.fillRect(QRect(0, 4, 4, 4), QColorConstants::Svg::black); + p.fillRect(QRect(4, 4, 4, 4), QColorConstants::Svg::white); + } + + return checkerPattern; +} + +QImage QSvgPattern::patternImage(QPainter *p, QSvgExtraStates &states, const QSvgNode *patternElement) +{ + // pe stands for Pattern Element + QRectF peBoundingBox; + QRectF peWorldBoundingBox; + + QTransform t = p->transform(); + p->resetTransform(); + peBoundingBox = patternElement->bounds(p, states); + peWorldBoundingBox = t.mapRect(peBoundingBox); + p->setTransform(t); + + // This function renders the pattern into an Image, so we need to apply the correct + // scaling values when we draw the pattern. The scaling is affected by two factors : + // - The "patternTransform" attribute which itself might contain a scaling + // - The scaling applied globally. + // The first is obtained from m11 and m22 matrix elements, + // while the second is calculated by dividing the patternElement global size + // by its local size. + qreal contentScaleFactorX = m_transform.m11(); + qreal contentScaleFactorY = m_transform.m22(); + if (m_contentUnits == QSvg::UnitTypes::userSpaceOnUse) { + contentScaleFactorX *= t.m11(); + contentScaleFactorY *= t.m22(); + } else { + contentScaleFactorX *= peWorldBoundingBox.width(); + contentScaleFactorY *= peWorldBoundingBox.height(); + } + + // Calculate the pattern bounding box depending on the used UnitTypes + QRectF patternBoundingBox = m_rect.combineWithLocalRect(peBoundingBox); + + QSize imageSize; + imageSize.setWidth(qCeil(patternBoundingBox.width() * t.m11() * m_transform.m11())); + imageSize.setHeight(qCeil(patternBoundingBox.height() * t.m22() * m_transform.m22())); + + calculateAppliedTransform(t, peBoundingBox, imageSize); + return renderPattern(p, imageSize, contentScaleFactorX, contentScaleFactorY); +} + +QSvgNode::Type QSvgPattern::type() const +{ + return Pattern; +} + +QImage QSvgPattern::renderPattern(QPainter *p, QSize size, qreal contentScaleX, qreal contentScaleY) +{ + if (size.isEmpty() || !qIsFinite(contentScaleX) || !qIsFinite(contentScaleY)) + return defaultPattern(); + + // Allocate a QImage to draw the pattern in with the calculated size. + QImage pattern; + if (!QImageIOHandler::allocateImage(size, QImage::Format_ARGB32, &pattern)) { + qCWarning(lcSvgDraw) << "The requested pattern size is too big, ignoring"; + return defaultPattern(); + } + pattern.fill(Qt::transparent); + + // Draw the pattern using our QPainter. + QPainter patternPainter(&pattern); + patternPainter.setRenderHints(p->renderHints()); + QPen pen(Qt::NoBrush, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin); + pen.setMiterLimit(4); + patternPainter.setPen(pen); + patternPainter.setBrush(Qt::black); + + // According to the <pattern> definition, if viewBox exists then patternContentUnits + // is ignored + if (m_viewBox.isNull()) + patternPainter.scale(contentScaleX, contentScaleY); + else + patternPainter.setWindow(m_viewBox.toRect()); + + // Draw all this Pattern children nodes with our QPainter, + // no need to use any Extra States + QSvgExtraStates states2; + for (QSvgNode *node : m_renderers) { + node->draw(&patternPainter, states2); + } + + return pattern; +} + +void QSvgPattern::calculateAppliedTransform(QTransform &worldTransform, QRectF peLocalBB, QSize imageSize) +{ + // Calculate the required transform to be applied to the QBrush used for correct + // pattern drawing with the object being rendered. + // Scale : Apply inverse the scale used above because QBrush uses the transform used + // by the QPainter and this function has already rendered the QImage with the + // correct size. Moreover, take into account the difference between the required + // ideal image size in float and the QSize given to image as an integer value. + // + // Translate : Apply translation depending on the calculated x and y values so that the + // drawn pattern can be shifted inside the object. + // Pattern Transform : Apply the transform in the "patternTransform" attribute. This + // transform contains everything except scaling, because it is + // already applied above on the QImage and the QPainter while + // drawing the pattern tile. + m_appliedTransform.reset(); + qreal imageDownScaleFactorX = 1 / worldTransform.m11(); + qreal imageDownScaleFactorY = 1 / worldTransform.m22(); + + m_appliedTransform.scale(qIsFinite(imageDownScaleFactorX) ? imageDownScaleFactorX : 1.0, + qIsFinite(imageDownScaleFactorY) ? imageDownScaleFactorY : 1.0); + + QRectF p = m_rect.combineWithLocalRect(peLocalBB); + m_appliedTransform.scale((p.width() * worldTransform.m11() * m_transform.m11()) / imageSize.width(), + (p.height() * worldTransform.m22() * m_transform.m22()) / imageSize.height()); + + QPointF translation = m_rect.translationRelativeToBoundingBox(peLocalBB); + m_appliedTransform.translate(translation.x() * worldTransform.m11(), translation.y() * worldTransform.m22()); + + QTransform scalelessTransform = m_transform; + scalelessTransform.scale(1 / m_transform.m11(), 1 / m_transform.m22()); + + m_appliedTransform = m_appliedTransform * scalelessTransform; +} + QT_END_NAMESPACE diff --git a/src/svg/qsvgstructure_p.h b/src/svg/qsvgstructure_p.h index a0cc107..9b4d110 100644 --- a/src/svg/qsvgstructure_p.h +++ b/src/svg/qsvgstructure_p.h @@ -184,6 +184,28 @@ private: QSvg::UnitTypes m_contentUnits; }; +class Q_SVG_PRIVATE_EXPORT QSvgPattern : public QSvgStructureNode +{ +public: + QSvgPattern(QSvgNode *parent, QSvgRectF bounds, QRectF viewBox, + QSvg::UnitTypes contentUnits, QTransform transform); + void drawCommand(QPainter *, QSvgExtraStates &) override {}; + QImage patternImage(QPainter *p, QSvgExtraStates &states, const QSvgNode *patternElement); + Type type() const override; + const QTransform& appliedTransform() const { return m_appliedTransform; } + +private: + QImage renderPattern(QPainter *p, QSize size, qreal contentScaleX, qreal contentScaleY); + void calculateAppliedTransform(QTransform& worldTransform, QRectF peLocalBB, QSize imageSize); + +private: + QTransform m_appliedTransform; + QSvgRectF m_rect; + QRectF m_viewBox; + QSvg::UnitTypes m_contentUnits; + QTransform m_transform; +}; + QT_END_NAMESPACE #endif // QSVGSTRUCTURE_P_H diff --git a/src/svg/qsvgstyle.cpp b/src/svg/qsvgstyle.cpp index a92ebac..88cfcc6 100644 --- a/src/svg/qsvgstyle.cpp +++ b/src/svg/qsvgstyle.cpp @@ -126,7 +126,7 @@ void QSvgFillStyle::setBrush(QBrush brush) m_fillSet = 1; } -void QSvgFillStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &states) +void QSvgFillStyle::apply(QPainter *p, const QSvgNode *n, QSvgExtraStates &states) { m_oldFill = p->brush(); m_oldFillRule = states.fillRule; @@ -136,7 +136,7 @@ void QSvgFillStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &states states.fillRule = m_fillRule; if (m_fillSet) { if (m_style) - p->setBrush(m_style->brush(p, states)); + p->setBrush(m_style->brush(p, n, states)); else p->setBrush(m_fill); } @@ -264,7 +264,7 @@ QSvgStrokeStyle::QSvgStrokeStyle() { } -void QSvgStrokeStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &states) +void QSvgStrokeStyle::apply(QPainter *p, const QSvgNode *n, QSvgExtraStates &states) { m_oldStroke = p->pen(); m_oldStrokeOpacity = states.strokeOpacity; @@ -289,7 +289,7 @@ void QSvgStrokeStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &stat if (m_strokeSet) { if (m_style) - pen.setBrush(m_style->brush(p, states)); + pen.setBrush(m_style->brush(p, n, states)); else pen.setBrush(m_stroke.brush()); } @@ -384,7 +384,7 @@ QSvgGradientStyle::QSvgGradientStyle(QGradient *grad) { } -QBrush QSvgGradientStyle::brush(QPainter *, QSvgExtraStates &) +QBrush QSvgGradientStyle::brush(QPainter *, const QSvgNode *, QSvgExtraStates &) { if (!m_link.isEmpty()) { resolveStops(); @@ -410,6 +410,20 @@ void QSvgGradientStyle::setTransform(const QTransform &transform) m_transform = transform; } +QSvgPatternStyle::QSvgPatternStyle(QSvgPattern *pattern) + : m_pattern(pattern) +{ + +} + +QBrush QSvgPatternStyle::brush(QPainter *p, const QSvgNode *node, QSvgExtraStates &states) +{ + m_patternImage = m_pattern->patternImage(p, states, node); + QBrush b(m_patternImage); + b.setTransform(m_pattern->appliedTransform()); + return b; +} + QSvgTransformStyle::QSvgTransformStyle(const QTransform &trans) : m_transform(trans) { @@ -461,6 +475,11 @@ QSvgStyleProperty::Type QSvgGradientStyle::type() const return GRADIENT; } +QSvgStyleProperty::Type QSvgPatternStyle::type() const +{ + return PATTERN; +} + QSvgStyleProperty::Type QSvgTransformStyle::type() const { return TRANSFORM; diff --git a/src/svg/qsvgstyle_p.h b/src/svg/qsvgstyle_p.h index 3ce2946..4ae59bb 100644 --- a/src/svg/qsvgstyle_p.h +++ b/src/svg/qsvgstyle_p.h @@ -30,6 +30,7 @@ class QPainter; class QSvgNode; class QSvgFont; class QSvgTinyDocument; +class QSvgPattern; template <class T> class QSvgRefCounter { @@ -130,6 +131,7 @@ public: STROKE, SOLID_COLOR, GRADIENT, + PATTERN, TRANSFORM, ANIMATE_TRANSFORM, ANIMATE_COLOR, @@ -147,7 +149,7 @@ public: class Q_SVG_PRIVATE_EXPORT QSvgPaintStyleProperty : public QSvgStyleProperty { public: - virtual QBrush brush(QPainter *p, QSvgExtraStates &states) = 0; + virtual QBrush brush(QPainter *p, const QSvgNode *node, QSvgExtraStates &states) = 0; void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states) override; void revert(QPainter *p, QSvgExtraStates &states) override; }; @@ -535,7 +537,7 @@ public: return m_solidColor; } - QBrush brush(QPainter *, QSvgExtraStates &) override + QBrush brush(QPainter *, const QSvgNode *, QSvgExtraStates &) override { return m_solidColor; } @@ -582,7 +584,7 @@ public: m_gradientStopsSet = set; } - QBrush brush(QPainter *, QSvgExtraStates &) override; + QBrush brush(QPainter *, const QSvgNode *, QSvgExtraStates &) override; private: QGradient *m_gradient; QTransform m_transform; @@ -592,6 +594,22 @@ private: bool m_gradientStopsSet; }; +class Q_SVG_PRIVATE_EXPORT QSvgPatternStyle : public QSvgPaintStyleProperty +{ +public: + QSvgPatternStyle(QSvgPattern *pattern); + ~QSvgPatternStyle() = default; + Type type() const override; + + QBrush brush(QPainter *, const QSvgNode *, QSvgExtraStates &) override; + QSvgPattern *patternNode() { return m_pattern; } +private: + QSvgPattern *m_pattern; + QImage m_patternImage; + QRectF m_parentBound; +}; + + class Q_SVG_PRIVATE_EXPORT QSvgTransformStyle : public QSvgStyleProperty { public: @@ -739,6 +757,7 @@ public: stroke(0), solidColor(0), gradient(0), + pattern(0), transform(0), animateColor(0), opacity(0), @@ -755,6 +774,7 @@ public: QSvgRefCounter<QSvgStrokeStyle> stroke; QSvgRefCounter<QSvgSolidColorStyle> solidColor; QSvgRefCounter<QSvgGradientStyle> gradient; + QSvgRefCounter<QSvgPatternStyle> pattern; QSvgRefCounter<QSvgTransformStyle> transform; QSvgRefCounter<QSvgAnimateColor> animateColor; QList<QSvgRefCounter<QSvgAnimateTransform> > animateTransforms; |