qwt_painter.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  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. #include "qwt_painter.h"
  10. #include "qwt_math.h"
  11. #include "qwt_clipper.h"
  12. #include "qwt_color_map.h"
  13. #include "qwt_scale_map.h"
  14. #include <qwindowdefs.h>
  15. #include <qwidget.h>
  16. #include <qrect.h>
  17. #include <qpainter.h>
  18. #include <qpalette.h>
  19. #include <qpaintdevice.h>
  20. #include <qpixmap.h>
  21. #include <qstyle.h>
  22. #include <qtextdocument.h>
  23. #include <qabstracttextdocumentlayout.h>
  24. #include <qstyleoption.h>
  25. #include <qpaintengine.h>
  26. #include <qapplication.h>
  27. #include <qdesktopwidget.h>
  28. bool QwtPainter::d_polylineSplitting = true;
  29. bool QwtPainter::d_roundingAlignment = true;
  30. static inline bool isClippingNeeded( const QPainter *painter, QRectF &clipRect )
  31. {
  32. bool doClipping = false;
  33. const QPaintEngine *pe = painter->paintEngine();
  34. if ( pe && pe->type() == QPaintEngine::SVG )
  35. {
  36. // The SVG paint engine ignores any clipping,
  37. if ( painter->hasClipping() )
  38. {
  39. doClipping = true;
  40. clipRect = painter->clipRegion().boundingRect();
  41. }
  42. }
  43. return doClipping;
  44. }
  45. static inline void drawPolyline( QPainter *painter,
  46. const QPointF *points, int pointCount, bool polylineSplitting )
  47. {
  48. bool doSplit = false;
  49. if ( polylineSplitting )
  50. {
  51. const QPaintEngine *pe = painter->paintEngine();
  52. if ( pe && pe->type() == QPaintEngine::Raster )
  53. {
  54. /*
  55. The raster paint engine seems to use some algo with O(n*n).
  56. ( Qt 4.3 is better than Qt 4.2, but remains unacceptable)
  57. To work around this problem, we have to split the polygon into
  58. smaller pieces.
  59. */
  60. doSplit = true;
  61. }
  62. }
  63. if ( doSplit )
  64. {
  65. const int splitSize = 20;
  66. for ( int i = 0; i < pointCount; i += splitSize )
  67. {
  68. const int n = qMin( splitSize + 1, pointCount - i );
  69. painter->drawPolyline( points + i, n );
  70. }
  71. }
  72. else
  73. painter->drawPolyline( points, pointCount );
  74. }
  75. static inline void unscaleFont( QPainter *painter )
  76. {
  77. if ( painter->font().pixelSize() >= 0 )
  78. return;
  79. static QSize screenResolution;
  80. if ( !screenResolution.isValid() )
  81. {
  82. QDesktopWidget *desktop = QApplication::desktop();
  83. if ( desktop )
  84. {
  85. screenResolution.setWidth( desktop->logicalDpiX() );
  86. screenResolution.setHeight( desktop->logicalDpiY() );
  87. }
  88. }
  89. const QPaintDevice *pd = painter->device();
  90. if ( pd->logicalDpiX() != screenResolution.width() ||
  91. pd->logicalDpiY() != screenResolution.height() )
  92. {
  93. QFont pixelFont( painter->font(), QApplication::desktop() );
  94. pixelFont.setPixelSize( QFontInfo( pixelFont ).pixelSize() );
  95. painter->setFont( pixelFont );
  96. }
  97. }
  98. /*!
  99. Check if the painter is using a paint engine, that aligns
  100. coordinates to integers. Today these are all paint engines
  101. beside QPaintEngine::Pdf and QPaintEngine::SVG.
  102. \param painter Painter
  103. \return true, when the paint engine is aligning
  104. \sa setRoundingAlignment()
  105. */
  106. bool QwtPainter::isAligning( QPainter *painter )
  107. {
  108. if ( painter && painter->isActive() )
  109. {
  110. switch ( painter->paintEngine()->type() )
  111. {
  112. case QPaintEngine::Pdf:
  113. case QPaintEngine::SVG:
  114. return false;
  115. default:;
  116. }
  117. }
  118. return true;
  119. }
  120. /*!
  121. Enable whether coordinates should be rounded, before they are painted
  122. to a paint engine that floors to integer values. For other paint engines
  123. this ( Pdf, SVG ), this flag has no effect.
  124. QwtPainter stores this flag only, the rounding itsself is done in
  125. the painting code ( f.e the plot items ).
  126. The default setting is true.
  127. \sa roundingAlignment(), isAligning()
  128. */
  129. void QwtPainter::setRoundingAlignment( bool enable )
  130. {
  131. d_roundingAlignment = enable;
  132. }
  133. /*!
  134. \brief En/Disable line splitting for the raster paint engine
  135. The raster paint engine paints polylines of many points
  136. much faster when they are splitted in smaller chunks.
  137. \sa polylineSplitting()
  138. */
  139. void QwtPainter::setPolylineSplitting( bool enable )
  140. {
  141. d_polylineSplitting = enable;
  142. }
  143. //! Wrapper for QPainter::drawRect()
  144. void QwtPainter::drawRect( QPainter *painter, double x, double y, double w, double h )
  145. {
  146. drawRect( painter, QRectF( x, y, w, h ) );
  147. }
  148. //! Wrapper for QPainter::drawRect()
  149. void QwtPainter::drawRect( QPainter *painter, const QRectF &rect )
  150. {
  151. const QRectF r = rect;
  152. QRectF clipRect;
  153. const bool deviceClipping = isClippingNeeded( painter, clipRect );
  154. if ( deviceClipping )
  155. {
  156. if ( !clipRect.intersects( r ) )
  157. return;
  158. if ( !clipRect.contains( r ) )
  159. {
  160. fillRect( painter, r & clipRect, painter->brush() );
  161. painter->save();
  162. painter->setBrush( Qt::NoBrush );
  163. drawPolyline( painter, QPolygonF( r ) );
  164. painter->restore();
  165. return;
  166. }
  167. }
  168. painter->drawRect( r );
  169. }
  170. //! Wrapper for QPainter::fillRect()
  171. void QwtPainter::fillRect( QPainter *painter,
  172. const QRectF &rect, const QBrush &brush )
  173. {
  174. if ( !rect.isValid() )
  175. return;
  176. QRectF clipRect;
  177. const bool deviceClipping = isClippingNeeded( painter, clipRect );
  178. /*
  179. Performance of Qt4 is horrible for non trivial brushs. Without
  180. clipping expect minutes or hours for repainting large rects
  181. (might result from zooming)
  182. */
  183. if ( deviceClipping )
  184. clipRect &= painter->window();
  185. else
  186. clipRect = painter->window();
  187. if ( painter->hasClipping() )
  188. clipRect &= painter->clipRegion().boundingRect();
  189. QRectF r = rect;
  190. if ( deviceClipping )
  191. r = r.intersect( clipRect );
  192. if ( r.isValid() )
  193. painter->fillRect( r, brush );
  194. }
  195. //! Wrapper for QPainter::drawPie()
  196. void QwtPainter::drawPie( QPainter *painter, const QRectF &rect,
  197. int a, int alen )
  198. {
  199. QRectF clipRect;
  200. const bool deviceClipping = isClippingNeeded( painter, clipRect );
  201. if ( deviceClipping && !clipRect.contains( rect ) )
  202. return;
  203. painter->drawPie( rect, a, alen );
  204. }
  205. //! Wrapper for QPainter::drawEllipse()
  206. void QwtPainter::drawEllipse( QPainter *painter, const QRectF &rect )
  207. {
  208. QRectF clipRect;
  209. const bool deviceClipping = isClippingNeeded( painter, clipRect );
  210. if ( deviceClipping && !clipRect.contains( rect ) )
  211. return;
  212. painter->drawEllipse( rect );
  213. }
  214. //! Wrapper for QPainter::drawText()
  215. void QwtPainter::drawText( QPainter *painter, double x, double y,
  216. const QString &text )
  217. {
  218. drawText( painter, QPointF( x, y ), text );
  219. }
  220. //! Wrapper for QPainter::drawText()
  221. void QwtPainter::drawText( QPainter *painter, const QPointF &pos,
  222. const QString &text )
  223. {
  224. QRectF clipRect;
  225. const bool deviceClipping = isClippingNeeded( painter, clipRect );
  226. if ( deviceClipping && !clipRect.contains( pos ) )
  227. return;
  228. painter->save();
  229. unscaleFont( painter );
  230. painter->drawText( pos, text );
  231. painter->restore();
  232. }
  233. //! Wrapper for QPainter::drawText()
  234. void QwtPainter::drawText( QPainter *painter,
  235. double x, double y, double w, double h,
  236. int flags, const QString &text )
  237. {
  238. drawText( painter, QRectF( x, y, w, h ), flags, text );
  239. }
  240. //! Wrapper for QPainter::drawText()
  241. void QwtPainter::drawText( QPainter *painter, const QRectF &rect,
  242. int flags, const QString &text )
  243. {
  244. painter->save();
  245. unscaleFont( painter );
  246. painter->drawText( rect, flags, text );
  247. painter->restore();
  248. }
  249. #ifndef QT_NO_RICHTEXT
  250. /*!
  251. Draw a text document into a rectangle
  252. \param painter Painter
  253. \param rect Traget rectangle
  254. \param flags Alignments/Text flags, see QPainter::drawText()
  255. \param text Text document
  256. */
  257. void QwtPainter::drawSimpleRichText( QPainter *painter, const QRectF &rect,
  258. int flags, const QTextDocument &text )
  259. {
  260. QTextDocument *txt = text.clone();
  261. painter->save();
  262. painter->setFont( txt->defaultFont() );
  263. unscaleFont( painter );
  264. txt->setDefaultFont( painter->font() );
  265. txt->setPageSize( QSizeF( rect.width(), QWIDGETSIZE_MAX ) );
  266. QAbstractTextDocumentLayout* layout = txt->documentLayout();
  267. const double height = layout->documentSize().height();
  268. double y = rect.y();
  269. if ( flags & Qt::AlignBottom )
  270. y += ( rect.height() - height );
  271. else if ( flags & Qt::AlignVCenter )
  272. y += ( rect.height() - height ) / 2;
  273. QAbstractTextDocumentLayout::PaintContext context;
  274. context.palette.setColor( QPalette::Text, painter->pen().color() );
  275. painter->translate( rect.x(), y );
  276. layout->draw( painter, context );
  277. painter->restore();
  278. delete txt;
  279. }
  280. #endif // !QT_NO_RICHTEXT
  281. //! Wrapper for QPainter::drawLine()
  282. void QwtPainter::drawLine( QPainter *painter,
  283. const QPointF &p1, const QPointF &p2 )
  284. {
  285. QRectF clipRect;
  286. const bool deviceClipping = isClippingNeeded( painter, clipRect );
  287. if ( deviceClipping &&
  288. !( clipRect.contains( p1 ) && clipRect.contains( p2 ) ) )
  289. {
  290. QPolygonF polygon;
  291. polygon += p1;
  292. polygon += p2;
  293. drawPolyline( painter, polygon );
  294. return;
  295. }
  296. painter->drawLine( p1, p2 );
  297. }
  298. //! Wrapper for QPainter::drawPolygon()
  299. void QwtPainter::drawPolygon( QPainter *painter, const QPolygonF &polygon )
  300. {
  301. QRectF clipRect;
  302. const bool deviceClipping = isClippingNeeded( painter, clipRect );
  303. QPolygonF cpa = polygon;
  304. if ( deviceClipping )
  305. cpa = QwtClipper::clipPolygonF( clipRect, polygon );
  306. painter->drawPolygon( cpa );
  307. }
  308. //! Wrapper for QPainter::drawPolyline()
  309. void QwtPainter::drawPolyline( QPainter *painter, const QPolygonF &polygon )
  310. {
  311. QRectF clipRect;
  312. const bool deviceClipping = isClippingNeeded( painter, clipRect );
  313. QPolygonF cpa = polygon;
  314. if ( deviceClipping )
  315. cpa = QwtClipper::clipPolygonF( clipRect, cpa );
  316. ::drawPolyline( painter,
  317. cpa.constData(), cpa.size(), d_polylineSplitting );
  318. }
  319. //! Wrapper for QPainter::drawPolyline()
  320. void QwtPainter::drawPolyline( QPainter *painter,
  321. const QPointF *points, int pointCount )
  322. {
  323. QRectF clipRect;
  324. const bool deviceClipping = isClippingNeeded( painter, clipRect );
  325. if ( deviceClipping )
  326. {
  327. QPolygonF polygon( pointCount );
  328. qMemCopy( polygon.data(), points, pointCount * sizeof( QPointF ) );
  329. polygon = QwtClipper::clipPolygonF( clipRect, polygon );
  330. ::drawPolyline( painter,
  331. polygon.constData(), polygon.size(), d_polylineSplitting );
  332. }
  333. else
  334. ::drawPolyline( painter, points, pointCount, d_polylineSplitting );
  335. }
  336. //! Wrapper for QPainter::drawPoint()
  337. void QwtPainter::drawPoint( QPainter *painter, const QPointF &pos )
  338. {
  339. QRectF clipRect;
  340. const bool deviceClipping = isClippingNeeded( painter, clipRect );
  341. if ( deviceClipping && !clipRect.contains( pos ) )
  342. return;
  343. painter->drawPoint( pos );
  344. }
  345. //! Wrapper for QPainter::drawImage()
  346. void QwtPainter::drawImage( QPainter *painter,
  347. const QRectF &rect, const QImage &image )
  348. {
  349. const QRect alignedRect = rect.toAlignedRect();
  350. if ( alignedRect != rect )
  351. {
  352. const QRectF clipRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
  353. painter->save();
  354. painter->setClipRect( clipRect, Qt::IntersectClip );
  355. painter->drawImage( alignedRect, image );
  356. painter->restore();
  357. }
  358. else
  359. {
  360. painter->drawImage( alignedRect, image );
  361. }
  362. }
  363. //! Wrapper for QPainter::drawPixmap()
  364. void QwtPainter::drawPixmap( QPainter *painter,
  365. const QRectF &rect, const QPixmap &pixmap )
  366. {
  367. const QRect alignedRect = rect.toAlignedRect();
  368. if ( alignedRect != rect )
  369. {
  370. const QRectF clipRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
  371. painter->save();
  372. painter->setClipRect( clipRect, Qt::IntersectClip );
  373. painter->drawPixmap( alignedRect, pixmap );
  374. painter->restore();
  375. }
  376. else
  377. {
  378. painter->drawPixmap( alignedRect, pixmap );
  379. }
  380. }
  381. /*!
  382. Draw a arc with a linear gradient
  383. \note This method needs to be replaced by using QGradient
  384. */
  385. void QwtPainter::drawColoredArc( QPainter *painter, const QRect &rect,
  386. int peak, int arc, int interval, const QColor &c1, const QColor &c2 )
  387. {
  388. int h1, s1, v1;
  389. int h2, s2, v2;
  390. c1.getHsv( &h1, &s1, &v1 );
  391. c2.getHsv( &h2, &s2, &v2 );
  392. arc /= 2;
  393. for ( int angle = -arc; angle < arc; angle += interval )
  394. {
  395. double ratio;
  396. if ( angle >= 0 )
  397. ratio = 1.0 - angle / double( arc );
  398. else
  399. ratio = 1.0 + angle / double( arc );
  400. QColor c;
  401. c.setHsv( h1 + qRound( ratio * ( h2 - h1 ) ),
  402. s1 + qRound( ratio * ( s2 - s1 ) ),
  403. v1 + qRound( ratio * ( v2 - v1 ) ) );
  404. painter->setPen( QPen( c, painter->pen().width() ) );
  405. painter->drawArc( rect, ( peak + angle ) * 16, interval * 16 );
  406. }
  407. }
  408. //! Draw a focus rectangle on a widget using its style.
  409. void QwtPainter::drawFocusRect( QPainter *painter, QWidget *widget )
  410. {
  411. drawFocusRect( painter, widget, widget->rect() );
  412. }
  413. //! Draw a focus rectangle on a widget using its style.
  414. void QwtPainter::drawFocusRect( QPainter *painter, QWidget *widget,
  415. const QRect &rect )
  416. {
  417. QStyleOptionFocusRect opt;
  418. opt.init( widget );
  419. opt.rect = rect;
  420. opt.state |= QStyle::State_HasFocus;
  421. widget->style()->drawPrimitive( QStyle::PE_FrameFocusRect,
  422. &opt, painter, widget );
  423. }
  424. //! Draw a round frame
  425. void QwtPainter::drawRoundFrame( QPainter *painter, const QRect &rect,
  426. int width, const QPalette &palette, bool sunken )
  427. {
  428. QColor c0 = palette.color( QPalette::Mid );
  429. QColor c1, c2;
  430. if ( sunken )
  431. {
  432. c1 = palette.color( QPalette::Dark );
  433. c2 = palette.color( QPalette::Light );
  434. }
  435. else
  436. {
  437. c1 = palette.color( QPalette::Light );
  438. c2 = palette.color( QPalette::Dark );
  439. }
  440. painter->setPen( QPen( c0, width ) );
  441. painter->drawArc( rect, 0, 360 * 16 ); // full
  442. const int peak = 150;
  443. const int interval = 2;
  444. if ( c0 != c1 )
  445. drawColoredArc( painter, rect, peak, 160, interval, c0, c1 );
  446. if ( c0 != c2 )
  447. drawColoredArc( painter, rect, peak + 180, 120, interval, c0, c2 );
  448. }
  449. /*!
  450. Draw a color bar into a rectangle
  451. \param painter Painter
  452. \param colorMap Color map
  453. \param interval Value range
  454. \param scaleMap Scale map
  455. \param orientation Orientation
  456. \param rect Traget rectangle
  457. */
  458. void QwtPainter::drawColorBar( QPainter *painter,
  459. const QwtColorMap &colorMap, const QwtInterval &interval,
  460. const QwtScaleMap &scaleMap, Qt::Orientation orientation,
  461. const QRectF &rect )
  462. {
  463. QVector<QRgb> colorTable;
  464. if ( colorMap.format() == QwtColorMap::Indexed )
  465. colorTable = colorMap.colorTable( interval );
  466. QColor c;
  467. const QRect devRect = rect.toAlignedRect();
  468. /*
  469. We paint to a pixmap first to have something scalable for printing
  470. ( f.e. in a Pdf document )
  471. */
  472. QPixmap pixmap( devRect.size() );
  473. QPainter pmPainter( &pixmap );
  474. pmPainter.translate( -devRect.x(), -devRect.y() );
  475. if ( orientation == Qt::Horizontal )
  476. {
  477. QwtScaleMap sMap = scaleMap;
  478. sMap.setPaintInterval( rect.left(), rect.right() );
  479. for ( int x = devRect.left(); x <= devRect.right(); x++ )
  480. {
  481. const double value = sMap.invTransform( x );
  482. if ( colorMap.format() == QwtColorMap::RGB )
  483. c.setRgb( colorMap.rgb( interval, value ) );
  484. else
  485. c = colorTable[colorMap.colorIndex( interval, value )];
  486. pmPainter.setPen( c );
  487. pmPainter.drawLine( x, devRect.top(), x, devRect.bottom() );
  488. }
  489. }
  490. else // Vertical
  491. {
  492. QwtScaleMap sMap = scaleMap;
  493. sMap.setPaintInterval( rect.bottom(), rect.top() );
  494. for ( int y = devRect.top(); y <= devRect.bottom(); y++ )
  495. {
  496. const double value = sMap.invTransform( y );
  497. if ( colorMap.format() == QwtColorMap::RGB )
  498. c.setRgb( colorMap.rgb( interval, value ) );
  499. else
  500. c = colorTable[colorMap.colorIndex( interval, value )];
  501. pmPainter.setPen( c );
  502. pmPainter.drawLine( devRect.left(), y, devRect.right(), y );
  503. }
  504. }
  505. pmPainter.end();
  506. drawPixmap( painter, rect, pixmap );
  507. }