QGIS API Documentation 3.41.0-Master (88383c3d16f)
Loading...
Searching...
No Matches
qgsgeometrypaintdevice.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsgeometrypaintdevice.cpp
3 --------------------------------------
4 Date : May 2024
5 Copyright : (C) 2024 by Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17#include "qgspolygon.h"
18#include "qgslinestring.h"
19#include "qgsgeos.h"
20#include "qgsmultipolygon.h"
21#include "qgsmultilinestring.h"
22#include "qgspainting.h"
23#include "qgssymbollayerutils.h"
24
25//
26// QgsGeometryPaintEngine
27//
28
30 : QPaintEngine( QPaintEngine::AllFeatures ) // we lie and say we support all paint features, as we don't want Qt trying to be helpful and rasterizing shapes
31 , mUsePathStroker( usePathStroker )
32{
33}
34
36{
37 mStrokedPathsSegments = segments;
38}
39
41{
42 mSimplifyTolerance = tolerance;
43}
44
45bool QgsGeometryPaintEngine::begin( QPaintDevice * )
46{
47 return true;
48}
49
51{
52 return true;
53}
54
55QPaintEngine::Type QgsGeometryPaintEngine::type() const
56{
57 return QPaintEngine::User;
58}
59
60void QgsGeometryPaintEngine::updateState( const QPaintEngineState &state )
61{
62 if ( mUsePathStroker && state.state().testFlag( QPaintEngine::DirtyFlag::DirtyPen ) )
63 {
64 mPen = state.pen();
65 }
66}
67
68void QgsGeometryPaintEngine::drawImage( const QRectF &, const QImage &, const QRectF &, Qt::ImageConversionFlags )
69{
70 // ignore, we don't need to support raster drawing
71 QgsDebugError( QStringLiteral( "QgsGeometryPaintEngine does not support drawImage method" ) );
72}
73
74void QgsGeometryPaintEngine::drawPixmap( const QRectF &, const QPixmap &, const QRectF & )
75{
76 // ignore, we don't need to support raster drawing
77 QgsDebugError( QStringLiteral( "QgsGeometryPaintEngine does not support drawPixmap method" ) );
78}
79
80void QgsGeometryPaintEngine::drawTiledPixmap( const QRectF &, const QPixmap &, const QPointF & )
81{
82 // ignore, we don't need to support raster drawing
83 QgsDebugError( QStringLiteral( "QgsGeometryPaintEngine does not support drawTiledPixmap method" ) );
84}
85
86template <typename T>
87void drawLinesImp( const QTransform &transform, QgsGeometryCollection &geometry, const T *lines, int lineCount )
88{
89 geometry.reserve( geometry.numGeometries() + lineCount );
90 if ( transform.isIdentity() )
91 {
92 for ( int i = 0; i < lineCount; ++i, ++lines )
93 {
94 geometry.addGeometry( new QgsLineString(
95 QVector<double> { static_cast< double >( lines->x1() ), static_cast< double >( lines->x2() ) },
96 QVector<double> { static_cast< double >( lines->y1() ), static_cast< double >( lines->y2() ) } )
97 );
98 }
99 }
100 else
101 {
102 for ( int i = 0; i < lineCount; ++i, ++lines )
103 {
104 double x1 = lines->x1();
105 double x2 = lines->x2();
106 double y1 = lines->y1();
107 double y2 = lines->y2();
108
109 double tx1, tx2, ty1, ty2;
110 transform.map( x1, y1, &tx1, &ty1 );
111 transform.map( x2, y2, &tx2, &ty2 );
112
113 geometry.addGeometry( new QgsLineString(
114 QVector<double> { tx1, tx2 },
115 QVector<double> { ty1, ty2 } )
116 );
117 }
118 }
119}
120
121void QgsGeometryPaintEngine::drawLines( const QLineF *lines, int lineCount )
122{
123 if ( mUsePathStroker )
124 {
125 // if stroking we have no choice but to go via the QPainterPath route
126 QPaintEngine::drawLines( lines, lineCount );
127 }
128 else
129 {
130 const QTransform transform = painter()->combinedTransform();
131 drawLinesImp( transform, mGeometry, lines, lineCount );
132 }
133}
134
135void QgsGeometryPaintEngine::drawLines( const QLine *lines, int lineCount )
136{
137 if ( mUsePathStroker )
138 {
139 // if stroking we have no choice but to go via the QPainterPath route
140 QPaintEngine::drawLines( lines, lineCount );
141 }
142 else
143 {
144 const QTransform transform = painter()->combinedTransform();
145 drawLinesImp( transform, mGeometry, lines, lineCount );
146 }
147}
148
149template <typename T>
150void drawPointsImp( const QTransform &transform, QgsGeometryCollection &geometry, const T *points, int pointCount )
151{
152 geometry.reserve( geometry.numGeometries() + pointCount );
153 if ( transform.isIdentity() )
154 {
155 for ( int i = 0; i < pointCount; ++i, ++points )
156 {
157 geometry.addGeometry( new QgsPoint( static_cast< double >( points->x() ),
158 static_cast< double >( points->y() ) ) );
159 }
160 }
161 else
162 {
163 for ( int i = 0; i < pointCount; ++i, ++points )
164 {
165 double x = points->x();
166 double y = points->y();
167
168 double tx, ty;
169 transform.map( x, y, &tx, &ty );
170
171 geometry.addGeometry( new QgsPoint( tx, ty ) );
172 }
173 }
174}
175
176void QgsGeometryPaintEngine::drawPoints( const QPointF *points, int pointCount )
177{
178 const QTransform transform = painter()->combinedTransform();
179 drawPointsImp( transform, mGeometry, points, pointCount );
180}
181
182void QgsGeometryPaintEngine::drawPoints( const QPoint *points, int pointCount )
183{
184 const QTransform transform = painter()->combinedTransform();
185 drawPointsImp( transform, mGeometry, points, pointCount );
186}
187
188template <typename T>
189void drawRectsImp( const QTransform &transform, QgsGeometryCollection &geometry, const T *rects, int rectCount )
190{
191 geometry.reserve( geometry.numGeometries() + rectCount );
192 if ( transform.isIdentity() )
193 {
194 for ( int i = 0; i < rectCount; ++i, ++rects )
195 {
196 QgsLineString *exterior = new QgsLineString(
197 QVector<double> { static_cast< double >( rects->left() ),
198 static_cast< double >( rects->right() ),
199 static_cast< double >( rects->right() ),
200 static_cast< double >( rects->left() ),
201 static_cast< double>( rects->left() )
202 },
203 QVector<double> { static_cast< double >( rects->bottom() ),
204 static_cast< double >( rects->bottom() ),
205 static_cast< double >( rects->top() ),
206 static_cast< double >( rects->top() ),
207 static_cast< double >( rects->bottom() )
208 } );
209 geometry.addGeometry( new QgsPolygon( exterior ) );
210 }
211 }
212 else
213 {
214 for ( int i = 0; i < rectCount; ++i, ++rects )
215 {
216 const double left = rects->left();
217 const double right = rects->right();
218 const double top = rects->top();
219 const double bottom = rects->bottom();
220
221 double bottomLeftX, bottomLeftY, bottomRightX, bottomRightY, topLeftX, topLeftY, topRightX, topRightY;
222 transform.map( left, bottom, &bottomLeftX, &bottomLeftY );
223 transform.map( right, bottom, &bottomRightX, &bottomRightY );
224 transform.map( left, top, &topLeftX, &topLeftY );
225 transform.map( right, top, &topRightX, &topRightY );
226
227 QgsLineString *exterior = new QgsLineString(
228 QVector<double> { bottomLeftX, bottomRightX, topRightX, topLeftX, bottomLeftX },
229 QVector<double> { bottomLeftY, bottomRightY, topRightY, topLeftY, bottomLeftY } );
230 geometry.addGeometry( new QgsPolygon( exterior ) );
231 }
232 }
233}
234
235void QgsGeometryPaintEngine::drawRects( const QRectF *rects, int rectCount )
236{
237 const QTransform transform = painter()->combinedTransform();
238 drawRectsImp( transform, mGeometry, rects, rectCount );
239}
240
241void QgsGeometryPaintEngine::drawRects( const QRect *rects, int rectCount )
242{
243 const QTransform transform = painter()->combinedTransform();
244 drawRectsImp( transform, mGeometry, rects, rectCount );
245}
246
247template <typename T>
248void drawPolygonImp( const QTransform &transform, QgsGeometryCollection &geometry, const T *points, int pointCount, QPaintEngine::PolygonDrawMode mode, double simplifyTolerance )
249{
250 QVector< double > x;
251 QVector< double > y;
252 x.resize( pointCount );
253 y.resize( pointCount );
254 double *xData = x.data();
255 double *yData = y.data();
256
257 if ( transform.isIdentity() )
258 {
259 for ( int i = 0; i < pointCount; ++i, ++points )
260 {
261 *xData++ = points->x();
262 *yData++ = points->y();
263 }
264 }
265 else
266 {
267 for ( int i = 0; i < pointCount; ++i, ++points )
268 {
269 const double x = points->x();
270 const double y = points->y();
271 double tx, ty;
272 transform.map( x, y, &tx, &ty );
273
274 *xData++ = tx;
275 *yData++ = ty;
276 }
277 }
278
279 switch ( mode )
280 {
281 case QPaintEngine::PolylineMode:
282 if ( simplifyTolerance > 0 )
283 geometry.addGeometry( QgsLineString( x, y ).simplifyByDistance( simplifyTolerance ) );
284 else
285 geometry.addGeometry( new QgsLineString( x, y ) );
286 break;
287
288 case QPaintEngine::OddEvenMode:
289 case QPaintEngine::WindingMode:
290 case QPaintEngine::ConvexMode:
291 if ( simplifyTolerance > 0 )
292 geometry.addGeometry( new QgsPolygon( QgsLineString( x, y ).simplifyByDistance( simplifyTolerance ) ) );
293 else
294 geometry.addGeometry( new QgsPolygon( new QgsLineString( x, y ) ) );
295 break;
296 }
297}
298
299void QgsGeometryPaintEngine::drawPolygon( const QPoint *points, int pointCount, QPaintEngine::PolygonDrawMode mode )
300{
301 if ( mUsePathStroker && mode == PolygonDrawMode::PolylineMode )
302 {
303 // if stroking we have no choice but to go via the QPainterPath route
304 if ( pointCount > 0 )
305 {
306 QPainterPath path;
307 path.moveTo( *points++ );
308 for ( int i = 1; i < pointCount; ++i )
309 {
310 path.lineTo( *points++ );
311 }
312 drawPath( path );
313 }
314 }
315 else
316 {
317 const QTransform transform = painter()->combinedTransform();
318 drawPolygonImp( transform, mGeometry, points, pointCount, mode, mSimplifyTolerance );
319 }
320}
321
322void QgsGeometryPaintEngine::drawPolygon( const QPointF *points, int pointCount, QPaintEngine::PolygonDrawMode mode )
323{
324 if ( mUsePathStroker )
325 {
326 // if stroking we have no choice but to go via the QPainterPath route
327 if ( pointCount > 0 )
328 {
329 QPainterPath path;
330 path.moveTo( *points++ );
331 for ( int i = 1; i < pointCount; ++i )
332 {
333 path.lineTo( *points++ );
334 }
335 drawPath( path );
336 }
337 }
338 else
339 {
340 const QTransform transform = painter()->combinedTransform();
341 drawPolygonImp( transform, mGeometry, points, pointCount, mode, mSimplifyTolerance );
342 }
343}
344
345void QgsGeometryPaintEngine::addStrokedLine( const QgsLineString *line, double penWidth, Qgis::EndCapStyle endCapStyle, Qgis::JoinStyle joinStyle, double miterLimit, const QTransform *matrix )
346{
347 std::unique_ptr< QgsAbstractGeometry > buffered;
348 if ( mSimplifyTolerance > 0 )
349 {
350 // For performance, we apply a lower level of simplification to the line BEFORE doing the buffer.
351 // This avoids making the call to GEOS buffer function too expensive, as we'd otherwise be doing it
352 // on the unsimplified line and then immediately discarding most of the detail when we simplify
353 // the resultant buffer.
354 // The 0.75 factor here is just a guess! This could likely be made smarter, eg by considering the pen width?
355 const double preBufferedSimplificationFactor = mSimplifyTolerance * 0.75;
356 std::unique_ptr< QgsLineString > simplified( line->simplifyByDistance( preBufferedSimplificationFactor ) );
357 QgsGeos geos( simplified.get() );
358 buffered.reset( geos.buffer( penWidth / 2, mStrokedPathsSegments, endCapStyle, joinStyle, miterLimit ) );
359 }
360 else
361 {
362 QgsGeos geos( line );
363 buffered.reset( geos.buffer( penWidth / 2, mStrokedPathsSegments, endCapStyle, joinStyle, miterLimit ) );
364 }
365
366 if ( !buffered )
367 return;
368
369 if ( matrix )
370 buffered->transform( *matrix );
371
372 if ( QgsGeometryCollection *bufferedCollection = qgsgeometry_cast< QgsGeometryCollection * >( buffered.get() ) )
373 {
374 if ( mSimplifyTolerance > 0 )
375 {
376 for ( auto it = bufferedCollection->const_parts_begin(); it != bufferedCollection->const_parts_end(); ++it )
377 {
378 mGeometry.addGeometry( ( *it )->simplifyByDistance( mSimplifyTolerance ) );
379 }
380 }
381 else
382 {
383 mGeometry.addGeometries( bufferedCollection->takeGeometries() );
384 }
385 }
386 else if ( buffered )
387 {
388 if ( mSimplifyTolerance > 0 )
389 {
390 mGeometry.addGeometry( buffered->simplifyByDistance( mSimplifyTolerance ) );
391 }
392 else
393 {
394 mGeometry.addGeometry( buffered.release() );
395 }
396 }
397}
398
399// based on QPainterPath::toSubpathPolygons()
400void QgsGeometryPaintEngine::addSubpathGeometries( const QPainterPath &path, const QTransform &matrix )
401{
402 if ( path.isEmpty() )
403 return;
404
405 const bool transformIsIdentity = matrix.isIdentity();
406
407 const Qgis::EndCapStyle endCapStyle = QgsSymbolLayerUtils::penCapStyleToEndCapStyle( mPen.capStyle() );
408 const Qgis::JoinStyle joinStyle = QgsSymbolLayerUtils::penJoinStyleToJoinStyle( mPen.joinStyle() );
409 const double penWidth = mPen.widthF() <= 0 ? 1 : mPen.widthF();
410 const double miterLimit = mPen.miterLimit();
411
412 QVector< double > currentX;
413 QVector< double > currentY;
414 const int count = path.elementCount();
415
416 // polygon parts get queued and post-processed before adding them to the collection
417 std::vector< std::unique_ptr< QgsPolygon > > queuedPolygons;
418
419 for ( int i = 0; i < count; ++i )
420 {
421 const QPainterPath::Element &e = path.elementAt( i );
422 switch ( e.type )
423 {
424 case QPainterPath::MoveToElement:
425 {
426 if ( currentX.size() > 1 )
427 {
428 auto line = std::make_unique< QgsLineString >( currentX, currentY );
429 if ( mUsePathStroker )
430 {
431 addStrokedLine( line.get(), penWidth, endCapStyle, joinStyle, miterLimit, transformIsIdentity ? nullptr : &matrix );
432 }
433 else if ( line->isClosed() )
434 {
435 if ( !transformIsIdentity )
436 line->transform( matrix );
437
438 if ( mSimplifyTolerance > 0 )
439 {
440 queuedPolygons.emplace_back( std::make_unique< QgsPolygon >( line->simplifyByDistance( mSimplifyTolerance ) ) );
441 line.reset();
442 }
443 else
444 {
445 queuedPolygons.emplace_back( std::make_unique< QgsPolygon >( line.release() ) );
446 }
447 }
448 else
449 {
450 if ( !transformIsIdentity )
451 line->transform( matrix );
452 if ( mSimplifyTolerance > 0 )
453 {
454 mGeometry.addGeometry( line->simplifyByDistance( mSimplifyTolerance ) );
455 line.reset();
456 }
457 else
458 {
459 mGeometry.addGeometry( line.release() );
460 }
461 }
462 }
463 currentX.resize( 0 );
464 currentY.resize( 0 );
465
466 currentX.reserve( 16 );
467 currentY.reserve( 16 );
468 currentX << e.x;
469 currentY << e.y;
470 break;
471 }
472
473 case QPainterPath::LineToElement:
474 {
475 currentX << e.x;
476 currentY << e.y;
477 break;
478 }
479
480 case QPainterPath::CurveToElement:
481 {
482 Q_ASSERT( path.elementAt( i + 1 ).type == QPainterPath::CurveToDataElement );
483 Q_ASSERT( path.elementAt( i + 2 ).type == QPainterPath::CurveToDataElement );
484
485 const double x1 = path.elementAt( i - 1 ).x;
486 const double y1 = path.elementAt( i - 1 ).y;
487
488 const double x3 = path.elementAt( i + 1 ).x;
489 const double y3 = path.elementAt( i + 1 ).y;
490
491 const double x4 = path.elementAt( i + 2 ).x;
492 const double y4 = path.elementAt( i + 2 ).y;
493
494 // TODO -- we could likely reduce the number of segmented points here!
495 std::unique_ptr< QgsLineString> bezier( QgsLineString::fromBezierCurve(
496 QgsPoint( x1, y1 ),
497 QgsPoint( e.x, e.y ),
498 QgsPoint( x3, y3 ),
499 QgsPoint( x4, y4 ) ) );
500
501 currentX << bezier->xVector();
502 currentY << bezier->yVector();
503
504 i += 2;
505 break;
506 }
507 case QPainterPath::CurveToDataElement:
508 Q_ASSERT( !"addSubpathGeometries(), bad element type" );
509 break;
510 }
511 }
512
513 if ( currentX.size() > 1 )
514 {
515 auto line = std::make_unique< QgsLineString >( currentX, currentY );
516 if ( mUsePathStroker )
517 {
518 addStrokedLine( line.get(), penWidth, endCapStyle, joinStyle, miterLimit, transformIsIdentity ? nullptr : &matrix );
519 }
520 else if ( line->isClosed() )
521 {
522 if ( !transformIsIdentity )
523 line->transform( matrix );
524 if ( mSimplifyTolerance > 0 )
525 {
526 queuedPolygons.emplace_back( std::make_unique< QgsPolygon >( line->simplifyByDistance( mSimplifyTolerance ) ) );
527 line.reset();
528 }
529 else
530 {
531 queuedPolygons.emplace_back( std::make_unique< QgsPolygon >( line.release() ) );
532 }
533 }
534 else
535 {
536 if ( !transformIsIdentity )
537 line->transform( matrix );
538 if ( mSimplifyTolerance > 0 )
539 {
540 mGeometry.addGeometry( line->simplifyByDistance( mSimplifyTolerance ) );
541 line.reset();
542 }
543 else
544 {
545 mGeometry.addGeometry( line.release() );
546 }
547 }
548 }
549
550 if ( queuedPolygons.empty() )
551 return;
552
553 mGeometry.reserve( static_cast< int >( mGeometry.numGeometries() + queuedPolygons.size() ) );
554
555 QgsMultiPolygon tempMultiPolygon;
556 tempMultiPolygon.reserve( static_cast< int >( queuedPolygons.size() ) );
557 for ( auto &part : queuedPolygons )
558 {
559 tempMultiPolygon.addGeometry( part.release() );
560 }
561
562 // ensure holes are holes, not overlapping polygons
563 QgsGeos geosCollection( &tempMultiPolygon );
564 std::unique_ptr< QgsAbstractGeometry > g = geosCollection.makeValid( Qgis::MakeValidMethod::Linework );
565 if ( !g )
566 return;
567
568 for ( auto it = g->const_parts_begin(); it != g->const_parts_end(); ++it )
569 {
570 mGeometry.addGeometry( ( *it )->clone() );
571 }
572}
573
574void QgsGeometryPaintEngine::drawPath( const QPainterPath &path )
575{
576 const QTransform transform = painter()->combinedTransform();
577 addSubpathGeometries( path, transform );
578}
579
580
581//
582// QgsGeometryPaintDevice
583//
584
586{
587 mPaintEngine = std::make_unique<QgsGeometryPaintEngine>( usePathStroker );
588}
589
591{
592 if ( mPaintEngine )
593 mPaintEngine->setStrokedPathSegments( segments );
594}
595
597{
598 if ( mPaintEngine )
599 mPaintEngine->setSimplificationTolerance( tolerance );
600}
601
603{
604 return mPaintEngine.get();
605}
606
607int QgsGeometryPaintDevice::metric( PaintDeviceMetric m ) const
608{
609 // copy/paste from qpicture.cpp
610 int val;
611
612 switch ( m )
613 {
614 case PdmWidth:
615 val = static_cast< int >( mPaintEngine->geometry().boundingBox().width() );
616 break;
617 case PdmHeight:
618 val = static_cast< int >( mPaintEngine->geometry().boundingBox().height() );
619 break;
620 case PdmWidthMM:
621 val = static_cast< int >( 25.4 / QgsPainting::qtDefaultDpiX() * mPaintEngine->geometry().boundingBox().width() );
622 break;
623 case PdmHeightMM:
624 val = static_cast< int >( 25.4 / QgsPainting::qtDefaultDpiY() * mPaintEngine->geometry().boundingBox().height() );
625 break;
626 case PdmDpiX:
627 case PdmPhysicalDpiX:
629 break;
630 case PdmDpiY:
631 case PdmPhysicalDpiY:
633 break;
634 case PdmNumColors:
635 val = 16777216;
636 break;
637 case PdmDepth:
638 val = 24;
639 break;
640 case PdmDevicePixelRatio:
641 val = 1;
642 break;
643 case PdmDevicePixelRatioScaled:
644 val = static_cast< int >( 1 * QPaintDevice::devicePixelRatioFScale() );
645 break;
646 default:
647 val = 0;
648 qWarning( "QPicture::metric: Invalid metric command" );
649 }
650 return val;
651}
652
654{
655 return mPaintEngine->geometry();
656}
657
659{
661 QPainter painter( &device );
662 painter.drawPath( path );
663 painter.end();
664 return QgsGeometry( device.geometry().clone() );
665}
666
JoinStyle
Join styles for buffers.
Definition qgis.h:2051
EndCapStyle
End cap styles for buffers.
Definition qgis.h:2038
@ Linework
Combines all rings into a set of noded lines and then extracts valid polygons from that linework.
Abstract base class for all geometries.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
void reserve(int size)
Attempts to allocate memory for at least size geometries.
virtual bool addGeometries(const QVector< QgsAbstractGeometry * > &geometries)
Adds a list of geometries to the collection, transferring ownership to the collection.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
int numGeometries() const
Returns the number of geometries within the collection.
A paint device which converts everything renderer to a QgsGeometry representation of the rendered sha...
QgsGeometryPaintDevice(bool usePathStroker=false)
Constructor for QgsGeometryPaintDevice.
void setStrokedPathSegments(int segments)
Sets the number of segments to use when drawing stroked paths with a rounded pen.
int metric(PaintDeviceMetric metric) const override
void setSimplificationTolerance(double tolerance)
Sets a simplification tolerance (in painter units) to use for on-the-fly simplification of geometries...
QPaintEngine * paintEngine() const override
static QgsGeometry painterPathToGeometry(const QPainterPath &path)
Converts a painter path to a QgsGeometry.
const QgsAbstractGeometry & geometry() const
Returns the rendered geometry.
void setSimplificationTolerance(double tolerance)
Sets a simplification tolerance (in painter units) to use for on-the-fly simplification of geometries...
void drawImage(const QRectF &rectangle, const QImage &image, const QRectF &sr, Qt::ImageConversionFlags flags=Qt::AutoColor) final
void drawRects(const QRectF *rects, int rectCount) final
void drawPolygon(const QPointF *points, int pointCount, QPaintEngine::PolygonDrawMode mode) final
void drawPixmap(const QRectF &, const QPixmap &, const QRectF &) final
void drawPoints(const QPointF *points, int pointCount) final
bool begin(QPaintDevice *) final
QgsGeometryPaintEngine(bool usePathStroker=false)
Constructor for QgsGeometryPaintEngine.
void drawLines(const QLineF *lines, int lineCount) final
QPaintEngine::Type type() const final
void drawTiledPixmap(const QRectF &rect, const QPixmap &pixmap, const QPointF &p) final
void updateState(const QPaintEngineState &) final
void drawPath(const QPainterPath &path) final
void setStrokedPathSegments(int segments)
Sets the number of segments to use when drawing stroked paths with a rounded pen.
A geometry is the spatial representation of a feature.
Does vector analysis using the GEOS library and handles import, export, and exception handling.
Definition qgsgeos.h:139
Line string geometry type, with support for z-dimension and m-values.
static std::unique_ptr< QgsLineString > fromBezierCurve(const QgsPoint &start, const QgsPoint &controlPoint1, const QgsPoint &controlPoint2, const QgsPoint &end, int segments=30)
Returns a new linestring created by segmentizing the bezier curve between start and end,...
bool isClosed() const override
Returns true if the curve is closed.
QgsLineString * simplifyByDistance(double tolerance) const override
Simplifies the geometry by applying the Douglas Peucker simplification by distance algorithm.
void transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection d=Qgis::TransformDirection::Forward, bool transformZ=false) override
Transforms the geometry using a coordinate transform.
Multi polygon geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
static int qtDefaultDpiY()
Returns the default Qt vertical DPI.
static int qtDefaultDpiX()
Returns the default Qt horizontal DPI.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
Polygon geometry type.
Definition qgspolygon.h:33
static Qgis::EndCapStyle penCapStyleToEndCapStyle(Qt::PenCapStyle style)
Converts a Qt pen cap style to a QGIS end cap style.
static Qgis::JoinStyle penJoinStyleToJoinStyle(Qt::PenJoinStyle style)
Converts a Qt pen joinstyle to a QGIS join style.
Contains geos related utilities and functions.
Definition qgsgeos.h:75
void drawPointsImp(const QTransform &transform, QgsGeometryCollection &geometry, const T *points, int pointCount)
void drawRectsImp(const QTransform &transform, QgsGeometryCollection &geometry, const T *rects, int rectCount)
void drawLinesImp(const QTransform &transform, QgsGeometryCollection &geometry, const T *lines, int lineCount)
void drawPolygonImp(const QTransform &transform, QgsGeometryCollection &geometry, const T *points, int pointCount, QPaintEngine::PolygonDrawMode mode, double simplifyTolerance)
#define QgsDebugError(str)
Definition qgslogger.h:40