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

Last change on this file since 4271 was 4271, checked in by mervart, 7 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.