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_plot_directpainter.h"
11 #include "qwt_scale_map.h"
12 #include "qwt_plot.h"
13 #include "qwt_plot_canvas.h"
14 #include "qwt_plot_seriesitem.h"
15 #include <qpainter.h>
16 #include <qevent.h>
17 #include <qapplication.h>
18 #include <qpixmap.h>
19 
qwtRenderItem(QPainter * painter,const QRect & canvasRect,QwtPlotSeriesItem * seriesItem,int from,int to)20 static inline void qwtRenderItem(
21     QPainter *painter, const QRect &canvasRect,
22     QwtPlotSeriesItem *seriesItem, int from, int to )
23 {
24     // A minor performance improvement is possible
25     // with caching the maps. TODO ...
26 
27     QwtPlot *plot = seriesItem->plot();
28     const QwtScaleMap xMap = plot->canvasMap( seriesItem->xAxis() );
29     const QwtScaleMap yMap = plot->canvasMap( seriesItem->yAxis() );
30 
31     painter->setRenderHint( QPainter::Antialiasing,
32         seriesItem->testRenderHint( QwtPlotItem::RenderAntialiased ) );
33     seriesItem->drawSeries( painter, xMap, yMap, canvasRect, from, to );
34 }
35 
qwtHasBackingStore(const QwtPlotCanvas * canvas)36 static inline bool qwtHasBackingStore( const QwtPlotCanvas *canvas )
37 {
38     return canvas->testPaintAttribute( QwtPlotCanvas::BackingStore )
39         && canvas->backingStore() && !canvas->backingStore()->isNull();
40 }
41 
42 class QwtPlotDirectPainter::PrivateData
43 {
44 public:
PrivateData()45     PrivateData():
46         attributes( 0 ),
47         hasClipping(false),
48         seriesItem( NULL ),
49         from( 0 ),
50         to( 0 )
51     {
52     }
53 
54     QwtPlotDirectPainter::Attributes attributes;
55 
56     bool hasClipping;
57     QRegion clipRegion;
58 
59     QPainter painter;
60 
61     QwtPlotSeriesItem *seriesItem;
62     int from;
63     int to;
64 };
65 
66 //! Constructor
QwtPlotDirectPainter(QObject * parent)67 QwtPlotDirectPainter::QwtPlotDirectPainter( QObject *parent ):
68     QObject( parent )
69 {
70     d_data = new PrivateData;
71 }
72 
73 //! Destructor
~QwtPlotDirectPainter()74 QwtPlotDirectPainter::~QwtPlotDirectPainter()
75 {
76     delete d_data;
77 }
78 
79 /*!
80   Change an attribute
81 
82   \param attribute Attribute to change
83   \param on On/Off
84 
85   \sa Attribute, testAttribute()
86 */
setAttribute(Attribute attribute,bool on)87 void QwtPlotDirectPainter::setAttribute( Attribute attribute, bool on )
88 {
89     if ( bool( d_data->attributes & attribute ) != on )
90     {
91         if ( on )
92             d_data->attributes |= attribute;
93         else
94             d_data->attributes &= ~attribute;
95 
96         if ( ( attribute == AtomicPainter ) && on )
97             reset();
98     }
99 }
100 
101 /*!
102   \return True, when attribute is enabled
103   \param attribute Attribute to be tested
104   \sa Attribute, setAttribute()
105 */
testAttribute(Attribute attribute) const106 bool QwtPlotDirectPainter::testAttribute( Attribute attribute ) const
107 {
108     return d_data->attributes & attribute;
109 }
110 
111 /*!
112   En/Disables clipping
113 
114   \param enable Enables clipping is true, disable it otherwise
115   \sa hasClipping(), clipRegion(), setClipRegion()
116 */
setClipping(bool enable)117 void QwtPlotDirectPainter::setClipping( bool enable )
118 {
119     d_data->hasClipping = enable;
120 }
121 
122 /*!
123   \return true, when clipping is enabled
124   \sa setClipping(), clipRegion(), setClipRegion()
125 */
hasClipping() const126 bool QwtPlotDirectPainter::hasClipping() const
127 {
128     return d_data->hasClipping;
129 }
130 
131 /*!
132    \brief Assign a clip region and enable clipping
133 
134    Depending on the environment setting a proper clip region might improve
135    the performance heavily. F.e. on Qt embedded only the clipped part of
136    the backing store will be copied to a ( maybe unaccelerated ) frame buffer
137    device.
138 
139    \param region Clip region
140    \sa clipRegion(), hasClipping(), setClipping()
141 */
setClipRegion(const QRegion & region)142 void QwtPlotDirectPainter::setClipRegion( const QRegion &region )
143 {
144     d_data->clipRegion = region;
145     d_data->hasClipping = true;
146 }
147 
148 /*!
149    \return Currently set clip region.
150    \sa setClipRegion(), setClipping(), hasClipping()
151 */
clipRegion() const152 QRegion QwtPlotDirectPainter::clipRegion() const
153 {
154     return d_data->clipRegion;
155 }
156 
157 /*!
158   \brief Draw a set of points of a seriesItem.
159 
160   When observing an measurement while it is running, new points have to be
161   added to an existing seriesItem. drawSeries() can be used to display them avoiding
162   a complete redraw of the canvas.
163 
164   Setting plot()->canvas()->setAttribute(Qt::WA_PaintOutsidePaintEvent, true);
165   will result in faster painting, if the paint engine of the canvas widget
166   supports this feature.
167 
168   \param seriesItem Item to be painted
169   \param from Index of the first point to be painted
170   \param to Index of the last point to be painted. If to < 0 the
171          series will be painted to its last point.
172 */
drawSeries(QwtPlotSeriesItem * seriesItem,int from,int to)173 void QwtPlotDirectPainter::drawSeries(
174     QwtPlotSeriesItem *seriesItem, int from, int to )
175 {
176     if ( seriesItem == NULL || seriesItem->plot() == NULL )
177         return;
178 
179     QWidget *canvas = seriesItem->plot()->canvas();
180     const QRect canvasRect = canvas->contentsRect();
181 
182     QwtPlotCanvas *plotCanvas = qobject_cast<QwtPlotCanvas *>( canvas );
183 
184     if ( plotCanvas && qwtHasBackingStore( plotCanvas ) )
185     {
186         QPainter painter( const_cast<QPixmap *>( plotCanvas->backingStore() ) );
187 
188         if ( d_data->hasClipping )
189             painter.setClipRegion( d_data->clipRegion );
190 
191         qwtRenderItem( &painter, canvasRect, seriesItem, from, to );
192 
193         painter.end();
194 
195         if ( testAttribute( QwtPlotDirectPainter::FullRepaint ) )
196         {
197             plotCanvas->repaint();
198             return;
199         }
200     }
201 
202     bool immediatePaint = true;
203     if ( !canvas->testAttribute( Qt::WA_WState_InPaintEvent ) )
204     {
205 #if QT_VERSION < 0x050000
206         if ( !canvas->testAttribute( Qt::WA_PaintOutsidePaintEvent ) )
207 #endif
208             immediatePaint = false;
209     }
210 
211     if ( immediatePaint )
212     {
213         if ( !d_data->painter.isActive() )
214         {
215             reset();
216 
217             d_data->painter.begin( canvas );
218             canvas->installEventFilter( this );
219         }
220 
221         if ( d_data->hasClipping )
222         {
223             d_data->painter.setClipRegion(
224                 QRegion( canvasRect ) & d_data->clipRegion );
225         }
226         else
227         {
228             if ( !d_data->painter.hasClipping() )
229                 d_data->painter.setClipRect( canvasRect );
230         }
231 
232         qwtRenderItem( &d_data->painter, canvasRect, seriesItem, from, to );
233 
234         if ( d_data->attributes & QwtPlotDirectPainter::AtomicPainter )
235         {
236             reset();
237         }
238         else
239         {
240             if ( d_data->hasClipping )
241                 d_data->painter.setClipping( false );
242         }
243     }
244     else
245     {
246         reset();
247 
248         d_data->seriesItem = seriesItem;
249         d_data->from = from;
250         d_data->to = to;
251 
252         QRegion clipRegion = canvasRect;
253         if ( d_data->hasClipping )
254             clipRegion &= d_data->clipRegion;
255 
256         canvas->installEventFilter( this );
257         canvas->repaint(clipRegion);
258         canvas->removeEventFilter( this );
259 
260         d_data->seriesItem = NULL;
261     }
262 }
263 
264 //! Close the internal QPainter
reset()265 void QwtPlotDirectPainter::reset()
266 {
267     if ( d_data->painter.isActive() )
268     {
269         QWidget *w = static_cast<QWidget *>( d_data->painter.device() );
270         if ( w )
271             w->removeEventFilter( this );
272 
273         d_data->painter.end();
274     }
275 }
276 
277 //! Event filter
eventFilter(QObject *,QEvent * event)278 bool QwtPlotDirectPainter::eventFilter( QObject *, QEvent *event )
279 {
280     if ( event->type() == QEvent::Paint )
281     {
282         reset();
283 
284         if ( d_data->seriesItem )
285         {
286             const QPaintEvent *pe = static_cast< QPaintEvent *>( event );
287 
288             QWidget *canvas = d_data->seriesItem->plot()->canvas();
289 
290             QPainter painter( canvas );
291             painter.setClipRegion( pe->region() );
292 
293             bool doCopyCache = testAttribute( CopyBackingStore );
294 
295             if ( doCopyCache )
296             {
297                 QwtPlotCanvas *plotCanvas =
298                     qobject_cast<QwtPlotCanvas *>( canvas );
299                 if ( plotCanvas )
300                 {
301                     doCopyCache = qwtHasBackingStore( plotCanvas );
302                     if ( doCopyCache )
303                     {
304                         painter.drawPixmap( plotCanvas->rect().topLeft(),
305                             *plotCanvas->backingStore() );
306                     }
307                 }
308             }
309 
310             if ( !doCopyCache )
311             {
312                 qwtRenderItem( &painter, canvas->contentsRect(),
313                     d_data->seriesItem, d_data->from, d_data->to );
314             }
315 
316             return true; // don't call QwtPlotCanvas::paintEvent()
317         }
318     }
319 
320     return false;
321 }
322