Skip to content

Commit 0648be5

Browse files
author
mhugent
committed
[FEATURE]: SVG fill symbol layer for polygon textures
git-svn-id: http://svn.osgeo.org/qgis/trunk/qgis@12777 c8812cc2-4d05-0410-92ff-de0c093fc19c
1 parent b5cf5b5 commit 0648be5

9 files changed

+534
-26
lines changed

src/core/symbology-ng/qgsfillsymbollayerv2.cpp

+210-16
Original file line numberDiff line numberDiff line change
@@ -64,22 +64,7 @@ void QgsSimpleFillSymbolLayerV2::renderPolygon( const QPolygonF& points, QList<Q
6464
p->setBrush( mBrush );
6565
p->setPen( mPen );
6666

67-
if ( rings == NULL )
68-
{
69-
// simple polygon without holes
70-
p->drawPolygon( points );
71-
}
72-
else
73-
{
74-
// polygon with holes must be drawn using painter path
75-
QPainterPath path;
76-
path.addPolygon( points );
77-
QList<QPolygonF>::iterator it;
78-
for ( it = rings->begin(); it != rings->end(); ++it )
79-
path.addPolygon( *it );
80-
81-
p->drawPath( path );
82-
}
67+
_renderPolygon( p, points, rings );
8368
}
8469

