KD Chart API Documentation  3.1
KDChartTextLabelCache.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** This file is part of the KD Chart library.
4 **
5 ** SPDX-FileCopyrightText: 2001 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
6 **
7 ** SPDX-License-Identifier: MIT
8 **
9 ****************************************************************************/
10 
11 #include "KDChartTextLabelCache.h"
12 
13 #include <cmath>
14 
15 #include <QApplication>
16 #include <QImage>
17 #include <QPainter>
18 #include <QPixmap>
19 #include <QtDebug>
20 
21 #ifndef NDEBUG
22 int HitCount = 0;
23 int MissCount = 0;
24 #define INC_HIT_COUNT \
25  { \
26  ++HitCount; \
27  }
28 #define INC_MISS_COUNT \
29  { \
30  ++MissCount; \
31  }
32 #define DUMP_CACHE_STATS \
33  if (HitCount != 0 && MissCount != 0) { \
34  int total = HitCount + MissCount; \
35  qreal hitQuote = (1.0 * HitCount) / total; \
36  qDebug() << "PrerenderedLabel dtor: hits/misses/total:" \
37  << HitCount << "/" << MissCount << "/" << total \
38  << "(" << 100 * hitQuote << "% hits)"; \
39  }
40 #else
41 #define INC_HIT_COUNT
42 #define INC_MISS_COUNT
43 #define DUMP_CACHE_STATS
44 #endif
45 
47 {
48 }
49 
50 void PrerenderedElement::setPosition(const QPointF &position)
51 { // this does not invalidate the element
52  m_position = position;
53 }
54 
55 const QPointF &PrerenderedElement::position() const
56 {
57  return m_position;
58 }
59 
61 { // this does not invalidate the element
62  m_referencePoint = point;
63 }
64 
66 {
67  return m_referencePoint;
68 }
69 
72  , m_font(qApp->font())
73  , m_brush(Qt::black)
74  , m_pen(Qt::black) // do not use anything invisible
75 {
76 }
77 
79 {
81 }
82 
87 {
88  m_dirty = true;
89 }
90 
94 void PrerenderedLabel::setFont(const QFont &font)
95 {
96  m_font = font;
97  invalidate();
98 }
99 
103 const QFont &PrerenderedLabel::font() const
104 {
105  return m_font;
106 }
107 
111 void PrerenderedLabel::setText(const QString &text)
112 {
113  m_text = text;
114  invalidate();
115 }
116 
120 const QString &PrerenderedLabel::text() const
121 {
122  return m_text;
123 }
124 
128 void PrerenderedLabel::setBrush(const QBrush &brush)
129 {
130  m_brush = brush;
131  invalidate();
132 }
133 
137 const QBrush &PrerenderedLabel::brush() const
138 {
139  return m_brush;
140 }
141 
145 void PrerenderedLabel::setAngle(qreal angle)
146 {
147  m_angle = angle;
148  invalidate();
149 }
150 
155 {
156  return m_angle;
157 }
158 
159 const QPixmap &PrerenderedLabel::pixmap() const
160 {
161  if (m_dirty) {
163  paint();
164  } else {
166  }
167  return m_pixmap;
168 }
169 
170 void PrerenderedLabel::paint() const
171 {
172  // FIXME find a better value using font metrics of text (this
173  // requires finding the diameter of the circle formed by rotating
174  // the bounding rect around the center):
175  const int Width = 1000;
176  const int Height = Width;
177 
178  QRectF boundingRect;
179  const QColor FullTransparent(255, 255, 255, 0);
180 #ifdef Q_WS_X11
181  QImage pixmap(Width, Height, QImage::Format_ARGB32_Premultiplied);
182  qWarning() << "PrerenderedLabel::paint: using QImage for prerendered labels "
183  << "to work around XRender/Qt4 bug.";
184 #else
185  QPixmap pixmap(Width, Height);
186 #endif
187  // pixmap.fill( FullTransparent );
188  {
189  static const QPointF Center(0.0, 0.0);
190  QPointF textBottomRight;
191  QPainter painter(&pixmap);
192  painter.setRenderHint(QPainter::TextAntialiasing, true);
193  painter.setRenderHint(QPainter::Antialiasing, true);
194 
195  // QImage (X11 workaround) does not have fill():
196  painter.setPen(FullTransparent);
197  painter.setBrush(FullTransparent);
198  QPainter::CompositionMode mode = painter.compositionMode();
199  painter.setCompositionMode(QPainter::CompositionMode_Clear);
200  painter.drawRect(0, 0, Width, Height);
201  painter.setCompositionMode(mode);
202 
203  QTransform matrix;
204  matrix.translate(0.5 * Width, 0.5 * Height);
205  matrix.rotate(m_angle);
206  painter.setWorldTransform(matrix);
207 
208  painter.setPen(m_pen);
209  painter.setBrush(m_brush);
210  painter.setFont(m_font);
211  QRectF container(-0.5 * Width, -0.5 * Height, Width, 0.5 * Height);
212  painter.drawText(container, Qt::AlignHCenter | Qt::AlignBottom,
213  m_text, &boundingRect);
214  m_referenceBottomLeft = QPointF(boundingRect.bottomLeft().x(), 0.0);
215  textBottomRight = QPointF(boundingRect.bottomRight().x(), 0.0);
216  m_textAscendVector = boundingRect.topRight() - textBottomRight;
217  m_textBaseLineVector = textBottomRight - m_referenceBottomLeft;
218 
219  // FIXME translate topright by char height
220  boundingRect = matrix.mapRect(boundingRect);
221  m_referenceBottomLeft = matrix.map(m_referenceBottomLeft)
222  - boundingRect.topLeft();
223  textBottomRight = matrix.map(textBottomRight)
224  - boundingRect.topLeft();
225  m_textAscendVector = matrix.map(m_textAscendVector)
226  - matrix.map(Center);
227  m_textBaseLineVector = matrix.map(m_textBaseLineVector)
228  - matrix.map(Center);
229  }
230 
231  m_dirty = false; // now all the calculation vectors are valid
232 
233  QPixmap temp(static_cast<int>(boundingRect.width()),
234  static_cast<int>(boundingRect.height()));
235  {
236  temp.fill(FullTransparent);
237  QPainter painter(&temp);
238 #ifdef Q_WS_X11
239  painter.drawImage(QPointF(0.0, 0.0), pixmap, boundingRect);
240 #else
241  painter.drawPixmap(QPointF(0.0, 0.0), pixmap, boundingRect);
242 #endif
243 // #define PRERENDEREDLABEL_DEBUG
244 #ifdef PRERENDEREDLABEL_DEBUG
245  painter.setPen(QPen(Qt::red, 2));
246  painter.setBrush(Qt::red);
247  // paint markers for the reference points
248  const QList<KDChartEnums::PositionValue> positions = {
258  };
259  for (KDChartEnums::PositionValue position : positions) {
260  static const double Radius = 0.5;
261  static const double Diameter = 2 * Radius;
262 
263  QPointF point(referencePointLocation(position));
264  painter.drawEllipse(QRectF(point - QPointF(Radius, Radius),
265  QSizeF(Diameter, Diameter)));
266  }
267 #endif
268  }
269 
270  m_pixmap = temp;
271 }
272 
274 {
276 }
277 
279 {
280  if (m_dirty) {
282  paint();
283  } else {
285  }
286 
287  switch (position) {
289  return m_referenceBottomLeft + 0.5 * m_textBaseLineVector + 0.5 * m_textAscendVector;
291  return m_referenceBottomLeft + m_textAscendVector;
293  return m_referenceBottomLeft + 0.5 * m_textBaseLineVector + m_textAscendVector;
295  return m_referenceBottomLeft + m_textBaseLineVector + m_textAscendVector;
297  return m_referenceBottomLeft + 0.5 * m_textAscendVector;
299  return m_referenceBottomLeft + m_textBaseLineVector;
301  return m_referenceBottomLeft + 0.5 * m_textBaseLineVector;
303  return m_referenceBottomLeft;
305  return m_referenceBottomLeft + m_textBaseLineVector + 0.5 * m_textAscendVector;
306 
307  case KDChartEnums::PositionUnknown: // intentional fall-through
308  case KDChartEnums::PositionFloating: // intentional fall-through
309  return QPointF();
310  }
311 
312  return QPointF();
313 }
#define INC_HIT_COUNT
#define DUMP_CACHE_STATS
int MissCount
#define INC_MISS_COUNT
int HitCount
base class for prerendered elements like labels, pixmaps, markers, etc.
KDChartEnums::PositionValue referencePoint() const
void setReferencePoint(KDChartEnums::PositionValue)
void setPosition(const QPointF &position)
const QPointF & position() const
void setAngle(qreal angle)
const QString & text() const
QPointF referencePointLocation() const
void setFont(const QFont &font)
const QBrush & brush() const
void invalidate() const override
const QFont & font() const
void setBrush(const QBrush &brush)
const QPixmap & pixmap() const override
void setText(const QString &text)

© 2001 Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/
https://www.kdab.com/development-resources/qt-tools/kd-chart/
Generated by doxygen 1.9.1