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 {
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 {
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 )
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
00133
00134
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
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
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
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;
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
00205 #ifdef PRERENDEREDLABEL_DEBUG
00206 painter.setPen( QPen( Qt::red, 2 ) );
00207 painter.setBrush( Qt::red );
00208
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:
00268 case KDChartEnums::PositionFloating:
00269 default:
00270 return QPointF();
00271 }
00272 }
00273