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();
|
||||
}
|
||||
}
|
||||
148
lib/libkdchart/src/PrerenderedElements/KDChartTextLabelCache.h
Normal file
148
lib/libkdchart/src/PrerenderedElements/KDChartTextLabelCache.h
Normal file
@@ -0,0 +1,148 @@
|
||||
/****************************************************************************
|
||||
** 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.
|
||||
**
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef KDCHARTTEXTLABELCACHE_H
|
||||
#define KDCHARTTEXTLABELCACHE_H
|
||||
|
||||
#include <QPixmap>
|
||||
#include <QFont>
|
||||
#include <QBrush>
|
||||
#include <QPen>
|
||||
|
||||
#include "KDChartEnums.h"
|
||||
|
||||
/**
|
||||
* @brief base class for prerendered elements like labels, pixmaps, markers, etc.
|
||||
*/
|
||||
class PrerenderedElement {
|
||||
public:
|
||||
PrerenderedElement();
|
||||
virtual ~PrerenderedElement() {}
|
||||
|
||||
/** Returns the rendered element.
|
||||
If any of the properties have change, the element will be
|
||||
regenerated. */
|
||||
virtual const QPixmap& pixmap() const = 0;
|
||||
|
||||
/** Return the location of the reference point relatively to the
|
||||
pixmap's origin. */
|
||||
virtual QPointF referencePointLocation( KDChartEnums::PositionValue ) const = 0;
|
||||
|
||||
/** Set the position of the element. */
|
||||
void setPosition( const QPointF& position );
|
||||
/** Get the position of the element. */
|
||||
const QPointF& position() const;
|
||||
|
||||
/** Set the reference point of the element.
|
||||
Every element has nine possible reference points (all compass
|
||||
directions, plus the center.
|
||||
*/
|
||||
void setReferencePoint( KDChartEnums::PositionValue );
|
||||
/** Get the reference point of the element. */
|
||||
KDChartEnums::PositionValue referencePoint() const;
|
||||
|
||||
protected:
|
||||
/** invalidate() needs to be called if any of the properties that
|
||||
determine the visual appearance of the prerendered element
|
||||
change.
|
||||
It can be called for a const object, as objects may need to
|
||||
force recalculation of the pixmap.
|
||||
*/
|
||||
virtual void invalidate() const = 0;
|
||||
|
||||
private:
|
||||
QPointF m_position;
|
||||
KDChartEnums::PositionValue m_referencePoint;
|
||||
};
|
||||
|
||||
/**
|
||||
@brief PrerenderedLabel is an internal KDChart class that simplifies creation
|
||||
and caching of cached text labels.
|
||||
|
||||
It provides referenze points to anchor the text to other
|
||||
elements. Reference points use the positions defined in
|
||||
KDChartEnums.
|
||||
|
||||
Usage:
|
||||
<pre>
|
||||
double angle = 90.0;
|
||||
CachedLabel label;
|
||||
label.paint( font, tr("Label"), angle );
|
||||
</pre>
|
||||
*/
|
||||
|
||||
// FIXME this is merely a prototype
|
||||
// FIXME caching could be done by a second layer that can be used to,
|
||||
// e.g., query for a prerendered element by id or name, or by changing
|
||||
// the pixmap() method to do lazy evaluation.
|
||||
class PrerenderedLabel : public PrerenderedElement
|
||||
{
|
||||
public:
|
||||
PrerenderedLabel();
|
||||
~PrerenderedLabel();
|
||||
|
||||
void setFont( const QFont& font );
|
||||
const QFont& font() const;
|
||||
|
||||
void setText( const QString& text );
|
||||
const QString& text() const;
|
||||
|
||||
void setBrush( const QBrush& brush );
|
||||
const QBrush& brush() const;
|
||||
|
||||
void setPen( const QPen& );
|
||||
const QPen& pen() const;
|
||||
|
||||
void setAngle( double angle );
|
||||
double angle() const;
|
||||
|
||||
// reimpl PrerenderedElement:
|
||||
const QPixmap& pixmap() const;
|
||||
QPointF referencePointLocation( KDChartEnums::PositionValue position ) const;
|
||||
// overload: return location of referencePoint():
|
||||
QPointF referencePointLocation() const;
|
||||
|
||||
protected:
|
||||
void invalidate() const;
|
||||
|
||||
private:
|
||||
/** Create a label with the given text and the given rotation
|
||||
angle. Needs to be const, otherwise the pixmap() method cannot
|
||||
update when needed. */
|
||||
void paint() const;
|
||||
|
||||
// store the settings (these are used for the painting):
|
||||
mutable bool m_dirty;
|
||||
QFont m_font;
|
||||
QString m_text;
|
||||
QBrush m_brush;
|
||||
QPen m_pen;
|
||||
double m_angle;
|
||||
|
||||
// these are valid once the label has been rendered:
|
||||
mutable QPixmap m_pixmap;
|
||||
mutable QPointF m_referenceBottomLeft;
|
||||
mutable QPointF m_textBaseLineVector;
|
||||
mutable QPointF m_textAscendVector;
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user