summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEirik Aavitsland <eirik.aavitsland@qt.io>2017-02-13 12:38:10 +0100
committerEirik Aavitsland <eirik.aavitsland@qt.io>2017-02-14 13:56:52 +0000
commit914e25bf01e6264dd80b6f27e50b45a578a7fe89 (patch)
tree90b852d8ad4d9a36997c2a1e4e79ae0ff6de7596
parent8ea914c667e3d021d6618915e694a4d8bed5fe9f (diff)
downloadqtsvg-914e25bf01e6264dd80b6f27e50b45a578a7fe89.tar.gz
Add support for pattern brushes to svg generator
Pattern brushes was not implemented in the svg generator. Shapes drawn with such brushes would not be included in the svg file. [ChangeLog][][QSvgGenerator] Add support for pattern brushes Task-number: QTBUG-58148 Change-Id: Ib275661c596631fea64cb250c9743a529cd7b834 Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
-rw-r--r--src/svg/qsvggenerator.cpp64
-rw-r--r--tests/auto/qsvggenerator/tst_qsvggenerator.cpp42
2 files changed, 105 insertions, 1 deletions
diff --git a/src/svg/qsvggenerator.cpp b/src/svg/qsvggenerator.cpp
index 6af4370..de6e8d4 100644
--- a/src/svg/qsvggenerator.cpp
+++ b/src/svg/qsvggenerator.cpp
@@ -52,6 +52,7 @@
#include "qtextstream.h"
#include "qbuffer.h"
#include "qmath.h"
+#include "qbitmap.h"
#include "qdebug.h"
@@ -128,6 +129,9 @@ public:
QString currentGradientName;
int numGradients;
+ QStringList savedPatternBrushes;
+ QStringList savedPatternMasks;
+
struct _attributes {
QString document_title;
QString document_description;
@@ -145,12 +149,13 @@ static inline QPaintEngine::PaintEngineFeatures svgEngineFeatures()
{
return QPaintEngine::PaintEngineFeatures(
QPaintEngine::AllFeatures
- & ~QPaintEngine::PatternBrush
& ~QPaintEngine::PerspectiveTransform
& ~QPaintEngine::ConicalGradientFill
& ~QPaintEngine::PorterDuff);
}
+Q_GUI_EXPORT QImage qt_imageForBrush(int brushStyle, bool invert);
+
class QSvgPaintEngine : public QPaintEngine
{
Q_DECLARE_PRIVATE(QSvgPaintEngine)
@@ -212,6 +217,41 @@ public:
Q_ASSERT(!isActive());
d_func()->resolution = resolution;
}
+
+ QString savePatternMask(Qt::BrushStyle style)
+ {
+ QString maskId = QString(QStringLiteral("patternmask%1")).arg(style);
+ if (!d_func()->savedPatternMasks.contains(maskId)) {
+ QImage img = qt_imageForBrush(style, true);
+ QRegion reg(QBitmap::fromData(img.size(), img.constBits()));
+ QString rct(QStringLiteral("<rect x=\"%1\" y=\"%2\" width=\"%3\" height=\"%4\" />"));
+ QTextStream str(&d_func()->defs, QIODevice::Append);
+ str << "<mask id=\"" << maskId << "\" x=\"0\" y=\"0\" width=\"8\" height=\"8\" "
+ << "stroke=\"none\" fill=\"#ffffff\" patternUnits=\"userSpaceOnUse\" >" << endl;
+ for (QRect r : reg.rects()) {
+ str << rct.arg(r.x()).arg(r.y()).arg(r.width()).arg(r.height()) << endl;
+ }
+ str << QStringLiteral("</mask>") << endl << endl;
+ d_func()->savedPatternMasks.append(maskId);
+ }
+ return maskId;
+ }
+
+ QString savePatternBrush(const QString &color, const QBrush &brush)
+ {
+ QString patternId = QString(QStringLiteral("fillpattern%1_")).arg(brush.style()) + color.midRef(1);
+ if (!d_func()->savedPatternBrushes.contains(patternId)) {
+ QString maskId = savePatternMask(brush.style());
+ QString geo(QStringLiteral("x=\"0\" y=\"0\" width=\"8\" height=\"8\""));
+ QTextStream str(&d_func()->defs, QIODevice::Append);
+ str << QString(QStringLiteral("<pattern id=\"%1\" %2 patternUnits=\"userSpaceOnUse\" >")).arg(patternId, geo) << endl;
+ str << QString(QStringLiteral("<rect %1 stroke=\"none\" fill=\"%2\" mask=\"url(#%3);\" />")).arg(geo, color, maskId) << endl;
+ str << QStringLiteral("</pattern>") << endl << endl;
+ d_func()->savedPatternBrushes.append(patternId);
+ }
+ return patternId;
+ }
+
void saveLinearGradientBrush(const QGradient *g)
{
QTextStream str(&d_func()->defs, QIODevice::Append);
@@ -421,6 +461,28 @@ public:
d_func()->attributes.fillOpacity = colorOpacity;
}
break;
+ case Qt::Dense1Pattern:
+ case Qt::Dense2Pattern:
+ case Qt::Dense3Pattern:
+ case Qt::Dense4Pattern:
+ case Qt::Dense5Pattern:
+ case Qt::Dense6Pattern:
+ case Qt::Dense7Pattern:
+ case Qt::HorPattern:
+ case Qt::VerPattern:
+ case Qt::CrossPattern:
+ case Qt::BDiagPattern:
+ case Qt::FDiagPattern:
+ case Qt::DiagCrossPattern: {
+ QString color, colorOpacity;
+ translate_color(sbrush.color(), &color, &colorOpacity);
+ QString patternId = savePatternBrush(color, sbrush);
+ QString patternRef = QString(QStringLiteral("url(#%1)")).arg(patternId);
+ stream() << "fill=\"" << patternRef << "\" fill-opacity=\"" << colorOpacity << "\" ";
+ d_func()->attributes.fill = patternRef;
+ d_func()->attributes.fillOpacity = colorOpacity;
+ break;
+ }
case Qt::LinearGradientPattern:
saveLinearGradientBrush(sbrush.gradient());
d_func()->attributes.fill = QString::fromLatin1("url(#%1)").arg(d_func()->currentGradientName);
diff --git a/tests/auto/qsvggenerator/tst_qsvggenerator.cpp b/tests/auto/qsvggenerator/tst_qsvggenerator.cpp
index 4795b55..b55b687 100644
--- a/tests/auto/qsvggenerator/tst_qsvggenerator.cpp
+++ b/tests/auto/qsvggenerator/tst_qsvggenerator.cpp
@@ -58,6 +58,7 @@ private slots:
void fractionalFontSize();
void titleAndDescription();
void gradientInterpolation();
+ void patternBrush();
};
tst_QSvgGenerator::tst_QSvgGenerator()
@@ -423,5 +424,46 @@ void tst_QSvgGenerator::gradientInterpolation()
QVERIFY(sqrImageDiff(image, refImage) < 2); // pixel error < 1.41 (L2-norm)
}
+void tst_QSvgGenerator::patternBrush()
+{
+ { // Pattern brush should create mask and pattern used as fill
+ QSvgGenerator generator;
+ QByteArray byteArray;
+ QBuffer buffer(&byteArray);
+ generator.setOutputDevice(&buffer);
+ QPainter painter(&generator);
+ painter.setBrush(Qt::CrossPattern);
+ painter.drawRect(0, 0, 100, 100);
+ painter.end();
+
+ QVERIFY(byteArray.contains("<mask id=\"patternmask"));
+ QVERIFY(byteArray.contains("<pattern id=\"fillpattern"));
+ QVERIFY(byteArray.contains("<g fill=\"url(#fillpattern"));
+ }
+
+ { // Masks and patterns should be reused, not regenerated
+ QSvgGenerator generator;
+ QByteArray byteArray;
+ QBuffer buffer(&byteArray);
+ generator.setOutputDevice(&buffer);
+ QPainter painter(&generator);
+ painter.setBrush(QBrush(Qt::red, Qt::Dense3Pattern));
+ painter.drawRect(0, 0, 100, 100);
+ painter.drawEllipse(200, 50, 50, 50);
+ painter.setBrush(QBrush(Qt::green, Qt::Dense3Pattern));
+ painter.drawRoundedRect(0, 200, 100, 100, 10, 10);
+ painter.setBrush(QBrush(Qt::blue, Qt::Dense4Pattern));
+ painter.drawRect(200, 200, 100, 100);
+ painter.setBrush(QBrush(Qt::red, Qt::Dense3Pattern));
+ painter.drawRoundedRect(120, 120, 60, 60, 5, 5);
+ painter.end();
+
+ QCOMPARE(byteArray.count("<mask id=\"patternmask"), 2);
+ QCOMPARE(byteArray.count("<pattern id=\"fillpattern"), 3);
+ QVERIFY(byteArray.count("<g fill=\"url(#fillpattern") >= 4);
+ }
+
+}
+
QTEST_MAIN(tst_QSvgGenerator)
#include "tst_qsvggenerator.moc"