8570
QgsStringMap QgsSimpleFillSymbolLayerV2::properties() const
@@ -97,3 +82,212 @@ QgsSymbolLayerV2* QgsSimpleFillSymbolLayerV2::clone() const
9782
{
9883
return new QgsSimpleFillSymbolLayerV2( mColor, mBrushStyle, mBorderColor, mBorderStyle, mBorderWidth );
9984
}
85+
86+
//QgsSVGFillSymbolLayer
87+
#include <QFile>
88+
#include <QSvgRenderer>
89+
90+
QgsSVGFillSymbolLayer::QgsSVGFillSymbolLayer( const QString& svgFilePath, double width ): mPatternWidth( width ), mOutline( 0 )
91+
{
92+
setSvgFilePath( svgFilePath );
93+
mOutlineWidth = 0.3;
94+
setSubSymbol( new QgsLineSymbolV2() );
95+
}
96+
97+
QgsSVGFillSymbolLayer::QgsSVGFillSymbolLayer( const QByteArray& svgData, double width ): mSvgData( svgData ), mPatternWidth( width ), mOutline( 0 )
98+
{
99+
storeViewBox();
100+
mOutlineWidth = 0.3;
101+
setSubSymbol( new QgsLineSymbolV2() );
102+
}
103+
104+
QgsSVGFillSymbolLayer::~QgsSVGFillSymbolLayer()
105+
{
106+
delete mOutline;
107+
}
108+
109+
void QgsSVGFillSymbolLayer::setSvgFilePath( const QString& svgPath )
110+
{
111+
QFile svgFile( svgPath );
112+
if ( svgFile.open( QFile::ReadOnly ) )
113+
{
114+
mSvgData = svgFile.readAll();
115+
storeViewBox();
116+
}
117+
mSvgFilePath = svgPath;
118+
}
119+
120+
QgsSymbolLayerV2* QgsSVGFillSymbolLayer::create( const QgsStringMap& properties )
121+
{
122+
QByteArray data;
123+
double width = 20;
124+
QString svgFilePath;
125+
126+
127+
if ( properties.contains( "width" ) )
128+
{
129+
width = properties["width"].toDouble();
130+
}
131+
if ( properties.contains( "svgFile" ) )
132+
{
133+
svgFilePath = properties["svgFile"];
134+
}
135+
136+
if ( !svgFilePath.isEmpty() )
137+
{
138+
return new QgsSVGFillSymbolLayer( svgFilePath, width );
139+
}
140+
else
141+
{
142+
if ( properties.contains( "data" ) )
143+
{
144+
data = QByteArray::fromHex( properties["data"].toLocal8Bit() );
145+
}
146+
147+
return new QgsSVGFillSymbolLayer( data, width );
148+
}
149+
}
150+
151+
QString QgsSVGFillSymbolLayer::layerType() const
152+
{
153+
return "SVGFill";
154+
}
155+
156+
void QgsSVGFillSymbolLayer::startRender( QgsSymbolV2RenderContext& context )
157+
{
158+
if ( mSvgViewBox.isNull() )
159+
{
160+
return;
161+
}
162+
163+
//create QImage with appropriate dimensions
164+
int pixelWidth = context.outputPixelSize( mPatternWidth );//mPatternWidth.value( context, QgsOutputUnit::Pixel );
165+
int pixelHeight = pixelWidth / mSvgViewBox.width() * mSvgViewBox.height();
166+
167+
QImage textureImage( pixelWidth, pixelHeight, QImage::Format_ARGB32_Premultiplied );
168+
textureImage.fill( QColor( 255, 255, 255, 0 ).rgba() );
169+
170+
//rasterise byte array to image
171+
QPainter p( &textureImage );
172+
QSvgRenderer r( mSvgData );
173+
if ( !r.isValid() )
174+
{
175+
return;
176+
}
177+
r.render( &p );
178+
179+
QTransform brushTransform;
180+
brushTransform.scale( 1.0 / context.renderContext().rasterScaleFactor(), 1.0 / context.renderContext().rasterScaleFactor() );
181+
mBrush.setTextureImage( textureImage );
182+
mBrush.setTransform( brushTransform );
183+
184+
if ( mOutline )
185+
{
186+
mOutline->startRender( context.renderContext() );
187+
}
188+
}
189+
190+
void QgsSVGFillSymbolLayer::stopRender( QgsSymbolV2RenderContext& context )
191+
{
192+
if ( mOutline )
193+
{
194+
mOutline->stopRender( context.renderContext() );
195+
}
196+
}
197+
198+
void QgsSVGFillSymbolLayer::renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
199+
{
200+
QPainter* p = context.renderContext().painter();
201+
if ( !p )
202+
{
203+
return;
204+
}
205+
p->setBrush( mBrush );
206+
p->setPen( QPen( Qt::NoPen ) );
207+
_renderPolygon( p, points, rings );
208+
if ( mOutline )
209+
{
210+
mOutline->renderPolyline( points, context.renderContext() );
211+
if ( rings )
212+
{
213+
QList<QPolygonF>::const_iterator ringIt = rings->constBegin();
214+
for ( ; ringIt != rings->constEnd(); ++ringIt )
215+
{
216+
mOutline->renderPolyline( *ringIt, context.renderContext() );
217+
}
218+
}
219+
}
220+
}
221+
222+
QgsStringMap QgsSVGFillSymbolLayer::properties() const
223+
{
224+
QgsStringMap map;
225+
if ( !mSvgFilePath.isEmpty() )
226+
{
227+
map.insert( "svgFile", mSvgFilePath );
228+
}
229+
else
230+
{
231+
map.insert( "data", QString( mSvgData.toHex() ) );
232+
}
233+
234+
map.insert( "width", QString::number( mPatternWidth ) );
235+
return map;
236+
}
237+
238+
QgsSymbolLayerV2* QgsSVGFillSymbolLayer::clone() const
239+
{
240+
QgsSymbolLayerV2* clonedLayer = 0;
241+
if ( !mSvgFilePath.isEmpty() )
242+
{
243+
clonedLayer = new QgsSVGFillSymbolLayer( mSvgFilePath, mPatternWidth );
244+
}
245+
else
246+
{
247+
clonedLayer = new QgsSVGFillSymbolLayer( mSvgData, mPatternWidth );
248+
}
249+
250+
if ( mOutline )
251+
{
252+
clonedLayer->setSubSymbol( mOutline->clone() );
253+
}
254+
return clonedLayer;
255+
}
256+
257+
void QgsSVGFillSymbolLayer::storeViewBox()
258+
{
259+
if ( !mSvgData.isEmpty() )
260+
{
261+
QSvgRenderer r( mSvgData );
262+
if ( r.isValid() )
263+
{
264+
mSvgViewBox = r.viewBoxF();
265+
return;
266+
}
267+
}
268+
269+
mSvgViewBox = QRectF();
270+
return;
271+
}
272+
273+
bool QgsSVGFillSymbolLayer::setSubSymbol( QgsSymbolV2* symbol )
274+
{
275+
276+
if ( !symbol || symbol->type() != QgsSymbolV2::Line )
277+
{
278+
delete symbol;
279+
return false;
280+
}
281+
282+
283+
QgsLineSymbolV2* lineSymbol = dynamic_cast<QgsLineSymbolV2*>( symbol );
284+
if ( lineSymbol )
285+
{
286+
delete mOutline;
287+
mOutline = lineSymbol;
288+
return true;
289+
}
290+
291+
delete symbol;
292+
return false;
293+
}

src/core/symbology-ng/qgsfillsymbollayerv2.h

+54
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,58 @@ class CORE_EXPORT QgsSimpleFillSymbolLayerV2 : public QgsFillSymbolLayerV2
6161
QPen mPen;
6262
};
6363

