1 | /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
|
---|
2 | * Qwt Widget Library
|
---|
3 | * Copyright (C) 1997 Josef Wilgen
|
---|
4 | * Copyright (C) 2002 Uwe Rathmann
|
---|
5 | *
|
---|
6 | * This library is free software; you can redistribute it and/or
|
---|
7 | * modify it under the terms of the Qwt License, Version 1.0
|
---|
8 | *****************************************************************************/
|
---|
9 |
|
---|
10 | #include "qwt_graphic.h"
|
---|
11 | #include "qwt_painter_command.h"
|
---|
12 | #include <qvector.h>
|
---|
13 | #include <qpainter.h>
|
---|
14 | #include <qpaintengine.h>
|
---|
15 | #include <qimage.h>
|
---|
16 | #include <qpixmap.h>
|
---|
17 | #include <qpainterpath.h>
|
---|
18 | #include <qmath.h>
|
---|
19 |
|
---|
20 | static bool qwtHasScalablePen( const QPainter *painter )
|
---|
21 | {
|
---|
22 | const QPen pen = painter->pen();
|
---|
23 |
|
---|
24 | bool scalablePen = false;
|
---|
25 |
|
---|
26 | if ( pen.style() != Qt::NoPen && pen.brush().style() != Qt::NoBrush )
|
---|
27 | {
|
---|
28 | scalablePen = !pen.isCosmetic();
|
---|
29 | if ( !scalablePen && pen.widthF() == 0.0 )
|
---|
30 | {
|
---|
31 | const QPainter::RenderHints hints = painter->renderHints();
|
---|
32 | if ( hints.testFlag( QPainter::NonCosmeticDefaultPen ) )
|
---|
33 | scalablePen = true;
|
---|
34 | }
|
---|
35 | }
|
---|
36 |
|
---|
37 | return scalablePen;
|
---|
38 | }
|
---|
39 |
|
---|
40 | static QRectF qwtStrokedPathRect(
|
---|
41 | const QPainter *painter, const QPainterPath &path )
|
---|
42 | {
|
---|
43 | QPainterPathStroker stroker;
|
---|
44 | stroker.setWidth( painter->pen().widthF() );
|
---|
45 | stroker.setCapStyle( painter->pen().capStyle() );
|
---|
46 | stroker.setJoinStyle( painter->pen().joinStyle() );
|
---|
47 | stroker.setMiterLimit( painter->pen().miterLimit() );
|
---|
48 |
|
---|
49 | QRectF rect;
|
---|
50 | if ( qwtHasScalablePen( painter ) )
|
---|
51 | {
|
---|
52 | QPainterPath stroke = stroker.createStroke(path);
|
---|
53 | rect = painter->transform().map(stroke).boundingRect();
|
---|
54 | }
|
---|
55 | else
|
---|
56 | {
|
---|
57 | QPainterPath mappedPath = painter->transform().map(path);
|
---|
58 | mappedPath = stroker.createStroke( mappedPath );
|
---|
59 |
|
---|
60 | rect = mappedPath.boundingRect();
|
---|
61 | }
|
---|
62 |
|
---|
63 | return rect;
|
---|
64 | }
|
---|
65 |
|
---|
66 | static inline void qwtExecCommand(
|
---|
67 | QPainter *painter, const QwtPainterCommand &cmd,
|
---|
68 | QwtGraphic::RenderHints renderHints,
|
---|
69 | const QTransform &transform,
|
---|
70 | const QTransform *initialTransform )
|
---|
71 | {
|
---|
72 | switch( cmd.type() )
|
---|
73 | {
|
---|
74 | case QwtPainterCommand::Path:
|
---|
75 | {
|
---|
76 | bool doMap = false;
|
---|
77 |
|
---|
78 | if ( renderHints.testFlag( QwtGraphic::RenderPensUnscaled )
|
---|
79 | && painter->transform().isScaling() )
|
---|
80 | {
|
---|
81 | bool isCosmetic = painter->pen().isCosmetic();
|
---|
82 | if ( isCosmetic && painter->pen().widthF() == 0.0 )
|
---|
83 | {
|
---|
84 | QPainter::RenderHints hints = painter->renderHints();
|
---|
85 | if ( hints.testFlag( QPainter::NonCosmeticDefaultPen ) )
|
---|
86 | isCosmetic = false;
|
---|
87 | }
|
---|
88 |
|
---|
89 | doMap = !isCosmetic;
|
---|
90 | }
|
---|
91 |
|
---|
92 | if ( doMap )
|
---|
93 | {
|
---|
94 | const QTransform tr = painter->transform();
|
---|
95 |
|
---|
96 | painter->resetTransform();
|
---|
97 |
|
---|
98 | QPainterPath path = tr.map( *cmd.path() );
|
---|
99 | if ( initialTransform )
|
---|
100 | {
|
---|
101 | painter->setTransform( *initialTransform );
|
---|
102 | path = initialTransform->inverted().map( path );
|
---|
103 | }
|
---|
104 |
|
---|
105 | painter->drawPath( path );
|
---|
106 |
|
---|
107 | painter->setTransform( tr );
|
---|
108 | }
|
---|
109 | else
|
---|
110 | {
|
---|
111 | painter->drawPath( *cmd.path() );
|
---|
112 | }
|
---|
113 | break;
|
---|
114 | }
|
---|
115 | case QwtPainterCommand::Pixmap:
|
---|
116 | {
|
---|
117 | const QwtPainterCommand::PixmapData *data = cmd.pixmapData();
|
---|
118 | painter->drawPixmap( data->rect, data->pixmap, data->subRect );
|
---|
119 | break;
|
---|
120 | }
|
---|
121 | case QwtPainterCommand::Image:
|
---|
122 | {
|
---|
123 | const QwtPainterCommand::ImageData *data = cmd.imageData();
|
---|
124 | painter->drawImage( data->rect, data->image,
|
---|
125 | data->subRect, data->flags );
|
---|
126 | break;
|
---|
127 | }
|
---|
128 | case QwtPainterCommand::State:
|
---|
129 | {
|
---|
130 | const QwtPainterCommand::StateData *data = cmd.stateData();
|
---|
131 |
|
---|
132 | if ( data->flags & QPaintEngine::DirtyPen )
|
---|
133 | painter->setPen( data->pen );
|
---|
134 |
|
---|
135 | if ( data->flags & QPaintEngine::DirtyBrush )
|
---|
136 | painter->setBrush( data->brush );
|
---|
137 |
|
---|
138 | if ( data->flags & QPaintEngine::DirtyBrushOrigin )
|
---|
139 | painter->setBrushOrigin( data->brushOrigin );
|
---|
140 |
|
---|
141 | if ( data->flags & QPaintEngine::DirtyFont )
|
---|
142 | painter->setFont( data->font );
|
---|
143 |
|
---|
144 | if ( data->flags & QPaintEngine::DirtyBackground )
|
---|
145 | {
|
---|
146 | painter->setBackgroundMode( data->backgroundMode );
|
---|
147 | painter->setBackground( data->backgroundBrush );
|
---|
148 | }
|
---|
149 |
|
---|
150 | if ( data->flags & QPaintEngine::DirtyTransform )
|
---|
151 | {
|
---|
152 | painter->setTransform( data->transform * transform );
|
---|
153 | }
|
---|
154 |
|
---|
155 | if ( data->flags & QPaintEngine::DirtyClipEnabled )
|
---|
156 | painter->setClipping( data->isClipEnabled );
|
---|
157 |
|
---|
158 | if ( data->flags & QPaintEngine::DirtyClipRegion)
|
---|
159 | {
|
---|
160 | painter->setClipRegion( data->clipRegion,
|
---|
161 | data->clipOperation );
|
---|
162 | }
|
---|
163 |
|
---|
164 | if ( data->flags & QPaintEngine::DirtyClipPath )
|
---|
165 | {
|
---|
166 | painter->setClipPath( data->clipPath, data->clipOperation );
|
---|
167 | }
|
---|
168 |
|
---|
169 | if ( data->flags & QPaintEngine::DirtyHints)
|
---|
170 | {
|
---|
171 | const QPainter::RenderHints hints = data->renderHints;
|
---|
172 |
|
---|
173 | painter->setRenderHint( QPainter::Antialiasing,
|
---|
174 | hints.testFlag( QPainter::Antialiasing ) );
|
---|
175 |
|
---|
176 | painter->setRenderHint( QPainter::TextAntialiasing,
|
---|
177 | hints.testFlag( QPainter::TextAntialiasing ) );
|
---|
178 |
|
---|
179 | painter->setRenderHint( QPainter::SmoothPixmapTransform,
|
---|
180 | hints.testFlag( QPainter::SmoothPixmapTransform ) );
|
---|
181 |
|
---|
182 | painter->setRenderHint( QPainter::HighQualityAntialiasing,
|
---|
183 | hints.testFlag( QPainter::HighQualityAntialiasing ) );
|
---|
184 |
|
---|
185 | painter->setRenderHint( QPainter::NonCosmeticDefaultPen,
|
---|
186 | hints.testFlag( QPainter::NonCosmeticDefaultPen ) );
|
---|
187 | }
|
---|
188 |
|
---|
189 | if ( data->flags & QPaintEngine::DirtyCompositionMode)
|
---|
190 | painter->setCompositionMode( data->compositionMode );
|
---|
191 |
|
---|
192 | if ( data->flags & QPaintEngine::DirtyOpacity)
|
---|
193 | painter->setOpacity( data->opacity );
|
---|
194 |
|
---|
195 | break;
|
---|
196 | }
|
---|
197 | default:
|
---|
198 | break;
|
---|
199 | }
|
---|
200 |
|
---|
201 | }
|
---|
202 |
|
---|
203 | class QwtGraphic::PathInfo
|
---|
204 | {
|
---|
205 | public:
|
---|
206 | PathInfo():
|
---|
207 | d_scalablePen( false )
|
---|
208 | {
|
---|
209 | // QVector needs a default constructor
|
---|
210 | }
|
---|
211 |
|
---|
212 | PathInfo( const QRectF &pointRect,
|
---|
213 | const QRectF &boundingRect, bool scalablePen ):
|
---|
214 | d_pointRect( pointRect ),
|
---|
215 | d_boundingRect( boundingRect ),
|
---|
216 | d_scalablePen( scalablePen )
|
---|
217 | {
|
---|
218 | }
|
---|
219 |
|
---|
220 | inline QRectF scaledBoundingRect( double sx, double sy,
|
---|
221 | bool scalePens ) const
|
---|
222 | {
|
---|
223 | if ( sx == 1.0 && sy == 1.0 )
|
---|
224 | return d_boundingRect;
|
---|
225 |
|
---|
226 | QTransform transform;
|
---|
227 | transform.scale( sx, sy );
|
---|
228 |
|
---|
229 | QRectF rect;
|
---|
230 | if ( scalePens && d_scalablePen )
|
---|
231 | {
|
---|
232 | rect = transform.mapRect( d_boundingRect );
|
---|
233 | }
|
---|
234 | else
|
---|
235 | {
|
---|
236 | rect = transform.mapRect( d_pointRect );
|
---|
237 |
|
---|
238 | const double l = qAbs( d_pointRect.left() - d_boundingRect.left() );
|
---|
239 | const double r = qAbs( d_pointRect.right() - d_boundingRect.right() );
|
---|
240 | const double t = qAbs( d_pointRect.top() - d_boundingRect.top() );
|
---|
241 | const double b = qAbs( d_pointRect.bottom() - d_boundingRect.bottom() );
|
---|
242 |
|
---|
243 | rect.adjust( -l, -t, r, b );
|
---|
244 | }
|
---|
245 |
|
---|
246 | return rect;
|
---|
247 | }
|
---|
248 |
|
---|
249 | inline double scaleFactorX( const QRectF& pathRect,
|
---|
250 | const QRectF &targetRect, bool scalePens ) const
|
---|
251 | {
|
---|
252 | if ( pathRect.width() <= 0.0 )
|
---|
253 | return 0.0;
|
---|
254 |
|
---|
255 | const QPointF p0 = d_pointRect.center();
|
---|
256 |
|
---|
257 | const double l = qAbs( pathRect.left() - p0.x() );
|
---|
258 | const double r = qAbs( pathRect.right() - p0.x() );
|
---|
259 |
|
---|
260 | const double w = 2.0 * qMin( l, r )
|
---|
261 | * targetRect.width() / pathRect.width();
|
---|
262 |
|
---|
263 | double sx;
|
---|
264 | if ( scalePens && d_scalablePen )
|
---|
265 | {
|
---|
266 | sx = w / d_boundingRect.width();
|
---|
267 | }
|
---|
268 | else
|
---|
269 | {
|
---|
270 | const double pw = qMax(
|
---|
271 | qAbs( d_boundingRect.left() - d_pointRect.left() ),
|
---|
272 | qAbs( d_boundingRect.right() - d_pointRect.right() ) );
|
---|
273 |
|
---|
274 | sx = ( w - 2 * pw ) / d_pointRect.width();
|
---|
275 | }
|
---|
276 |
|
---|
277 | return sx;
|
---|
278 | }
|
---|
279 |
|
---|
280 | inline double scaleFactorY( const QRectF& pathRect,
|
---|
281 | const QRectF &targetRect, bool scalePens ) const
|
---|
282 | {
|
---|
283 | if ( pathRect.height() <= 0.0 )
|
---|
284 | return 0.0;
|
---|
285 |
|
---|
286 | const QPointF p0 = d_pointRect.center();
|
---|
287 |
|
---|
288 | const double t = qAbs( pathRect.top() - p0.y() );
|
---|
289 | const double b = qAbs( pathRect.bottom() - p0.y() );
|
---|
290 |
|
---|
291 | const double h = 2.0 * qMin( t, b )
|
---|
292 | * targetRect.height() / pathRect.height();
|
---|
293 |
|
---|
294 | double sy;
|
---|
295 | if ( scalePens && d_scalablePen )
|
---|
296 | {
|
---|
297 | sy = h / d_boundingRect.height();
|
---|
298 | }
|
---|
299 | else
|
---|
300 | {
|
---|
301 | const double pw =
|
---|
302 | qMax( qAbs( d_boundingRect.top() - d_pointRect.top() ),
|
---|
303 | qAbs( d_boundingRect.bottom() - d_pointRect.bottom() ) );
|
---|
304 |
|
---|
305 | sy = ( h - 2 * pw ) / d_pointRect.height();
|
---|
306 | }
|
---|
307 |
|
---|
308 | return sy;
|
---|
309 | }
|
---|
310 |
|
---|
311 | private:
|
---|
312 | QRectF d_pointRect;
|
---|
313 | QRectF d_boundingRect;
|
---|
314 | bool d_scalablePen;
|
---|
315 | };
|
---|
316 |
|
---|
317 | class QwtGraphic::PrivateData
|
---|
318 | {
|
---|
319 | public:
|
---|
320 | PrivateData():
|
---|
321 | boundingRect( 0.0, 0.0, -1.0, -1.0 ),
|
---|
322 | pointRect( 0.0, 0.0, -1.0, -1.0 ),
|
---|
323 | initialTransform( NULL )
|
---|
324 | {
|
---|
325 | }
|
---|
326 |
|
---|
327 | QSizeF defaultSize;
|
---|
328 | QVector<QwtPainterCommand> commands;
|
---|
329 | QVector<QwtGraphic::PathInfo> pathInfos;
|
---|
330 |
|
---|
331 | QRectF boundingRect;
|
---|
332 | QRectF pointRect;
|
---|
333 |
|
---|
334 | QwtGraphic::RenderHints renderHints;
|
---|
335 | QTransform *initialTransform;
|
---|
336 | };
|
---|
337 |
|
---|
338 | /*!
|
---|
339 | \brief Constructor
|
---|
340 |
|
---|
341 | Initializes a null graphic
|
---|
342 | \sa isNull()
|
---|
343 | */
|
---|
344 | QwtGraphic::QwtGraphic():
|
---|
345 | QwtNullPaintDevice()
|
---|
346 | {
|
---|
347 | setMode( QwtNullPaintDevice::PathMode );
|
---|
348 | d_data = new PrivateData;
|
---|
349 | }
|
---|
350 |
|
---|
351 | /*!
|
---|
352 | \brief Copy constructor
|
---|
353 |
|
---|
354 | \param other Source
|
---|
355 | \sa operator=()
|
---|
356 | */
|
---|
357 | QwtGraphic::QwtGraphic( const QwtGraphic &other ):
|
---|
358 | QwtNullPaintDevice()
|
---|
359 | {
|
---|
360 | setMode( other.mode() );
|
---|
361 | d_data = new PrivateData( *other.d_data );
|
---|
362 | }
|
---|
363 |
|
---|
364 | //! Destructor
|
---|
365 | QwtGraphic::~QwtGraphic()
|
---|
366 | {
|
---|
367 | delete d_data;
|
---|
368 | }
|
---|
369 |
|
---|
370 | /*!
|
---|
371 | \brief Assignment operator
|
---|
372 |
|
---|
373 | \param other Source
|
---|
374 | \return A reference of this object
|
---|
375 | */
|
---|
376 | QwtGraphic& QwtGraphic::operator=(const QwtGraphic &other)
|
---|
377 | {
|
---|
378 | setMode( other.mode() );
|
---|
379 | *d_data = *other.d_data;
|
---|
380 |
|
---|
381 | return *this;
|
---|
382 | }
|
---|
383 |
|
---|
384 | /*!
|
---|
385 | \brief Clear all stored commands
|
---|
386 | \sa isNull()
|
---|
387 | */
|
---|
388 | void QwtGraphic::reset()
|
---|
389 | {
|
---|
390 | d_data->commands.clear();
|
---|
391 | d_data->pathInfos.clear();
|
---|
392 |
|
---|
393 | d_data->boundingRect = QRectF( 0.0, 0.0, -1.0, -1.0 );
|
---|
394 | d_data->pointRect = QRectF( 0.0, 0.0, -1.0, -1.0 );
|
---|
395 | d_data->defaultSize = QSizeF();
|
---|
396 |
|
---|
397 | }
|
---|
398 |
|
---|
399 | /*!
|
---|
400 | \return True, when no painter commands have been stored
|
---|
401 | \sa isEmpty(), commands()
|
---|
402 | */
|
---|
403 | bool QwtGraphic::isNull() const
|
---|
404 | {
|
---|
405 | return d_data->commands.isEmpty();
|
---|
406 | }
|
---|
407 |
|
---|
408 | /*!
|
---|
409 | \return True, when the bounding rectangle is empty
|
---|
410 | \sa boundingRect(), isNull()
|
---|
411 | */
|
---|
412 | bool QwtGraphic::isEmpty() const
|
---|
413 | {
|
---|
414 | return d_data->boundingRect.isEmpty();
|
---|
415 | }
|
---|
416 |
|
---|
417 | /*!
|
---|
418 | Toggle an render hint
|
---|
419 |
|
---|
420 | \param hint Render hint
|
---|
421 | \param on true/false
|
---|
422 |
|
---|
423 | \sa testRenderHint(), RenderHint
|
---|
424 | */
|
---|
425 | void QwtGraphic::setRenderHint( RenderHint hint, bool on )
|
---|
426 | {
|
---|
427 | if ( on )
|
---|
428 | d_data->renderHints |= hint;
|
---|
429 | else
|
---|
430 | d_data->renderHints &= ~hint;
|
---|
431 | }
|
---|
432 |
|
---|
433 | /*!
|
---|
434 | Test a render hint
|
---|
435 |
|
---|
436 | \param hint Render hint
|
---|
437 | \return true/false
|
---|
438 | \sa setRenderHint(), RenderHint
|
---|
439 | */
|
---|
440 | bool QwtGraphic::testRenderHint( RenderHint hint ) const
|
---|
441 | {
|
---|
442 | return d_data->renderHints.testFlag( hint );
|
---|
443 | }
|
---|
444 |
|
---|
445 | /*!
|
---|
446 | The bounding rectangle is the controlPointRect()
|
---|
447 | extended by the areas needed for rendering the outlines
|
---|
448 | with unscaled pens.
|
---|
449 |
|
---|
450 | \return Bounding rectangle of the graphic
|
---|
451 | \sa controlPointRect(), scaledBoundingRect()
|
---|
452 | */
|
---|
453 | QRectF QwtGraphic::boundingRect() const
|
---|
454 | {
|
---|
455 | if ( d_data->boundingRect.width() < 0 )
|
---|
456 | return QRectF();
|
---|
457 |
|
---|
458 | return d_data->boundingRect;
|
---|
459 | }
|
---|
460 |
|
---|
461 | /*!
|
---|
462 | The control point rectangle is the bounding rectangle
|
---|
463 | of all control points of the paths and the target
|
---|
464 | rectangles of the images/pixmaps.
|
---|
465 |
|
---|
466 | \return Control point rectangle
|
---|
467 | \sa boundingRect(), scaledBoundingRect()
|
---|
468 | */
|
---|
469 | QRectF QwtGraphic::controlPointRect() const
|
---|
470 | {
|
---|
471 | if ( d_data->pointRect.width() < 0 )
|
---|
472 | return QRectF();
|
---|
473 |
|
---|
474 | return d_data->pointRect;
|
---|
475 | }
|
---|
476 |
|
---|
477 | /*!
|
---|
478 | \brief Calculate the target rectangle for scaling the graphic
|
---|
479 |
|
---|
480 | \param sx Horizontal scaling factor
|
---|
481 | \param sy Vertical scaling factor
|
---|
482 |
|
---|
483 | \note In case of paths that are painted with a cosmetic pen
|
---|
484 | ( see QPen::isCosmetic() ) the target rectangle is different to
|
---|
485 | multiplying the bounding rectangle.
|
---|
486 |
|
---|
487 | \return Scaled bounding rectangle
|
---|
488 | \sa boundingRect(), controlPointRect()
|
---|
489 | */
|
---|
490 | QRectF QwtGraphic::scaledBoundingRect( double sx, double sy ) const
|
---|
491 | {
|
---|
492 | if ( sx == 1.0 && sy == 1.0 )
|
---|
493 | return d_data->boundingRect;
|
---|
494 |
|
---|
495 | QTransform transform;
|
---|
496 | transform.scale( sx, sy );
|
---|
497 |
|
---|
498 | QRectF rect = transform.mapRect( d_data->pointRect );
|
---|
499 |
|
---|
500 | for ( int i = 0; i < d_data->pathInfos.size(); i++ )
|
---|
501 | {
|
---|
502 | rect |= d_data->pathInfos[i].scaledBoundingRect( sx, sy,
|
---|
503 | !d_data->renderHints.testFlag( RenderPensUnscaled ) );
|
---|
504 | }
|
---|
505 |
|
---|
506 | return rect;
|
---|
507 | }
|
---|
508 |
|
---|
509 | //! \return Ceiled defaultSize()
|
---|
510 | QSize QwtGraphic::sizeMetrics() const
|
---|
511 | {
|
---|
512 | const QSizeF sz = defaultSize();
|
---|
513 | return QSize( qCeil( sz.width() ), qCeil( sz.height() ) );
|
---|
514 | }
|
---|
515 |
|
---|
516 | /*!
|
---|
517 | \brief Set a default size
|
---|
518 |
|
---|
519 | The default size is used in all methods rendering the graphic,
|
---|
520 | where no size is explicitly specified. Assigning an empty size
|
---|
521 | means, that the default size will be calculated from the bounding
|
---|
522 | rectangle.
|
---|
523 |
|
---|
524 | The default setting is an empty size.
|
---|
525 |
|
---|
526 | \param size Default size
|
---|
527 |
|
---|
528 | \sa defaultSize(), boundingRect()
|
---|
529 | */
|
---|
530 | void QwtGraphic::setDefaultSize( const QSizeF &size )
|
---|
531 | {
|
---|
532 | const double w = qMax( qreal( 0.0 ), size.width() );
|
---|
533 | const double h = qMax( qreal( 0.0 ), size.height() );
|
---|
534 |
|
---|
535 | d_data->defaultSize = QSizeF( w, h );
|
---|
536 | }
|
---|
537 |
|
---|
538 | /*!
|
---|
539 | \brief Default size
|
---|
540 |
|
---|
541 | When a non empty size has been assigned by setDefaultSize() this
|
---|
542 | size will be returned. Otherwise the default size is the size
|
---|
543 | of the bounding rectangle.
|
---|
544 |
|
---|
545 | The default size is used in all methods rendering the graphic,
|
---|
546 | where no size is explicitly specified.
|
---|
547 |
|
---|
548 | \return Default size
|
---|
549 | \sa setDefaultSize(), boundingRect()
|
---|
550 | */
|
---|
551 | QSizeF QwtGraphic::defaultSize() const
|
---|
552 | {
|
---|
553 | if ( !d_data->defaultSize.isEmpty() )
|
---|
554 | return d_data->defaultSize;
|
---|
555 |
|
---|
556 | return boundingRect().size();
|
---|
557 | }
|
---|
558 |
|
---|
559 | /*!
|
---|
560 | \brief Replay all recorded painter commands
|
---|
561 | \param painter Qt painter
|
---|
562 | */
|
---|
563 | void QwtGraphic::render( QPainter *painter ) const
|
---|
564 | {
|
---|
565 | if ( isNull() )
|
---|
566 | return;
|
---|
567 |
|
---|
568 | const int numCommands = d_data->commands.size();
|
---|
569 | const QwtPainterCommand *commands = d_data->commands.constData();
|
---|
570 |
|
---|
571 | const QTransform transform = painter->transform();
|
---|
572 |
|
---|
573 | painter->save();
|
---|
574 |
|
---|
575 | for ( int i = 0; i < numCommands; i++ )
|
---|
576 | {
|
---|
577 | qwtExecCommand( painter, commands[i],
|
---|
578 | d_data->renderHints, transform, d_data->initialTransform );
|
---|
579 | }
|
---|
580 |
|
---|
581 | painter->restore();
|
---|
582 | }
|
---|
583 |
|
---|
584 | /*!
|
---|
585 | \brief Replay all recorded painter commands
|
---|
586 |
|
---|
587 | The graphic is scaled to fit into the rectangle
|
---|
588 | of the given size starting at ( 0, 0 ).
|
---|
589 |
|
---|
590 | \param painter Qt painter
|
---|
591 | \param size Size for the scaled graphic
|
---|
592 | \param aspectRatioMode Mode how to scale - See Qt::AspectRatioMode
|
---|
593 | */
|
---|
594 | void QwtGraphic::render( QPainter *painter, const QSizeF &size,
|
---|
595 | Qt::AspectRatioMode aspectRatioMode ) const
|
---|
596 | {
|
---|
597 | const QRectF r( 0.0, 0.0, size.width(), size.height() );
|
---|
598 | render( painter, r, aspectRatioMode );
|
---|
599 | }
|
---|
600 |
|
---|
601 | /*!
|
---|
602 | \brief Replay all recorded painter commands
|
---|
603 |
|
---|
604 | The graphic is scaled to fit into the given rectangle
|
---|
605 |
|
---|
606 | \param painter Qt painter
|
---|
607 | \param rect Rectangle for the scaled graphic
|
---|
608 | \param aspectRatioMode Mode how to scale - See Qt::AspectRatioMode
|
---|
609 | */
|
---|
610 | void QwtGraphic::render( QPainter *painter, const QRectF &rect,
|
---|
611 | Qt::AspectRatioMode aspectRatioMode ) const
|
---|
612 | {
|
---|
613 | if ( isEmpty() || rect.isEmpty() )
|
---|
614 | return;
|
---|
615 |
|
---|
616 | double sx = 1.0;
|
---|
617 | double sy = 1.0;
|
---|
618 |
|
---|
619 | if ( d_data->pointRect.width() > 0.0 )
|
---|
620 | sx = rect.width() / d_data->pointRect.width();
|
---|
621 |
|
---|
622 | if ( d_data->pointRect.height() > 0.0 )
|
---|
623 | sy = rect.height() / d_data->pointRect.height();
|
---|
624 |
|
---|
625 | const bool scalePens =
|
---|
626 | !d_data->renderHints.testFlag( RenderPensUnscaled );
|
---|
627 |
|
---|
628 | for ( int i = 0; i < d_data->pathInfos.size(); i++ )
|
---|
629 | {
|
---|
630 | const PathInfo info = d_data->pathInfos[i];
|
---|
631 |
|
---|
632 | const double ssx = info.scaleFactorX(
|
---|
633 | d_data->pointRect, rect, scalePens );
|
---|
634 |
|
---|
635 | if ( ssx > 0.0 )
|
---|
636 | sx = qMin( sx, ssx );
|
---|
637 |
|
---|
638 | const double ssy = info.scaleFactorY(
|
---|
639 | d_data->pointRect, rect, scalePens );
|
---|
640 |
|
---|
641 | if ( ssy > 0.0 )
|
---|
642 | sy = qMin( sy, ssy );
|
---|
643 | }
|
---|
644 |
|
---|
645 | if ( aspectRatioMode == Qt::KeepAspectRatio )
|
---|
646 | {
|
---|
647 | const double s = qMin( sx, sy );
|
---|
648 | sx = s;
|
---|
649 | sy = s;
|
---|
650 | }
|
---|
651 | else if ( aspectRatioMode == Qt::KeepAspectRatioByExpanding )
|
---|
652 | {
|
---|
653 | const double s = qMax( sx, sy );
|
---|
654 | sx = s;
|
---|
655 | sy = s;
|
---|
656 | }
|
---|
657 |
|
---|
658 | QTransform tr;
|
---|
659 | tr.translate( rect.center().x() - 0.5 * sx * d_data->pointRect.width(),
|
---|
660 | rect.center().y() - 0.5 * sy * d_data->pointRect.height() );
|
---|
661 | tr.scale( sx, sy );
|
---|
662 | tr.translate( -d_data->pointRect.x(), -d_data->pointRect.y() );
|
---|
663 |
|
---|
664 | const QTransform transform = painter->transform();
|
---|
665 | if ( !scalePens && transform.isScaling() )
|
---|
666 | {
|
---|
667 | // we don't want to scale pens according to sx/sy,
|
---|
668 | // but we want to apply the scaling from the
|
---|
669 | // painter transformation later
|
---|
670 |
|
---|
671 | d_data->initialTransform = new QTransform();
|
---|
672 | d_data->initialTransform->scale( transform.m11(), transform.m22() );
|
---|
673 | }
|
---|
674 |
|
---|
675 | painter->setTransform( tr, true );
|
---|
676 | render( painter );
|
---|
677 |
|
---|
678 | painter->setTransform( transform );
|
---|
679 |
|
---|
680 | delete d_data->initialTransform;
|
---|
681 | d_data->initialTransform = NULL;
|
---|
682 | }
|
---|
683 |
|
---|
684 | /*!
|
---|
685 | \brief Replay all recorded painter commands
|
---|
686 |
|
---|
687 | The graphic is scaled to the defaultSize() and aligned
|
---|
688 | to a position.
|
---|
689 |
|
---|
690 | \param painter Qt painter
|
---|
691 | \param pos Reference point, where to render
|
---|
692 | \param alignment Flags how to align the target rectangle
|
---|
693 | to pos.
|
---|
694 | */
|
---|
695 | void QwtGraphic::render( QPainter *painter,
|
---|
696 | const QPointF &pos, Qt::Alignment alignment ) const
|
---|
697 | {
|
---|
698 | QRectF r( pos, defaultSize() );
|
---|
699 |
|
---|
700 | if ( alignment & Qt::AlignLeft )
|
---|
701 | {
|
---|
702 | r.moveLeft( pos.x() );
|
---|
703 | }
|
---|
704 | else if ( alignment & Qt::AlignHCenter )
|
---|
705 | {
|
---|
706 | r.moveCenter( QPointF( pos.x(), r.center().y() ) );
|
---|
707 | }
|
---|
708 | else if ( alignment & Qt::AlignRight )
|
---|
709 | {
|
---|
710 | r.moveRight( pos.x() );
|
---|
711 | }
|
---|
712 |
|
---|
713 | if ( alignment & Qt::AlignTop )
|
---|
714 | {
|
---|
715 | r.moveTop( pos.y() );
|
---|
716 | }
|
---|
717 | else if ( alignment & Qt::AlignVCenter )
|
---|
718 | {
|
---|
719 | r.moveCenter( QPointF( r.center().x(), pos.y() ) );
|
---|
720 | }
|
---|
721 | else if ( alignment & Qt::AlignBottom )
|
---|
722 | {
|
---|
723 | r.moveBottom( pos.y() );
|
---|
724 | }
|
---|
725 |
|
---|
726 | render( painter, r );
|
---|
727 | }
|
---|
728 |
|
---|
729 | /*!
|
---|
730 | \brief Convert the graphic to a QPixmap
|
---|
731 |
|
---|
732 | All pixels of the pixmap get initialized by Qt::transparent
|
---|
733 | before the graphic is scaled and rendered on it.
|
---|
734 |
|
---|
735 | The size of the pixmap is the default size ( ceiled to integers )
|
---|
736 | of the graphic.
|
---|
737 |
|
---|
738 | \return The graphic as pixmap in default size
|
---|
739 | \sa defaultSize(), toImage(), render()
|
---|
740 | */
|
---|
741 | QPixmap QwtGraphic::toPixmap() const
|
---|
742 | {
|
---|
743 | if ( isNull() )
|
---|
744 | return QPixmap();
|
---|
745 |
|
---|
746 | const QSizeF sz = defaultSize();
|
---|
747 |
|
---|
748 | const int w = qCeil( sz.width() );
|
---|
749 | const int h = qCeil( sz.height() );
|
---|
750 |
|
---|
751 | QPixmap pixmap( w, h );
|
---|
752 | pixmap.fill( Qt::transparent );
|
---|
753 |
|
---|
754 | const QRectF r( 0.0, 0.0, sz.width(), sz.height() );
|
---|
755 |
|
---|
756 | QPainter painter( &pixmap );
|
---|
757 | render( &painter, r, Qt::KeepAspectRatio );
|
---|
758 | painter.end();
|
---|
759 |
|
---|
760 | return pixmap;
|
---|
761 | }
|
---|
762 |
|
---|
763 | /*!
|
---|
764 | \brief Convert the graphic to a QPixmap
|
---|
765 |
|
---|
766 | All pixels of the pixmap get initialized by Qt::transparent
|
---|
767 | before the graphic is scaled and rendered on it.
|
---|
768 |
|
---|
769 | \param size Size of the image
|
---|
770 | \param aspectRatioMode Aspect ratio how to scale the graphic
|
---|
771 |
|
---|
772 | \return The graphic as pixmap
|
---|
773 | \sa toImage(), render()
|
---|
774 | */
|
---|
775 | QPixmap QwtGraphic::toPixmap( const QSize &size,
|
---|
776 | Qt::AspectRatioMode aspectRatioMode ) const
|
---|
777 | {
|
---|
778 | QPixmap pixmap( size );
|
---|
779 | pixmap.fill( Qt::transparent );
|
---|
780 |
|
---|
781 | const QRect r( 0, 0, size.width(), size.height() );
|
---|
782 |
|
---|
783 | QPainter painter( &pixmap );
|
---|
784 | render( &painter, r, aspectRatioMode );
|
---|
785 | painter.end();
|
---|
786 |
|
---|
787 | return pixmap;
|
---|
788 | }
|
---|
789 |
|
---|
790 | /*!
|
---|
791 | \brief Convert the graphic to a QImage
|
---|
792 |
|
---|
793 | All pixels of the image get initialized by 0 ( transparent )
|
---|
794 | before the graphic is scaled and rendered on it.
|
---|
795 |
|
---|
796 | The format of the image is QImage::Format_ARGB32_Premultiplied.
|
---|
797 |
|
---|
798 | \param size Size of the image
|
---|
799 | \param aspectRatioMode Aspect ratio how to scale the graphic
|
---|
800 |
|
---|
801 | \return The graphic as image
|
---|
802 | \sa toPixmap(), render()
|
---|
803 | */
|
---|
804 | QImage QwtGraphic::toImage( const QSize &size,
|
---|
805 | Qt::AspectRatioMode aspectRatioMode ) const
|
---|
806 | {
|
---|
807 | QImage image( size, QImage::Format_ARGB32_Premultiplied );
|
---|
808 | image.fill( 0 );
|
---|
809 |
|
---|
810 | const QRect r( 0, 0, size.width(), size.height() );
|
---|
811 |
|
---|
812 | QPainter painter( &image );
|
---|
813 | render( &painter, r, aspectRatioMode );
|
---|
814 | painter.end();
|
---|
815 |
|
---|
816 | return image;
|
---|
817 | }
|
---|
818 |
|
---|
819 | /*!
|
---|
820 | \brief Convert the graphic to a QImage
|
---|
821 |
|
---|
822 | All pixels of the image get initialized by 0 ( transparent )
|
---|
823 | before the graphic is scaled and rendered on it.
|
---|
824 |
|
---|
825 | The format of the image is QImage::Format_ARGB32_Premultiplied.
|
---|
826 |
|
---|
827 | The size of the image is the default size ( ceiled to integers )
|
---|
828 | of the graphic.
|
---|
829 |
|
---|
830 | \return The graphic as image in default size
|
---|
831 | \sa defaultSize(), toPixmap(), render()
|
---|
832 | */
|
---|
833 | QImage QwtGraphic::toImage() const
|
---|
834 | {
|
---|
835 | if ( isNull() )
|
---|
836 | return QImage();
|
---|
837 |
|
---|
838 | const QSizeF sz = defaultSize();
|
---|
839 |
|
---|
840 | const int w = qCeil( sz.width() );
|
---|
841 | const int h = qCeil( sz.height() );
|
---|
842 |
|
---|
843 | QImage image( w, h, QImage::Format_ARGB32 );
|
---|
844 | image.fill( 0 );
|
---|
845 |
|
---|
846 | const QRect r( 0, 0, sz.width(), sz.height() );
|
---|
847 |
|
---|
848 | QPainter painter( &image );
|
---|
849 | render( &painter, r, Qt::KeepAspectRatio );
|
---|
850 | painter.end();
|
---|
851 |
|
---|
852 | return image;
|
---|
853 | }
|
---|
854 |
|
---|
855 | /*!
|
---|
856 | Store a path command in the command list
|
---|
857 |
|
---|
858 | \param path Painter path
|
---|
859 | \sa QPaintEngine::drawPath()
|
---|
860 | */
|
---|
861 | void QwtGraphic::drawPath( const QPainterPath &path )
|
---|
862 | {
|
---|
863 | const QPainter *painter = paintEngine()->painter();
|
---|
864 | if ( painter == NULL )
|
---|
865 | return;
|
---|
866 |
|
---|
867 | d_data->commands += QwtPainterCommand( path );
|
---|
868 |
|
---|
869 | if ( !path.isEmpty() )
|
---|
870 | {
|
---|
871 | const QPainterPath scaledPath = painter->transform().map( path );
|
---|
872 |
|
---|
873 | QRectF pointRect = scaledPath.boundingRect();
|
---|
874 | QRectF boundingRect = pointRect;
|
---|
875 |
|
---|
876 | if ( painter->pen().style() != Qt::NoPen
|
---|
877 | && painter->pen().brush().style() != Qt::NoBrush )
|
---|
878 | {
|
---|
879 | boundingRect = qwtStrokedPathRect( painter, path );
|
---|
880 | }
|
---|
881 |
|
---|
882 | updateControlPointRect( pointRect );
|
---|
883 | updateBoundingRect( boundingRect );
|
---|
884 |
|
---|
885 | d_data->pathInfos += PathInfo( pointRect,
|
---|
886 | boundingRect, qwtHasScalablePen( painter ) );
|
---|
887 | }
|
---|
888 | }
|
---|
889 |
|
---|
890 | /*!
|
---|
891 | \brief Store a pixmap command in the command list
|
---|
892 |
|
---|
893 | \param rect target rectangle
|
---|
894 | \param pixmap Pixmap to be painted
|
---|
895 | \param subRect Reactangle of the pixmap to be painted
|
---|
896 |
|
---|
897 | \sa QPaintEngine::drawPixmap()
|
---|
898 | */
|
---|
899 | void QwtGraphic::drawPixmap( const QRectF &rect,
|
---|
900 | const QPixmap &pixmap, const QRectF &subRect )
|
---|
901 | {
|
---|
902 | const QPainter *painter = paintEngine()->painter();
|
---|
903 | if ( painter == NULL )
|
---|
904 | return;
|
---|
905 |
|
---|
906 | d_data->commands += QwtPainterCommand( rect, pixmap, subRect );
|
---|
907 |
|
---|
908 | const QRectF r = painter->transform().mapRect( rect );
|
---|
909 | updateControlPointRect( r );
|
---|
910 | updateBoundingRect( r );
|
---|
911 | }
|
---|
912 |
|
---|
913 | /*!
|
---|
914 | \brief Store a image command in the command list
|
---|
915 |
|
---|
916 | \param rect traget rectangle
|
---|
917 | \param image Image to be painted
|
---|
918 | \param subRect Reactangle of the pixmap to be painted
|
---|
919 | \param flags Image conversion flags
|
---|
920 |
|
---|
921 | \sa QPaintEngine::drawImage()
|
---|
922 | */
|
---|
923 | void QwtGraphic::drawImage( const QRectF &rect, const QImage &image,
|
---|
924 | const QRectF &subRect, Qt::ImageConversionFlags flags)
|
---|
925 | {
|
---|
926 | const QPainter *painter = paintEngine()->painter();
|
---|
927 | if ( painter == NULL )
|
---|
928 | return;
|
---|
929 |
|
---|
930 | d_data->commands += QwtPainterCommand( rect, image, subRect, flags );
|
---|
931 |
|
---|
932 | const QRectF r = painter->transform().mapRect( rect );
|
---|
933 |
|
---|
934 | updateControlPointRect( r );
|
---|
935 | updateBoundingRect( r );
|
---|
936 | }
|
---|
937 |
|
---|
938 | /*!
|
---|
939 | \brief Store a state command in the command list
|
---|
940 |
|
---|
941 | \param state State to be stored
|
---|
942 | \sa QPaintEngine::updateState()
|
---|
943 | */
|
---|
944 | void QwtGraphic::updateState( const QPaintEngineState &state)
|
---|
945 | {
|
---|
946 | d_data->commands += QwtPainterCommand( state );
|
---|
947 | }
|
---|
948 |
|
---|
949 | void QwtGraphic::updateBoundingRect( const QRectF &rect )
|
---|
950 | {
|
---|
951 | QRectF br = rect;
|
---|
952 |
|
---|
953 | const QPainter *painter = paintEngine()->painter();
|
---|
954 | if ( painter && painter->hasClipping() )
|
---|
955 | {
|
---|
956 | QRectF cr = painter->clipRegion().boundingRect();
|
---|
957 | cr = painter->transform().mapRect( cr );
|
---|
958 |
|
---|
959 | br &= cr;
|
---|
960 | }
|
---|
961 |
|
---|
962 | if ( d_data->boundingRect.width() < 0 )
|
---|
963 | d_data->boundingRect = br;
|
---|
964 | else
|
---|
965 | d_data->boundingRect |= br;
|
---|
966 | }
|
---|
967 |
|
---|
968 | void QwtGraphic::updateControlPointRect( const QRectF &rect )
|
---|
969 | {
|
---|
970 | if ( d_data->pointRect.width() < 0.0 )
|
---|
971 | d_data->pointRect = rect;
|
---|
972 | else
|
---|
973 | d_data->pointRect |= rect;
|
---|
974 | }
|
---|
975 |
|
---|
976 | /*!
|
---|
977 | \return List of recorded paint commands
|
---|
978 | \sa setCommands()
|
---|
979 | */
|
---|
980 | const QVector< QwtPainterCommand > &QwtGraphic::commands() const
|
---|
981 | {
|
---|
982 | return d_data->commands;
|
---|
983 | }
|
---|
984 |
|
---|
985 | /*!
|
---|
986 | \brief Append paint commands
|
---|
987 |
|
---|
988 | \param commands Paint commands
|
---|
989 | \sa commands()
|
---|
990 | */
|
---|
991 | void QwtGraphic::setCommands( QVector< QwtPainterCommand > &commands )
|
---|
992 | {
|
---|
993 | reset();
|
---|
994 |
|
---|
995 | const int numCommands = commands.size();
|
---|
996 | if ( numCommands <= 0 )
|
---|
997 | return;
|
---|
998 |
|
---|
999 | // to calculate a proper bounding rectangle we don't simply copy
|
---|
1000 | // the commands.
|
---|
1001 |
|
---|
1002 | const QwtPainterCommand *cmds = commands.constData();
|
---|
1003 |
|
---|
1004 | QPainter painter( this );
|
---|
1005 | for ( int i = 0; i < numCommands; i++ )
|
---|
1006 | qwtExecCommand( &painter, cmds[i], RenderHints(), QTransform(), NULL );
|
---|
1007 |
|
---|
1008 | painter.end();
|
---|
1009 | }
|
---|