summaryrefslogtreecommitdiffstats
path: root/src/charts
diff options
context:
space:
mode:
authorMiikka Heikkinen <miikka.heikkinen@theqtcompany.com>2015-09-15 17:39:54 +0300
committerMiikka Heikkinen <miikka.heikkinen@theqtcompany.com>2015-09-25 06:44:35 +0000
commit79a856530b6986ca6d6d7485b2e6cec810c3b7fe (patch)
tree6f3f8dfe76e64da9ec48909fef1d7cccb87c3fc0 /src/charts
parent3a89e3fee61a52927f836f3b3de4c922c6b954e4 (diff)
Accelerating lineseries with OpenGL
Added support for QAbstractSeries::useOpenGL property. When true, the series in question is drawn on a separate offscreen buffer using OpenGL and then superimposed on the chart. Currently this property is only supported for line and scatter series. Change-Id: I174fec541f9f3c23464270c1fe08f824af16a0fb Reviewed-by: Titta Heikkala <titta.heikkala@theqtcompany.com> Reviewed-by: Tomi Korpipää <tomi.korpipaa@theqtcompany.com>
Diffstat (limited to 'src/charts')
-rw-r--r--src/charts/areachart/qareaseries.cpp12
-rw-r--r--src/charts/axis/qabstractaxis.cpp2
-rw-r--r--src/charts/barchart/qbarset.cpp4
-rw-r--r--src/charts/chartdataset.cpp10
-rw-r--r--src/charts/chartdataset_p.h4
-rw-r--r--src/charts/chartelement.cpp14
-rw-r--r--src/charts/chartelement_p.h4
-rw-r--r--src/charts/chartpresenter.cpp35
-rw-r--r--src/charts/chartpresenter_p.h9
-rw-r--r--src/charts/charts.pro6
-rw-r--r--src/charts/doc/images/examples_openglseries.pngbin0 -> 65565 bytes
-rw-r--r--src/charts/doc/src/examples-openglseries.qdoc37
-rw-r--r--src/charts/domain/abstractdomain_p.h2
-rw-r--r--src/charts/domain/logxlogydomain.cpp2
-rw-r--r--src/charts/domain/logxlogydomain_p.h2
-rw-r--r--src/charts/domain/logxydomain.cpp2
-rw-r--r--src/charts/domain/logxydomain_p.h2
-rw-r--r--src/charts/domain/polardomain.cpp2
-rw-r--r--src/charts/domain/polardomain_p.h2
-rw-r--r--src/charts/domain/xlogydomain.cpp2
-rw-r--r--src/charts/domain/xlogydomain_p.h2
-rw-r--r--src/charts/domain/xydomain.cpp2
-rw-r--r--src/charts/domain/xydomain_p.h2
-rw-r--r--src/charts/glwidget.cpp222
-rw-r--r--src/charts/glwidget_p.h80
-rw-r--r--src/charts/linechart/linechartitem.cpp14
-rw-r--r--src/charts/qabstractseries.cpp98
-rw-r--r--src/charts/qabstractseries.h5
-rw-r--r--src/charts/qabstractseries_p.h4
-rw-r--r--src/charts/scatterchart/scatterchartitem.cpp15
-rw-r--r--src/charts/xychart/glxyseriesdata.cpp147
-rw-r--r--src/charts/xychart/glxyseriesdata_p.h91
-rw-r--r--src/charts/xychart/qxyseries.cpp25
-rw-r--r--src/charts/xychart/qxyseries.h2
-rw-r--r--src/charts/xychart/xychart.cpp119
-rw-r--r--src/charts/xychart/xychart.pri6
-rw-r--r--src/charts/xychart/xychart_p.h1
37 files changed, 913 insertions, 75 deletions
diff --git a/src/charts/areachart/qareaseries.cpp b/src/charts/areachart/qareaseries.cpp
index a33db927..c6dd9f41 100644
--- a/src/charts/areachart/qareaseries.cpp
+++ b/src/charts/areachart/qareaseries.cpp
@@ -345,7 +345,7 @@ QT_CHARTS_BEGIN_NAMESPACE
\sa pointLabelsVisible
*/
/*!
- \fn void QAreaSeries::pointLabelsClippintChanged(bool clipping)
+ \fn void QAreaSeries::pointLabelsClippingChanged(bool clipping)
The clipping of the data point labels is changed to \a clipping.
*/
/*!
@@ -395,8 +395,10 @@ QAbstractSeries::SeriesType QAreaSeries::type() const
void QAreaSeries::setUpperSeries(QLineSeries *series)
{
Q_D(QAreaSeries);
- if (d->m_upperSeries != series)
+ if (d->m_upperSeries != series) {
+ series->d_ptr->setBlockOpenGL(true);
d->m_upperSeries = series;
+ }
}
QLineSeries *QAreaSeries::upperSeries() const
@@ -411,6 +413,7 @@ QLineSeries *QAreaSeries::upperSeries() const
void QAreaSeries::setLowerSeries(QLineSeries *series)
{
Q_D(QAreaSeries);
+ series->d_ptr->setBlockOpenGL(true);
d->m_lowerSeries = series;
}
@@ -624,7 +627,7 @@ void QAreaSeriesPrivate::initializeDomain()
QLineSeries *lowerSeries = q->lowerSeries();
if (upperSeries) {
- const QList<QPointF>& points = upperSeries->points();
+ const QVector<QPointF> &points = upperSeries->pointsVector();
for (int i = 0; i < points.count(); i++) {
qreal x = points[i].x();
@@ -636,8 +639,7 @@ void QAreaSeriesPrivate::initializeDomain()
}
}
if (lowerSeries) {
-
- const QList<QPointF>& points = lowerSeries->points();
+ const QVector<QPointF> &points = lowerSeries->pointsVector();
for (int i = 0; i < points.count(); i++) {
qreal x = points[i].x();
diff --git a/src/charts/axis/qabstractaxis.cpp b/src/charts/axis/qabstractaxis.cpp
index e286ba51..1bbe63cb 100644
--- a/src/charts/axis/qabstractaxis.cpp
+++ b/src/charts/axis/qabstractaxis.cpp
@@ -883,7 +883,7 @@ bool QAbstractAxis::isVisible() const
}
/*!
- Sets axis, shades, labels and grid lines to be visible.
+ Sets axis, shades, labels and grid lines visibility to \a visible.
*/
void QAbstractAxis::setVisible(bool visible)
{
diff --git a/src/charts/barchart/qbarset.cpp b/src/charts/barchart/qbarset.cpp
index 4eb1cdbe..0ff859d8 100644
--- a/src/charts/barchart/qbarset.cpp
+++ b/src/charts/barchart/qbarset.cpp
@@ -546,7 +546,7 @@ QColor QBarSet::borderColor()
}
/*!
- Sets the color of pen for this bar set.
+ Sets the \a color of pen for this bar set.
*/
void QBarSet::setBorderColor(QColor color)
{
@@ -567,7 +567,7 @@ QColor QBarSet::labelColor()
}
/*!
- Sets the color of labels for this bar set.
+ Sets the \a color of labels for this bar set.
*/
void QBarSet::setLabelColor(QColor color)
{
diff --git a/src/charts/chartdataset.cpp b/src/charts/chartdataset.cpp
index 35361ff6..1da28b95 100644
--- a/src/charts/chartdataset.cpp
+++ b/src/charts/chartdataset.cpp
@@ -38,6 +38,7 @@
#include <private/xlogypolardomain_p.h>
#include <private/logxypolardomain_p.h>
#include <private/logxlogypolardomain_p.h>
+#include <private/glxyseriesdata_p.h>
#ifndef QT_ON_ARM
#include <QtCharts/QDateTimeAxis>
@@ -47,7 +48,8 @@ QT_CHARTS_BEGIN_NAMESPACE
ChartDataSet::ChartDataSet(QChart *chart)
: QObject(chart),
- m_chart(chart)
+ m_chart(chart),
+ m_glXYSeriesDataManager(new GLXYSeriesDataManager(this))
{
}
@@ -77,6 +79,8 @@ void ChartDataSet::addSeries(QAbstractSeries *series)
qWarning() << QObject::tr("Can not add series. Series type is not supported by a polar chart.");
return;
}
+ // Disable OpenGL for series in polar charts
+ series->setUseOpenGL(false);
series->d_ptr->setDomain(new XYPolarDomain());
// Set the correct domain for upper and lower series too
if (series->type() == QAbstractSeries::SeriesTypeArea) {
@@ -157,6 +161,10 @@ void ChartDataSet::removeSeries(QAbstractSeries *series)
series->d_ptr->setDomain(new XYDomain());
series->setParent(0);
series->d_ptr->m_chart = 0;
+
+ QXYSeries *xySeries = qobject_cast<QXYSeries *>(series);
+ if (xySeries)
+ m_glXYSeriesDataManager->removeSeries(xySeries);
}
/*
diff --git a/src/charts/chartdataset_p.h b/src/charts/chartdataset_p.h
index 3fd71a79..2cc8c2d0 100644
--- a/src/charts/chartdataset_p.h
+++ b/src/charts/chartdataset_p.h
@@ -37,6 +37,7 @@ QT_CHARTS_BEGIN_NAMESPACE
class QAbstractAxis;
class ChartPresenter;
+class GLXYSeriesDataManager;
class QT_CHARTS_AUTOTEST_EXPORT ChartDataSet : public QObject
{
@@ -67,6 +68,8 @@ public:
QPointF mapToValue(const QPointF &position, QAbstractSeries *series = 0);
QPointF mapToPosition(const QPointF &value, QAbstractSeries *series = 0);
+ GLXYSeriesDataManager *glXYSeriesDataManager() { return m_glXYSeriesDataManager; }
+
Q_SIGNALS:
void axisAdded(QAbstractAxis* axis);
void axisRemoved(QAbstractAxis* axis);
@@ -85,6 +88,7 @@ private:
QList<QAbstractSeries *> m_seriesList;
QList<QAbstractAxis *> m_axisList;
QChart* m_chart;
+ GLXYSeriesDataManager *m_glXYSeriesDataManager;
};
QT_CHARTS_END_NAMESPACE
diff --git a/src/charts/chartelement.cpp b/src/charts/chartelement.cpp
index 5ccf8d4e..9e540cf0 100644
--- a/src/charts/chartelement.cpp
+++ b/src/charts/chartelement.cpp
@@ -19,13 +19,15 @@
#include <private/chartelement_p.h>
#include <private/chartpresenter_p.h>
#include <private/abstractdomain_p.h>
+#include <private/chartdataset_p.h>
QT_CHARTS_BEGIN_NAMESPACE
ChartElement::ChartElement(QGraphicsItem* item):
QGraphicsObject(item),
m_presenter(0),
- m_themeManager(0)
+ m_themeManager(0),
+ m_dataSet(0)
{
}
@@ -50,4 +52,14 @@ ChartThemeManager* ChartElement::themeManager() const
return m_themeManager;
}
+void ChartElement::setDataSet(ChartDataSet *dataSet)
+{
+ m_dataSet = dataSet;
+}
+
+ChartDataSet *ChartElement::dataSet() const
+{
+ return m_dataSet;
+}
+
QT_CHARTS_END_NAMESPACE
diff --git a/src/charts/chartelement_p.h b/src/charts/chartelement_p.h
index 60b2552c..72b55e78 100644
--- a/src/charts/chartelement_p.h
+++ b/src/charts/chartelement_p.h
@@ -40,6 +40,7 @@ class ChartPresenter;
class ChartAnimation;
class ChartThemeManager;
class AbstractDomain;
+class ChartDataSet;
class ChartElement: public QGraphicsObject
{
@@ -52,10 +53,13 @@ public:
ChartPresenter *presenter() const;
virtual void setThemeManager(ChartThemeManager *manager);
ChartThemeManager* themeManager() const;
+ virtual void setDataSet(ChartDataSet *dataSet);
+ ChartDataSet *dataSet() const;
private:
ChartPresenter *m_presenter;
ChartThemeManager *m_themeManager;
+ ChartDataSet *m_dataSet;
};
QT_CHARTS_END_NAMESPACE
diff --git a/src/charts/chartpresenter.cpp b/src/charts/chartpresenter.cpp
index df66c01b..6fc35033 100644
--- a/src/charts/chartpresenter.cpp
+++ b/src/charts/chartpresenter.cpp
@@ -46,6 +46,10 @@ ChartPresenter::ChartPresenter(QChart *chart, QChart::ChartType type)
m_plotAreaBackground(0),
m_title(0),
m_localizeNumbers(false)
+#ifndef QT_NO_OPENGL
+ , m_glWidget(0)
+ , m_glUseWidget(true)
+#endif
{
if (type == QChart::ChartTypeCartesian)
m_layout = new CartesianChartLayout(this);
@@ -56,7 +60,9 @@ ChartPresenter::ChartPresenter(QChart *chart, QChart::ChartType type)
ChartPresenter::~ChartPresenter()
{
-
+#ifndef QT_NO_OPENGL
+ delete m_glWidget.data();
+#endif
}
void ChartPresenter::setGeometry(const QRectF rect)
@@ -67,6 +73,10 @@ void ChartPresenter::setGeometry(const QRectF rect)
chart->domain()->setSize(rect.size());
chart->setPos(rect.topLeft());
}
+#ifndef QT_NO_OPENGL
+ if (!m_glWidget.isNull())
+ m_glWidget->setGeometry(m_rect.toRect());
+#endif
emit plotAreaChanged(m_rect);
}
}
@@ -108,6 +118,7 @@ void ChartPresenter::handleSeriesAdded(QAbstractSeries *series)
ChartItem *chart = series->d_ptr->chartItem();
chart->setPresenter(this);
chart->setThemeManager(m_chart->d_ptr->m_themeManager);
+ chart->setDataSet(m_chart->d_ptr->m_dataset);
chart->domain()->setSize(m_rect.size());
chart->setPos(m_rect.topLeft());
chart->handleDomainUpdated(); //this could be moved to intializeGraphics when animator is refactored
@@ -531,6 +542,28 @@ QString ChartPresenter::numberToString(int value)
return QString::number(value);
}
+void ChartPresenter::ensureGLWidget()
+{
+#ifndef QT_NO_OPENGL
+ // GLWidget pointer is wrapped in QPointer as its parent is not in our control, and therefore
+ // can potentially get deleted unexpectedly.
+ if (m_glWidget.isNull() && m_glUseWidget && m_chart->scene()) {
+ QObject *parent = m_chart->scene()->parent();
+ while (parent) {
+ QWidget *parentWidget = qobject_cast<QWidget *>(parent);
+ if (parentWidget) {
+ m_glWidget = new GLWidget(m_chart->d_ptr->m_dataset->glXYSeriesDataManager(),
+ parentWidget);
+ m_glWidget->setGeometry(m_rect.toRect());
+ m_glWidget->show();
+ break;
+ }
+ parent = parent->parent();
+ }
+ }
+#endif
+}
+
#include "moc_chartpresenter_p.cpp"
QT_CHARTS_END_NAMESPACE
diff --git a/src/charts/chartpresenter_p.h b/src/charts/chartpresenter_p.h
index bf9d5a90..8dadbbc3 100644
--- a/src/charts/chartpresenter_p.h
+++ b/src/charts/chartpresenter_p.h
@@ -30,9 +30,11 @@
#include <QtCharts/QChartGlobal>
#include <QtCharts/QChart> //because of QChart::ChartThemeId
+#include <private/glwidget_p.h>
#include <QtCore/QRectF>
#include <QtCore/QMargins>
#include <QtCore/QLocale>
+#include <QtCore/QPointer>
QT_CHARTS_BEGIN_NAMESPACE
@@ -160,6 +162,9 @@ public:
QString numberToString(double value, char f = 'g', int prec = 6);
QString numberToString(int value);
+ void ensureGLWidget();
+ void glSetUseWidget(bool enable) { m_glUseWidget = enable; }
+
private:
void createBackgroundItem();
void createPlotAreaBackgroundItem();
@@ -192,6 +197,10 @@ private:
QRectF m_rect;
bool m_localizeNumbers;
QLocale m_locale;
+#ifndef QT_NO_OPENGL
+ QPointer<GLWidget> m_glWidget;
+#endif
+ bool m_glUseWidget;
};
QT_CHARTS_END_NAMESPACE
diff --git a/src/charts/charts.pro b/src/charts/charts.pro
index a89b1dd1..2ec6ce01 100644
--- a/src/charts/charts.pro
+++ b/src/charts/charts.pro
@@ -32,6 +32,9 @@ SOURCES += \
$$PWD/scroller.cpp \
$$PWD/charttitle.cpp \
$$PWD/qpolarchart.cpp
+
+contains(QT_CONFIG, opengl): SOURCES += $$PWD/glwidget.cpp
+
PRIVATE_HEADERS += \
$$PWD/chartdataset_p.h \
$$PWD/chartitem_p.h \
@@ -46,6 +49,9 @@ PRIVATE_HEADERS += \
$$PWD/qabstractseries_p.h \
$$PWD/charttitle_p.h \
$$PWD/charthelpers_p.h
+
+contains(QT_CONFIG, opengl): PRIVATE_HEADERS += $$PWD/glwidget_p.h
+
PUBLIC_HEADERS += \
$$PWD/qchart.h \
$$PWD/qchartglobal.h \
diff --git a/src/charts/doc/images/examples_openglseries.png b/src/charts/doc/images/examples_openglseries.png
new file mode 100644
index 00000000..56719a97
--- /dev/null
+++ b/src/charts/doc/images/examples_openglseries.png
Binary files differ
diff --git a/src/charts/doc/src/examples-openglseries.qdoc b/src/charts/doc/src/examples-openglseries.qdoc
new file mode 100644
index 00000000..9c28af3c
--- /dev/null
+++ b/src/charts/doc/src/examples-openglseries.qdoc
@@ -0,0 +1,37 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://qt.io
+**
+** This file is part of the Qt Charts module.
+**
+** Licensees holding valid commercial license for Qt may use this file in
+** accordance with the Qt License Agreement provided with the Software
+** or, alternatively, in accordance with the terms contained in a written
+** agreement between you and The Qt Company.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.io
+**
+****************************************************************************/
+
+/*!
+ \example openglseries
+ \title OpenGL Accelerated Series Example
+ \ingroup qtcharts_examples
+
+ \brief The example shows how to enable OpenGL acceleration for QLineSeries and QScatterSeries.
+
+ \image examples_openglseries.png
+
+ To create an OpenGL accelerated series, all you have to do compared to a regular
+ series is to set QAbstractSeries::useOpenGL property to \c{true}:
+
+ \snippet openglseries/main.cpp 1
+
+ This makes the chart to instantiate a transparent QOpenGLWidget that is used to draw
+ the accelerated series on top of the chart.
+
+ \note The OpenGL acceleration is only supported for QLineSeries and QScatterSeries.
+*/
diff --git a/src/charts/domain/abstractdomain_p.h b/src/charts/domain/abstractdomain_p.h
index 50a629fd..f8c0e942 100644
--- a/src/charts/domain/abstractdomain_p.h
+++ b/src/charts/domain/abstractdomain_p.h
@@ -92,7 +92,7 @@ public:
virtual QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const = 0;
virtual QPointF calculateDomainPoint(const QPointF &point) const = 0;
- virtual QVector<QPointF> calculateGeometryPoints(const QList<QPointF> &vector) const = 0;
+ virtual QVector<QPointF> calculateGeometryPoints(const QVector<QPointF> &vector) const = 0;
virtual bool attachAxis(QAbstractAxis *axis);
virtual bool detachAxis(QAbstractAxis *axis);
diff --git a/src/charts/domain/logxlogydomain.cpp b/src/charts/domain/logxlogydomain.cpp
index 292b259c..665a13f1 100644
--- a/src/charts/domain/logxlogydomain.cpp
+++ b/src/charts/domain/logxlogydomain.cpp
@@ -162,7 +162,7 @@ QPointF LogXLogYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) c
return QPointF(x, y);
}
-QVector<QPointF> LogXLogYDomain::calculateGeometryPoints(const QList<QPointF> &vector) const
+QVector<QPointF> LogXLogYDomain::calculateGeometryPoints(const QVector<QPointF> &vector) const
{
const qreal deltaX = m_size.width() / qAbs(m_logRightX - m_logLeftX);
const qreal deltaY = m_size.height() / qAbs(m_logRightY - m_logLeftY);
diff --git a/src/charts/domain/logxlogydomain_p.h b/src/charts/domain/logxlogydomain_p.h
index ed8fb265..11bc6010 100644
--- a/src/charts/domain/logxlogydomain_p.h
+++ b/src/charts/domain/logxlogydomain_p.h
@@ -54,7 +54,7 @@ public:
QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const;
QPointF calculateDomainPoint(const QPointF &point) const;
- QVector<QPointF> calculateGeometryPoints(const QList<QPointF> &vector) const;
+ QVector<QPointF> calculateGeometryPoints(const QVector<QPointF> &vector) const;
bool attachAxis(QAbstractAxis *axis);
bool detachAxis(QAbstractAxis *axis);
diff --git a/src/charts/domain/logxydomain.cpp b/src/charts/domain/logxydomain.cpp
index 00e8b1ce..1c306070 100644
--- a/src/charts/domain/logxydomain.cpp
+++ b/src/charts/domain/logxydomain.cpp
@@ -146,7 +146,7 @@ QPointF LogXYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) cons
return QPointF(x, y);
}
-QVector<QPointF> LogXYDomain::calculateGeometryPoints(const QList<QPointF> &vector) const
+QVector<QPointF> LogXYDomain::calculateGeometryPoints(const QVector<QPointF> &vector) const
{
const qreal deltaX = m_size.width() / (m_logRightX - m_logLeftX);
const qreal deltaY = m_size.height() / (m_maxY - m_minY);
diff --git a/src/charts/domain/logxydomain_p.h b/src/charts/domain/logxydomain_p.h
index 72c89b18..5ff8b0c3 100644
--- a/src/charts/domain/logxydomain_p.h
+++ b/src/charts/domain/logxydomain_p.h
@@ -54,7 +54,7 @@ public:
QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const;
QPointF calculateDomainPoint(const QPointF &point) const;
- QVector<QPointF> calculateGeometryPoints(const QList<QPointF> &vector) const;
+ QVector<QPointF> calculateGeometryPoints(const QVector<QPointF> &vector) const;
bool attachAxis(QAbstractAxis *axis);
bool detachAxis(QAbstractAxis *axis);
diff --git a/src/charts/domain/polardomain.cpp b/src/charts/domain/polardomain.cpp
index 4681f6c4..55fedf06 100644
--- a/src/charts/domain/polardomain.cpp
+++ b/src/charts/domain/polardomain.cpp
@@ -53,7 +53,7 @@ QPointF PolarDomain::calculateGeometryPoint(const QPointF &point, bool &ok) cons
}
}
-QVector<QPointF> PolarDomain::calculateGeometryPoints(const QList<QPointF> &vector) const
+QVector<QPointF> PolarDomain::calculateGeometryPoints(const QVector<QPointF> &vector) const
{
QVector<QPointF> result;
result.resize(vector.count());
diff --git a/src/charts/domain/polardomain_p.h b/src/charts/domain/polardomain_p.h
index 6ed5b85d..e179a8a7 100644
--- a/src/charts/domain/polardomain_p.h
+++ b/src/charts/domain/polardomain_p.h
@@ -43,7 +43,7 @@ public:
void setSize(const QSizeF &size);
QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const;
- QVector<QPointF> calculateGeometryPoints(const QList<QPointF> &vector) const;
+ QVector<QPointF> calculateGeometryPoints(const QVector<QPointF> &vector) const;
virtual qreal toAngularCoordinate(qreal value, bool &ok) const = 0;
virtual qreal toRadialCoordinate(qreal value, bool &ok) const = 0;
diff --git a/src/charts/domain/xlogydomain.cpp b/src/charts/domain/xlogydomain.cpp
index bc9d367b..ca4986d8 100644
--- a/src/charts/domain/xlogydomain.cpp
+++ b/src/charts/domain/xlogydomain.cpp
@@ -146,7 +146,7 @@ QPointF XLogYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) cons
return QPointF(x, y);
}
-QVector<QPointF> XLogYDomain::calculateGeometryPoints(const QList<QPointF> &vector) const
+QVector<QPointF> XLogYDomain::calculateGeometryPoints(const QVector<QPointF> &vector) const
{
const qreal deltaX = m_size.width() / (m_maxX - m_minX);
const qreal deltaY = m_size.height() / qAbs(m_logRightY - m_logLeftY);
diff --git a/src/charts/domain/xlogydomain_p.h b/src/charts/domain/xlogydomain_p.h
index 5dbd2a4c..c49f7bb1 100644
--- a/src/charts/domain/xlogydomain_p.h
+++ b/src/charts/domain/xlogydomain_p.h
@@ -54,7 +54,7 @@ public:
QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const;
QPointF calculateDomainPoint(const QPointF &point) const;
- QVector<QPointF> calculateGeometryPoints(const QList<QPointF> &vector) const;
+ QVector<QPointF> calculateGeometryPoints(const QVector<QPointF> &vector) const;
bool attachAxis(QAbstractAxis *axis);
bool detachAxis(QAbstractAxis *axis);
diff --git a/src/charts/domain/xydomain.cpp b/src/charts/domain/xydomain.cpp
index cad9dcea..da205907 100644
--- a/src/charts/domain/xydomain.cpp
+++ b/src/charts/domain/xydomain.cpp
@@ -144,7 +144,7 @@ QPointF XYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) const
return QPointF(x, y);
}
-QVector<QPointF> XYDomain::calculateGeometryPoints(const QList<QPointF> &vector) const
+QVector<QPointF> XYDomain::calculateGeometryPoints(const QVector<QPointF> &vector) const
{
const qreal deltaX = m_size.width() / (m_maxX - m_minX);
const qreal deltaY = m_size.height() / (m_maxY - m_minY);
diff --git a/src/charts/domain/xydomain_p.h b/src/charts/domain/xydomain_p.h
index 735fc0f0..3ba8e1f3 100644
--- a/src/charts/domain/xydomain_p.h
+++ b/src/charts/domain/xydomain_p.h
@@ -54,7 +54,7 @@ public:
QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const;
QPointF calculateDomainPoint(const QPointF &point) const;
- QVector<QPointF> calculateGeometryPoints(const QList<QPointF> &vector) const;
+ QVector<QPointF> calculateGeometryPoints(const QVector<QPointF> &vector) const;
};
QT_CHARTS_END_NAMESPACE
diff --git a/src/charts/glwidget.cpp b/src/charts/glwidget.cpp
new file mode 100644
index 00000000..a4470489
--- /dev/null
+++ b/src/charts/glwidget.cpp
@@ -0,0 +1,222 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2015 The Qt Company Ltd
+ ** All rights reserved.
+ ** For any questions to The Qt Company, please use contact form at http://qt.io
+ **
+ ** This file is part of the Qt Charts module.
+ **
+ ** Licensees holding valid commercial license for Qt may use this file in
+ ** accordance with the Qt License Agreement provided with the Software
+ ** or, alternatively, in accordance with the terms contained in a written
+ ** agreement between you and The Qt Company.
+ **
+ ** If you have questions regarding the use of this file, please use
+ ** contact form at http://qt.io
+ **
+ ****************************************************************************/
+
+#ifndef QT_NO_OPENGL
+
+#include "private/glwidget_p.h"
+#include "private/glxyseriesdata_p.h"
+#include <QtGui/QOpenGLShaderProgram>
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QOpenGLBuffer>
+
+//#define QDEBUG_TRACE_GL_FPS
+#ifdef QDEBUG_TRACE_GL_FPS
+# include <QElapsedTimer>
+#endif
+
+QT_CHARTS_BEGIN_NAMESPACE
+
+GLWidget::GLWidget(GLXYSeriesDataManager *xyDataManager, QWidget *parent)
+ : QOpenGLWidget(parent),
+ m_program(0),
+ m_shaderAttribLoc(-1),
+ m_colorUniformLoc(-1),
+ m_minUniformLoc(-1),
+ m_deltaUniformLoc(-1),
+ m_pointSizeUniformLoc(-1),
+ m_xyDataManager(xyDataManager)
+{
+ setAttribute(Qt::WA_TranslucentBackground);
+ setAttribute(Qt::WA_AlwaysStackOnTop);
+ setAttribute(Qt::WA_TransparentForMouseEvents);
+
+ QSurfaceFormat surfaceFormat;
+ surfaceFormat.setDepthBufferSize(0);
+ surfaceFormat.setStencilBufferSize(0);
+ surfaceFormat.setRedBufferSize(8);
+ surfaceFormat.setGreenBufferSize(8);
+ surfaceFormat.setBlueBufferSize(8);
+ surfaceFormat.setAlphaBufferSize(8);
+ surfaceFormat.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
+ surfaceFormat.setRenderableType(QSurfaceFormat::DefaultRenderableType);
+ setFormat(surfaceFormat);
+
+ connect(xyDataManager, &GLXYSeriesDataManager::seriesRemoved,
+ this, &GLWidget::cleanXYSeriesResources);
+}
+
+GLWidget::~GLWidget()
+{
+ cleanup();
+}
+
+void GLWidget::cleanup()
+{
+ makeCurrent();
+
+ delete m_program;
+ m_program = 0;
+
+ foreach (QOpenGLBuffer *buffer, m_seriesBufferMap.values())
+ delete buffer;
+ m_seriesBufferMap.clear();
+
+ doneCurrent();
+}
+
+void GLWidget::cleanXYSeriesResources(const QXYSeries *series)
+{
+ makeCurrent();
+ if (series) {
+ delete m_seriesBufferMap.take(series);
+ } else {
+ // Null series means all series were removed
+ foreach (QOpenGLBuffer *buffer, m_seriesBufferMap.values())
+ delete buffer;
+ m_seriesBufferMap.clear();
+ }
+ doneCurrent();
+}
+
+static const char *vertexSource =
+ "attribute highp vec2 points;\n"
+ "uniform highp vec2 min;\n"
+ "uniform highp vec2 delta;\n"
+ "uniform highp float pointSize;\n"
+ "void main() {\n"
+ " vec2 normalPoint = vec2(-1, -1) + ((points - min) / delta);\n"
+ " gl_Position = vec4(normalPoint, 0, 1);\n"
+ " gl_PointSize = pointSize;\n"
+ "}";
+static const char *fragmentSource =
+ "uniform highp vec3 color;\n"
+ "void main() {\n"
+ " gl_FragColor = vec4(color,1);\n"
+ "}\n";
+
+void GLWidget::initializeGL()
+{
+ connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &GLWidget::cleanup);
+
+ initializeOpenGLFunctions();
+ glClearColor(0, 0, 0, 0);
+
+ m_program = new QOpenGLShaderProgram;
+ m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexSource);
+ m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentSource);
+ m_program->bindAttributeLocation("points", 0);
+ m_program->link();
+
+ m_program->bind();
+ m_colorUniformLoc = m_program->uniformLocation("color");
+ m_minUniformLoc = m_program->uniformLocation("min");
+ m_deltaUniformLoc = m_program->uniformLocation("delta");
+ m_pointSizeUniformLoc = m_program->uniformLocation("pointSize");
+
+
+ // Create a vertex array object. In OpenGL ES 2.0 and OpenGL 2.x
+ // implementations this is optional and support may not be present
+ // at all. Nonetheless the below code works in all cases and makes
+ // sure there is a VAO when one is needed.
+ m_vao.create();
+ QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
+
+ glEnableVertexAttribArray(0);
+
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_STENCIL_TEST);
+
+#if !defined(QT_OPENGL_ES_2)
+ if (!QOpenGLContext::currentContext()->isOpenGLES()) {
+ // Make it possible to change point primitive size and use textures with them in
+ // the shaders. These are implicitly enabled in ES2.
+ glEnable(GL_PROGRAM_POINT_SIZE);
+ }
+#endif
+
+ m_program->release();
+}
+
+void GLWidget::paintGL()
+{
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
+ m_program->bind();
+
+ GLXYDataMapIterator i(m_xyDataManager->dataMap());
+ while (i.hasNext()) {
+ i.next();
+ QOpenGLBuffer *vbo = m_seriesBufferMap.value(i.key());
+ GLXYSeriesData *data = i.value();
+
+ m_program->setUniformValue(m_colorUniformLoc, data->color);
+ m_program->setUniformValue(m_minUniformLoc, data->min);
+ m_program->setUniformValue(m_deltaUniformLoc, data->delta);
+
+ if (!vbo) {
+ vbo = new QOpenGLBuffer;
+ m_seriesBufferMap.insert(i.key(), vbo);
+ vbo->create();
+ }
+ vbo->bind();
+ if (data->dirty) {
+ vbo->allocate(data->array.constData(), data->array.count() * sizeof(GLfloat));
+ data->dirty = false;
+ }
+
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
+ if (data->type == QAbstractSeries::SeriesTypeLine) {
+ glLineWidth(data->width);
+ glDrawArrays(GL_LINE_STRIP, 0, data->array.size() / 2);
+ } else { // Scatter
+ m_program->setUniformValue(m_pointSizeUniformLoc, data->width);
+ glDrawArrays(GL_POINTS, 0, data->array.size() / 2);
+ }
+ vbo->release();
+ }
+
+#ifdef QDEBUG_TRACE_GL_FPS
+ static QElapsedTimer stopWatch;
+ static int frameCount = -1;
+ if (frameCount == -1) {
+ stopWatch.start();
+ frameCount = 0;
+ }
+ frameCount++;
+ int elapsed = stopWatch.elapsed();
+ if (elapsed >= 1000) {
+ elapsed = stopWatch.restart();
+ qreal fps = qreal(0.1 * int(10000.0 * (qreal(frameCount) / qreal(elapsed))));
+ qDebug() << "FPS:" << fps;
+ frameCount = 0;
+ }
+#endif
+
+ m_program->release();
+}
+
+void GLWidget::resizeGL(int w, int h)
+{
+ Q_UNUSED(w)
+ Q_UNUSED(h)
+}
+
+QT_CHARTS_END_NAMESPACE
+
+#endif
diff --git a/src/charts/glwidget_p.h b/src/charts/glwidget_p.h
new file mode 100644
index 00000000..209c634f
--- /dev/null
+++ b/src/charts/glwidget_p.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://qt.io
+**
+** This file is part of the Qt Charts module.
+**
+** Licensees holding valid commercial license for Qt may use this file in
+** accordance with the Qt License Agreement provided with the Software
+** or, alternatively, in accordance with the terms contained in a written
+** agreement between you and The Qt Company.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.io
+**
+****************************************************************************/
+
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt Chart API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#ifndef GLWIDGET_H
+#define GLWIDGET_H
+
+#ifndef QT_NO_OPENGL
+
+#include <QtWidgets/QOpenGLWidget>
+#include <QtGui/QOpenGLFunctions>
+#include <QtGui/QOpenGLVertexArrayObject>
+#include <QtCore/QHash>
+#include <QtCharts/QAbstractSeries>
+#include <QtCharts/QXYSeries>
+
+QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram)
+
+class QOpenGLBuffer;
+
+QT_CHARTS_BEGIN_NAMESPACE
+
+class GLXYSeriesDataManager;
+
+class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions
+{
+ Q_OBJECT
+
+public:
+ GLWidget(GLXYSeriesDataManager *xyDataManager, QWidget *parent = 0);
+ ~GLWidget();
+
+public Q_SLOTS:
+ void cleanup();
+ void cleanXYSeriesResources(const QXYSeries *series);
+
+protected:
+ void initializeGL() Q_DECL_OVERRIDE;
+ void paintGL() Q_DECL_OVERRIDE;
+ void resizeGL(int width, int height) Q_DECL_OVERRIDE;
+
+private:
+ QOpenGLShaderProgram *m_program;
+ int m_shaderAttribLoc;
+ int m_colorUniformLoc;
+ int m_minUniformLoc;
+ int m_deltaUniformLoc;
+ int m_pointSizeUniformLoc;
+ QOpenGLVertexArrayObject m_vao;
+
+ QHash<const QAbstractSeries *, QOpenGLBuffer *> m_seriesBufferMap;
+ GLXYSeriesDataManager *m_xyDataManager;
+};
+
+QT_CHARTS_END_NAMESPACE
+#endif
+#endif
diff --git a/src/charts/linechart/linechartitem.cpp b/src/charts/linechart/linechartitem.cpp
index 84d9e1ef..f0b0a577 100644
--- a/src/charts/linechart/linechartitem.cpp
+++ b/src/charts/linechart/linechartitem.cpp
@@ -70,6 +70,17 @@ QPainterPath LineChartItem::shape() const
void LineChartItem::updateGeometry()
{
+ static const QRectF dummyRect = QRectF(0.0, 0.0, 0.001, 0.001);
+ if (m_series->useOpenGL()) {
+ // Fake a miniscule region, so we trigger changed signal.
+ if (m_rect.width() != dummyRect.width()) {
+ prepareGeometryChange();
+ m_rect = dummyRect;
+ }
+ update();
+ return;
+ }
+
// Store the points to a local variable so that the old line gets properly cleared
// when animation starts.
m_linePoints = geometryPoints();
@@ -345,6 +356,9 @@ void LineChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt
Q_UNUSED(widget)
Q_UNUSED(option)
+ if (m_series->useOpenGL())
+ return;
+
QRectF clipRect = QRectF(QPointF(0, 0), domain()->size());
painter->save();
diff --git a/src/charts/qabstractseries.cpp b/src/charts/qabstractseries.cpp
index cb3c7c5a..8ce0c9dd 100644
--- a/src/charts/qabstractseries.cpp
+++ b/src/charts/qabstractseries.cpp
@@ -135,6 +135,69 @@ QT_CHARTS_BEGIN_NAMESPACE
*/
/*!
+ \property QAbstractSeries::useOpenGL
+ \brief Specifies whether or not the series drawing is accelerated with OpenGL.
+
+ Drawing series with OpenGL is supported only for QLineSeries and QScatterSeries.
+ Line series used as edge series for a QAreaSeries cannot use OpenGL acceleration.
+ When a chart contains any series that are drawn with OpenGL, a transparent QOpenGLWidget
+ is created on top of the chart plot area. Specified series are not drawn on the underlying
+ QGraphicsView, but are instead drawn on the created QOpenGLWidget.
+
+ Performance gained from using OpenGL to accelerate series drawing depends on the underlying
+ hardware, but in most cases it is significant. For example, on a standard desktop computer,
+ enabling OpenGL acceleration for a series typically allows rendering at least hundred times
+ more points without reduction on the frame rate.
+ Chart size also has less effect on the frame rate.
+
+ The OpenGL acceleration of series drawing is meant for use cases that need fast drawing of
+ large numbers of points. It is optimized for efficiency, and therefore the series using
+ it lack support for some features available to non-accelerated series.
+
+ There are the following restrictions imposed on charts and series when using OpenGL
+ acceleration:
+
+ \list
+ \li Series animations are not supported for accelerated series.
+ \li Antialiasing is not supported for accelerated series.
+ \li Pen styles and marker shapes are ignored for accelerated series.
+ Only solid lines and plain scatter dots are supported.
+ The scatter dots may be circular or rectangular, depending on the underlying graphics
+ hardware and drivers.
+ \li Polar charts are not supported for accelerated series.
+ \li Since the accelerated series are drawn on top of the entire graphics view, they get drawn
+ on top of any other graphics items that you may have on top chart in the same scene.
+ \li To enable QOpenGLWidget to be partially transparent, it needs to be stacked on top of
+ all other widgets. This means you cannot have other widgets partially covering the
+ chart.
+ \endlist
+
+ The default value is \c{false}.
+*/
+/*!
+ \qmlproperty bool AbstractSeries::useOpenGL
+ Specifies whether or not the series is drawn with OpenGL.
+
+ Drawing series with OpenGL is supported only for LineSeries and ScatterSeries.
+
+ For more details, see QAbstractSeries::useOpenGL documentation. QML applications have similar
+ restrictions as those listed in QAbstractSeries::useOpenGL documentation,
+ except there is no restriction about covering the ChartView partially with other
+ items due to different rendering mechanism.
+
+ The default value is \c{false}.
+*/
+
+/*!
+ \fn void QAbstractSeries::useOpenGLChanged()
+ Emitted when the useOpenGL property value changes.
+*/
+/*!
+ \qmlsignal AbstractSeries::onUseOpenGLChanged()
+ Emitted when the useOpenGL property value changes.
+*/
+
+/*!
\internal
\brief Constructs QAbstractSeries object with \a parent.
*/
@@ -192,6 +255,28 @@ void QAbstractSeries::setOpacity(qreal opacity)
}
}
+void QAbstractSeries::setUseOpenGL(bool enable)
+{
+#ifdef QT_NO_OPENGL
+ Q_UNUSED(enable)
+#else
+ bool polarChart = d_ptr->m_chart && d_ptr->m_chart->chartType() == QChart::ChartTypePolar;
+ bool supportedSeries = (type() == SeriesTypeLine || type() == SeriesTypeScatter);
+ if ((!enable || !d_ptr->m_blockOpenGL)
+ && supportedSeries
+ && enable != d_ptr->m_useOpenGL
+ && (!enable || !polarChart)) {
+ d_ptr->m_useOpenGL = enable;
+ emit useOpenGLChanged();
+ }
+#endif
+}
+
+bool QAbstractSeries::useOpenGL() const
+{
+ return d_ptr->m_useOpenGL;
+}
+
/*!
\brief Returns the chart where series belongs to.
@@ -274,7 +359,9 @@ QAbstractSeriesPrivate::QAbstractSeriesPrivate(QAbstractSeries *q)
m_item(0),
m_domain(new XYDomain()),
m_visible(true),
- m_opacity(1.0)
+ m_opacity(1.0),
+ m_useOpenGL(false),
+ m_blockOpenGL(false)
{
}
@@ -354,6 +441,15 @@ bool QAbstractSeriesPrivate::reverseYAxis()
return reverseYAxis;
}
+// This function can be used to explicitly block OpenGL use from some otherwise supported series,
+// such as the line series used as edge series of an area series.
+void QAbstractSeriesPrivate::setBlockOpenGL(bool enable)
+{
+ m_blockOpenGL = enable;
+ if (enable)
+ q_ptr->setUseOpenGL(false);
+}
+
#include "moc_qabstractseries.cpp"
#include "moc_qabstractseries_p.cpp"
diff --git a/src/charts/qabstractseries.h b/src/charts/qabstractseries.h
index c861068c..3e34591b 100644
--- a/src/charts/qabstractseries.h
+++ b/src/charts/qabstractseries.h
@@ -36,6 +36,7 @@ class QT_CHARTS_EXPORT QAbstractSeries : public QObject
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged)
Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged)
Q_PROPERTY(SeriesType type READ type)
+ Q_PROPERTY(bool useOpenGL READ useOpenGL WRITE setUseOpenGL NOTIFY useOpenGLChanged)
Q_ENUMS(SeriesType)
public:
@@ -67,6 +68,8 @@ public:
bool isVisible() const;
qreal opacity() const;
void setOpacity(qreal opacity);
+ void setUseOpenGL(bool enable = true);
+ bool useOpenGL() const;
QChart *chart() const;
@@ -81,6 +84,7 @@ Q_SIGNALS:
void nameChanged();
void visibleChanged();
void opacityChanged();
+ void useOpenGLChanged();
protected:
QScopedPointer<QAbstractSeriesPrivate> d_ptr;
@@ -89,6 +93,7 @@ protected:
friend class ChartThemeManager;
friend class QLegendPrivate;
friend class DeclarativeChart;
+ friend class QAreaSeries;
};
QT_CHARTS_END_NAMESPACE
diff --git a/src/charts/qabstractseries_p.h b/src/charts/qabstractseries_p.h
index 7e481f4e..7120c0d2 100644
--- a/src/charts/qabstractseries_p.h
+++ b/src/charts/qabstractseries_p.h
@@ -81,6 +81,8 @@ public:
bool reverseXAxis();
bool reverseYAxis();
+ void setBlockOpenGL(bool enable);
+
Q_SIGNALS:
void countChanged();
@@ -96,6 +98,8 @@ private:
bool m_visible;
qreal m_opacity;
ChartPresenter *m_presenter;
+ bool m_useOpenGL;
+ bool m_blockOpenGL;
friend class QAbstractSeries;
friend class ChartDataSet;
diff --git a/src/charts/scatterchart/scatterchartitem.cpp b/src/charts/scatterchart/scatterchartitem.cpp
index ac0611e0..e8e4fe43 100644
--- a/src/charts/scatterchart/scatterchartitem.cpp
+++ b/src/charts/scatterchart/scatterchartitem.cpp
@@ -130,6 +130,18 @@ void ScatterChartItem::markerDoubleClicked(QGraphicsItem *marker)
void ScatterChartItem::updateGeometry()
{
+ static const QRectF dummyRect = QRectF(0.0, 0.0, 0.001, 0.001);
+ if (m_series->useOpenGL()) {
+ if (m_items.childItems().count())
+ deletePoints(m_items.childItems().count());
+ // Fake a miniscule region, so we trigger changed signal.
+ if (m_rect.width() != dummyRect.width()) {
+ prepareGeometryChange();
+ m_rect = dummyRect;
+ }
+ update();
+ return;
+ }
const QVector<QPointF>& points = geometryPoints();
@@ -199,6 +211,9 @@ void ScatterChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *
Q_UNUSED(option)
Q_UNUSED(widget)
+ if (m_series->useOpenGL())
+ return;
+
QRectF clipRect = QRectF(QPointF(0, 0), domain()->size());
painter->save();
diff --git a/src/charts/xychart/glxyseriesdata.cpp b/src/charts/xychart/glxyseriesdata.cpp
new file mode 100644
index 00000000..2dafafd2
--- /dev/null
+++ b/src/charts/xychart/glxyseriesdata.cpp
@@ -0,0 +1,147 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2015 The Qt Company Ltd
+ ** All rights reserved.
+ ** For any questions to The Qt Company, please use contact form at http://qt.io
+ **
+ ** This file is part of the Qt Charts module.
+ **
+ ** Licensees holding valid commercial license for Qt may use this file in
+ ** accordance with the Qt License Agreement provided with the Software
+ ** or, alternatively, in accordance with the terms contained in a written
+ ** agreement between you and The Qt Company.
+ **
+ ** If you have questions regarding the use of this file, please use
+ ** contact form at http://qt.io
+ **
+ ****************************************************************************/
+
+#include "private/glxyseriesdata_p.h"
+#include "private/abstractdomain_p.h"
+#include <QtCharts/QScatterSeries>
+
+QT_CHARTS_BEGIN_NAMESPACE
+
+GLXYSeriesDataManager::GLXYSeriesDataManager(QObject *parent)
+ : QObject(parent),
+ m_mapDirty(false)
+{
+}
+
+GLXYSeriesDataManager::~GLXYSeriesDataManager()
+{
+ cleanup();
+}
+
+void GLXYSeriesDataManager::setPoints(QXYSeries *series, const AbstractDomain *domain)
+{
+ GLXYSeriesData *data = m_seriesDataMap.value(series);
+ if (!data) {
+ data = new GLXYSeriesData;
+ data->type = series->type();
+ QColor sc;
+ if (data->type == QAbstractSeries::SeriesTypeScatter) {
+ QScatterSeries *scatter = static_cast<QScatterSeries *>(series);
+ data->width = float(scatter->markerSize());
+ sc = scatter->color(); // Scatter overwrites color property
+ } else {
+ data->width = float(series->pen().widthF());
+ sc = series->color();
+ }
+ data->color = QVector3D(float(sc.redF()), float(sc.greenF()), float(sc.blueF()));
+ connect(series, &QXYSeries::penChanged, this,
+ &GLXYSeriesDataManager::handleSeriesPenChange);
+ connect(series, &QXYSeries::useOpenGLChanged, this,
+ &GLXYSeriesDataManager::handleSeriesOpenGLChange);
+ m_seriesDataMap.insert(series, data);
+ m_mapDirty = true;
+ }
+ QVector<float> &array = data->array;
+
+ bool logAxis = false;
+ foreach (QAbstractAxis* axis, series->attachedAxes()) {
+ if (axis->type() == QAbstractAxis::AxisTypeLogValue) {
+ logAxis = true;
+ break;
+ }
+ }
+
+ int count = series->count();
+ int index = 0;
+ array.resize(count * 2);
+ if (logAxis) {
+ // Use domain to resolve geometry points. Not as fast as shaders, but simpler that way
+ QVector<QPointF> geometryPoints = domain->calculateGeometryPoints(series->pointsVector());
+ const float height = domain->size().height();
+ if (geometryPoints.size()) {
+ for (int i = 0; i < count; i++) {
+ const QPointF &point = geometryPoints.at(i);
+ array[index++] = float(point.x());
+ array[index++] = float(height - point.y());
+ }
+ } else {
+ // If there are invalid log values, geometry points generation fails
+ for (int i = 0; i < count; i++) {
+ array[index++] = 0.0f;
+ array[index++] = 0.0f;
+ }
+ }
+ data->min = QVector2D(0, 0);
+ data->delta = QVector2D(domain->size().width() / 2.0f, domain->size().height() / 2.0f);
+ } else {
+ // Regular value axes, so we can do the math easily on shaders.
+ QVector<QPointF> seriesPoints = series->pointsVector();
+ for (int i = 0; i < count; i++) {
+ const QPointF &point = seriesPoints.at(i);
+ array[index++] = float(point.x());
+ array[index++] = float(point.y());
+ }
+ data->min = QVector2D(domain->minX(), domain->minY());
+ data->delta = QVector2D((domain->maxX() - domain->minX()) / 2.0f,
+ (domain->maxY() - domain->minY()) / 2.0f);
+ }
+ data->dirty = true;
+}
+
+void GLXYSeriesDataManager::removeSeries(const QXYSeries *series)
+{
+ GLXYSeriesData *data = m_seriesDataMap.take(series);
+ if (data) {
+ disconnect(series, 0, this, 0);
+ delete data;
+ emit seriesRemoved(series);
+ m_mapDirty = true;
+ }
+}
+
+void GLXYSeriesDataManager::cleanup()
+{
+ foreach (GLXYSeriesData *data, m_seriesDataMap.values())
+ delete data;
+ m_seriesDataMap.clear();
+ m_mapDirty = true;
+ // Signal all series removal by using zero as parameter
+ emit seriesRemoved(0);
+}
+
+void GLXYSeriesDataManager::handleSeriesPenChange()
+{
+ QXYSeries *series = qobject_cast<QXYSeries *>(sender());
+ if (series) {
+ GLXYSeriesData *data = m_seriesDataMap.value(series);
+ if (data) {
+ QColor sc = series->color();
+ data->color = QVector3D(float(sc.redF()), float(sc.greenF()), float(sc.blueF()));
+ data->width = float(series->pen().widthF());
+ }
+ }
+}
+
+void GLXYSeriesDataManager::handleSeriesOpenGLChange()
+{
+ QXYSeries *series = qobject_cast<QXYSeries *>(sender());
+ if (!series->useOpenGL())
+ removeSeries(series);
+}
+
+QT_CHARTS_END_NAMESPACE
diff --git a/src/charts/xychart/glxyseriesdata_p.h b/src/charts/xychart/glxyseriesdata_p.h
new file mode 100644
index 00000000..0f59effe
--- /dev/null
+++ b/src/charts/xychart/glxyseriesdata_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://qt.io
+**
+** This file is part of the Qt Charts module.
+**
+** Licensees holding valid commercial license for Qt may use this file in
+** accordance with the Qt License Agreement provided with the Software
+** or, alternatively, in accordance with the terms contained in a written
+** agreement between you and The Qt Company.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.io
+**
+****************************************************************************/
+
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt Chart API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#ifndef GLXYSERIESDATA_H
+#define GLXYSERIESDATA_H
+
+#include <QtCore/QMap>
+#include <QtCharts/QAbstractSeries>
+#include <QtCharts/QXYSeries>
+#include <QtGui/QVector3D>
+#include <QtGui/QVector2D>
+
+QT_CHARTS_BEGIN_NAMESPACE
+
+class AbstractDomain;
+
+struct GLXYSeriesData {
+ QVector<float> array;
+ bool dirty;
+ QVector3D color;
+ float width;
+ QAbstractSeries::SeriesType type;
+ QVector2D min;
+ QVector2D delta;
+};
+
+typedef QMap<const QXYSeries *, GLXYSeriesData *> GLXYDataMap;
+typedef QMapIterator<const QXYSeries *, GLXYSeriesData *> GLXYDataMapIterator;
+
+class GLXYSeriesDataManager : public QObject
+{
+ Q_OBJECT
+
+public:
+ GLXYSeriesDataManager(QObject *parent = 0);
+ ~GLXYSeriesDataManager();
+
+ void setPoints(QXYSeries *series, const AbstractDomain *domain);
+
+ void removeSeries(const QXYSeries *series);
+
+ GLXYDataMap &dataMap() { return m_seriesDataMap; }
+
+ // These functions are needed by qml side, so they must be inline
+ bool mapDirty() const { return m_mapDirty; }
+ void clearAllDirty() {
+ m_mapDirty = false;
+ foreach (GLXYSeriesData *data, m_seriesDataMap.values())
+ data->dirty = false;
+ }
+
+public Q_SLOTS:
+ void cleanup();
+ void handleSeriesPenChange();
+ void handleSeriesOpenGLChange();
+
+Q_SIGNALS:
+ void seriesRemoved(const QXYSeries *series);
+
+private:
+ GLXYDataMap m_seriesDataMap;
+ bool m_mapDirty;
+};
+
+QT_CHARTS_END_NAMESPACE
+
+#endif
diff --git a/src/charts/xychart/qxyseries.cpp b/src/charts/xychart/qxyseries.cpp
index a88c5f85..681781ec 100644
--- a/src/charts/xychart/qxyseries.cpp
+++ b/src/charts/xychart/qxyseries.cpp
@@ -238,7 +238,7 @@ QT_CHARTS_BEGIN_NAMESPACE
\sa pointLabelsVisible
*/
/*!
- \fn void QXYSeries::pointLabelsClippintChanged(bool clipping)
+ \fn void QXYSeries::pointLabelsClippingChanged(bool clipping)
The clipping of the data point labels is changed to \a clipping.
*/
/*!
@@ -398,6 +398,11 @@ QT_CHARTS_BEGIN_NAMESPACE
*/
/*!
+ \fn void QXYSeries::penChanged(const QPen &pen)
+ \brief Signal is emitted when the line pen has changed to \a pen.
+*/
+
+/*!
\fn void QXYSeriesPrivate::updated()
\brief \internal
*/
@@ -539,7 +544,7 @@ void QXYSeries::replace(int index, const QPointF &newPoint)
\note This is much faster than replacing data points one by one,
or first clearing all data, and then appending the new data. Emits QXYSeries::pointsReplaced()
when the points have been replaced. However, note that using the overload that takes
- \c{QVector<QPointF>} as parameter is slightly faster than using this overload.
+ \c{QVector<QPointF>} as parameter is faster than using this overload.
\sa pointsReplaced()
*/
void QXYSeries::replace(QList<QPointF> points)
@@ -634,7 +639,8 @@ void QXYSeries::clear()
}
/*!
- Returns list of points in the series.
+ Returns the points in the series as a list.
+ Use QXYSeries::pointsVector() for better performance.
*/
QList<QPointF> QXYSeries::points() const
{
@@ -643,6 +649,16 @@ QList<QPointF> QXYSeries::points() const
}
/*!
+ Returns the points in the series as a vector.
+ This is more efficient that calling QXYSeries::points();
+*/
+QVector<QPointF> QXYSeries::pointsVector() const
+{
+ Q_D(const QXYSeries);
+ return d->m_points;
+}
+
+/*!
Returns point at \a index in internal points vector.
*/
const QPointF &QXYSeries::at(int index) const
@@ -675,6 +691,7 @@ void QXYSeries::setPen(const QPen &pen)
emit d->updated();
if (emitColorChanged)
emit colorChanged(pen.color());
+ emit penChanged(pen);
}
}
@@ -864,7 +881,7 @@ void QXYSeriesPrivate::initializeDomain()
Q_Q(QXYSeries);
- const QList<QPointF>& points = q->points();
+ const QVector<QPointF> &points = q->pointsVector();
if (!points.isEmpty()) {
minX = points[0].x();
diff --git a/src/charts/xychart/qxyseries.h b/src/charts/xychart/qxyseries.h
index 6a211fbf..25f52ead 100644
--- a/src/charts/xychart/qxyseries.h
+++ b/src/charts/xychart/qxyseries.h
@@ -65,6 +65,7 @@ public:
int count() const;
QList<QPointF> points() const;
+ QVector<QPointF> pointsVector() const;
const QPointF &at(int index) const;
QXYSeries &operator << (const QPointF &point);
@@ -117,6 +118,7 @@ Q_SIGNALS:
void pointLabelsColorChanged(const QColor &color);
void pointLabelsClippingChanged(bool clipping);
void pointsRemoved(int index, int count);
+ void penChanged(const QPen &pen);
private:
Q_DECLARE_PRIVATE(QXYSeries)
diff --git a/src/charts/xychart/xychart.cpp b/src/charts/xychart/xychart.cpp
index 16dd707f..e7d3fddc 100644
--- a/src/charts/xychart/xychart.cpp
+++ b/src/charts/xychart/xychart.cpp
@@ -21,6 +21,8 @@
#include <private/qxyseries_p.h>
#include <private/chartpresenter_p.h>
#include <private/abstractdomain_p.h>
+#include <private/chartdataset_p.h>
+#include <private/glxyseriesdata_p.h>
#include <QtCharts/QXYModelMapper>
#include <private/qabstractaxis_p.h>
#include <QtGui/QPainter>
@@ -106,6 +108,13 @@ void XYChart::updateChart(QVector<QPointF> &oldPoints, QVector<QPointF> &newPoin
}
}
+void XYChart::updateGlChart()
+{
+ presenter()->ensureGLWidget();
+ dataSet()->glXYSeriesDataManager()->setPoints(m_series, domain());
+ updateGeometry();
+}
+
//handlers
void XYChart::handlePointAdded(int index)
@@ -113,20 +122,23 @@ void XYChart::handlePointAdded(int index)
Q_ASSERT(index < m_series->count());
Q_ASSERT(index >= 0);
- QVector<QPointF> points;
-
- if (m_dirty || m_points.isEmpty()) {
- points = domain()->calculateGeometryPoints(m_series->points());
+ if (m_series->useOpenGL()) {
+ updateGlChart();
} else {
- points = m_points;
- QPointF point = domain()->calculateGeometryPoint(m_series->points()[index], m_validData);
- if (!m_validData)
- m_points.clear();
- else
- points.insert(index, point);
+ QVector<QPointF> points;
+ if (m_dirty || m_points.isEmpty()) {
+ points = domain()->calculateGeometryPoints(m_series->pointsVector());
+ } else {
+ points = m_points;
+ QPointF point = domain()->calculateGeometryPoint(m_series->pointsVector().at(index),
+ m_validData);
+ if (!m_validData)
+ m_points.clear();
+ else
+ points.insert(index, point);
+ }
+ updateChart(m_points, points, index);
}
-
- updateChart(m_points, points, index);
}
void XYChart::handlePointRemoved(int index)
@@ -134,16 +146,18 @@ void XYChart::handlePointRemoved(int index)
Q_ASSERT(index <= m_series->count());
Q_ASSERT(index >= 0);
- QVector<QPointF> points;
-
- if (m_dirty || m_points.isEmpty()) {
- points = domain()->calculateGeometryPoints(m_series->points());
+ if (m_series->useOpenGL()) {
+ updateGlChart();
} else {
- points = m_points;
- points.remove(index);
+ QVector<QPointF> points;
+ if (m_dirty || m_points.isEmpty()) {
+ points = domain()->calculateGeometryPoints(m_series->pointsVector());
+ } else {
+ points = m_points;
+ points.remove(index);
+ }
+ updateChart(m_points, points, index);
}
-
- updateChart(m_points, points, index);
}
void XYChart::handlePointsRemoved(int index, int count)
@@ -151,16 +165,18 @@ void XYChart::handlePointsRemoved(int index, int count)
Q_ASSERT(index <= m_series->count());
Q_ASSERT(index >= 0);
- QVector<QPointF> points;
-
- if (m_dirty || m_points.isEmpty()) {
- points = domain()->calculateGeometryPoints(m_series->points());
+ if (m_series->useOpenGL()) {
+ updateGlChart();
} else {
- points = m_points;
- points.remove(index, count);
+ QVector<QPointF> points;
+ if (m_dirty || m_points.isEmpty()) {
+ points = domain()->calculateGeometryPoints(m_series->pointsVector());
+ } else {
+ points = m_points;
+ points.remove(index, count);
+ }
+ updateChart(m_points, points, index);
}
-
- updateChart(m_points, points, index);
}
void XYChart::handlePointReplaced(int index)
@@ -168,34 +184,45 @@ void XYChart::handlePointReplaced(int index)
Q_ASSERT(index < m_series->count());
Q_ASSERT(index >= 0);
- QVector<QPointF> points;
-
- if (m_dirty || m_points.isEmpty()) {
- points = domain()->calculateGeometryPoints(m_series->points());
+ if (m_series->useOpenGL()) {
+ updateGlChart();
} else {
- QPointF point = domain()->calculateGeometryPoint(m_series->points()[index], m_validData);
- if (!m_validData)
- m_points.clear();
- points = m_points;
- if (m_validData)
- points.replace(index, point);
+ QVector<QPointF> points;
+ if (m_dirty || m_points.isEmpty()) {
+ points = domain()->calculateGeometryPoints(m_series->pointsVector());
+ } else {
+ QPointF point = domain()->calculateGeometryPoint(m_series->pointsVector().at(index),
+ m_validData);
+ if (!m_validData)
+ m_points.clear();
+ points = m_points;
+ if (m_validData)
+ points.replace(index, point);
+ }
+ updateChart(m_points, points, index);
}
-
- updateChart(m_points, points, index);
}
void XYChart::handlePointsReplaced()
{
- // All the points were replaced -> recalculate
- QVector<QPointF> points = domain()->calculateGeometryPoints(m_series->points());
- updateChart(m_points, points, -1);
+ if (m_series->useOpenGL()) {
+ updateGlChart();
+ } else {
+ // All the points were replaced -> recalculate
+ QVector<QPointF> points = domain()->calculateGeometryPoints(m_series->pointsVector());
+ updateChart(m_points, points, -1);
+ }
}
void XYChart::handleDomainUpdated()
{
- if (isEmpty()) return;
- QVector<QPointF> points = domain()->calculateGeometryPoints(m_series->points());
- updateChart(m_points, points);
+ if (m_series->useOpenGL()) {
+ updateGlChart();
+ } else {
+ if (isEmpty()) return;
+ QVector<QPointF> points = domain()->calculateGeometryPoints(m_series->pointsVector());
+ updateChart(m_points, points);
+ }
}
bool XYChart::isEmpty()
diff --git a/src/charts/xychart/xychart.pri b/src/charts/xychart/xychart.pri
index e7af66e5..1c5efb90 100644
--- a/src/charts/xychart/xychart.pri
+++ b/src/charts/xychart/xychart.pri
@@ -6,12 +6,14 @@ SOURCES += \
$$PWD/qxyseries.cpp \
$$PWD/qxymodelmapper.cpp \
$$PWD/qvxymodelmapper.cpp \
- $$PWD/qhxymodelmapper.cpp
+ $$PWD/qhxymodelmapper.cpp \
+ $$PWD/glxyseriesdata.cpp
PRIVATE_HEADERS += \
$$PWD/xychart_p.h \
$$PWD/qxyseries_p.h \
- $$PWD/qxymodelmapper_p.h
+ $$PWD/qxymodelmapper_p.h \
+ $$PWD/glxyseriesdata_p.h
PUBLIC_HEADERS += \
$$PWD/qxyseries.h \
diff --git a/src/charts/xychart/xychart_p.h b/src/charts/xychart/xychart_p.h
index f45d9c6d..e72dcd38 100644
--- a/src/charts/xychart/xychart_p.h
+++ b/src/charts/xychart/xychart_p.h
@@ -76,6 +76,7 @@ Q_SIGNALS:
protected:
virtual void updateChart(QVector<QPointF> &oldPoints, QVector<QPointF> &newPoints, int index = -1);
+ virtual void updateGlChart();
private:
inline bool isEmpty();