summaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--examples/charts/audio/xyseriesiodevice.cpp6
-rw-r--r--examples/charts/charts.pro8
-rw-r--r--examples/charts/chartthemes/themewidget.cpp2
-rw-r--r--examples/charts/openglseries/datasource.cpp118
-rw-r--r--examples/charts/openglseries/datasource.h53
-rw-r--r--examples/charts/openglseries/main.cpp167
-rw-r--r--examples/charts/openglseries/openglseries.pro9
-rw-r--r--examples/charts/qmloscilloscope/qml/qmloscilloscope/ControlPanel.qml12
-rw-r--r--examples/charts/qmloscilloscope/qml/qmloscilloscope/ScopeView.qml29
-rw-r--r--examples/charts/qmloscilloscope/qml/qmloscilloscope/main.qml18
-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
-rw-r--r--src/chartsqml2/chartsqml2.pro8
-rw-r--r--src/chartsqml2/declarativechart.cpp152
-rw-r--r--src/chartsqml2/declarativechart.h14
-rw-r--r--src/chartsqml2/declarativechartnode.cpp78
-rw-r--r--src/chartsqml2/declarativechartnode.h50
-rw-r--r--src/chartsqml2/declarativerendernode.cpp317
-rw-r--r--src/chartsqml2/declarativerendernode.h74
-rw-r--r--tests/auto/qlineseries/tst_qlineseries.cpp3
-rw-r--r--tests/auto/qscatterseries/tst_qscatterseries.cpp2
-rw-r--r--tests/auto/qsplineseries/tst_qsplineseries.cpp2
-rw-r--r--tests/auto/qxyseries/tst_qxyseries.cpp1
58 files changed, 1959 insertions, 152 deletions
diff --git a/examples/charts/audio/xyseriesiodevice.cpp b/examples/charts/audio/xyseriesiodevice.cpp
index ef9789cd..4b7d2f52 100644
--- a/examples/charts/audio/xyseriesiodevice.cpp
+++ b/examples/charts/audio/xyseriesiodevice.cpp
@@ -35,12 +35,12 @@ qint64 XYSeriesIODevice::readData(char * data, qint64 maxSize)
qint64 XYSeriesIODevice::writeData(const char * data, qint64 maxSize)
{
qint64 range = 2000;
- QList<QPointF> oldPoints = m_series->points();
- QList<QPointF> points;
+ QVector<QPointF> oldPoints = m_series->pointsVector();
+ QVector<QPointF> points;
int resolution = 4;
if (oldPoints.count() < range) {
- points = m_series->points();
+ points = m_series->pointsVector();
} else {
for (int i = maxSize/resolution; i < oldPoints.count(); i++)
points.append(QPointF(i - maxSize/resolution, oldPoints.at(i).y()));
diff --git a/examples/charts/charts.pro b/examples/charts/charts.pro
index a104fcdd..3f463e46 100644
--- a/examples/charts/charts.pro
+++ b/examples/charts/charts.pro
@@ -50,7 +50,13 @@ qtHaveModule(quick) {
qtHaveModule(multimedia) {
SUBDIRS += audio
} else {
- message("QtMultimedia library not available. Some examples are disabled")
+ message("QtMultimedia library not available. Some examples are disabled.")
+}
+
+contains(QT_CONFIG, opengl) {
+ SUBDIRS += openglseries
+} else {
+ message("OpenGL not available. Some examples are disabled.")
}
!linux-arm*: {
diff --git a/examples/charts/chartthemes/themewidget.cpp b/examples/charts/chartthemes/themewidget.cpp
index 797356b7..a78ab9b4 100644
--- a/examples/charts/chartthemes/themewidget.cpp
+++ b/examples/charts/chartthemes/themewidget.cpp
@@ -189,7 +189,7 @@ QChart *ThemeWidget::createAreaChart() const
for (int j(0); j < m_dataTable[i].count(); j++) {
Data data = m_dataTable[i].at(j);
if (lowerSeries) {
- const QList<QPointF>& points = lowerSeries->points();
+ const QVector<QPointF>& points = lowerSeries->pointsVector();
upperSeries->append(QPointF(j, points[i].y() + data.first.y()));
} else {
upperSeries->append(QPointF(j, data.first.y()));
diff --git a/examples/charts/openglseries/datasource.cpp b/examples/charts/openglseries/datasource.cpp
new file mode 100644
index 00000000..64b45405
--- /dev/null
+++ b/examples/charts/openglseries/datasource.cpp
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** 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 "datasource.h"
+#include <QtCore/QtMath>
+
+QT_CHARTS_USE_NAMESPACE
+
+DataSource::DataSource(QObject *parent) :
+ QObject(parent),
+ m_index(-1)
+{
+ generateData(0, 0, 0);
+}
+
+void DataSource::update(QAbstractSeries *series, int seriesIndex)
+{
+ if (series) {
+ QXYSeries *xySeries = static_cast<QXYSeries *>(series);
+ const QVector<QVector<QPointF> > &seriesData = m_data.at(seriesIndex);
+ if (seriesIndex == 0)
+ m_index++;
+ if (m_index > seriesData.count() - 1)
+ m_index = 0;
+
+ QVector<QPointF> points = seriesData.at(m_index);
+ // Use replace instead of clear + append, it's optimized for performance
+ xySeries->replace(points);
+ }
+}
+
+void DataSource::handleSceneChanged()
+{
+ m_dataUpdater.start();
+}
+
+void DataSource::updateAllSeries()
+{
+ static int frameCount = 0;
+ static QString labelText = QStringLiteral("FPS: %1");
+
+ for (int i = 0; i < m_seriesList.size(); i++)
+ update(m_seriesList[i], i);
+
+ frameCount++;
+ int elapsed = m_fpsTimer.elapsed();
+ if (elapsed >= 1000) {
+ elapsed = m_fpsTimer.restart();
+ qreal fps = qreal(0.1 * int(10000.0 * (qreal(frameCount) / qreal(elapsed))));
+ m_fpsLabel->setText(labelText.arg(QString::number(fps, 'f', 1)));
+ frameCount = 0;
+ }
+}
+
+void DataSource::startUpdates(const QList<QXYSeries *> &seriesList, QLabel *fpsLabel)
+{
+ m_seriesList = seriesList;
+ m_fpsLabel = fpsLabel;
+
+ m_dataUpdater.setInterval(0);
+ m_dataUpdater.setSingleShot(true);
+ QObject::connect(&m_dataUpdater, &QTimer::timeout,
+ this, &DataSource::updateAllSeries);
+
+ m_fpsTimer.start();
+ updateAllSeries();
+}
+
+void DataSource::generateData(int seriesCount, int rowCount, int colCount)
+{
+ // Remove previous data
+ foreach (QVector<QVector<QPointF> > seriesData, m_data) {
+ foreach (QVector<QPointF> row, seriesData)
+ row.clear();
+ }
+
+ m_data.clear();
+
+ qreal xAdjustment = 20.0 / (colCount * rowCount);
+ qreal yMultiplier = 3.0 / qreal(seriesCount);
+
+ // Append the new data depending on the type
+ for (int k(0); k < seriesCount; k++) {
+ QVector<QVector<QPointF> > seriesData;
+ qreal height = qreal(k) * (10.0 / qreal(seriesCount)) + 0.3;
+ for (int i(0); i < rowCount; i++) {
+ QVector<QPointF> points;
+ points.reserve(colCount);
+ for (int j(0); j < colCount; j++) {
+ qreal x(0);
+ qreal y(0);
+ // data with sin + random component
+ y = height + (yMultiplier * qSin(3.14159265358979 / 50 * j)
+ + (yMultiplier * (qreal) rand() / (qreal) RAND_MAX));
+ // 0.000001 added to make values logaxis compatible
+ x = 0.000001 + 20.0 * (qreal(j) / qreal(colCount)) + (xAdjustment * qreal(i));
+ points.append(QPointF(x, y));
+ }
+ seriesData.append(points);
+ }
+ m_data.append(seriesData);
+ }
+}
diff --git a/examples/charts/openglseries/datasource.h b/examples/charts/openglseries/datasource.h
new file mode 100644
index 00000000..295d93a3
--- /dev/null
+++ b/examples/charts/openglseries/datasource.h
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** 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 DATASOURCE_H
+#define DATASOURCE_H
+
+#include <QtCore/QObject>
+#include <QtCharts/QXYSeries>
+#include <QtWidgets/QLabel>
+#include <QtCore/QElapsedTimer>
+#include <QtCore/QTimer>
+
+QT_CHARTS_USE_NAMESPACE
+
+class DataSource : public QObject
+{
+ Q_OBJECT
+public:
+ explicit DataSource(QObject *parent = 0);
+
+ void startUpdates(const QList<QXYSeries *> &seriesList, QLabel *fpsLabel);
+
+public slots:
+ void generateData(int seriesCount, int rowCount, int colCount);
+ void update(QAbstractSeries *series, int seriesIndex);
+ void handleSceneChanged();
+ void updateAllSeries();
+
+private:
+ QVector<QVector<QVector<QPointF> > > m_data;
+ int m_index;
+ QList<QXYSeries *> m_seriesList;
+ QLabel *m_fpsLabel;
+ QElapsedTimer m_fpsTimer;
+ QTimer m_dataUpdater;
+};
+
+#endif // DATASOURCE_H
diff --git a/examples/charts/openglseries/main.cpp b/examples/charts/openglseries/main.cpp
new file mode 100644
index 00000000..7b654a5d
--- /dev/null
+++ b/examples/charts/openglseries/main.cpp
@@ -0,0 +1,167 @@
+/****************************************************************************
+**
+** 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 "datasource.h"
+#include <QtWidgets/QApplication>
+#include <QtWidgets/QMainWindow>
+#include <QtCharts/QChartView>
+#include <QtCharts/QLineSeries>
+#include <QtCharts/QScatterSeries>
+#include <QtCharts/QValueAxis>
+#include <QtCharts/QLogValueAxis>
+#include <QtWidgets/QLabel>
+
+// Uncomment to use logarithmic axes instead of regular value axes
+//#define USE_LOG_AXIS
+
+// Uncomment to use regular series instead of OpenGL accelerated series
+//#define DONT_USE_GL_SERIES
+
+// Uncomment to add a simple regular series (thick line) and a matching OpenGL series (thinner line)
+// to verify the series have same visible geometry.
+//#define ADD_SIMPLE_SERIES
+
+QT_CHARTS_USE_NAMESPACE
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+ QStringList colors;
+ colors << "red" << "blue" << "green" << "black";
+
+ QChart *chart = new QChart();
+ chart->legend()->hide();
+
+#ifdef USE_LOG_AXIS
+ QLogValueAxis *axisX = new QLogValueAxis;
+ QLogValueAxis *axisY = new QLogValueAxis;
+#else
+ QValueAxis *axisX = new QValueAxis;
+ QValueAxis *axisY = new QValueAxis;
+#endif
+
+ chart->addAxis(axisX, Qt::AlignBottom);
+ chart->addAxis(axisY, Qt::AlignLeft);
+
+ const int seriesCount = 10;
+#ifdef DONT_USE_GL_SERIES
+ const int pointCount = 100;
+ chart->setTitle("Unaccelerated Series");
+#else
+ const int pointCount = 10000;
+ chart->setTitle("OpenGL Accelerated Series");
+#endif
+
+ QList<QXYSeries *> seriesList;
+ for (int i = 0; i < seriesCount; i++) {
+ QXYSeries *series = 0;
+ int colorIndex = i % colors.size();
+ if (i % 2) {
+ series = new QScatterSeries;
+ QScatterSeries *scatter = static_cast<QScatterSeries *>(series);
+ scatter->setColor(QColor(colors.at(colorIndex)));
+ scatter->setMarkerSize(qreal(colorIndex + 2) / 2.0);
+ // Scatter pen doesn't have affect in OpenGL drawing, but if you disable OpenGL drawing
+ // this makes the marker border visible and gives comparable marker size to OpenGL
+ // scatter points.
+ scatter->setPen(QPen("black"));
+ } else {
+ series = new QLineSeries;
+ series->setPen(QPen(QBrush(QColor(colors.at(colorIndex))),
+ qreal(colorIndex + 2) / 2.0));
+ }
+ seriesList.append(series);
+#ifdef DONT_USE_GL_SERIES
+ series->setUseOpenGL(false);
+#else
+ //![1]
+ series->setUseOpenGL(true);
+ //![1]
+#endif
+ chart->addSeries(series);
+ series->attachAxis(axisX);
+ series->attachAxis(axisY);
+ }
+
+ if (axisX->type() == QAbstractAxis::AxisTypeLogValue)
+ axisX->setRange(0.1, 20.0);
+ else
+ axisX->setRange(0, 20.0);
+
+ if (axisY->type() == QAbstractAxis::AxisTypeLogValue)
+ axisY->setRange(0.1, 10.0);
+ else
+ axisY->setRange(0, 10.0);
+
+#ifdef ADD_SIMPLE_SERIES
+ QLineSeries *simpleRasterSeries = new QLineSeries;
+ *simpleRasterSeries << QPointF(0.001, 0.001)
+ << QPointF(2.5, 8.0)
+ << QPointF(5.0, 4.0)
+ << QPointF(7.5, 9.0)
+ << QPointF(10.0, 0.001)
+ << QPointF(12.5, 2.0)
+ << QPointF(15.0, 1.0)
+ << QPointF(17.5, 6.0)
+ << QPointF(20.0, 10.0);
+ simpleRasterSeries->setUseOpenGL(false);
+ simpleRasterSeries->setPen(QPen(QBrush("magenta"), 8));
+ chart->addSeries(simpleRasterSeries);
+ simpleRasterSeries->attachAxis(axisX);
+ simpleRasterSeries->attachAxis(axisY);
+
+ QLineSeries *simpleGLSeries = new QLineSeries;
+ simpleGLSeries->setUseOpenGL(true);
+ simpleGLSeries->setPen(QPen(QBrush("black"), 2));
+ simpleGLSeries->replace(simpleRasterSeries->points());
+ chart->addSeries(simpleGLSeries);
+ simpleGLSeries->attachAxis(axisX);
+ simpleGLSeries->attachAxis(axisY);
+#endif
+
+ QChartView *chartView = new QChartView(chart);
+
+ QMainWindow window;
+ window.setCentralWidget(chartView);
+ window.resize(600, 400);
+ window.show();
+
+ DataSource dataSource;
+ dataSource.generateData(seriesCount, 10, pointCount);
+
+ QLabel *fpsLabel = new QLabel(&window);
+ QLabel *countLabel = new QLabel(&window);
+ QString countText = QStringLiteral("Total point count: %1");
+ countLabel->setText(countText.arg(pointCount * seriesCount));
+ countLabel->resize(window.width(), countLabel->height());
+ fpsLabel->move(10,2);
+ fpsLabel->raise();
+ fpsLabel->show();
+ countLabel->move(10, 14);
+ fpsLabel->raise();
+ countLabel->show();
+
+ // We can get more than one changed event per frame, so do async update.
+ // This also allows the application to be responsive.
+ QObject::connect(chart->scene(), &QGraphicsScene::changed,
+ &dataSource, &DataSource::handleSceneChanged);
+
+ dataSource.startUpdates(seriesList, fpsLabel);
+
+ return a.exec();
+}
diff --git a/examples/charts/openglseries/openglseries.pro b/examples/charts/openglseries/openglseries.pro
new file mode 100644
index 00000000..0ae11595
--- /dev/null
+++ b/examples/charts/openglseries/openglseries.pro
@@ -0,0 +1,9 @@
+!include( ../examples.pri ) {
+ error( "Couldn't find the examples.pri file!" )
+}
+
+TARGET = openglseries
+SOURCES += main.cpp \
+ datasource.cpp
+HEADERS += datasource.h
+
diff --git a/examples/charts/qmloscilloscope/qml/qmloscilloscope/ControlPanel.qml b/examples/charts/qmloscilloscope/qml/qmloscilloscope/ControlPanel.qml
index 478a2cc3..ccf53fa1 100644
--- a/examples/charts/qmloscilloscope/qml/qmloscilloscope/ControlPanel.qml
+++ b/examples/charts/qmloscilloscope/qml/qmloscilloscope/ControlPanel.qml
@@ -16,10 +16,11 @@
**
****************************************************************************/
-import QtQuick 2.0
+import QtQuick 2.1
import QtQuick.Layouts 1.0
ColumnLayout {
+ property alias openGLButton: openGLButton
spacing: 8
Layout.fillHeight: true
signal animationsEnabled(bool enabled)
@@ -27,6 +28,7 @@ ColumnLayout {
signal refreshRateChanged(variant rate);
signal signalSourceChanged(string source, int signalCount, int sampleCount);
signal antialiasingEnabled(bool enabled)
+ signal openGlChanged(bool enabled)
Text {
text: "Scope"
@@ -35,6 +37,14 @@ ColumnLayout {
}
MultiButton {
+ id: openGLButton
+ text: "OpenGL: "
+ items: ["false", "true"]
+ currentSelection: 0
+ onSelectionChanged: openGlChanged(currentSelection == 1);
+ }
+
+ MultiButton {
text: "Graph: "
items: ["line", "spline", "scatter"]
currentSelection: 0
diff --git a/examples/charts/qmloscilloscope/qml/qmloscilloscope/ScopeView.qml b/examples/charts/qmloscilloscope/qml/qmloscilloscope/ScopeView.qml
index e422d056..443e515a 100644
--- a/examples/charts/qmloscilloscope/qml/qmloscilloscope/ScopeView.qml
+++ b/examples/charts/qmloscilloscope/qml/qmloscilloscope/ScopeView.qml
@@ -17,13 +17,18 @@
****************************************************************************/
import QtQuick 2.0
-import QtCharts 2.0
+import QtCharts 2.1
//![1]
ChartView {
id: chartView
animationOptions: ChartView.NoAnimation
theme: ChartView.ChartThemeDark
+ property bool openGL: false
+ onOpenGLChanged: {
+ series("signal 1").useOpenGL = openGL;
+ series("signal 2").useOpenGL = openGL;
+ }
ValueAxis {
id: axisY1
@@ -40,7 +45,7 @@ ChartView {
ValueAxis {
id: axisX
min: 0
- max: 1000
+ max: 1024
}
LineSeries {
@@ -48,12 +53,14 @@ ChartView {
name: "signal 1"
axisX: axisX
axisY: axisY1
+ useOpenGL: chartView.openGL
}
LineSeries {
id: lineSeries2
name: "signal 2"
axisX: axisX
axisYRight: axisY2
+ useOpenGL: chartView.openGL
}
//![1]
@@ -78,18 +85,28 @@ ChartView {
// but the series have their own y-axes to make it possible to control the y-offset
// of the "signal sources".
if (type == "line") {
- chartView.createSeries(ChartView.SeriesTypeLine, "signal 1", axisX, axisY1);
- chartView.createSeries(ChartView.SeriesTypeLine, "signal 2", axisX, axisY2);
+ var series1 = chartView.createSeries(ChartView.SeriesTypeLine, "signal 1",
+ axisX, axisY1);
+ series1.useOpenGL = chartView.openGL
+
+ var series2 = chartView.createSeries(ChartView.SeriesTypeLine, "signal 2",
+ axisX, axisY2);
+ series2.useOpenGL = chartView.openGL
} else if (type == "spline") {
chartView.createSeries(ChartView.SeriesTypeSpline, "signal 1", axisX, axisY1);
chartView.createSeries(ChartView.SeriesTypeSpline, "signal 2", axisX, axisY2);
} else {
- var series1 = chartView.createSeries(ChartView.SeriesTypeScatter, "signal 1", axisX, axisY1);
+ var series1 = chartView.createSeries(ChartView.SeriesTypeScatter, "signal 1",
+ axisX, axisY1);
series1.markerSize = 3;
series1.borderColor = "transparent";
- var series2 = chartView.createSeries(ChartView.SeriesTypeScatter, "signal 2", axisX, axisY2);
+ series1.useOpenGL = chartView.openGL
+
+ var series2 = chartView.createSeries(ChartView.SeriesTypeScatter, "signal 2",
+ axisX, axisY2);
series2.markerSize = 3;
series2.borderColor = "transparent";
+ series2.useOpenGL = chartView.openGL
}
}
diff --git a/examples/charts/qmloscilloscope/qml/qmloscilloscope/main.qml b/examples/charts/qmloscilloscope/qml/qmloscilloscope/main.qml
index 0ff40c21..5340177e 100644
--- a/examples/charts/qmloscilloscope/qml/qmloscilloscope/main.qml
+++ b/examples/charts/qmloscilloscope/qml/qmloscilloscope/main.qml
@@ -21,8 +21,8 @@ import QtQuick 2.0
//![1]
Rectangle {
id: main
- width: 400
- height: 300
+ width: 600
+ height: 400
color: "#404040"
ControlPanel {
@@ -39,11 +39,22 @@ Rectangle {
dataSource.generateData(0, signalCount, sampleCount);
else
dataSource.generateData(1, signalCount, sampleCount);
+ scopeView.axisX().max = sampleCount;
}
onAnimationsEnabled: scopeView.setAnimations(enabled);
- onSeriesTypeChanged: scopeView.changeSeriesType(type);
+ onSeriesTypeChanged: {
+ scopeView.changeSeriesType(type);
+ if (type === "spline") {
+ controlPanel.openGLButton.currentSelection = 0;
+ controlPanel.openGLButton.enabled = false;
+ scopeView.openGL = false;
+ } else {
+ controlPanel.openGLButton.enabled = true;
+ }
+ }
onRefreshRateChanged: scopeView.changeRefreshRate(rate);
onAntialiasingEnabled: scopeView.antialiasing = enabled;
+ onOpenGlChanged: scopeView.openGL = enabled;
}
//![2]
@@ -56,4 +67,5 @@ Rectangle {
height: main.height
}
//![2]
+
}
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();
diff --git a/src/chartsqml2/chartsqml2.pro b/src/chartsqml2/chartsqml2.pro
index cc544d3a..4dd82950 100644
--- a/src/chartsqml2/chartsqml2.pro
+++ b/src/chartsqml2/chartsqml2.pro
@@ -33,7 +33,9 @@ SOURCES += \
declarativemargins.cpp \
declarativeaxes.cpp \
declarativepolarchart.cpp \
- declarativeboxplotseries.cpp
+ declarativeboxplotseries.cpp \
+ declarativechartnode.cpp \
+ declarativerendernode.cpp
HEADERS += \
declarativechart.h \
@@ -49,7 +51,9 @@ HEADERS += \
declarativemargins.h \
declarativeaxes.h \
declarativepolarchart.h \
- declarativeboxplotseries.h
+ declarativeboxplotseries.h \
+ declarativechartnode.h \
+ declarativerendernode.h
OTHER_FILES = qmldir
diff --git a/src/chartsqml2/declarativechart.cpp b/src/chartsqml2/declarativechart.cpp
index a8910f43..c028b3d8 100644
--- a/src/chartsqml2/declarativechart.cpp
+++ b/src/chartsqml2/declarativechart.cpp
@@ -25,6 +25,8 @@
#include "declarativesplineseries.h"
#include "declarativeboxplotseries.h"
#include "declarativescatterseries.h"
+#include "declarativechartnode.h"
+#include "declarativerendernode.h"
#include <QtCharts/QBarCategoryAxis>
#include <QtCharts/QValueAxis>
#include <QtCharts/QLogValueAxis>
@@ -34,6 +36,7 @@
#include <private/chartdataset_p.h>
#include "declarativeaxes.h"
#include <private/qchart_p.h>
+#include <private/chartpresenter_p.h>
#include <QtCharts/QPolarChart>
#ifndef QT_ON_ARM
@@ -88,6 +91,7 @@ QT_CHARTS_BEGIN_NAMESPACE
/*!
\qmlproperty easing ChartView::animationEasingCurve
The easing curve of the animation for the chart.
+*/
/*!
\qmlproperty Font ChartView::titleFont
@@ -312,34 +316,42 @@ QT_CHARTS_BEGIN_NAMESPACE
*/
DeclarativeChart::DeclarativeChart(QQuickItem *parent)
- : QQuickPaintedItem(parent)
+ : QQuickItem(parent)
{
initChart(QChart::ChartTypeCartesian);
}
DeclarativeChart::DeclarativeChart(QChart::ChartType type, QQuickItem *parent)
- : QQuickPaintedItem(parent)
+ : QQuickItem(parent)
{
initChart(type);
}
void DeclarativeChart::initChart(QChart::ChartType type)
{
- m_currentSceneImage = 0;
+ m_sceneImage = 0;
+ m_sceneImageDirty = false;
m_guiThreadId = QThread::currentThreadId();
m_paintThreadId = 0;
m_updatePending = false;
+ setFlag(ItemHasContents, true);
+
if (type == QChart::ChartTypePolar)
m_chart = new QPolarChart();
else
m_chart = new QChart();
+ m_chart->d_ptr->m_presenter->glSetUseWidget(false);
+ m_glXYDataManager = m_chart->d_ptr->m_dataset->glXYSeriesDataManager();
+
m_scene = new QGraphicsScene(this);
m_scene->addItem(m_chart);
setAntialiasing(QQuickItem::antialiasing());
- connect(m_scene, SIGNAL(changed(QList<QRectF>)), this, SLOT(sceneChanged(QList<QRectF>)));
+ connect(m_scene, &QGraphicsScene::changed, this, &DeclarativeChart::sceneChanged);
+ connect(this, &DeclarativeChart::needRender, this, &DeclarativeChart::renderScene,
+ Qt::QueuedConnection);
connect(this, SIGNAL(antialiasingChanged(bool)), this, SLOT(handleAntialiasingChanged(bool)));
setAcceptedMouseButtons(Qt::AllButtons);
@@ -377,10 +389,7 @@ void DeclarativeChart::changeMargins(int top, int bottom, int left, int right)
DeclarativeChart::~DeclarativeChart()
{
delete m_chart;
- m_sceneImageLock.lock();
- delete m_currentSceneImage;
- m_currentSceneImage = 0;
- m_sceneImageLock.unlock();
+ delete m_sceneImage;
}
void DeclarativeChart::childEvent(QChildEvent *event)
@@ -493,19 +502,79 @@ void DeclarativeChart::geometryChanged(const QRectF &newGeometry, const QRectF &
QQuickItem::geometryChanged(newGeometry, oldGeometry);
}
-void DeclarativeChart::sceneChanged(QList<QRectF> region)
+QSGNode *DeclarativeChart::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
{
- Q_UNUSED(region);
+ DeclarativeChartNode *node = static_cast<DeclarativeChartNode *>(oldNode);
- if (m_guiThreadId == m_paintThreadId) {
- // Rendering in gui thread, no need for shenannigans, just update
- update();
- } else {
- // Multi-threaded rendering, need to ensure scene is actually rendered in gui thread
- if (!m_updatePending) {
+ if (!node) {
+ node = new DeclarativeChartNode(window());
+ connect(window(), &QQuickWindow::beforeRendering,
+ node->glRenderNode(), &DeclarativeRenderNode::render);
+ }
+
+ const QRectF &bRect = boundingRect();
+
+ // Update GL data
+ if (m_glXYDataManager->dataMap().size() || m_glXYDataManager->mapDirty()) {
+ const QRectF &plotArea = m_chart->plotArea();
+ const QSizeF &chartAreaSize = m_chart->size();
+
+ // We can't use chart's plot area directly, as graphicscene has some internal minimum size
+ const qreal normalizedX = plotArea.x() / chartAreaSize.width();
+ const qreal normalizedY = plotArea.y() / chartAreaSize.height();
+ const qreal normalizedWidth = plotArea.width() / chartAreaSize.width();
+ const qreal normalizedHeight = plotArea.height() / chartAreaSize.height();
+
+ QRectF adjustedPlotArea(normalizedX * bRect.width(),
+ normalizedY * bRect.height(),
+ normalizedWidth * bRect.width(),
+ normalizedHeight * bRect.height());
+
+ const QSize &adjustedPlotSize = adjustedPlotArea.size().toSize();
+ if (adjustedPlotSize != node->glRenderNode()->textureSize())
+ node->glRenderNode()->setTextureSize(adjustedPlotSize);
+
+ node->glRenderNode()->setRect(adjustedPlotArea);
+ node->glRenderNode()->setSeriesData(m_glXYDataManager->mapDirty(),
+ m_glXYDataManager->dataMap());
+
+ // Clear dirty flags from original xy data
+ m_glXYDataManager->clearAllDirty();
+ }
+
+ // Copy chart (if dirty) to chart node
+ if (m_sceneImageDirty) {
+ node->createTextureFromImage(*m_sceneImage);
+ m_sceneImageDirty = false;
+ }
+
+ node->setRect(bRect);
+
+ return node;
+}
+
+void DeclarativeChart::sceneChanged(QList<QRectF> region)
+{
+ const int count = region.size();
+ const qreal limitSize = 0.01;
+ if (count && !m_updatePending) {
+ qreal totalSize = 0.0;
+ for (int i = 0; i < count; i++) {
+ const QRectF &reg = region.at(i);
+ totalSize += (reg.height() * reg.width());
+ if (totalSize >= limitSize)
+ break;
+ }
+ // Ignore region updates that change less than small fraction of a pixel, as there is
+ // little point regenerating the image in these cases. These are typically cases
+ // where OpenGL series are drawn to otherwise static chart.
+ if (totalSize >= limitSize) {
m_updatePending = true;
// Do async render to avoid some unnecessary renders.
- QTimer::singleShot(0, this, SLOT(renderScene()));
+ emit needRender();
+ } else {
+ // We do want to call update to trigger possible gl series updates.
+ update();
}
}
}
@@ -513,45 +582,22 @@ void DeclarativeChart::sceneChanged(QList<QRectF> region)
void DeclarativeChart::renderScene()
{
m_updatePending = false;
- m_sceneImageLock.lock();
- delete m_currentSceneImage;
- m_currentSceneImage = new QImage(m_chart->size().toSize(), QImage::Format_ARGB32);
- m_currentSceneImage->fill(Qt::transparent);
- QPainter painter(m_currentSceneImage);
- if (antialiasing())
- painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
- QRect renderRect(QPoint(0, 0), m_chart->size().toSize());
- m_scene->render(&painter, renderRect, renderRect);
- m_sceneImageLock.unlock();
-
- update();
-}
-
-void DeclarativeChart::paint(QPainter *painter)
-{
- if (!m_paintThreadId) {
- m_paintThreadId = QThread::currentThreadId();
- if (m_guiThreadId == m_paintThreadId) {
- // No need for scene image in single threaded rendering, so delete
- // the one that got made by default before the rendering type was
- // detected.
- delete m_currentSceneImage;
- m_currentSceneImage = 0;
- }
+ m_sceneImageDirty = true;
+ QSize chartSize = m_chart->size().toSize();
+ if (!m_sceneImage || chartSize != m_sceneImage->size()) {
+ delete m_sceneImage;
+ m_sceneImage = new QImage(chartSize, QImage::Format_ARGB32);
+ m_sceneImage->fill(Qt::transparent);
}
- if (m_guiThreadId == m_paintThreadId) {
- QRectF renderRect(QPointF(0, 0), m_chart->size());
- m_scene->render(painter, renderRect, renderRect);
- } else {
- m_sceneImageLock.lock();
- if (m_currentSceneImage) {
- QRect imageRect(QPoint(0, 0), m_currentSceneImage->size());
- QRect itemRect(QPoint(0, 0), QSize(width(), height()));
- painter->drawImage(itemRect, *m_currentSceneImage, imageRect);
- }
- m_sceneImageLock.unlock();
+ QPainter painter(m_sceneImage);
+ if (antialiasing()) {
+ painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing
+ | QPainter::SmoothPixmapTransform);
}
+ QRect renderRect(QPoint(0, 0), chartSize);
+ m_scene->render(&painter, renderRect, renderRect);
+ update();
}
void DeclarativeChart::mousePressEvent(QMouseEvent *event)
diff --git a/src/chartsqml2/declarativechart.h b/src/chartsqml2/declarativechart.h
index 80505d4d..c5d06b8d 100644
--- a/src/chartsqml2/declarativechart.h
+++ b/src/chartsqml2/declarativechart.h
@@ -19,11 +19,11 @@
#ifndef DECLARATIVECHART_H
#define DECLARATIVECHART_H
+#include <private/glxyseriesdata_p.h>
+
#include <QtCore/QtGlobal>
#include <QtQuick/QQuickItem>
-#include <QtQuick/QQuickPaintedItem>
#include <QtWidgets/QGraphicsScene>
-#include <QtCore/QMutex>
#include <QtCharts/QChart>
#include <QtCore/QLocale>
@@ -34,7 +34,7 @@ class DeclarativeMargins;
class Domain;
class DeclarativeAxes;
-class DeclarativeChart : public QQuickPaintedItem
+class DeclarativeChart : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(Theme theme READ theme WRITE setTheme)
@@ -102,7 +102,7 @@ public: // From parent classes
void childEvent(QChildEvent *event);
void componentComplete();
void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry);
- void paint(QPainter *painter);
+ QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *);
protected:
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
@@ -199,6 +199,7 @@ Q_SIGNALS:
Q_REVISION(4) void localeChanged();
Q_REVISION(5) void animationDurationChanged(int msecs);
Q_REVISION(5) void animationEasingCurveChanged(QEasingCurve curve);
+ void needRender();
private Q_SLOTS:
void changeMargins(int top, int bottom, int left, int right);
@@ -227,12 +228,13 @@ private:
QPoint m_lastMouseMoveScreenPoint;
Qt::MouseButton m_mousePressButton;
Qt::MouseButtons m_mousePressButtons;
- QMutex m_sceneImageLock;
- QImage *m_currentSceneImage;
+ QImage *m_sceneImage;
+ bool m_sceneImageDirty;
bool m_updatePending;
Qt::HANDLE m_paintThreadId;
Qt::HANDLE m_guiThreadId;
DeclarativeMargins *m_margins;
+ GLXYSeriesDataManager *m_glXYDataManager;
};
QT_CHARTS_END_NAMESPACE
diff --git a/src/chartsqml2/declarativechartnode.cpp b/src/chartsqml2/declarativechartnode.cpp
new file mode 100644
index 00000000..4e32344b
--- /dev/null
+++ b/src/chartsqml2/declarativechartnode.cpp
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** 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 "declarativechartnode.h"
+#include "declarativerendernode.h"
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QOpenGLFunctions>
+#include <QtGui/QOpenGLFramebufferObjectFormat>
+#include <QtGui/QOpenGLFramebufferObject>
+#include <QOpenGLShaderProgram>
+#include <QtGui/QOpenGLBuffer>
+
+QT_CHARTS_BEGIN_NAMESPACE
+
+// This node handles displaying of the chart itself
+DeclarativeChartNode::DeclarativeChartNode(QQuickWindow *window) :
+ QSGSimpleTextureNode(),
+ m_texture(0),
+ m_window(window),
+ m_textureOptions(0),
+ m_textureSize(1, 1),
+ m_glRenderNode(0)
+{
+ initializeOpenGLFunctions();
+
+ // Our texture node must have a texture, so use a default one pixel texture
+ GLuint defaultTexture = 0;
+ glGenTextures(1, &defaultTexture);
+ glBindTexture(GL_TEXTURE_2D, defaultTexture);
+ uchar buf[4] = { 0, 0, 0, 0 };
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &buf);
+
+ QQuickWindow::CreateTextureOptions defaultTextureOptions = QQuickWindow::CreateTextureOptions(
+ QQuickWindow::TextureHasAlphaChannel | QQuickWindow::TextureOwnsGLTexture);
+ m_texture = m_window->createTextureFromId(defaultTexture, QSize(1, 1), defaultTextureOptions);
+
+ setTexture(m_texture);
+ setFiltering(QSGTexture::Linear);
+
+ // Create child node for rendering GL graphics
+ m_glRenderNode = new DeclarativeRenderNode(m_window);
+ m_glRenderNode->setFlag(OwnedByParent);
+ appendChildNode(m_glRenderNode);
+ m_glRenderNode->setRect(0, 0, 0, 0); // Hide child node by default
+}
+
+DeclarativeChartNode::~DeclarativeChartNode()
+{
+ delete m_texture;
+}
+
+// Must be called on render thread and in context
+void DeclarativeChartNode::createTextureFromImage(const QImage &chartImage)
+{
+ if (chartImage.size() != m_textureSize)
+ m_textureSize = chartImage.size();
+
+ delete m_texture;
+ m_texture = m_window->createTextureFromImage(chartImage, m_textureOptions);
+ setTexture(m_texture);
+}
+
+QT_CHARTS_END_NAMESPACE
diff --git a/src/chartsqml2/declarativechartnode.h b/src/chartsqml2/declarativechartnode.h
new file mode 100644
index 00000000..46936c8c
--- /dev/null
+++ b/src/chartsqml2/declarativechartnode.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** 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 DECLARATIVECHARTNODE_P_H
+#define DECLARATIVECHARTNODE_P_H
+
+#include <QtCharts/QChartGlobal>
+#include <QtQuick/QSGSimpleTextureNode>
+#include <QtQuick/QQuickWindow>
+#include <QtGui/QOpenGLFunctions>
+
+QT_CHARTS_BEGIN_NAMESPACE
+
+class DeclarativeRenderNode;
+
+class DeclarativeChartNode : public QSGSimpleTextureNode, QOpenGLFunctions
+{
+public:
+ DeclarativeChartNode(QQuickWindow *window);
+ ~DeclarativeChartNode();
+
+ void createTextureFromImage(const QImage &chartImage);
+ DeclarativeRenderNode *glRenderNode() const { return m_glRenderNode; }
+
+private:
+ QSGTexture *m_texture;
+ QQuickWindow *m_window;
+ QQuickWindow::CreateTextureOptions m_textureOptions;
+ QSize m_textureSize;
+ DeclarativeRenderNode *m_glRenderNode;
+};
+
+QT_CHARTS_END_NAMESPACE
+
+#endif // DECLARATIVECHARTNODE_P_H
diff --git a/src/chartsqml2/declarativerendernode.cpp b/src/chartsqml2/declarativerendernode.cpp
new file mode 100644
index 00000000..b271dcc4
--- /dev/null
+++ b/src/chartsqml2/declarativerendernode.cpp
@@ -0,0 +1,317 @@
+/****************************************************************************
+**
+** 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 "declarativerendernode.h"
+
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QOpenGLFunctions>
+#include <QtGui/QOpenGLFramebufferObjectFormat>
+#include <QtGui/QOpenGLFramebufferObject>
+#include <QOpenGLShaderProgram>
+#include <QtGui/QOpenGLBuffer>
+
+//#define QDEBUG_TRACE_GL_FPS
+#ifdef QDEBUG_TRACE_GL_FPS
+# include <QElapsedTimer>
+#endif
+
+QT_CHARTS_BEGIN_NAMESPACE
+
+// This node draws the xy series data on a transparent background using OpenGL.
+// It is used as a child node of the chart node.
+DeclarativeRenderNode::DeclarativeRenderNode(QQuickWindow *window) :
+ QObject(),
+ QSGSimpleTextureNode(),
+ m_texture(0),
+ m_window(window),
+ m_textureOptions(QQuickWindow::TextureHasAlphaChannel),
+ m_textureSize(1, 1),
+ m_recreateFbo(false),
+ m_fbo(0),
+ m_program(0),
+ m_shaderAttribLoc(-1),
+ m_colorUniformLoc(-1),
+ m_minUniformLoc(-1),
+ m_deltaUniformLoc(-1),
+ m_pointSizeUniformLoc(-1),
+ m_renderNeeded(true)
+{
+ initializeOpenGLFunctions();
+
+ // Our texture node must have a texture, so use a default one pixel texture
+ GLuint defaultTexture = 0;
+ glGenTextures(1, &defaultTexture);
+ glBindTexture(GL_TEXTURE_2D, defaultTexture);
+ uchar buf[4] = { 0, 0, 0, 0 };
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &buf);
+
+ QQuickWindow::CreateTextureOptions defaultTextureOptions = QQuickWindow::CreateTextureOptions(
+ QQuickWindow::TextureHasAlphaChannel | QQuickWindow::TextureOwnsGLTexture);
+ m_texture = m_window->createTextureFromId(defaultTexture, QSize(1, 1), defaultTextureOptions);
+
+ setTexture(m_texture);
+ setFiltering(QSGTexture::Linear);
+ setTextureCoordinatesTransform(QSGSimpleTextureNode::MirrorVertically);
+}
+
+DeclarativeRenderNode::~DeclarativeRenderNode()
+{
+ delete m_texture;
+ delete m_fbo;
+
+ delete m_program;
+ m_program = 0;
+
+ cleanXYSeriesResources(0);
+}
+
+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";
+
+// Must be called on render thread and in context
+void DeclarativeRenderNode::initGL()
+{
+ recreateFBO();
+
+ 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);
+
+#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.
+ // Qt Quick doesn't change these flags, so it should be safe to just enable them
+ // at initialization.
+ glEnable(GL_PROGRAM_POINT_SIZE);
+ }
+#endif
+
+ m_program->release();
+}
+
+void DeclarativeRenderNode::recreateFBO()
+{
+ QOpenGLFramebufferObjectFormat fboFormat;
+ fboFormat.setAttachment(QOpenGLFramebufferObject::NoAttachment);
+ delete m_fbo;
+ m_fbo = new QOpenGLFramebufferObject(m_textureSize.width(),
+ m_textureSize.height(),
+ fboFormat);
+
+ delete m_texture;
+ m_texture = m_window->createTextureFromId(m_fbo->texture(), m_textureSize, m_textureOptions);
+ setTexture(m_texture);
+
+ m_recreateFbo = false;
+}
+
+// Must be called on render thread and in context
+void DeclarativeRenderNode::setTextureSize(const QSize &size)
+{
+ m_textureSize = size;
+ m_recreateFbo = true;
+ m_renderNeeded = true;
+}
+
+// Must be called on render thread while gui thread is blocked, and in context
+void DeclarativeRenderNode::setSeriesData(bool mapDirty, const GLXYDataMap &dataMap)
+{
+ if (mapDirty) {
+ // Series have changed, recreate map, but utilize old data where feasible
+ GLXYDataMap oldMap = m_xyDataMap;
+ m_xyDataMap.clear();
+
+ GLXYDataMapIterator i(dataMap);
+ while (i.hasNext()) {
+ i.next();
+ GLXYSeriesData *data = oldMap.take(i.key());
+ const GLXYSeriesData *newData = i.value();
+ if (!data || newData->dirty) {
+ data = new GLXYSeriesData;
+ data->array = newData->array;
+ data->color = newData->color;
+ data->dirty = newData->dirty;
+ data->width = newData->width;
+ data->type = newData->type;
+ data->min = newData->min;
+ data->delta = newData->delta;
+ }
+ m_xyDataMap.insert(i.key(), data);
+ }
+ // Delete remaining old data
+ i = oldMap;
+ while (i.hasNext()) {
+ i.next();
+ delete i.value();
+ cleanXYSeriesResources(i.key());
+ }
+ } else {
+ // Series have not changed, so just copy dirty data over
+ GLXYDataMapIterator i(dataMap);
+ while (i.hasNext()) {
+ i.next();
+ const GLXYSeriesData *newData = i.value();
+ if (i.value()->dirty) {
+ GLXYSeriesData *data = m_xyDataMap.value(i.key());
+ if (data) {
+ data->array = newData->array;
+ data->color = newData->color;
+ data->dirty = newData->dirty;
+ data->width = newData->width;
+ data->type = newData->type;
+ data->min = newData->min;
+ data->delta = newData->delta;
+ }
+ }
+ }
+ }
+ markDirty(DirtyMaterial);
+ m_renderNeeded = true;
+}
+
+void DeclarativeRenderNode::renderGL()
+{
+ glClearColor(0, 0, 0, 0);
+
+ QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
+ m_program->bind();
+ m_fbo->bind();
+
+ glClear(GL_COLOR_BUFFER_BIT);
+ glEnableVertexAttribArray(0);
+
+ glViewport(0, 0, m_textureSize.width(), m_textureSize.height());
+
+ GLXYDataMapIterator i(m_xyDataMap);
+ 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
+
+ markDirty(DirtyMaterial);
+ m_window->resetOpenGLState();
+}
+
+// Must be called on render thread as response to beforeRendering signal
+void DeclarativeRenderNode::render()
+{
+ if (m_renderNeeded) {
+ if (m_xyDataMap.size()) {
+ if (!m_program)
+ initGL();
+ if (m_recreateFbo)
+ recreateFBO();
+ renderGL();
+ } else {
+ if (rect() != QRectF()) {
+ glClearColor(0, 0, 0, 0);
+ m_fbo->bind();
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ // If last series was removed, zero out the node rect
+ setRect(QRectF());
+ }
+ }
+ m_renderNeeded = false;
+ }
+}
+
+void DeclarativeRenderNode::cleanXYSeriesResources(const QXYSeries *series)
+{
+ if (series) {
+ delete m_seriesBufferMap.take(series);
+ } else {
+ foreach (QOpenGLBuffer *buffer, m_seriesBufferMap.values())
+ delete buffer;
+ m_seriesBufferMap.clear();
+ }
+}
+
+QT_CHARTS_END_NAMESPACE
diff --git a/src/chartsqml2/declarativerendernode.h b/src/chartsqml2/declarativerendernode.h
new file mode 100644
index 00000000..f00f5c44
--- /dev/null
+++ b/src/chartsqml2/declarativerendernode.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** 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 DECLARATIVERENDERNODE_P_H
+#define DECLARATIVERENDERNODE_P_H
+
+#include <QtCharts/QChartGlobal>
+#include <private/glxyseriesdata_p.h>
+#include <QtQuick/QSGSimpleTextureNode>
+#include <QtQuick/QQuickWindow>
+#include <QtGui/QOpenGLVertexArrayObject>
+#include <QtGui/QOpenGLFunctions>
+
+class QOpenGLFramebufferObject;
+class QOpenGLBuffer;
+
+QT_CHARTS_BEGIN_NAMESPACE
+
+class DeclarativeRenderNode : public QObject, public QSGSimpleTextureNode, QOpenGLFunctions
+{
+ Q_OBJECT
+public:
+ DeclarativeRenderNode(QQuickWindow *window);
+ ~DeclarativeRenderNode();
+
+ void initGL();
+ QSize textureSize() const { return m_textureSize; }
+ void setTextureSize(const QSize &size);
+ void setSeriesData(bool mapDirty, const GLXYDataMap &dataMap);
+
+public Q_SLOTS:
+ void render();
+
+private:
+ void renderGL();
+ void recreateFBO();
+ void cleanXYSeriesResources(const QXYSeries *series);
+
+ QSGTexture *m_texture;
+ QQuickWindow *m_window;
+ QQuickWindow::CreateTextureOptions m_textureOptions;
+ QSize m_textureSize;
+ bool m_recreateFbo;
+ GLXYDataMap m_xyDataMap;
+ QOpenGLFramebufferObject *m_fbo;
+ 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;
+ bool m_renderNeeded;
+};
+
+QT_CHARTS_END_NAMESPACE
+
+#endif // DECLARATIVERENDERNODE_P_H
diff --git a/tests/auto/qlineseries/tst_qlineseries.cpp b/tests/auto/qlineseries/tst_qlineseries.cpp
index e1df6c78..1b7a57b6 100644
--- a/tests/auto/qlineseries/tst_qlineseries.cpp
+++ b/tests/auto/qlineseries/tst_qlineseries.cpp
@@ -19,8 +19,8 @@
#include "../qxyseries/tst_qxyseries.h"
#include <QtCharts/QLineSeries>
-
Q_DECLARE_METATYPE(QList<QPointF>)
+Q_DECLARE_METATYPE(QVector<QPointF>)
class tst_QLineSeries : public tst_QXYSeries
{
@@ -76,6 +76,7 @@ void tst_QLineSeries::qlineseries()
QCOMPARE(series.count(),0);
QCOMPARE(series.brush(), QBrush());
QCOMPARE(series.points(), QList<QPointF>());
+ QCOMPARE(series.pointsVector(), QVector<QPointF>());
QCOMPARE(series.pen(), QPen());
QCOMPARE(series.pointsVisible(), false);
QCOMPARE(series.pointLabelsVisible(), false);
diff --git a/tests/auto/qscatterseries/tst_qscatterseries.cpp b/tests/auto/qscatterseries/tst_qscatterseries.cpp
index 588a6549..eedb4c3e 100644
--- a/tests/auto/qscatterseries/tst_qscatterseries.cpp
+++ b/tests/auto/qscatterseries/tst_qscatterseries.cpp
@@ -20,6 +20,7 @@
#include <QtCharts/QScatterSeries>
Q_DECLARE_METATYPE(QList<QPointF>)
+Q_DECLARE_METATYPE(QVector<QPointF>)
class tst_QScatterSeries : public tst_QXYSeries
{
@@ -75,6 +76,7 @@ void tst_QScatterSeries::qscatterseries()
QCOMPARE(series.count(),0);
QCOMPARE(series.brush(), QBrush());
QCOMPARE(series.points(), QList<QPointF>());
+ QCOMPARE(series.pointsVector(), QVector<QPointF>());
QCOMPARE(series.pen(), QPen());
QCOMPARE(series.pointsVisible(), false);
diff --git a/tests/auto/qsplineseries/tst_qsplineseries.cpp b/tests/auto/qsplineseries/tst_qsplineseries.cpp
index 53ddf011..4f023088 100644
--- a/tests/auto/qsplineseries/tst_qsplineseries.cpp
+++ b/tests/auto/qsplineseries/tst_qsplineseries.cpp
@@ -20,6 +20,7 @@
#include <QtCharts/QSplineSeries>
Q_DECLARE_METATYPE(QList<QPointF>)
+Q_DECLARE_METATYPE(QVector<QPointF>)
class tst_QSplineSeries : public tst_QXYSeries
{
@@ -73,6 +74,7 @@ void tst_QSplineSeries::qsplineseries()
QCOMPARE(series.count(),0);
QCOMPARE(series.brush(), QBrush());
QCOMPARE(series.points(), QList<QPointF>());
+ QCOMPARE(series.pointsVector(), QVector<QPointF>());
QCOMPARE(series.pen(), QPen());
QCOMPARE(series.pointsVisible(), false);
diff --git a/tests/auto/qxyseries/tst_qxyseries.cpp b/tests/auto/qxyseries/tst_qxyseries.cpp
index bb827412..762c7a09 100644
--- a/tests/auto/qxyseries/tst_qxyseries.cpp
+++ b/tests/auto/qxyseries/tst_qxyseries.cpp
@@ -198,6 +198,7 @@ void tst_QXYSeries::append_raw()
TRY_COMPARE(spy0.count(), 0);
TRY_COMPARE(addedSpy.count(), points.count());
QCOMPARE(m_series->points(), points);
+ QCOMPARE(m_series->pointsVector(), points.toVector());
// Process events between appends
foreach (const QPointF &point, otherPoints) {