Add lib/libkdchart
This commit is contained in:
321
lib/libkdchart/src/PrerenderedElements/KDChartTextLabelCache.cpp
Normal file
321
lib/libkdchart/src/PrerenderedElements/KDChartTextLabelCache.cpp
Normal file
@@ -0,0 +1,321 @@
|
||||
/****************************************************************************
|
||||
** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved.
|
||||
**
|
||||
** This file is part of the KD Chart library.
|
||||
**
|
||||
** Licensees holding valid commercial KD Chart licenses may use this file in
|
||||
** accordance with the KD Chart Commercial License Agreement provided with
|
||||
** the Software.
|
||||
**
|
||||
**
|
||||
** This file may be distributed and/or modified under the terms of the
|
||||
** GNU General Public License version 2 and version 3 as published by the
|
||||
** Free Software Foundation and appearing in the file LICENSE.GPL.txt included.
|
||||
**
|
||||
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
**
|
||||
** Contact info@kdab.com if any conditions of this licensing are not
|
||||
** clear to you.
|
||||
**
|
||||
**********************************************************************/
|
||||
|
||||
#include "KDChartTextLabelCache.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <QtDebug>
|
||||
#include <QImage>
|
||||
#include <QPixmap>
|
||||
#include <QPainter>
|
||||
#include <QApplication>
|
||||
|
||||
#ifndef NDEBUG
|
||||
int HitCount = 0;
|
||||
int MissCount = 0;
|
||||
#define INC_HIT_COUNT { ++HitCount; }
|
||||
#define INC_MISS_COUNT { ++MissCount; }
|
||||
#define DUMP_CACHE_STATS \
|
||||
if ( HitCount != 0 && MissCount != 0 ) { \
|
||||
int total = HitCount + MissCount; \
|
||||
double hitQuote = ( 1.0 * HitCount ) / total; \
|
||||
qDebug() << "PrerenderedLabel dtor: hits/misses/total:" \
|
||||
<< HitCount << "/" << MissCount << "/" << total \
|
||||
<< "(" << 100 * hitQuote << "% hits)"; \
|
||||
}
|
||||
#else
|
||||
#define INC_HIT_COUNT
|
||||
#define INC_MISS_COUNT
|
||||
#define DUMP_CACHE_STATS
|
||||
#endif
|
||||
|
||||
PrerenderedElement::PrerenderedElement()
|
||||
: m_referencePoint( KDChartEnums::PositionNorthWest )
|
||||
{
|
||||
}
|
||||
|
||||
void PrerenderedElement::setPosition( const QPointF& position )
|
||||
{ // this does not invalidate the element
|
||||
m_position = position;
|
||||
}
|
||||
|
||||
const QPointF& PrerenderedElement::position() const
|
||||
{
|
||||
return m_position;
|
||||
}
|
||||
|
||||
void PrerenderedElement::setReferencePoint( KDChartEnums::PositionValue point )
|
||||
{ // this does not invalidate the element
|
||||
m_referencePoint = point;
|
||||
}
|
||||
|
||||
KDChartEnums::PositionValue PrerenderedElement::referencePoint() const
|
||||
{
|
||||
return m_referencePoint;
|
||||
}
|
||||
|
||||
PrerenderedLabel::PrerenderedLabel()
|
||||
: PrerenderedElement()
|
||||
, m_dirty( true )
|
||||
, m_font( qApp->font() )
|
||||
, m_brush( Qt::black )
|
||||
, m_pen( Qt::black ) // do not use anything invisible
|
||||
, m_angle( 0.0 )
|
||||
{
|
||||
}
|
||||
|
||||
PrerenderedLabel::~PrerenderedLabel()
|
||||
{
|
||||
DUMP_CACHE_STATS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates the preredendered data, forces re-rendering.
|
||||
*/
|
||||
void PrerenderedLabel::invalidate() const
|
||||
{
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the label's font to \a font.
|
||||
*/
|
||||
void PrerenderedLabel::setFont( const QFont& font )
|
||||
{
|
||||
m_font = font;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the label's font.
|
||||
*/
|
||||
const QFont& PrerenderedLabel::font() const
|
||||
{
|
||||
return m_font;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the label's text to \a text
|
||||
*/
|
||||
void PrerenderedLabel::setText( const QString& text )
|
||||
{
|
||||
m_text = text;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the label's text
|
||||
*/
|
||||
const QString& PrerenderedLabel::text() const
|
||||
{
|
||||
return m_text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the label's brush to \a brush
|
||||
*/
|
||||
void PrerenderedLabel::setBrush( const QBrush& brush )
|
||||
{
|
||||
m_brush = brush;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the label's brush
|
||||
*/
|
||||
const QBrush& PrerenderedLabel::brush() const
|
||||
{
|
||||
return m_brush;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the angle of the label to \a angle degrees
|
||||
*/
|
||||
void PrerenderedLabel::setAngle( double angle )
|
||||
{
|
||||
m_angle = angle;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the label's angle in degrees
|
||||
*/
|
||||
double PrerenderedLabel::angle() const
|
||||
{
|
||||
return m_angle;
|
||||
}
|
||||
|
||||
const QPixmap& PrerenderedLabel::pixmap() const
|
||||
{
|
||||
if ( m_dirty ) {
|
||||
INC_MISS_COUNT;
|
||||
paint();
|
||||
} else {
|
||||
INC_HIT_COUNT;
|
||||
}
|
||||
return m_pixmap;
|
||||
}
|
||||
|
||||
void PrerenderedLabel::paint() const
|
||||
{
|
||||
// FIXME find a better value using font metrics of text (this
|
||||
// requires finding the diameter of the circle formed by rotating
|
||||
// the bounding rect around the center):
|
||||
const int Width = 1000;
|
||||
const int Height = Width;
|
||||
|
||||
QRectF boundingRect;
|
||||
const QColor FullTransparent( 255, 255, 255, 0 );
|
||||
#ifdef Q_WS_X11
|
||||
QImage pixmap( Width, Height, QImage::Format_ARGB32_Premultiplied );
|
||||
qWarning() << "PrerenderedLabel::paint: using QImage for prerendered labels "
|
||||
<< "to work around XRender/Qt4 bug.";
|
||||
#else
|
||||
QPixmap pixmap( Width, Height );
|
||||
#endif
|
||||
// pixmap.fill( FullTransparent );
|
||||
{
|
||||
static const QPointF Center ( 0.0, 0.0 );
|
||||
QPointF textBottomRight;
|
||||
QPainter painter( &pixmap );
|
||||
painter.setRenderHint(QPainter::TextAntialiasing, true );
|
||||
painter.setRenderHint(QPainter::Antialiasing, true );
|
||||
|
||||
// QImage (X11 workaround) does not have fill():
|
||||
painter.setPen( FullTransparent );
|
||||
painter.setBrush( FullTransparent );
|
||||
painter.drawRect( 0, 0, Width, Height );
|
||||
|
||||
QMatrix matrix;
|
||||
matrix.translate( 0.5 * Width, 0.5 * Height );
|
||||
matrix.rotate( m_angle );
|
||||
#if QT_VERSION > 0x040199
|
||||
painter.setWorldMatrix( matrix );
|
||||
#else
|
||||
painter.setMatrix( matrix );
|
||||
#endif
|
||||
|
||||
painter.setPen( m_pen );
|
||||
painter.setBrush( m_brush );
|
||||
painter.setFont( m_font );
|
||||
QRectF container( -0.5 * Width, -0.5 * Height, Width, 0.5 * Height );
|
||||
painter.drawText( container, Qt::AlignHCenter | Qt::AlignBottom,
|
||||
m_text, &boundingRect );
|
||||
m_referenceBottomLeft = QPointF( boundingRect.bottomLeft().x(), 0.0 );
|
||||
textBottomRight = QPointF( boundingRect.bottomRight().x(), 0.0 );
|
||||
m_textAscendVector = boundingRect.topRight() - textBottomRight;
|
||||
m_textBaseLineVector = textBottomRight - m_referenceBottomLeft;
|
||||
|
||||
// FIXME translate topright by char height
|
||||
boundingRect = matrix.mapRect( boundingRect );
|
||||
m_referenceBottomLeft = matrix.map( m_referenceBottomLeft )
|
||||
- boundingRect.topLeft();
|
||||
textBottomRight = matrix.map( textBottomRight )
|
||||
- boundingRect.topLeft();
|
||||
m_textAscendVector = matrix.map( m_textAscendVector )
|
||||
- matrix.map( Center );
|
||||
m_textBaseLineVector = matrix.map( m_textBaseLineVector )
|
||||
- matrix.map( Center );
|
||||
}
|
||||
|
||||
m_dirty = false; // now all the calculation vectors are valid
|
||||
|
||||
QPixmap temp( static_cast<int>( boundingRect.width() ),
|
||||
static_cast<int>( boundingRect.height() ) );
|
||||
{
|
||||
temp.fill( FullTransparent );
|
||||
QPainter painter( &temp );
|
||||
#ifdef Q_WS_X11
|
||||
painter.drawImage( QPointF( 0.0, 0.0 ), pixmap, boundingRect );
|
||||
#else
|
||||
painter.drawPixmap( QPointF( 0.0, 0.0 ), pixmap, boundingRect );
|
||||
#endif
|
||||
// #define PRERENDEREDLABEL_DEBUG
|
||||
#ifdef PRERENDEREDLABEL_DEBUG
|
||||
painter.setPen( QPen( Qt::red, 2 ) );
|
||||
painter.setBrush( Qt::red );
|
||||
// paint markers for the reference points
|
||||
QList<KDChartEnums::PositionValue> positions;
|
||||
positions << KDChartEnums::PositionCenter
|
||||
<< KDChartEnums::PositionNorthWest
|
||||
<< KDChartEnums::PositionNorth
|
||||
<< KDChartEnums::PositionNorthEast
|
||||
<< KDChartEnums::PositionEast
|
||||
<< KDChartEnums::PositionSouthEast
|
||||
<< KDChartEnums::PositionSouth
|
||||
<< KDChartEnums::PositionSouthWest
|
||||
<< KDChartEnums::PositionWest;
|
||||
Q_FOREACH( KDChartEnums::PositionValue position, positions ) {
|
||||
static const double Radius = 0.5;
|
||||
static const double Diameter = 2 * Radius;
|
||||
|
||||
QPointF point ( referencePointLocation( position ) );
|
||||
painter.drawEllipse( QRectF( point - QPointF( Radius, Radius ),
|
||||
QSizeF( Diameter, Diameter ) ) );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
m_pixmap = temp;
|
||||
}
|
||||
|
||||
QPointF PrerenderedLabel::referencePointLocation() const
|
||||
{
|
||||
return referencePointLocation( referencePoint() );
|
||||
}
|
||||
|
||||
QPointF PrerenderedLabel::referencePointLocation( KDChartEnums::PositionValue position ) const
|
||||
{
|
||||
if ( m_dirty ) {
|
||||
INC_MISS_COUNT;
|
||||
paint();
|
||||
} else {
|
||||
INC_HIT_COUNT;
|
||||
}
|
||||
|
||||
switch( position ) {
|
||||
case KDChartEnums::PositionCenter:
|
||||
return m_referenceBottomLeft + 0.5 * m_textBaseLineVector + 0.5 * m_textAscendVector;
|
||||
case KDChartEnums::PositionNorthWest:
|
||||
return m_referenceBottomLeft + m_textAscendVector;
|
||||
case KDChartEnums::PositionNorth:
|
||||
return m_referenceBottomLeft + 0.5 * m_textBaseLineVector + m_textAscendVector;
|
||||
case KDChartEnums::PositionNorthEast:
|
||||
return m_referenceBottomLeft + m_textBaseLineVector + m_textAscendVector;
|
||||
case KDChartEnums::PositionEast:
|
||||
return m_referenceBottomLeft + 0.5 * m_textAscendVector;
|
||||
case KDChartEnums::PositionSouthEast:
|
||||
return m_referenceBottomLeft + m_textBaseLineVector;
|
||||
case KDChartEnums::PositionSouth:
|
||||
return m_referenceBottomLeft + 0.5 * m_textBaseLineVector;
|
||||
case KDChartEnums::PositionSouthWest:
|
||||
return m_referenceBottomLeft;
|
||||
case KDChartEnums::PositionWest:
|
||||
return m_referenceBottomLeft + m_textBaseLineVector + 0.5 * m_textAscendVector;
|
||||
|
||||
case KDChartEnums::PositionUnknown: // intentional fall-through
|
||||
case KDChartEnums::PositionFloating: // intentional fall-through
|
||||
default:
|
||||
return QPointF();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user