# KDChartTextLabelCache.cpp

Go to the documentation of this file.

    00001 #include <cmath>
    00002
    00003 #include <QtDebug>
    00004 #include <QImage>
    00005 #include <QPixmap>
    00006 #include <QPainter>
    00007 #include <QApplication>
    00008
    00009 #include "KDChartTextLabelCache.h"
    00010
    00011 #ifndef NDEBUG
    00012 int HitCount = 0;
    00013 int MissCount = 0;
    00014 #define INC_HIT_COUNT { ++HitCount; }
    00015 #define INC_MISS_COUNT  { ++MissCount; }
    00016 #define DUMP_CACHE_STATS \
    00017     if ( HitCount != 0 && MissCount != 0 ) { \
    00018         int total = HitCount + MissCount; \
    00019         double hitQuote = ( 1.0 * HitCount ) / total; \
    00020         qDebug() << "PrerenderedLabel dtor: hits/misses/total:" \
    00021         << HitCount << "/" << MissCount << "/" << total \
    00022                  << "(" << 100 * hitQuote << "% hits)"; \
    00023     }
    00024 #else
    00025 #define INC_HIT_COUNT
    00026 #define INC_MISS_COUNT
    00027 #define DUMP_CACHE_STATS
    00028 #endif
    00029
    00030 PrerenderedElement::PrerenderedElement()
    00031     : m_referencePoint( KDChartEnums::PositionNorthWest )
    00032 {
    00033 }
    00034
    00035 void PrerenderedElement::setPosition( const QPointF& position )
    00036 {   // this does not invalidate the element
    00037     m_position = position;
    00038 }
    00039
    00040 const QPointF& PrerenderedElement::position() const
    00041 {
    00042     return m_position;
    00043 }
    00044
    00045 void PrerenderedElement::setReferencePoint( KDChartEnums::PositionValue point )
    00046 {   // this does not invalidate the element
    00047     m_referencePoint = point;
    00048 }
    00049
    00050 KDChartEnums::PositionValue PrerenderedElement::referencePoint() const
    00051 {
    00052     return m_referencePoint;
    00053 }
    00054
    00055 PrerenderedLabel::PrerenderedLabel()
    00056     : PrerenderedElement()
    00057     , m_dirty( true )
    00058     , m_font( qApp->font() )
    00059     , m_brush( Qt::black )
    00060     , m_pen( Qt::black ) // do not use anything invisible
    00061     , m_angle( 0.0 )
    00062 {
    00063 }
    00064
    00065 PrerenderedLabel::~PrerenderedLabel()
    00066 {
    00067     DUMP_CACHE_STATS;
    00068 }
    00069
    00070 void PrerenderedLabel::invalidate() const
    00071 {
    00072     m_dirty = true;
    00073 }
    00074
    00075 void PrerenderedLabel::setFont( const QFont& font )
    00076 {
    00077     m_font = font;
    00078     invalidate();
    00079 }
    00080
    00081 const QFont& PrerenderedLabel::font() const
    00082 {
    00083     return m_font;
    00084 }
    00085
    00086 void PrerenderedLabel::setText( const QString& text )
    00087 {
    00088     m_text = text;
    00089     invalidate();
    00090 }
    00091
    00092 const QString& PrerenderedLabel::text() const
    00093 {
    00094     return m_text;
    00095 }
    00096
    00097 void PrerenderedLabel::setBrush( const QBrush& brush )
    00098 {
    00099     m_brush = brush;
    00100     invalidate();
    00101 }
    00102
    00103 const QBrush& PrerenderedLabel::brush() const
    00104 {
    00105     return m_brush;
    00106 }
    00107
    00108 void PrerenderedLabel::setAngle( double angle )
    00109 {
    00110     m_angle = angle;
    00111     invalidate();
    00112 }
    00113
    00114 double PrerenderedLabel::angle() const
    00115 {
    00116     return m_angle;
    00117 }
    00118
    00119 const QPixmap& PrerenderedLabel::pixmap() const
    00120 {
    00121     if ( m_dirty ) {
    00122         INC_MISS_COUNT;
    00123         paint();
    00124     } else {
    00125         INC_HIT_COUNT;
    00126     }
    00127     return m_pixmap;
    00128 }
    00129
    00130 void PrerenderedLabel::paint() const
    00131 {
    00132     // FIXME find a better value using font metrics of text (this
    00133     // requires finding the diameter of the circle formed by rotating
    00134     // the bounding rect around the center):
    00135     const int Width = 1000;
    00136     const int Height = Width;
    00137
    00138     QRectF boundingRect;
    00139     const QColor FullTransparent( 255, 255, 255, 0 );
    00140 #ifdef Q_WS_X11
    00141     QImage pixmap( Width, Height, QImage::Format_ARGB32_Premultiplied );
    00142     qWarning() << "PrerenderedLabel::paint: using QImage for prerendered labels "
    00143                << "to work around XRender/Qt4 bug.";
    00144 #else
    00145     QPixmap pixmap( Width, Height );
    00146 #endif
    00147     // pixmap.fill( FullTransparent );
    00148     {
    00149         static const QPointF Center ( 0.0, 0.0 );
    00150         QPointF textBottomRight;
    00151         QPainter painter( &pixmap );
    00152         painter.setRenderHint(QPainter::TextAntialiasing, true );
    00153         painter.setRenderHint(QPainter::Antialiasing, true );
    00154
    00155         // QImage (X11 workaround) does not have fill():
    00156         painter.setPen( FullTransparent );
    00157         painter.setBrush( FullTransparent );
    00158         painter.drawRect( 0, 0, Width, Height );
    00159
    00160         QMatrix matrix;
    00161         matrix.translate( 0.5 * Width,  0.5 * Height );
    00162         matrix.rotate( m_angle );
    00163 #if QT_VERSION > 0x040199
    00164         painter.setWorldMatrix( matrix );
    00165 #else
    00166         painter.setMatrix( matrix );
    00167 #endif
    00168
    00169         painter.setPen( m_pen );
    00170         painter.setBrush( m_brush );
    00171         painter.setFont( m_font );
    00172         QRectF container( -0.5 * Width, -0.5 * Height, Width, 0.5 * Height );
    00173         painter.drawText( container, Qt::AlignHCenter | Qt::AlignBottom,
    00174                           m_text, &boundingRect );
    00175         m_referenceBottomLeft = QPointF( boundingRect.bottomLeft().x(), 0.0 );
    00176         textBottomRight = QPointF( boundingRect.bottomRight().x(), 0.0 );
    00177         m_textAscendVector = boundingRect.topRight() - textBottomRight;
    00178         m_textBaseLineVector = textBottomRight - m_referenceBottomLeft;
    00179
    00180         // FIXME translate topright by char height
    00181         boundingRect = matrix.mapRect( boundingRect );
    00182         m_referenceBottomLeft = matrix.map( m_referenceBottomLeft )
    00183                                 - boundingRect.topLeft();
    00184         textBottomRight = matrix.map( textBottomRight )
    00185                           - boundingRect.topLeft();
    00186         m_textAscendVector = matrix.map( m_textAscendVector )
    00187                              - matrix.map( Center );
    00188         m_textBaseLineVector = matrix.map( m_textBaseLineVector )
    00189                             - matrix.map( Center );
    00190     }
    00191
    00192     m_dirty = false; // now all the calculation vectors are valid
    00193
    00194     QPixmap temp( static_cast<int>( boundingRect.width() ),
    00195                   static_cast<int>( boundingRect.height() ) );
    00196     {
    00197         temp.fill( FullTransparent );
    00198         QPainter painter( &temp );
    00199 #ifdef Q_WS_X11
    00200         painter.drawImage( QPointF( 0.0, 0.0 ), pixmap, boundingRect );
    00201 #else
    00202         painter.drawPixmap( QPointF( 0.0, 0.0 ), pixmap, boundingRect );
    00203 #endif
    00204 // #define PRERENDEREDLABEL_DEBUG
    00205 #ifdef PRERENDEREDLABEL_DEBUG
    00206         painter.setPen( QPen( Qt::red, 2 ) );
    00207         painter.setBrush( Qt::red );
    00208         // paint markers for the reference points
    00209         QList<KDChartEnums::PositionValue> positions;
    00210         positions << KDChartEnums::PositionCenter
    00211                   << KDChartEnums::PositionNorthWest
    00212                   << KDChartEnums::PositionNorth
    00213                   << KDChartEnums::PositionNorthEast
    00214                   << KDChartEnums::PositionEast
    00215                   << KDChartEnums::PositionSouthEast
    00216                   << KDChartEnums::PositionSouth
    00217                   << KDChartEnums::PositionSouthWest
    00218                   << KDChartEnums::PositionWest;
    00219         Q_FOREACH( KDChartEnums::PositionValue position, positions ) {
    00220             static const double Radius = 0.5;
    00221             static const double Diameter = 2 * Radius;
    00222
    00223             QPointF point ( referencePointLocation( position ) );
    00224             painter.drawEllipse( QRectF( point - QPointF( Radius, Radius ),
    00225                                          QSizeF( Diameter, Diameter ) ) );
    00226         }
    00227 #endif
    00228     }
    00229
    00230     m_pixmap = temp;
    00231 }
    00232
    00233 QPointF PrerenderedLabel::referencePointLocation() const
    00234 {
    00235     return referencePointLocation( referencePoint() );
    00236 }
    00237
    00238 QPointF PrerenderedLabel::referencePointLocation( KDChartEnums::PositionValue position ) const
    00239 {
    00240     if ( m_dirty ) {
    00241         INC_MISS_COUNT;
    00242         paint();
    00243     } else {
    00244         INC_HIT_COUNT;
    00245     }
    00246
    00247     switch( position ) {
    00248     case KDChartEnums::PositionCenter:
    00249         return m_referenceBottomLeft + 0.5 * m_textBaseLineVector + 0.5 * m_textAscendVector;
    00250     case KDChartEnums::PositionNorthWest:
    00251         return m_referenceBottomLeft + m_textAscendVector;
    00252     case KDChartEnums::PositionNorth:
    00253         return m_referenceBottomLeft + 0.5 * m_textBaseLineVector + m_textAscendVector;
    00254     case KDChartEnums::PositionNorthEast:
    00255         return m_referenceBottomLeft + m_textBaseLineVector + m_textAscendVector;
    00256     case KDChartEnums::PositionEast:
    00257         return m_referenceBottomLeft + 0.5 * m_textAscendVector;
    00258     case KDChartEnums::PositionSouthEast:
    00259         return m_referenceBottomLeft + m_textBaseLineVector;
    00260     case KDChartEnums::PositionSouth:
    00261         return m_referenceBottomLeft + 0.5 * m_textBaseLineVector;
    00262     case KDChartEnums::PositionSouthWest:
    00263         return m_referenceBottomLeft;
    00264     case KDChartEnums::PositionWest:
    00265         return m_referenceBottomLeft + m_textBaseLineVector + 0.5 * m_textAscendVector;
    00266
    00267     case KDChartEnums::PositionUnknown: // intentional fall-through
    00268     case KDChartEnums::PositionFloating: // intentional fall-through
    00269     default:
    00270         return QPointF();
    00271     }
    00272 }
    00273