KDChartTextLabelCache.cpp

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

Generated on Thu Mar 4 23:19:13 2010 for KD Chart 2 by  doxygen 1.5.4