1
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
3
* Copyright (C) 1997 Josef Wilgen
4
* Copyright (C) 2002 Uwe Rathmann
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
*****************************************************************************/
10
#include "qwt_painter.h"
12
#include "qwt_clipper.h"
13
#include "qwt_color_map.h"
14
#include "qwt_scale_map.h"
15
#include <qwindowdefs.h>
20
#include <qpaintdevice.h>
23
#include <qtextdocument.h>
24
#include <qabstracttextdocumentlayout.h>
25
#include <qstyleoption.h>
26
#include <qpaintengine.h>
27
#include <qapplication.h>
28
#include <qdesktopwidget.h>
30
bool QwtPainter::d_polylineSplitting = true;
31
bool QwtPainter::d_roundingAlignment = true;
33
static inline bool isClippingNeeded( const QPainter *painter, QRectF &clipRect )
35
bool doClipping = false;
36
const QPaintEngine *pe = painter->paintEngine();
37
if ( pe && pe->type() == QPaintEngine::SVG )
39
// The SVG paint engine ignores any clipping,
41
if ( painter->hasClipping() )
44
clipRect = painter->clipRegion().boundingRect();
51
static inline void drawPolyline( QPainter *painter,
52
const QPointF *points, int pointCount, bool polylineSplitting )
55
if ( polylineSplitting )
57
const QPaintEngine *pe = painter->paintEngine();
58
if ( pe && pe->type() == QPaintEngine::Raster )
61
The raster paint engine seems to use some algo with O(n*n).
62
( Qt 4.3 is better than Qt 4.2, but remains unacceptable)
63
To work around this problem, we have to split the polygon into
72
const int splitSize = 20;
73
for ( int i = 0; i < pointCount; i += splitSize )
75
const int n = qMin( splitSize + 1, pointCount - i );
76
painter->drawPolyline( points + i, n );
80
painter->drawPolyline( points, pointCount );
83
static inline void unscaleFont( QPainter *painter )
85
if ( painter->font().pixelSize() >= 0 )
88
static QSize screenResolution;
89
if ( !screenResolution.isValid() )
91
QDesktopWidget *desktop = QApplication::desktop();
94
screenResolution.setWidth( desktop->logicalDpiX() );
95
screenResolution.setHeight( desktop->logicalDpiY() );
99
const QPaintDevice *pd = painter->device();
100
if ( pd->logicalDpiX() != screenResolution.width() ||
101
pd->logicalDpiY() != screenResolution.height() )
103
QFont pixelFont( painter->font(), QApplication::desktop() );
104
pixelFont.setPixelSize( QFontInfo( pixelFont ).pixelSize() );
106
painter->setFont( pixelFont );
111
Check if the painter is using a paint engine, that aligns
112
coordinates to integers. Today these are all paint engines
113
beside QPaintEngine::Pdf and QPaintEngine::SVG.
115
\param painter Painter
116
\return true, when the paint engine is aligning
118
\sa setRoundingAlignment()
120
bool QwtPainter::isAligning( QPainter *painter )
122
if ( painter && painter->isActive() )
124
switch ( painter->paintEngine()->type() )
126
case QPaintEngine::Pdf:
127
case QPaintEngine::SVG:
138
Enable whether coordinates should be rounded, before they are painted
139
to a paint engine that floors to integer values. For other paint engines
140
this ( Pdf, SVG ), this flag has no effect.
141
QwtPainter stores this flag only, the rounding itsself is done in
142
the painting code ( f.e the plot items ).
144
The default setting is true.
146
\sa roundingAlignment(), isAligning()
148
void QwtPainter::setRoundingAlignment( bool enable )
150
d_roundingAlignment = enable;
154
\brief En/Disable line splitting for the raster paint engine
156
The raster paint engine paints polylines of many points
157
much faster when they are splitted in smaller chunks.
159
\sa polylineSplitting()
161
void QwtPainter::setPolylineSplitting( bool enable )
163
d_polylineSplitting = enable;
166
//! Wrapper for QPainter::drawPath()
167
void QwtPainter::drawPath( QPainter *painter, const QPainterPath &path )
169
painter->drawPath( path );
172
//! Wrapper for QPainter::drawRect()
173
void QwtPainter::drawRect( QPainter *painter, double x, double y, double w, double h )
175
drawRect( painter, QRectF( x, y, w, h ) );
178
//! Wrapper for QPainter::drawRect()
179
void QwtPainter::drawRect( QPainter *painter, const QRectF &rect )
181
const QRectF r = rect;
184
const bool deviceClipping = isClippingNeeded( painter, clipRect );
186
if ( deviceClipping )
188
if ( !clipRect.intersects( r ) )
191
if ( !clipRect.contains( r ) )
193
fillRect( painter, r & clipRect, painter->brush() );
196
painter->setBrush( Qt::NoBrush );
197
drawPolyline( painter, QPolygonF( r ) );
204
painter->drawRect( r );
207
//! Wrapper for QPainter::fillRect()
208
void QwtPainter::fillRect( QPainter *painter,
209
const QRectF &rect, const QBrush &brush )
211
if ( !rect.isValid() )
215
const bool deviceClipping = isClippingNeeded( painter, clipRect );
218
Performance of Qt4 is horrible for non trivial brushs. Without
219
clipping expect minutes or hours for repainting large rects
220
(might result from zooming)
223
if ( deviceClipping )
224
clipRect &= painter->window();
226
clipRect = painter->window();
228
if ( painter->hasClipping() )
229
clipRect &= painter->clipRegion().boundingRect();
232
if ( deviceClipping )
233
r = r.intersect( clipRect );
236
painter->fillRect( r, brush );
239
//! Wrapper for QPainter::drawPie()
240
void QwtPainter::drawPie( QPainter *painter, const QRectF &rect,
244
const bool deviceClipping = isClippingNeeded( painter, clipRect );
245
if ( deviceClipping && !clipRect.contains( rect ) )
248
painter->drawPie( rect, a, alen );
251
//! Wrapper for QPainter::drawEllipse()
252
void QwtPainter::drawEllipse( QPainter *painter, const QRectF &rect )
255
const bool deviceClipping = isClippingNeeded( painter, clipRect );
257
if ( deviceClipping && !clipRect.contains( rect ) )
260
painter->drawEllipse( rect );
263
//! Wrapper for QPainter::drawText()
264
void QwtPainter::drawText( QPainter *painter, double x, double y,
265
const QString &text )
267
drawText( painter, QPointF( x, y ), text );
270
//! Wrapper for QPainter::drawText()
271
void QwtPainter::drawText( QPainter *painter, const QPointF &pos,
272
const QString &text )
275
const bool deviceClipping = isClippingNeeded( painter, clipRect );
277
if ( deviceClipping && !clipRect.contains( pos ) )
282
unscaleFont( painter );
283
painter->drawText( pos, text );
287
//! Wrapper for QPainter::drawText()
288
void QwtPainter::drawText( QPainter *painter,
289
double x, double y, double w, double h,
290
int flags, const QString &text )
292
drawText( painter, QRectF( x, y, w, h ), flags, text );
295
//! Wrapper for QPainter::drawText()
296
void QwtPainter::drawText( QPainter *painter, const QRectF &rect,
297
int flags, const QString &text )
300
unscaleFont( painter );
301
painter->drawText( rect, flags, text );
305
#ifndef QT_NO_RICHTEXT
308
Draw a text document into a rectangle
310
\param painter Painter
311
\param rect Traget rectangle
312
\param flags Alignments/Text flags, see QPainter::drawText()
313
\param text Text document
315
void QwtPainter::drawSimpleRichText( QPainter *painter, const QRectF &rect,
316
int flags, const QTextDocument &text )
318
QTextDocument *txt = text.clone();
322
painter->setFont( txt->defaultFont() );
323
unscaleFont( painter );
325
txt->setDefaultFont( painter->font() );
326
txt->setPageSize( QSizeF( rect.width(), QWIDGETSIZE_MAX ) );
328
QAbstractTextDocumentLayout* layout = txt->documentLayout();
330
const double height = layout->documentSize().height();
332
if ( flags & Qt::AlignBottom )
333
y += ( rect.height() - height );
334
else if ( flags & Qt::AlignVCenter )
335
y += ( rect.height() - height ) / 2;
337
QAbstractTextDocumentLayout::PaintContext context;
338
context.palette.setColor( QPalette::Text, painter->pen().color() );
340
painter->translate( rect.x(), y );
341
layout->draw( painter, context );
347
#endif // !QT_NO_RICHTEXT
350
//! Wrapper for QPainter::drawLine()
351
void QwtPainter::drawLine( QPainter *painter,
352
const QPointF &p1, const QPointF &p2 )
355
const bool deviceClipping = isClippingNeeded( painter, clipRect );
357
if ( deviceClipping &&
358
!( clipRect.contains( p1 ) && clipRect.contains( p2 ) ) )
363
drawPolyline( painter, polygon );
367
painter->drawLine( p1, p2 );
370
//! Wrapper for QPainter::drawPolygon()
371
void QwtPainter::drawPolygon( QPainter *painter, const QPolygonF &polygon )
374
const bool deviceClipping = isClippingNeeded( painter, clipRect );
376
QPolygonF cpa = polygon;
377
if ( deviceClipping )
378
cpa = QwtClipper::clipPolygonF( clipRect, polygon );
380
painter->drawPolygon( cpa );
383
//! Wrapper for QPainter::drawPolyline()
384
void QwtPainter::drawPolyline( QPainter *painter, const QPolygonF &polygon )
387
const bool deviceClipping = isClippingNeeded( painter, clipRect );
389
QPolygonF cpa = polygon;
390
if ( deviceClipping )
391
cpa = QwtClipper::clipPolygonF( clipRect, cpa );
393
::drawPolyline( painter,
394
cpa.constData(), cpa.size(), d_polylineSplitting );
397
//! Wrapper for QPainter::drawPolyline()
398
void QwtPainter::drawPolyline( QPainter *painter,
399
const QPointF *points, int pointCount )
402
const bool deviceClipping = isClippingNeeded( painter, clipRect );
404
if ( deviceClipping )
406
QPolygonF polygon( pointCount );
407
qMemCopy( polygon.data(), points, pointCount * sizeof( QPointF ) );
409
polygon = QwtClipper::clipPolygonF( clipRect, polygon );
410
::drawPolyline( painter,
411
polygon.constData(), polygon.size(), d_polylineSplitting );
414
::drawPolyline( painter, points, pointCount, d_polylineSplitting );
417
//! Wrapper for QPainter::drawPoint()
418
void QwtPainter::drawPoint( QPainter *painter, const QPointF &pos )
421
const bool deviceClipping = isClippingNeeded( painter, clipRect );
423
if ( deviceClipping && !clipRect.contains( pos ) )
426
painter->drawPoint( pos );
429
//! Wrapper for QPainter::drawImage()
430
void QwtPainter::drawImage( QPainter *painter,
431
const QRectF &rect, const QImage &image )
433
const QRect alignedRect = rect.toAlignedRect();
435
if ( alignedRect != rect )
437
const QRectF clipRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
440
painter->setClipRect( clipRect, Qt::IntersectClip );
441
painter->drawImage( alignedRect, image );
446
painter->drawImage( alignedRect, image );
450
//! Wrapper for QPainter::drawPixmap()
451
void QwtPainter::drawPixmap( QPainter *painter,
452
const QRectF &rect, const QPixmap &pixmap )
454
const QRect alignedRect = rect.toAlignedRect();
456
if ( alignedRect != rect )
458
const QRectF clipRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
461
painter->setClipRect( clipRect, Qt::IntersectClip );
462
painter->drawPixmap( alignedRect, pixmap );
467
painter->drawPixmap( alignedRect, pixmap );
471
//! Draw a focus rectangle on a widget using its style.
472
void QwtPainter::drawFocusRect( QPainter *painter, QWidget *widget )
474
drawFocusRect( painter, widget, widget->rect() );
477
//! Draw a focus rectangle on a widget using its style.
478
void QwtPainter::drawFocusRect( QPainter *painter, QWidget *widget,
481
QStyleOptionFocusRect opt;
484
opt.state |= QStyle::State_HasFocus;
486
widget->style()->drawPrimitive( QStyle::PE_FrameFocusRect,
487
&opt, painter, widget );
491
Draw a frame with rounded borders
493
\param painter Painter
494
\param rect Frame rectangle
495
\param xRadius x-radius of the ellipses defining the corners
496
\param yRadius y-radius of the ellipses defining the corners
497
\param palette QPalette::WindowText is used for plain borders
498
QPalette::Dark and QPalette::Light for raised
500
\param lineWidth Line width
501
\param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow
504
void QwtPainter::drawRoundedFrame( QPainter *painter,
505
const QRectF &rect, double xRadius, double yRadius,
506
const QPalette &palette, int lineWidth, int frameStyle )
509
painter->setRenderHint( QPainter::Antialiasing, true );
510
painter->setBrush( Qt::NoBrush );
512
double lw2 = lineWidth * 0.5;
513
QRectF r = rect.adjusted( lw2, lw2, -lw2, -lw2 );
516
path.addRoundedRect( r, xRadius, yRadius );
526
if ( (frameStyle & QFrame::Sunken) == QFrame::Sunken )
528
else if ( (frameStyle & QFrame::Raised) == QFrame::Raised )
531
if ( style != Plain && path.elementCount() == 17 )
533
// move + 4 * ( cubicTo + lineTo )
534
QPainterPath pathList[8];
536
for ( int i = 0; i < 4; i++ )
538
const int j = i * 4 + 1;
540
pathList[ 2 * i ].moveTo(
541
path.elementAt(j - 1).x, path.elementAt( j - 1 ).y
544
pathList[ 2 * i ].cubicTo(
545
path.elementAt(j + 0).x, path.elementAt(j + 0).y,
546
path.elementAt(j + 1).x, path.elementAt(j + 1).y,
547
path.elementAt(j + 2).x, path.elementAt(j + 2).y );
549
pathList[ 2 * i + 1 ].moveTo(
550
path.elementAt(j + 2).x, path.elementAt(j + 2).y
552
pathList[ 2 * i + 1 ].lineTo(
553
path.elementAt(j + 3).x, path.elementAt(j + 3).y
557
QColor c1( palette.color( QPalette::Dark ) );
558
QColor c2( palette.color( QPalette::Light ) );
560
if ( style == Raised )
563
for ( int i = 0; i < 4; i++ )
565
QRectF r = pathList[2 * i].controlPointRect();
568
arcPen.setWidth( lineWidth );
571
linePen.setWidth( lineWidth );
577
arcPen.setColor( c1 );
578
linePen.setColor( c1 );
583
QLinearGradient gradient;
584
gradient.setStart( r.topLeft() );
585
gradient.setFinalStop( r.bottomRight() );
586
gradient.setColorAt( 0.0, c1 );
587
gradient.setColorAt( 1.0, c2 );
589
arcPen.setBrush( gradient );
590
linePen.setColor( c2 );
595
arcPen.setColor( c2 );
596
linePen.setColor( c2 );
601
QLinearGradient gradient;
603
gradient.setStart( r.bottomRight() );
604
gradient.setFinalStop( r.topLeft() );
605
gradient.setColorAt( 0.0, c2 );
606
gradient.setColorAt( 1.0, c1 );
608
arcPen.setBrush( gradient );
609
linePen.setColor( c1 );
615
painter->setPen( arcPen );
616
painter->drawPath( pathList[ 2 * i] );
618
painter->setPen( linePen );
619
painter->drawPath( pathList[ 2 * i + 1] );
624
QPen pen( palette.color( QPalette::WindowText ), lineWidth );
625
painter->setPen( pen );
626
painter->drawPath( path );
633
Draw a color bar into a rectangle
635
\param painter Painter
636
\param colorMap Color map
637
\param interval Value range
638
\param scaleMap Scale map
639
\param orientation Orientation
640
\param rect Traget rectangle
642
void QwtPainter::drawColorBar( QPainter *painter,
643
const QwtColorMap &colorMap, const QwtInterval &interval,
644
const QwtScaleMap &scaleMap, Qt::Orientation orientation,
647
QVector<QRgb> colorTable;
648
if ( colorMap.format() == QwtColorMap::Indexed )
649
colorTable = colorMap.colorTable( interval );
653
const QRect devRect = rect.toAlignedRect();
656
We paint to a pixmap first to have something scalable for printing
657
( f.e. in a Pdf document )
660
QPixmap pixmap( devRect.size() );
661
QPainter pmPainter( &pixmap );
662
pmPainter.translate( -devRect.x(), -devRect.y() );
664
if ( orientation == Qt::Horizontal )
666
QwtScaleMap sMap = scaleMap;
667
sMap.setPaintInterval( rect.left(), rect.right() );
669
for ( int x = devRect.left(); x <= devRect.right(); x++ )
671
const double value = sMap.invTransform( x );
673
if ( colorMap.format() == QwtColorMap::RGB )
674
c.setRgb( colorMap.rgb( interval, value ) );
676
c = colorTable[colorMap.colorIndex( interval, value )];
678
pmPainter.setPen( c );
679
pmPainter.drawLine( x, devRect.top(), x, devRect.bottom() );
684
QwtScaleMap sMap = scaleMap;
685
sMap.setPaintInterval( rect.bottom(), rect.top() );
687
for ( int y = devRect.top(); y <= devRect.bottom(); y++ )
689
const double value = sMap.invTransform( y );
691
if ( colorMap.format() == QwtColorMap::RGB )
692
c.setRgb( colorMap.rgb( interval, value ) );
694
c = colorTable[colorMap.colorIndex( interval, value )];
696
pmPainter.setPen( c );
697
pmPainter.drawLine( devRect.left(), y, devRect.right(), y );
702
drawPixmap( painter, rect, pixmap );