summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorHatem ElKharashy <hatem.elkharashy@qt.io>2023-07-27 13:27:33 +0300
committerHatem ElKharashy <hatem.elkharashy@qt.io>2023-12-05 14:48:37 +0200
commit6afb54cdf09da8610ce500ff384f84399c3e84d4 (patch)
tree2decc9f4f159575db0b0a52153f0b0a6bcc939be /src
parent7b0b31e9a03575b16fc05b116f68b48fa853fb62 (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.cpp154
-rw-r--r--src/svg/qsvghelper_p.h14
-rw-r--r--src/svg/qsvgnode.cpp10
-rw-r--r--src/svg/qsvgstructure.cpp148
-rw-r--r--src/svg/qsvgstructure_p.h22
-rw-r--r--src/svg/qsvgstyle.cpp29
-rw-r--r--src/svg/qsvgstyle_p.h26
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;