64+
/**A class for svg fill patterns. The class automatically scales the pattern to
65+
the appropriate pixel dimensions of the output device*/
66+
class CORE_EXPORT QgsSVGFillSymbolLayer: public QgsFillSymbolLayerV2
67+
{
68+
public:
69+
QgsSVGFillSymbolLayer( const QString& svgFilePath = "", double width = 20 );
70+
QgsSVGFillSymbolLayer( const QByteArray& svgData, double width = 20 );
71+
~QgsSVGFillSymbolLayer();
72+
73+
static QgsSymbolLayerV2* create( const QgsStringMap& properties = QgsStringMap() );
74+
75+
// implemented from base classes
76+
77+
QString layerType() const;
78+
79+
void startRender( QgsSymbolV2RenderContext& context );
80+
void stopRender( QgsSymbolV2RenderContext& context );
81+
82+
void renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context );
83+
84+
QgsStringMap properties() const;
85+
86+
QgsSymbolLayerV2* clone() const;
87+
88+
//gettersn and setters
89+
void setSvgFilePath( const QString& svgPath );
90+
QString svgFilePath() const { return mSvgFilePath; }
91+
void setPatternWidth( double width ) { mPatternWidth = width;}
92+
double patternWidth() const { return mPatternWidth; }
93+
94+
QgsSymbolV2* subSymbol() { return mOutline; }
95+
bool setSubSymbol( QgsSymbolV2* symbol );
96+
97+
protected:
98+
/**Width of the pattern (in QgsSymbolV2 output units)*/
99+
double mPatternWidth;
100+
/**SVG data*/
101+
QByteArray mSvgData;
102+
/**Path to the svg file (or empty if constructed directly from data)*/
103+
QString mSvgFilePath;
104+
/**SVG view box (to keep the aspect ratio */
105+
QRectF mSvgViewBox;
106+
/**Brush that receives rendered pixel image in startRender() method*/
107+
QBrush mBrush;
108+
/**Outline width*/
109+
double mOutlineWidth;
110+
/**Custom outline*/
111+
QgsLineSymbolV2* mOutline;
112+
113+
private:
114+
/**Helper function that gets the view box from the byte array*/
115+
void storeViewBox();
116+
};
117+
64118
#endif

src/core/symbology-ng/qgssymbollayerv2.cpp

+28
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "qgsrendercontext.h"
55

66
#include <QSize>
7+
#include <QPainter>
78
#include <QPointF>
89
#include <QPolygonF>
910

@@ -50,3 +51,30 @@ void QgsFillSymbolLayerV2::drawPreviewIcon( QgsSymbolV2RenderContext& context, Q
5051
renderPolygon( poly, NULL, context );
5152
stopRender( context );
5253
}
54+
55+
void QgsFillSymbolLayerV2::_renderPolygon( QPainter* p, const QPolygonF& points, const QList<QPolygonF>* rings )
56+
{
57+
if ( !p )
58+
{
59+
return;
60+
}
61+
62+
if ( rings == NULL )
63+
{
64+
// simple polygon without holes
65+
p->drawPolygon( points );
66+
}
67+
else
68+
{
69+
// polygon with holes must be drawn using painter path
70+
QPainterPath path;
71+
path.addPolygon( points );
72+
QList<QPolygonF>::const_iterator it = rings->constBegin();
73+
for ( ; it != rings->constEnd(); ++it )
74+
{
75+
path.addPolygon( *it );
76+
}
77+
78+
p->drawPath( path );
79+
}
80+
}

src/core/symbology-ng/qgssymbollayerv2.h

+2
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ class CORE_EXPORT QgsFillSymbolLayerV2 : public QgsSymbolLayerV2
114114

115115
protected:
116116
QgsFillSymbolLayerV2( bool locked = false );
117+
/**Default method to render polygon*/
118+
void _renderPolygon( QPainter* p, const QPolygonF& points, const QList<QPolygonF>* rings );
117119
};
118120

119121
class QgsSymbolLayerV2Widget;

src/core/symbology-ng/qgssymbollayerv2registry.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@ QgsSymbolLayerV2Registry::QgsSymbolLayerV2Registry()
2424

2525
addSymbolLayerType( new QgsSymbolLayerV2Metadata( "SimpleFill", QgsSymbolV2::Fill,
2626
QgsSimpleFillSymbolLayerV2::create ) );
27+
28+
addSymbolLayerType( new QgsSymbolLayerV2Metadata( "SVGFill", QgsSymbolV2::Fill, QgsSVGFillSymbolLayer::create ) );
2729
}
2830

2931
QgsSymbolLayerV2Registry::~QgsSymbolLayerV2Registry()
3032
{
31-
foreach (QString name, mMetadata.keys())
33+
foreach( QString name, mMetadata.keys() )
3234
{
3335
delete mMetadata[name];
3436
}
@@ -91,7 +93,7 @@ QStringList QgsSymbolLayerV2Registry::symbolLayersForType( QgsSymbolV2::SymbolTy
9193
QMap<QString, QgsSymbolLayerV2AbstractMetadata*>::ConstIterator it = mMetadata.begin();
9294
for ( ; it != mMetadata.end(); ++it )
9395
{
94-
if ( (*it)->type() == type )
96+
if (( *it )->type() == type )
9597
lst.append( it.key() );
9698
}
9799
return lst;

0 commit comments

Comments
 (0)