KD Chart 2
[rev.2.5]
|
00001 /**************************************************************************** 00002 ** Copyright (C) 2001-2012 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.txt 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 qreal 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( qreal angle ) 00155 { 00156 m_angle = angle; 00157 invalidate(); 00158 } 00159 00163 qreal 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 QPainter::CompositionMode mode = painter.compositionMode(); 00208 painter.setCompositionMode( QPainter::CompositionMode_Clear ); 00209 painter.drawRect( 0, 0, Width, Height ); 00210 painter.setCompositionMode( mode ); 00211 00212 QMatrix matrix; 00213 matrix.translate( 0.5 * Width, 0.5 * Height ); 00214 matrix.rotate( m_angle ); 00215 #if QT_VERSION > 0x040199 00216 painter.setWorldMatrix( matrix ); 00217 #else 00218 painter.setMatrix( matrix ); 00219 #endif 00220 00221 painter.setPen( m_pen ); 00222 painter.setBrush( m_brush ); 00223 painter.setFont( m_font ); 00224 QRectF container( -0.5 * Width, -0.5 * Height, Width, 0.5 * Height ); 00225 painter.drawText( container, Qt::AlignHCenter | Qt::AlignBottom, 00226 m_text, &boundingRect ); 00227 m_referenceBottomLeft = QPointF( boundingRect.bottomLeft().x(), 0.0 ); 00228 textBottomRight = QPointF( boundingRect.bottomRight().x(), 0.0 ); 00229 m_textAscendVector = boundingRect.topRight() - textBottomRight; 00230 m_textBaseLineVector = textBottomRight - m_referenceBottomLeft; 00231 00232 // FIXME translate topright by char height 00233 boundingRect = matrix.mapRect( boundingRect ); 00234 m_referenceBottomLeft = matrix.map( m_referenceBottomLeft ) 00235 - boundingRect.topLeft(); 00236 textBottomRight = matrix.map( textBottomRight ) 00237 - boundingRect.topLeft(); 00238 m_textAscendVector = matrix.map( m_textAscendVector ) 00239 - matrix.map( Center ); 00240 m_textBaseLineVector = matrix.map( m_textBaseLineVector ) 00241 - matrix.map( Center ); 00242 } 00243 00244 m_dirty = false; // now all the calculation vectors are valid 00245 00246 QPixmap temp( static_cast<int>( boundingRect.width() ), 00247 static_cast<int>( boundingRect.height() ) ); 00248 { 00249 temp.fill( FullTransparent ); 00250 QPainter painter( &temp ); 00251 #ifdef Q_WS_X11 00252 painter.drawImage( QPointF( 0.0, 0.0 ), pixmap, boundingRect ); 00253 #else 00254 painter.drawPixmap( QPointF( 0.0, 0.0 ), pixmap, boundingRect ); 00255 #endif 00256 // #define PRERENDEREDLABEL_DEBUG 00257 #ifdef PRERENDEREDLABEL_DEBUG 00258 painter.setPen( QPen( Qt::red, 2 ) ); 00259 painter.setBrush( Qt::red ); 00260 // paint markers for the reference points 00261 QList<KDChartEnums::PositionValue> positions; 00262 positions << KDChartEnums::PositionCenter 00263 << KDChartEnums::PositionNorthWest 00264 << KDChartEnums::PositionNorth 00265 << KDChartEnums::PositionNorthEast 00266 << KDChartEnums::PositionEast 00267 << KDChartEnums::PositionSouthEast 00268 << KDChartEnums::PositionSouth 00269 << KDChartEnums::PositionSouthWest 00270 << KDChartEnums::PositionWest; 00271 Q_FOREACH( KDChartEnums::PositionValue position, positions ) { //krazy:exclude=foreach 00272 static const double Radius = 0.5; 00273 static const double Diameter = 2 * Radius; 00274 00275 QPointF point ( referencePointLocation( position ) ); 00276 painter.drawEllipse( QRectF( point - QPointF( Radius, Radius ), 00277 QSizeF( Diameter, Diameter ) ) ); 00278 } 00279 #endif 00280 } 00281 00282 m_pixmap = temp; 00283 } 00284 00285 QPointF PrerenderedLabel::referencePointLocation() const 00286 { 00287 return referencePointLocation( referencePoint() ); 00288 } 00289 00290 QPointF PrerenderedLabel::referencePointLocation( KDChartEnums::PositionValue position ) const 00291 { 00292 if ( m_dirty ) { 00293 INC_MISS_COUNT; 00294 paint(); 00295 } else { 00296 INC_HIT_COUNT; 00297 } 00298 00299 switch( position ) { 00300 case KDChartEnums::PositionCenter: 00301 return m_referenceBottomLeft + 0.5 * m_textBaseLineVector + 0.5 * m_textAscendVector; 00302 case KDChartEnums::PositionNorthWest: 00303 return m_referenceBottomLeft + m_textAscendVector; 00304 case KDChartEnums::PositionNorth: 00305 return m_referenceBottomLeft + 0.5 * m_textBaseLineVector + m_textAscendVector; 00306 case KDChartEnums::PositionNorthEast: 00307 return m_referenceBottomLeft + m_textBaseLineVector + m_textAscendVector; 00308 case KDChartEnums::PositionEast: 00309 return m_referenceBottomLeft + 0.5 * m_textAscendVector; 00310 case KDChartEnums::PositionSouthEast: 00311 return m_referenceBottomLeft + m_textBaseLineVector; 00312 case KDChartEnums::PositionSouth: 00313 return m_referenceBottomLeft + 0.5 * m_textBaseLineVector; 00314 case KDChartEnums::PositionSouthWest: 00315 return m_referenceBottomLeft; 00316 case KDChartEnums::PositionWest: 00317 return m_referenceBottomLeft + m_textBaseLineVector + 0.5 * m_textAscendVector; 00318 00319 case KDChartEnums::PositionUnknown: // intentional fall-through 00320 case KDChartEnums::PositionFloating: // intentional fall-through 00321 return QPointF(); 00322 } 00323 00324 return QPointF(); 00325 }