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
*****************************************************************************/
12
#include <qwindowdefs.h>
17
#include <qpaintdevice.h>
20
#if QT_VERSION < 0x040000
21
#include <qsimplerichtext.h>
23
#include <qtextdocument.h>
24
#include <qabstracttextdocumentlayout.h>
25
#include <qstyleoption.h>
26
#include <qpaintengine.h>
30
#include "qwt_clipper.h"
31
#include "qwt_color_map.h"
32
#include "qwt_scale_map.h"
33
#include "qwt_painter.h"
35
QwtMetricsMap QwtPainter::d_metricsMap;
38
bool QwtPainter::d_deviceClipping = true;
40
bool QwtPainter::d_deviceClipping = false;
43
#if QT_VERSION < 0x040000
44
bool QwtPainter::d_SVGMode = false;
47
static inline bool isClippingNeeded(const QPainter *painter, QRect &clipRect)
49
bool doClipping = false;
50
#if QT_VERSION >= 0x040000
51
const QPaintEngine *pe = painter->paintEngine();
52
if ( pe && pe->type() == QPaintEngine::SVG )
54
if ( painter->device()->devType() == QInternal::Picture )
57
// The SVG paint engine ignores any clipping,
59
if ( painter->hasClipping() )
62
clipRect = painter->clipRegion().boundingRect();
66
if ( QwtPainter::deviceClipping() )
68
if (painter->device()->devType() == QInternal::Widget ||
69
painter->device()->devType() == QInternal::Pixmap )
73
clipRect &= QwtPainter::deviceClipRect();
78
clipRect = QwtPainter::deviceClipRect();
87
\brief En/Disable device clipping.
89
On X11 the default for device clipping is enabled,
90
otherwise it is disabled.
91
\sa QwtPainter::deviceClipping()
93
void QwtPainter::setDeviceClipping(bool enable)
95
d_deviceClipping = enable;
99
Returns rect for device clipping
100
\sa QwtPainter::setDeviceClipping()
102
const QRect &QwtPainter::deviceClipRect()
106
if ( !clip.isValid() )
108
clip.setCoords(QWT_COORD_MIN, QWT_COORD_MIN,
109
QWT_COORD_MAX, QWT_COORD_MAX);
114
#if QT_VERSION < 0x040000
117
\brief En/Disable SVG mode.
119
When saving a QPicture to a SVG some texts are misaligned.
120
In SVGMode QwtPainter tries to fix them.
122
\sa QwtPainter::isSVGMode()
123
\note A QPicture that is created in SVG mode and saved to the
124
native format, will be misaligned. Also it is not possible to
125
reload and play a SVG document, that was created in SVG mode.
127
void QwtPainter::setSVGMode(bool on)
132
bool QwtPainter::isSVGMode()
137
#endif // QT_VERSION < 0x040000
140
Scale all QwtPainter drawing operations using the ratio
141
QwtPaintMetrics(from).logicalDpiX() / QwtPaintMetrics(to).logicalDpiX()
142
and QwtPaintMetrics(from).logicalDpiY() / QwtPaintMetrics(to).logicalDpiY()
144
\sa QwtPainter::resetScaleMetrics(), QwtPainter::scaleMetricsX(),
145
QwtPainter::scaleMetricsY()
147
void QwtPainter::setMetricsMap(const QPaintDevice *layout,
148
const QPaintDevice *device)
150
d_metricsMap.setMetrics(layout, device);
154
Change the metrics map
155
\sa QwtPainter::resetMetricsMap(), QwtPainter::metricsMap()
157
void QwtPainter::setMetricsMap(const QwtMetricsMap &map)
163
Reset the metrics map to the ratio 1:1
164
\sa QwtPainter::setMetricsMap(), QwtPainter::resetMetricsMap()
166
void QwtPainter::resetMetricsMap()
168
d_metricsMap = QwtMetricsMap();
174
const QwtMetricsMap &QwtPainter::metricsMap()
180
Wrapper for QPainter::setClipRect()
182
void QwtPainter::setClipRect(QPainter *painter, const QRect &rect)
184
painter->setClipRect(d_metricsMap.layoutToDevice(rect, painter));
188
Wrapper for QPainter::drawRect()
190
void QwtPainter::drawRect(QPainter *painter, int x, int y, int w, int h)
192
drawRect(painter, QRect(x, y, w, h));
196
Wrapper for QPainter::drawRect()
198
void QwtPainter::drawRect(QPainter *painter, const QRect &rect)
200
const QRect r = d_metricsMap.layoutToDevice(rect, painter);
203
const bool deviceClipping = isClippingNeeded(painter, clipRect);
205
if ( deviceClipping )
207
if ( !clipRect.intersects(r) )
210
if ( !clipRect.contains(r) )
212
fillRect(painter, r & clipRect, painter->brush());
214
int pw = painter->pen().width();
215
pw = pw % 2 + pw / 2;
218
pa.setPoint(0, r.left(), r.top());
219
pa.setPoint(1, r.right() - pw, r.top());
220
pa.setPoint(2, r.right() - pw, r.bottom() - pw);
221
pa.setPoint(3, r.left(), r.bottom() - pw);
222
pa.setPoint(4, r.left(), r.top());
225
painter->setBrush(Qt::NoBrush);
226
drawPolyline(painter, pa);
233
painter->drawRect(r);
237
Wrapper for QPainter::fillRect()
239
void QwtPainter::fillRect(QPainter *painter,
240
const QRect &rect, const QBrush &brush)
242
if ( !rect.isValid() )
246
const bool deviceClipping = isClippingNeeded(painter, clipRect);
248
#if QT_VERSION >= 0x040000
250
Performance of Qt4 is horrible for non trivial brushs. Without
251
clipping expect minutes or hours for repainting large rects
252
(might result from zooming)
255
if ( deviceClipping )
256
clipRect &= painter->window();
258
clipRect = painter->window();
260
if ( painter->hasClipping() )
261
clipRect &= painter->clipRegion().boundingRect();
264
QRect r = d_metricsMap.layoutToDevice(rect, painter);
265
if ( deviceClipping )
266
r = r.intersect(clipRect);
269
painter->fillRect(r, brush);
273
Wrapper for QPainter::drawPie()
275
void QwtPainter::drawPie(QPainter *painter, const QRect &rect,
278
const QRect r = d_metricsMap.layoutToDevice(rect, painter);
281
const bool deviceClipping = isClippingNeeded(painter, clipRect);
282
if ( deviceClipping && !clipRect.contains(r) )
285
painter->drawPie(r, a, alen);
289
Wrapper for QPainter::drawEllipse()
291
void QwtPainter::drawEllipse(QPainter *painter, const QRect &rect)
293
QRect r = d_metricsMap.layoutToDevice(rect, painter);
296
const bool deviceClipping = isClippingNeeded(painter, clipRect);
298
if ( deviceClipping && !clipRect.contains(r) )
301
#if QT_VERSION >= 0x040000
302
if ( painter->pen().style() != Qt::NoPen &&
303
painter->pen().color().isValid() )
305
// Qt4 adds the pen to the rect, Qt3 not.
306
int pw = painter->pen().width();
310
r.setWidth(r.width() - pw);
311
r.setHeight(r.height() - pw);
315
painter->drawEllipse(r);
319
Wrapper for QPainter::drawText()
321
void QwtPainter::drawText(QPainter *painter, int x, int y,
324
drawText(painter, QPoint(x, y), text);
328
Wrapper for QPainter::drawText()
330
void QwtPainter::drawText(QPainter *painter, const QPoint &pos,
333
const QPoint p = d_metricsMap.layoutToDevice(pos, painter);
336
const bool deviceClipping = isClippingNeeded(painter, clipRect);
338
if ( deviceClipping && !clipRect.contains(p) )
341
painter->drawText(p, text);
345
Wrapper for QPainter::drawText()
347
void QwtPainter::drawText(QPainter *painter, int x, int y, int w, int h,
348
int flags, const QString &text)
350
drawText(painter, QRect(x, y, w, h), flags, text);
354
Wrapper for QPainter::drawText()
356
void QwtPainter::drawText(QPainter *painter, const QRect &rect,
357
int flags, const QString &text)
359
QRect textRect = d_metricsMap.layoutToDevice(rect, painter);
360
#if QT_VERSION < 0x040000
362
( flags == 0 || flags & Qt::AlignVCenter )
363
&& painter->device()->devType() == QInternal::Picture )
366
Qt3 misalignes texts, when saving a text
369
textRect.setY(textRect.y() - painter->fontMetrics().height() / 4);
372
painter->drawText(textRect, flags, text);
375
#ifndef QT_NO_RICHTEXT
378
Wrapper for QSimpleRichText::draw()
380
#if QT_VERSION < 0x040000
382
void QwtPainter::drawSimpleRichText(QPainter *painter, const QRect &rect,
383
int flags, QSimpleRichText &text)
386
cg.setColor(QColorGroup::Text, painter->pen().color());
388
const QRect scaledRect = d_metricsMap.layoutToDevice(rect, painter);
390
text.setWidth(painter, scaledRect.width());
392
// QSimpleRichText is Qt::AlignTop by default
394
int y = scaledRect.y();
395
if (flags & Qt::AlignBottom)
396
y += (scaledRect.height() - text.height());
397
else if (flags & Qt::AlignVCenter)
398
y += (scaledRect.height() - text.height())/2;
400
text.draw(painter, scaledRect.x(), y, scaledRect, cg);
403
void QwtPainter::drawSimpleRichText(QPainter *painter, const QRect &rect,
404
int flags, QTextDocument &text)
406
const QRect scaledRect = d_metricsMap.layoutToDevice(rect, painter);
407
text.setPageSize(QSize(scaledRect.width(), QWIDGETSIZE_MAX));
409
QAbstractTextDocumentLayout* layout = text.documentLayout();
411
const int height = qRound(layout->documentSize().height());
412
int y = scaledRect.y();
413
if (flags & Qt::AlignBottom)
414
y += (scaledRect.height() - height);
415
else if (flags & Qt::AlignVCenter)
416
y += (scaledRect.height() - height)/2;
418
QAbstractTextDocumentLayout::PaintContext context;
419
context.palette.setColor(QPalette::Text, painter->pen().color());
423
painter->translate(scaledRect.x(), y);
424
layout->draw(painter, context);
430
#endif // !QT_NO_RICHTEXT
434
Wrapper for QPainter::drawLine()
436
void QwtPainter::drawLine(QPainter *painter, int x1, int y1, int x2, int y2)
439
const bool deviceClipping = isClippingNeeded(painter, clipRect);
441
if ( deviceClipping &&
442
!(clipRect.contains(x1, y1) && clipRect.contains(x2, y2)) )
445
pa.setPoint(0, x1, y1);
446
pa.setPoint(1, x2, y2);
447
drawPolyline(painter, pa);
451
if ( d_metricsMap.isIdentity() )
453
#if QT_VERSION >= 0x030200 && QT_VERSION < 0x040000
454
if ( !painter->device()->isExtDev() )
457
painter->drawLine(x1, y1, x2, y2);
462
const QPoint p1 = d_metricsMap.layoutToDevice(QPoint(x1, y1));
463
const QPoint p2 = d_metricsMap.layoutToDevice(QPoint(x2, y2));
465
#if QT_VERSION >= 0x030200 && QT_VERSION < 0x040000
466
if ( painter->device()->isExtDev() )
468
// Strange: the postscript driver of QPrinter adds an offset
469
// of 0.5 to the start/endpoint when using drawLine, but not
470
// for lines painted with drawLineSegments.
475
painter->drawLineSegments(pa);
478
painter->drawLine(p1, p2);
480
painter->drawLine(p1, p2);
485
Wrapper for QPainter::drawPolygon()
487
void QwtPainter::drawPolygon(QPainter *painter, const QwtPolygon &pa)
490
const bool deviceClipping = isClippingNeeded(painter, clipRect);
492
QwtPolygon cpa = d_metricsMap.layoutToDevice(pa);
493
if ( deviceClipping )
497
cpa = QwtClipper::clipPolygon(clipRect, cpa);
499
painter->drawPolygon(cpa);
503
Wrapper for QPainter::drawPolyline()
505
void QwtPainter::drawPolyline(QPainter *painter, const QwtPolygon &pa)
508
const bool deviceClipping = isClippingNeeded(painter, clipRect);
510
QwtPolygon cpa = d_metricsMap.layoutToDevice(pa);
511
if ( deviceClipping )
512
cpa = QwtClipper::clipPolygon(clipRect, cpa);
514
#if QT_VERSION >= 0x040000 && QT_VERSION < 0x040400
515
bool doSplit = false;
517
const QPaintEngine *pe = painter->paintEngine();
518
if ( pe && pe->type() == QPaintEngine::Raster &&
519
painter->pen().width() >= 2 )
522
The raster paint engine seems to use some algo with O(n*n).
523
( Qt 4.3 is better than Qt 4.2, but remains unacceptable)
524
To work around this problem, we have to split the polygon into
532
const int numPoints = cpa.size();
533
const QPoint *points = cpa.data();
535
const int splitSize = 20;
536
for ( int i = 0; i < numPoints; i += splitSize )
538
const int n = qwtMin(splitSize + 1, cpa.size() - i);
539
painter->drawPolyline(points + i, n);
544
painter->drawPolyline(cpa);
548
Wrapper for QPainter::drawPoint()
551
void QwtPainter::drawPoint(QPainter *painter, int x, int y)
554
const bool deviceClipping = isClippingNeeded(painter, clipRect);
556
const QPoint pos = d_metricsMap.layoutToDevice(QPoint(x, y));
558
if ( deviceClipping && !clipRect.contains(pos) )
561
painter->drawPoint(pos);
564
void QwtPainter::drawColoredArc(QPainter *painter, const QRect &rect,
565
int peak, int arc, int interval, const QColor &c1, const QColor &c2)
570
#if QT_VERSION < 0x040000
571
c1.hsv(&h1, &s1, &v1);
572
c2.hsv(&h2, &s2, &v2);
574
c1.getHsv(&h1, &s1, &v1);
575
c2.getHsv(&h2, &s2, &v2);
579
for ( int angle = -arc; angle < arc; angle += interval)
583
ratio = 1.0 - angle / double(arc);
585
ratio = 1.0 + angle / double(arc);
589
c.setHsv( h1 + qRound(ratio * (h2 - h1)),
590
s1 + qRound(ratio * (s2 - s1)),
591
v1 + qRound(ratio * (v2 - v1)) );
593
painter->setPen(QPen(c, painter->pen().width()));
594
painter->drawArc(rect, (peak + angle) * 16, interval * 16);
598
void QwtPainter::drawFocusRect(QPainter *painter, QWidget *widget)
600
drawFocusRect(painter, widget, widget->rect());
603
void QwtPainter::drawFocusRect(QPainter *painter, QWidget *widget,
606
#if QT_VERSION < 0x040000
607
widget->style().drawPrimitive(QStyle::PE_FocusRect, painter,
608
rect, widget->colorGroup());
610
QStyleOptionFocusRect opt;
613
opt.state |= QStyle::State_HasFocus;
615
widget->style()->drawPrimitive(QStyle::PE_FrameFocusRect,
616
&opt, painter, widget);
621
//! Draw a round frame
622
#if QT_VERSION < 0x040000
623
void QwtPainter::drawRoundFrame(QPainter *painter, const QRect &rect,
624
int width, const QColorGroup &cg, bool sunken)
626
void QwtPainter::drawRoundFrame(QPainter *painter, const QRect &rect,
627
int width, const QPalette &palette, bool sunken)
631
#if QT_VERSION < 0x040000
632
QColor c0 = cg.mid();
645
QColor c0 = palette.color(QPalette::Mid);
649
c1 = palette.color(QPalette::Dark);
650
c2 = palette.color(QPalette::Light);
654
c1 = palette.color(QPalette::Light);
655
c2 = palette.color(QPalette::Dark);
659
painter->setPen(QPen(c0, width));
660
painter->drawArc(rect, 0, 360 * 16); // full
662
const int peak = 150;
663
const int interval = 2;
666
drawColoredArc(painter, rect, peak, 160, interval, c0, c1);
668
drawColoredArc(painter, rect, peak + 180, 120, interval, c0, c2);
671
void QwtPainter::drawColorBar(QPainter *painter,
672
const QwtColorMap &colorMap, const QwtDoubleInterval &interval,
673
const QwtScaleMap &scaleMap, Qt::Orientation orientation,
676
#if QT_VERSION < 0x040000
677
QValueVector<QRgb> colorTable;
679
QVector<QRgb> colorTable;
681
if ( colorMap.format() == QwtColorMap::Indexed )
682
colorTable = colorMap.colorTable(interval);
686
const QRect devRect = d_metricsMap.layoutToDevice(rect);
689
We paint to a pixmap first to have something scalable for printing
690
( f.e. in a Pdf document )
693
QPixmap pixmap(devRect.size());
694
QPainter pmPainter(&pixmap);
695
pmPainter.translate(-devRect.x(), -devRect.y());
697
if ( orientation == Qt::Horizontal )
699
QwtScaleMap sMap = scaleMap;
700
sMap.setPaintInterval(devRect.left(), devRect.right());
702
for ( int x = devRect.left(); x <= devRect.right(); x++ )
704
const double value = sMap.invTransform(x);
706
if ( colorMap.format() == QwtColorMap::RGB )
707
c.setRgb(colorMap.rgb(interval, value));
709
c = colorTable[colorMap.colorIndex(interval, value)];
712
pmPainter.drawLine(x, devRect.top(), x, devRect.bottom());
717
QwtScaleMap sMap = scaleMap;
718
sMap.setPaintInterval(devRect.bottom(), devRect.top());
720
for ( int y = devRect.top(); y <= devRect.bottom(); y++ )
722
const double value = sMap.invTransform(y);
724
if ( colorMap.format() == QwtColorMap::RGB )
725
c.setRgb(colorMap.rgb(interval, value));
727
c = colorTable[colorMap.colorIndex(interval, value)];
730
pmPainter.drawLine(devRect.left(), y, devRect.right(), y);
734
painter->drawPixmap(devRect, pixmap);
738
\brief Scale a pen according to the layout metrics
740
The width of non cosmetic pens is scaled from screen to layout metrics,
741
so that they look similar on paint devices with different resolutions.
743
\param pen Unscaled pen
747
QPen QwtPainter::scaledPen(const QPen &pen)
749
#if QT_VERSION < 0x040000
754
if ( !pen.isCosmetic() )
756
int pw = pen.width();
760
sPen.setWidth(QwtPainter::metricsMap().screenToLayoutX(pw));
761
sPen.setCosmetic(true);