KDChartTextLabelCache.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 ** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB.  All rights reserved.
00003 **
00004 ** This file is part of the KD Chart library.
00005 **
00006 ** Licensees holding valid commercial KD Chart licenses may use this file in
00007 ** accordance with the KD Chart Commercial License Agreement provided with
00008 ** the Software.
00009 **
00010 **
00011 ** This file may be distributed and/or modified under the terms of the
00012 ** GNU General Public License version 2 and version 3 as published by the
00013 ** Free Software Foundation and appearing in the file LICENSE.GPL included.
00014 **
00015 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00016 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00017 **
00018 ** Contact info@kdab.com if any conditions of this licensing are not
00019 ** clear to you.
00020 **
00021 **********************************************************************/
00022 
00023 #include "KDChartTextLabelCache.h"
00024 
00025 #include <cmath>
00026 
00027 #include <QtDebug>
00028 #include <QImage>
00029 #include <QPixmap>
00030 #include <QPainter>
00031 #include <QApplication>
00032 
00033 #ifndef NDEBUG
00034 int HitCount = 0;
00035 int MissCount = 0;
00036 #define INC_HIT_COUNT { ++HitCount; }
00037 #define INC_MISS_COUNT  { ++MissCount; }
00038 #define DUMP_CACHE_STATS \
00039     if ( HitCount != 0 && MissCount != 0 ) { \
00040         int total = HitCount + MissCount; \
00041         double hitQuote = ( 1.0 * HitCount ) / total; \
00042         qDebug() << "PrerenderedLabel dtor: hits/misses/total:" \
00043         << HitCount << "/" << MissCount << "/" << total \
00044                  << "(" << 100 * hitQuote << "% hits)"; \
00045     }
00046 #else
00047 #define INC_HIT_COUNT
00048 #define INC_MISS_COUNT
00049 #define DUMP_CACHE_STATS
00050 #endif
00051 
00052 PrerenderedElement::PrerenderedElement()
00053     : m_referencePoint( KDChartEnums::PositionNorthWest )
00054 {
00055 }
00056 
00057 void PrerenderedElement::setPosition( const QPointF& position )
00058 {   // this does not invalidate the element
00059     m_position = position;
00060 }
00061 
00062 const QPointF& PrerenderedElement::position() const
00063 {
00064     return m_position;
00065 }
00066 
00067 void PrerenderedElement::setReferencePoint( KDChartEnums::PositionValue point )
00068 {   // this does not invalidate the element
00069     m_referencePoint = point;
00070 }
00071 
00072 KDChartEnums::PositionValue PrerenderedElement::referencePoint() const
00073 {
00074     return m_referencePoint;
00075 }
00076 
00077 PrerenderedLabel::PrerenderedLabel()
00078     : PrerenderedElement()
00079     , m_dirty( true )
00080     , m_font( qApp->font() )
00081     , m_brush( Qt::black )
00082     , m_pen( Qt::black ) // do not use anything invisible
00083     , m_angle( 0.0 )
00084 {
00085 }
00086 
00087 PrerenderedLabel::~PrerenderedLabel()
00088 {
00089     DUMP_CACHE_STATS;
00090 }
00091 
00095 void PrerenderedLabel::invalidate() const
00096 {
00097     m_dirty = true;
00098 }
00099 
00103 void PrerenderedLabel::setFont( const QFont& font )
00104 {
00105     m_font = font;
00106     invalidate();
00107 }
00108 
00112 const QFont& PrerenderedLabel::font() const
00113 {
00114     return m_font;
00115 }
00116 
00120 void PrerenderedLabel::setText( const QString& text )
00121 {
00122     m_text = text;
00123     invalidate();
00124 }
00125 
00129 const QString& PrerenderedLabel::text() const
00130 {
00131     return m_text;
00132 }
00133 
00137 void PrerenderedLabel::setBrush( const QBrush& brush )
00138 {
00139     m_brush = brush;
00140     invalidate();
00141 }
00142 
00146 const QBrush& PrerenderedLabel::brush() const
00147 {
00148     return m_brush;
00149 }
00150 
00154 void PrerenderedLabel::setAngle( double angle )
00155 {
00156     m_angle = angle;
00157     invalidate();
00158 }
00159 
00163 double PrerenderedLabel::angle() const
00164 {
00165     return m_angle;
00166 }
00167 
00168 const QPixmap& PrerenderedLabel::pixmap() const
00169 {
00170     if ( m_dirty ) {
00171         INC_MISS_COUNT;
00172         paint();
00173     } else {
00174         INC_HIT_COUNT;
00175     }
00176     return m_pixmap;
00177 }
00178 
00179 void PrerenderedLabel::paint() const
00180 {
00181     // FIXME find a better value using font metrics of text (this
00182     // requires finding the diameter of the circle formed by rotating
00183     // the bounding rect around the center):
00184     const int Width = 1000;
00185     const int Height = Width;
00186 
00187     QRectF boundingRect;
00188     const QColor FullTransparent( 255, 255, 255, 0 );
00189 #ifdef Q_WS_X11
00190     QImage pixmap( Width, Height, QImage::Format_ARGB32_Premultiplied );
00191     qWarning() << "PrerenderedLabel::paint: using QImage for prerendered labels "
00192                << "to work around XRender/Qt4 bug.";
00193 #else
00194     QPixmap pixmap( Width, Height );
00195 #endif
00196     // pixmap.fill( FullTransparent );
00197     {
00198         static const QPointF Center ( 0.0, 0.0 );
00199         QPointF textBottomRight;
00200         QPainter painter( &pixmap );
00201         painter.setRenderHint(QPainter::TextAntialiasing, true );
00202         painter.setRenderHint(QPainter::Antialiasing, true );
00203 
00204         // QImage (X11 workaround) does not have fill():
00205         painter.setPen( FullTransparent );
00206         painter.setBrush( FullTransparent );
00207         painter.drawRect( 0, 0, Width, Height );
00208 
00209         QMatrix matrix;
00210         matrix.translate( 0.5 * Width,  0.5 * Height );
00211         matrix.rotate( m_angle );
00212 #if QT_VERSION > 0x040199
00213         painter.setWorldMatrix( matrix );
00214 #else
00215         painter.setMatrix( matrix );
00216 #endif
00217 
00218         painter.setPen( m_pen );
00219         painter.setBrush( m_brush );
00220         painter.setFont( m_font );
00221         QRectF container( -0.5 * Width, -0.5 * Height, Width, 0.5 * Height );
00222         painter.drawText( container, Qt::AlignHCenter | Qt::AlignBottom,
00223                           m_text, &boundingRect );
00224         m_referenceBottomLeft = QPointF( boundingRect.bottomLeft().x(), 0.0 );
00225         textBottomRight = QPointF( boundingRect.bottomRight().x(), 0.0 );
00226         m_textAscendVector = boundingRect.topRight() - textBottomRight;
00227         m_textBaseLineVector = textBottomRight - m_referenceBottomLeft;
00228 
00229         // FIXME translate topright by char height
00230         boundingRect = matrix.mapRect( boundingRect );
00231         m_referenceBottomLeft = matrix.map( m_referenceBottomLeft )
00232                                 - boundingRect.topLeft();
00233         textBottomRight = matrix.map( textBottomRight )
00234                           - boundingRect.topLeft();
00235         m_textAscendVector = matrix.map( m_textAscendVector )
00236                              - matrix.map( Center );
00237         m_textBaseLineVector = matrix.map( m_textBaseLineVector )
00238                             - matrix.map( Center );
00239     }
00240 
00241     m_dirty = false; // now all the calculation vectors are valid
00242 
00243     QPixmap temp( static_cast<int>( boundingRect.width() ),
00244                   static_cast<int>( boundingRect.height() ) );
00245     {
00246         temp.fill( FullTransparent );
00247         QPainter painter( &temp );
00248 #ifdef Q_WS_X11
00249         painter.drawImage( QPointF( 0.0, 0.0 ), pixmap, boundingRect );
00250 #else
00251         painter.drawPixmap( QPointF( 0.0, 0.0 ), pixmap, boundingRect );
00252 #endif
00253 // #define PRERENDEREDLABEL_DEBUG
00254 #ifdef PRERENDEREDLABEL_DEBUG
00255         painter.setPen( QPen( Qt::red, 2 ) );
00256         painter.setBrush( Qt::red );
00257         // paint markers for the reference points
00258         QList<KDChartEnums::PositionValue> positions;
00259         positions << KDChartEnums::PositionCenter
00260                   << KDChartEnums::PositionNorthWest
00261                   << KDChartEnums::PositionNorth
00262                   << KDChartEnums::PositionNorthEast
00263                   << KDChartEnums::PositionEast
00264                   << KDChartEnums::PositionSouthEast
00265                   << KDChartEnums::PositionSouth
00266                   << KDChartEnums::PositionSouthWest
00267                   << KDChartEnums::PositionWest;
00268         Q_FOREACH( KDChartEnums::PositionValue position, positions ) {
00269             static const double Radius = 0.5;
00270             static const double Diameter = 2 * Radius;
00271 
00272             QPointF point ( referencePointLocation( position ) );
00273             painter.drawEllipse( QRectF( point - QPointF( Radius, Radius ),
00274                                          QSizeF( Diameter, Diameter ) ) );
00275         }
00276 #endif
00277     }
00278 
00279     m_pixmap = temp;
00280 }
00281 
00282 QPointF PrerenderedLabel::referencePointLocation() const
00283 {
00284     return referencePointLocation( referencePoint() );
00285 }
00286 
00287 QPointF PrerenderedLabel::referencePointLocation( KDChartEnums::PositionValue position ) const
00288 {
00289     if ( m_dirty ) {
00290         INC_MISS_COUNT;
00291         paint();
00292     } else {
00293         INC_HIT_COUNT;
00294     }
00295 
00296     switch( position ) {
00297     case KDChartEnums::PositionCenter:
00298         return m_referenceBottomLeft + 0.5 * m_textBaseLineVector + 0.5 * m_textAscendVector;
00299     case KDChartEnums::PositionNorthWest:
00300         return m_referenceBottomLeft + m_textAscendVector;
00301     case KDChartEnums::PositionNorth:
00302         return m_referenceBottomLeft + 0.5 * m_textBaseLineVector + m_textAscendVector;
00303     case KDChartEnums::PositionNorthEast:
00304         return m_referenceBottomLeft + m_textBaseLineVector + m_textAscendVector;
00305     case KDChartEnums::PositionEast:
00306         return m_referenceBottomLeft + 0.5 * m_textAscendVector;
00307     case KDChartEnums::PositionSouthEast:
00308         return m_referenceBottomLeft + m_textBaseLineVector;
00309     case KDChartEnums::PositionSouth:
00310         return m_referenceBottomLeft + 0.5 * m_textBaseLineVector;
00311     case KDChartEnums::PositionSouthWest:
00312         return m_referenceBottomLeft;
00313     case KDChartEnums::PositionWest:
00314         return m_referenceBottomLeft + m_textBaseLineVector + 0.5 * m_textAscendVector;
00315 
00316     case KDChartEnums::PositionUnknown: // intentional fall-through
00317     case KDChartEnums::PositionFloating: // intentional fall-through
00318     default:
00319         return QPointF();
00320     }
00321 }