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 
qwtHasScalablePen(const QPainter * painter)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 
qwtStrokedPathRect(const QPainter * painter,const QPainterPath & path)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 
qwtExecCommand(QPainter * painter,const QwtPainterCommand & cmd,QwtGraphic::RenderHints renderHints,const QTransform & transform,const QTransform * initialTransform)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:
PathInfo()206     PathInfo():
207         d_scalablePen( false )
208     {
209         // QVector needs a default constructor
210     }
211 
PathInfo(const QRectF & pointRect,const QRectF & boundingRect,bool scalablePen)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 
scaledBoundingRect(double sx,double sy,bool scalePens) const220     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 
scaleFactorX(const QRectF & pathRect,const QRectF & targetRect,bool scalePens) const249     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 
scaleFactorY(const QRectF & pathRect,const QRectF & targetRect,bool scalePens) const280     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:
PrivateData()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  */
QwtGraphic()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  */
QwtGraphic(const QwtGraphic & other)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
~QwtGraphic()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  */
operator =(const QwtGraphic & other)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  */
reset()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 */
isNull() const403 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 */
isEmpty() const412 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 */
setRenderHint(RenderHint hint,bool on)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 */
testRenderHint(RenderHint hint) const440 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  */
boundingRect() const453 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  */
controlPointRect() const469 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  */
scaledBoundingRect(double sx,double sy) const490 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()
sizeMetrics() const510 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  */
setDefaultSize(const QSizeF & size)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  */
defaultSize() const551 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  */
render(QPainter * painter) const563 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  */
render(QPainter * painter,const QSizeF & size,Qt::AspectRatioMode aspectRatioMode) const594 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  */
render(QPainter * painter,const QRectF & rect,Qt::AspectRatioMode aspectRatioMode) const610 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  */
render(QPainter * painter,const QPointF & pos,Qt::Alignment alignment) const695 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  */
toPixmap() const741 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  */
toPixmap(const QSize & size,Qt::AspectRatioMode aspectRatioMode) const775 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  */
toImage(const QSize & size,Qt::AspectRatioMode aspectRatioMode) const804 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  */
toImage() const833 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 */
drawPath(const QPainterPath & path)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 */
drawPixmap(const QRectF & rect,const QPixmap & pixmap,const QRectF & subRect)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  */
drawImage(const QRectF & rect,const QImage & image,const QRectF & subRect,Qt::ImageConversionFlags flags)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  */
updateState(const QPaintEngineState & state)944 void QwtGraphic::updateState( const QPaintEngineState &state)
945 {
946     d_data->commands += QwtPainterCommand( state );
947 }
948 
updateBoundingRect(const QRectF & rect)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 
updateControlPointRect(const QRectF & rect)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  */
commands() const980 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  */
setCommands(QVector<QwtPainterCommand> & commands)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 }
1010