source: ntrip/trunk/BNC/qwt/qwt_painter.cpp@ 7262

Last change on this file since 7262 was 4271, checked in by mervart, 12 years ago
File size: 19.1 KB
Line 
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_painter.h"
11#include "qwt_math.h"
12#include "qwt_clipper.h"
13#include "qwt_color_map.h"
14#include "qwt_scale_map.h"
15#include <qwindowdefs.h>
16#include <qwidget.h>
17#include <qframe.h>
18#include <qrect.h>
19#include <qpainter.h>
20#include <qpalette.h>
21#include <qpaintdevice.h>
22#include <qpixmap.h>
23#include <qstyle.h>
24#include <qtextdocument.h>
25#include <qabstracttextdocumentlayout.h>
26#include <qstyleoption.h>
27#include <qpaintengine.h>
28#include <qapplication.h>
29#include <qdesktopwidget.h>
30
31bool QwtPainter::d_polylineSplitting = true;
32bool QwtPainter::d_roundingAlignment = true;
33
34static inline bool isClippingNeeded( const QPainter *painter, QRectF &clipRect )
35{
36 bool doClipping = false;
37 const QPaintEngine *pe = painter->paintEngine();
38 if ( pe && pe->type() == QPaintEngine::SVG )
39 {
40 // The SVG paint engine ignores any clipping,
41
42 if ( painter->hasClipping() )
43 {
44 doClipping = true;
45 clipRect = painter->clipRegion().boundingRect();
46 }
47 }
48
49 return doClipping;
50}
51
52static inline void drawPolyline( QPainter *painter,
53 const QPointF *points, int pointCount, bool polylineSplitting )
54{
55 bool doSplit = false;
56 if ( polylineSplitting )
57 {
58 const QPaintEngine *pe = painter->paintEngine();
59 if ( pe && pe->type() == QPaintEngine::Raster )
60 {
61 /*
62 The raster paint engine seems to use some algo with O(n*n).
63 ( Qt 4.3 is better than Qt 4.2, but remains unacceptable)
64 To work around this problem, we have to split the polygon into
65 smaller pieces.
66 */
67 doSplit = true;
68 }
69 }
70
71 if ( doSplit )
72 {
73 const int splitSize = 20;
74 for ( int i = 0; i < pointCount; i += splitSize )
75 {
76 const int n = qMin( splitSize + 1, pointCount - i );
77 painter->drawPolyline( points + i, n );
78 }
79 }
80 else
81 painter->drawPolyline( points, pointCount );
82}
83
84static inline void unscaleFont( QPainter *painter )
85{
86 if ( painter->font().pixelSize() >= 0 )
87 return;
88
89 static QSize screenResolution;
90 if ( !screenResolution.isValid() )
91 {
92 QDesktopWidget *desktop = QApplication::desktop();
93 if ( desktop )
94 {
95 screenResolution.setWidth( desktop->logicalDpiX() );
96 screenResolution.setHeight( desktop->logicalDpiY() );
97 }
98 }
99
100 const QPaintDevice *pd = painter->device();
101 if ( pd->logicalDpiX() != screenResolution.width() ||
102 pd->logicalDpiY() != screenResolution.height() )
103 {
104 QFont pixelFont( painter->font(), QApplication::desktop() );
105 pixelFont.setPixelSize( QFontInfo( pixelFont ).pixelSize() );
106
107 painter->setFont( pixelFont );
108 }
109}
110
111/*!
112 Check if the painter is using a paint engine, that aligns
113 coordinates to integers. Today these are all paint engines
114 beside QPaintEngine::Pdf and QPaintEngine::SVG.
115
116 \param painter Painter
117 \return true, when the paint engine is aligning
118
119 \sa setRoundingAlignment()
120*/
121bool QwtPainter::isAligning( QPainter *painter )
122{
123 if ( painter && painter->isActive() )
124 {
125 switch ( painter->paintEngine()->type() )
126 {
127 case QPaintEngine::Pdf:
128 case QPaintEngine::SVG:
129 return false;
130
131 default:;
132 }
133 }
134
135 return true;
136}
137
138/*!
139 Enable whether coordinates should be rounded, before they are painted
140 to a paint engine that floors to integer values. For other paint engines
141 this ( Pdf, SVG ), this flag has no effect.
142 QwtPainter stores this flag only, the rounding itsself is done in
143 the painting code ( f.e the plot items ).
144
145 The default setting is true.
146
147 \sa roundingAlignment(), isAligning()
148*/
149void QwtPainter::setRoundingAlignment( bool enable )
150{
151 d_roundingAlignment = enable;
152}
153
154/*!
155 \brief En/Disable line splitting for the raster paint engine
156
157 The raster paint engine paints polylines of many points
158 much faster when they are splitted in smaller chunks.
159
160 \sa polylineSplitting()
161*/
162void QwtPainter::setPolylineSplitting( bool enable )
163{
164 d_polylineSplitting = enable;
165}
166
167//! Wrapper for QPainter::drawPath()
168void QwtPainter::drawPath( QPainter *painter, const QPainterPath &path )
169{
170 painter->drawPath( path );
171}
172
173//! Wrapper for QPainter::drawRect()
174void QwtPainter::drawRect( QPainter *painter, double x, double y, double w, double h )
175{
176 drawRect( painter, QRectF( x, y, w, h ) );
177}
178
179//! Wrapper for QPainter::drawRect()
180void QwtPainter::drawRect( QPainter *painter, const QRectF &rect )
181{
182 const QRectF r = rect;
183
184 QRectF clipRect;
185 const bool deviceClipping = isClippingNeeded( painter, clipRect );
186
187 if ( deviceClipping )
188 {
189 if ( !clipRect.intersects( r ) )
190 return;
191
192 if ( !clipRect.contains( r ) )
193 {
194 fillRect( painter, r & clipRect, painter->brush() );
195
196 painter->save();
197 painter->setBrush( Qt::NoBrush );
198 drawPolyline( painter, QPolygonF( r ) );
199 painter->restore();
200
201 return;
202 }
203 }
204
205 painter->drawRect( r );
206}
207
208//! Wrapper for QPainter::fillRect()
209void QwtPainter::fillRect( QPainter *painter,
210 const QRectF &rect, const QBrush &brush )
211{
212 if ( !rect.isValid() )
213 return;
214
215 QRectF clipRect;
216 const bool deviceClipping = isClippingNeeded( painter, clipRect );
217
218 /*
219 Performance of Qt4 is horrible for non trivial brushs. Without
220 clipping expect minutes or hours for repainting large rects
221 (might result from zooming)
222 */
223
224 if ( deviceClipping )
225 clipRect &= painter->window();
226 else
227 clipRect = painter->window();
228
229 if ( painter->hasClipping() )
230 clipRect &= painter->clipRegion().boundingRect();
231
232 QRectF r = rect;
233 if ( deviceClipping )
234 r = r.intersect( clipRect );
235
236 if ( r.isValid() )
237 painter->fillRect( r, brush );
238}
239
240//! Wrapper for QPainter::drawPie()
241void QwtPainter::drawPie( QPainter *painter, const QRectF &rect,
242 int a, int alen )
243{
244 QRectF clipRect;
245 const bool deviceClipping = isClippingNeeded( painter, clipRect );
246 if ( deviceClipping && !clipRect.contains( rect ) )
247 return;
248
249 painter->drawPie( rect, a, alen );
250}
251
252//! Wrapper for QPainter::drawEllipse()
253void QwtPainter::drawEllipse( QPainter *painter, const QRectF &rect )
254{
255 QRectF clipRect;
256 const bool deviceClipping = isClippingNeeded( painter, clipRect );
257
258 if ( deviceClipping && !clipRect.contains( rect ) )
259 return;
260
261 painter->drawEllipse( rect );
262}
263
264//! Wrapper for QPainter::drawText()
265void QwtPainter::drawText( QPainter *painter, double x, double y,
266 const QString &text )
267{
268 drawText( painter, QPointF( x, y ), text );
269}
270
271//! Wrapper for QPainter::drawText()
272void QwtPainter::drawText( QPainter *painter, const QPointF &pos,
273 const QString &text )
274{
275 QRectF clipRect;
276 const bool deviceClipping = isClippingNeeded( painter, clipRect );
277
278 if ( deviceClipping && !clipRect.contains( pos ) )
279 return;
280
281
282 painter->save();
283 unscaleFont( painter );
284 painter->drawText( pos, text );
285 painter->restore();
286}
287
288//! Wrapper for QPainter::drawText()
289void QwtPainter::drawText( QPainter *painter,
290 double x, double y, double w, double h,
291 int flags, const QString &text )
292{
293 drawText( painter, QRectF( x, y, w, h ), flags, text );
294}
295
296//! Wrapper for QPainter::drawText()
297void QwtPainter::drawText( QPainter *painter, const QRectF &rect,
298 int flags, const QString &text )
299{
300 painter->save();
301 unscaleFont( painter );
302 painter->drawText( rect, flags, text );
303 painter->restore();
304}
305
306#ifndef QT_NO_RICHTEXT
307
308/*!
309 Draw a text document into a rectangle
310
311 \param painter Painter
312 \param rect Traget rectangle
313 \param flags Alignments/Text flags, see QPainter::drawText()
314 \param text Text document
315*/
316void QwtPainter::drawSimpleRichText( QPainter *painter, const QRectF &rect,
317 int flags, const QTextDocument &text )
318{
319 QTextDocument *txt = text.clone();
320
321 painter->save();
322
323 painter->setFont( txt->defaultFont() );
324 unscaleFont( painter );
325
326 txt->setDefaultFont( painter->font() );
327 txt->setPageSize( QSizeF( rect.width(), QWIDGETSIZE_MAX ) );
328
329 QAbstractTextDocumentLayout* layout = txt->documentLayout();
330
331 const double height = layout->documentSize().height();
332 double y = rect.y();
333 if ( flags & Qt::AlignBottom )
334 y += ( rect.height() - height );
335 else if ( flags & Qt::AlignVCenter )
336 y += ( rect.height() - height ) / 2;
337
338 QAbstractTextDocumentLayout::PaintContext context;
339 context.palette.setColor( QPalette::Text, painter->pen().color() );
340
341 painter->translate( rect.x(), y );
342 layout->draw( painter, context );
343
344 painter->restore();
345 delete txt;
346}
347
348#endif // !QT_NO_RICHTEXT
349
350
351//! Wrapper for QPainter::drawLine()
352void QwtPainter::drawLine( QPainter *painter,
353 const QPointF &p1, const QPointF &p2 )
354{
355 QRectF clipRect;
356 const bool deviceClipping = isClippingNeeded( painter, clipRect );
357
358 if ( deviceClipping &&
359 !( clipRect.contains( p1 ) && clipRect.contains( p2 ) ) )
360 {
361 QPolygonF polygon;
362 polygon += p1;
363 polygon += p2;
364 drawPolyline( painter, polygon );
365 return;
366 }
367
368 painter->drawLine( p1, p2 );
369}
370
371//! Wrapper for QPainter::drawPolygon()
372void QwtPainter::drawPolygon( QPainter *painter, const QPolygonF &polygon )
373{
374 QRectF clipRect;
375 const bool deviceClipping = isClippingNeeded( painter, clipRect );
376
377 QPolygonF cpa = polygon;
378 if ( deviceClipping )
379 cpa = QwtClipper::clipPolygonF( clipRect, polygon );
380
381 painter->drawPolygon( cpa );
382}
383
384//! Wrapper for QPainter::drawPolyline()
385void QwtPainter::drawPolyline( QPainter *painter, const QPolygonF &polygon )
386{
387 QRectF clipRect;
388 const bool deviceClipping = isClippingNeeded( painter, clipRect );
389
390 QPolygonF cpa = polygon;
391 if ( deviceClipping )
392 cpa = QwtClipper::clipPolygonF( clipRect, cpa );
393
394 ::drawPolyline( painter,
395 cpa.constData(), cpa.size(), d_polylineSplitting );
396}
397
398//! Wrapper for QPainter::drawPolyline()
399void QwtPainter::drawPolyline( QPainter *painter,
400 const QPointF *points, int pointCount )
401{
402 QRectF clipRect;
403 const bool deviceClipping = isClippingNeeded( painter, clipRect );
404
405 if ( deviceClipping )
406 {
407 QPolygonF polygon( pointCount );
408 qMemCopy( polygon.data(), points, pointCount * sizeof( QPointF ) );
409
410 polygon = QwtClipper::clipPolygonF( clipRect, polygon );
411 ::drawPolyline( painter,
412 polygon.constData(), polygon.size(), d_polylineSplitting );
413 }
414 else
415 ::drawPolyline( painter, points, pointCount, d_polylineSplitting );
416}
417
418//! Wrapper for QPainter::drawPoint()
419void QwtPainter::drawPoint( QPainter *painter, const QPointF &pos )
420{
421 QRectF clipRect;
422 const bool deviceClipping = isClippingNeeded( painter, clipRect );
423
424 if ( deviceClipping && !clipRect.contains( pos ) )
425 return;
426
427 painter->drawPoint( pos );
428}
429
430//! Wrapper for QPainter::drawImage()
431void QwtPainter::drawImage( QPainter *painter,
432 const QRectF &rect, const QImage &image )
433{
434 const QRect alignedRect = rect.toAlignedRect();
435
436 if ( alignedRect != rect )
437 {
438 const QRectF clipRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
439
440 painter->save();
441 painter->setClipRect( clipRect, Qt::IntersectClip );
442 painter->drawImage( alignedRect, image );
443 painter->restore();
444 }
445 else
446 {
447 painter->drawImage( alignedRect, image );
448 }
449}
450
451//! Wrapper for QPainter::drawPixmap()
452void QwtPainter::drawPixmap( QPainter *painter,
453 const QRectF &rect, const QPixmap &pixmap )
454{
455 const QRect alignedRect = rect.toAlignedRect();
456
457 if ( alignedRect != rect )
458 {
459 const QRectF clipRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
460
461 painter->save();
462 painter->setClipRect( clipRect, Qt::IntersectClip );
463 painter->drawPixmap( alignedRect, pixmap );
464 painter->restore();
465 }
466 else
467 {
468 painter->drawPixmap( alignedRect, pixmap );
469 }
470}
471
472//! Draw a focus rectangle on a widget using its style.
473void QwtPainter::drawFocusRect( QPainter *painter, QWidget *widget )
474{
475 drawFocusRect( painter, widget, widget->rect() );
476}
477
478//! Draw a focus rectangle on a widget using its style.
479void QwtPainter::drawFocusRect( QPainter *painter, QWidget *widget,
480 const QRect &rect )
481{
482 QStyleOptionFocusRect opt;
483 opt.init( widget );
484 opt.rect = rect;
485 opt.state |= QStyle::State_HasFocus;
486
487 widget->style()->drawPrimitive( QStyle::PE_FrameFocusRect,
488 &opt, painter, widget );
489}
490
491/*!
492 Draw a frame with rounded borders
493
494 \param painter Painter
495 \param rect Frame rectangle
496 \param xRadius x-radius of the ellipses defining the corners
497 \param yRadius y-radius of the ellipses defining the corners
498 \param palette QPalette::WindowText is used for plain borders
499 QPalette::Dark and QPalette::Light for raised
500 or sunken borders
501 \param lineWidth Line width
502 \param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow
503*/
504
505void QwtPainter::drawRoundedFrame( QPainter *painter,
506 const QRectF &rect, double xRadius, double yRadius,
507 const QPalette &palette, int lineWidth, int frameStyle )
508{
509 painter->save();
510 painter->setRenderHint( QPainter::Antialiasing, true );
511 painter->setBrush( Qt::NoBrush );
512
513 double lw2 = lineWidth * 0.5;
514 QRectF r = rect.adjusted( lw2, lw2, -lw2, -lw2 );
515
516 QPainterPath path;
517 path.addRoundedRect( r, xRadius, yRadius );
518
519 enum Style
520 {
521 Plain,
522 Sunken,
523 Raised
524 };
525
526 Style style = Plain;
527 if ( (frameStyle & QFrame::Sunken) == QFrame::Sunken )
528 style = Sunken;
529 else if ( (frameStyle & QFrame::Raised) == QFrame::Raised )
530 style = Raised;
531
532 if ( style != Plain && path.elementCount() == 17 )
533 {
534 // move + 4 * ( cubicTo + lineTo )
535 QPainterPath pathList[8];
536
537 for ( int i = 0; i < 4; i++ )
538 {
539 const int j = i * 4 + 1;
540
541 pathList[ 2 * i ].moveTo(
542 path.elementAt(j - 1).x, path.elementAt( j - 1 ).y
543 );
544
545 pathList[ 2 * i ].cubicTo(
546 path.elementAt(j + 0).x, path.elementAt(j + 0).y,
547 path.elementAt(j + 1).x, path.elementAt(j + 1).y,
548 path.elementAt(j + 2).x, path.elementAt(j + 2).y );
549
550 pathList[ 2 * i + 1 ].moveTo(
551 path.elementAt(j + 2).x, path.elementAt(j + 2).y
552 );
553 pathList[ 2 * i + 1 ].lineTo(
554 path.elementAt(j + 3).x, path.elementAt(j + 3).y
555 );
556 }
557
558 QColor c1( palette.color( QPalette::Dark ) );
559 QColor c2( palette.color( QPalette::Light ) );
560
561 if ( style == Raised )
562 qSwap( c1, c2 );
563
564 for ( int i = 0; i < 4; i++ )
565 {
566 QRectF r = pathList[2 * i].controlPointRect();
567
568 QPen arcPen;
569 arcPen.setWidth( lineWidth );
570
571 QPen linePen;
572 linePen.setWidth( lineWidth );
573
574 switch( i )
575 {
576 case 0:
577 {
578 arcPen.setColor( c1 );
579 linePen.setColor( c1 );
580 break;
581 }
582 case 1:
583 {
584 QLinearGradient gradient;
585 gradient.setStart( r.topLeft() );
586 gradient.setFinalStop( r.bottomRight() );
587 gradient.setColorAt( 0.0, c1 );
588 gradient.setColorAt( 1.0, c2 );
589
590 arcPen.setBrush( gradient );
591 linePen.setColor( c2 );
592 break;
593 }
594 case 2:
595 {
596 arcPen.setColor( c2 );
597 linePen.setColor( c2 );
598 break;
599 }
600 case 3:
601 {
602 QLinearGradient gradient;
603
604 gradient.setStart( r.bottomRight() );
605 gradient.setFinalStop( r.topLeft() );
606 gradient.setColorAt( 0.0, c2 );
607 gradient.setColorAt( 1.0, c1 );
608
609 arcPen.setBrush( gradient );
610 linePen.setColor( c1 );
611 break;
612 }
613 }
614
615
616 painter->setPen( arcPen );
617 painter->drawPath( pathList[ 2 * i] );
618
619 painter->setPen( linePen );
620 painter->drawPath( pathList[ 2 * i + 1] );
621 }
622 }
623 else
624 {
625 QPen pen( palette.color( QPalette::WindowText ), lineWidth );
626 painter->setPen( pen );
627 painter->drawPath( path );
628 }
629
630 painter->restore();
631}
632
633/*!
634 Draw a color bar into a rectangle
635
636 \param painter Painter
637 \param colorMap Color map
638 \param interval Value range
639 \param scaleMap Scale map
640 \param orientation Orientation
641 \param rect Traget rectangle
642*/
643void QwtPainter::drawColorBar( QPainter *painter,
644 const QwtColorMap &colorMap, const QwtInterval &interval,
645 const QwtScaleMap &scaleMap, Qt::Orientation orientation,
646 const QRectF &rect )
647{
648 QVector<QRgb> colorTable;
649 if ( colorMap.format() == QwtColorMap::Indexed )
650 colorTable = colorMap.colorTable( interval );
651
652 QColor c;
653
654 const QRect devRect = rect.toAlignedRect();
655
656 /*
657 We paint to a pixmap first to have something scalable for printing
658 ( f.e. in a Pdf document )
659 */
660
661 QPixmap pixmap( devRect.size() );
662 QPainter pmPainter( &pixmap );
663 pmPainter.translate( -devRect.x(), -devRect.y() );
664
665 if ( orientation == Qt::Horizontal )
666 {
667 QwtScaleMap sMap = scaleMap;
668 sMap.setPaintInterval( rect.left(), rect.right() );
669
670 for ( int x = devRect.left(); x <= devRect.right(); x++ )
671 {
672 const double value = sMap.invTransform( x );
673
674 if ( colorMap.format() == QwtColorMap::RGB )
675 c.setRgb( colorMap.rgb( interval, value ) );
676 else
677 c = colorTable[colorMap.colorIndex( interval, value )];
678
679 pmPainter.setPen( c );
680 pmPainter.drawLine( x, devRect.top(), x, devRect.bottom() );
681 }
682 }
683 else // Vertical
684 {
685 QwtScaleMap sMap = scaleMap;
686 sMap.setPaintInterval( rect.bottom(), rect.top() );
687
688 for ( int y = devRect.top(); y <= devRect.bottom(); y++ )
689 {
690 const double value = sMap.invTransform( y );
691
692 if ( colorMap.format() == QwtColorMap::RGB )
693 c.setRgb( colorMap.rgb( interval, value ) );
694 else
695 c = colorTable[colorMap.colorIndex( interval, value )];
696
697 pmPainter.setPen( c );
698 pmPainter.drawLine( devRect.left(), y, devRect.right(), y );
699 }
700 }
701 pmPainter.end();
702
703 drawPixmap( painter, rect, pixmap );
704}
Note: See TracBrowser for help on using the repository browser.