##// END OF EJS Templates
Accelerating lineseries with OpenGL...
Miikka Heikkinen -
r2820:79a856530b69
parent child
Show More
@@ -0,0 +1,118
1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd
4 ** All rights reserved.
5 ** For any questions to The Qt Company, please use contact form at http://qt.io
6 **
7 ** This file is part of the Qt Charts module.
8 **
9 ** Licensees holding valid commercial license for Qt may use this file in
10 ** accordance with the Qt License Agreement provided with the Software
11 ** or, alternatively, in accordance with the terms contained in a written
12 ** agreement between you and The Qt Company.
13 **
14 ** If you have questions regarding the use of this file, please use
15 ** contact form at http://qt.io
16 **
17 ****************************************************************************/
18
19 #include "datasource.h"
20 #include <QtCore/QtMath>
21
22 QT_CHARTS_USE_NAMESPACE
23
24 DataSource::DataSource(QObject *parent) :
25 QObject(parent),
26 m_index(-1)
27 {
28 generateData(0, 0, 0);
29 }
30
31 void DataSource::update(QAbstractSeries *series, int seriesIndex)
32 {
33 if (series) {
34 QXYSeries *xySeries = static_cast<QXYSeries *>(series);
35 const QVector<QVector<QPointF> > &seriesData = m_data.at(seriesIndex);
36 if (seriesIndex == 0)
37 m_index++;
38 if (m_index > seriesData.count() - 1)
39 m_index = 0;
40
41 QVector<QPointF> points = seriesData.at(m_index);
42 // Use replace instead of clear + append, it's optimized for performance
43 xySeries->replace(points);
44 }
45 }
46
47 void DataSource::handleSceneChanged()
48 {
49 m_dataUpdater.start();
50 }
51
52 void DataSource::updateAllSeries()
53 {
54 static int frameCount = 0;
55 static QString labelText = QStringLiteral("FPS: %1");
56
57 for (int i = 0; i < m_seriesList.size(); i++)
58 update(m_seriesList[i], i);
59
60 frameCount++;
61 int elapsed = m_fpsTimer.elapsed();
62 if (elapsed >= 1000) {
63 elapsed = m_fpsTimer.restart();
64 qreal fps = qreal(0.1 * int(10000.0 * (qreal(frameCount) / qreal(elapsed))));
65 m_fpsLabel->setText(labelText.arg(QString::number(fps, 'f', 1)));
66 frameCount = 0;
67 }
68 }
69
70 void DataSource::startUpdates(const QList<QXYSeries *> &seriesList, QLabel *fpsLabel)
71 {
72 m_seriesList = seriesList;
73 m_fpsLabel = fpsLabel;
74
75 m_dataUpdater.setInterval(0);
76 m_dataUpdater.setSingleShot(true);
77 QObject::connect(&m_dataUpdater, &QTimer::timeout,
78 this, &DataSource::updateAllSeries);
79
80 m_fpsTimer.start();
81 updateAllSeries();
82 }
83
84 void DataSource::generateData(int seriesCount, int rowCount, int colCount)
85 {
86 // Remove previous data
87 foreach (QVector<QVector<QPointF> > seriesData, m_data) {
88 foreach (QVector<QPointF> row, seriesData)
89 row.clear();
90 }
91
92 m_data.clear();
93
94 qreal xAdjustment = 20.0 / (colCount * rowCount);
95 qreal yMultiplier = 3.0 / qreal(seriesCount);
96
97 // Append the new data depending on the type
98 for (int k(0); k < seriesCount; k++) {
99 QVector<QVector<QPointF> > seriesData;
100 qreal height = qreal(k) * (10.0 / qreal(seriesCount)) + 0.3;
101 for (int i(0); i < rowCount; i++) {
102 QVector<QPointF> points;
103 points.reserve(colCount);
104 for (int j(0); j < colCount; j++) {
105 qreal x(0);
106 qreal y(0);
107 // data with sin + random component
108 y = height + (yMultiplier * qSin(3.14159265358979 / 50 * j)
109 + (yMultiplier * (qreal) rand() / (qreal) RAND_MAX));
110 // 0.000001 added to make values logaxis compatible
111 x = 0.000001 + 20.0 * (qreal(j) / qreal(colCount)) + (xAdjustment * qreal(i));
112 points.append(QPointF(x, y));
113 }
114 seriesData.append(points);
115 }
116 m_data.append(seriesData);
117 }
118 }
@@ -0,0 +1,53
1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd
4 ** All rights reserved.
5 ** For any questions to The Qt Company, please use contact form at http://qt.io
6 **
7 ** This file is part of the Qt Charts module.
8 **
9 ** Licensees holding valid commercial license for Qt may use this file in
10 ** accordance with the Qt License Agreement provided with the Software
11 ** or, alternatively, in accordance with the terms contained in a written
12 ** agreement between you and The Qt Company.
13 **
14 ** If you have questions regarding the use of this file, please use
15 ** contact form at http://qt.io
16 **
17 ****************************************************************************/
18
19 #ifndef DATASOURCE_H
20 #define DATASOURCE_H
21
22 #include <QtCore/QObject>
23 #include <QtCharts/QXYSeries>
24 #include <QtWidgets/QLabel>
25 #include <QtCore/QElapsedTimer>
26 #include <QtCore/QTimer>
27
28 QT_CHARTS_USE_NAMESPACE
29
30 class DataSource : public QObject
31 {
32 Q_OBJECT
33 public:
34 explicit DataSource(QObject *parent = 0);
35
36 void startUpdates(const QList<QXYSeries *> &seriesList, QLabel *fpsLabel);
37
38 public slots:
39 void generateData(int seriesCount, int rowCount, int colCount);
40 void update(QAbstractSeries *series, int seriesIndex);
41 void handleSceneChanged();
42 void updateAllSeries();
43
44 private:
45 QVector<QVector<QVector<QPointF> > > m_data;
46 int m_index;
47 QList<QXYSeries *> m_seriesList;
48 QLabel *m_fpsLabel;
49 QElapsedTimer m_fpsTimer;
50 QTimer m_dataUpdater;
51 };
52
53 #endif // DATASOURCE_H
@@ -0,0 +1,167
1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd
4 ** All rights reserved.
5 ** For any questions to The Qt Company, please use contact form at http://qt.io
6 **
7 ** This file is part of the Qt Charts module.
8 **
9 ** Licensees holding valid commercial license for Qt may use this file in
10 ** accordance with the Qt License Agreement provided with the Software
11 ** or, alternatively, in accordance with the terms contained in a written
12 ** agreement between you and The Qt Company.
13 **
14 ** If you have questions regarding the use of this file, please use
15 ** contact form at http://qt.io
16 **
17 ****************************************************************************/
18
19 #include "datasource.h"
20 #include <QtWidgets/QApplication>
21 #include <QtWidgets/QMainWindow>
22 #include <QtCharts/QChartView>
23 #include <QtCharts/QLineSeries>
24 #include <QtCharts/QScatterSeries>
25 #include <QtCharts/QValueAxis>
26 #include <QtCharts/QLogValueAxis>
27 #include <QtWidgets/QLabel>
28
29 // Uncomment to use logarithmic axes instead of regular value axes
30 //#define USE_LOG_AXIS
31
32 // Uncomment to use regular series instead of OpenGL accelerated series
33 //#define DONT_USE_GL_SERIES
34
35 // Uncomment to add a simple regular series (thick line) and a matching OpenGL series (thinner line)
36 // to verify the series have same visible geometry.
37 //#define ADD_SIMPLE_SERIES
38
39 QT_CHARTS_USE_NAMESPACE
40
41 int main(int argc, char *argv[])
42 {
43 QApplication a(argc, argv);
44 QStringList colors;
45 colors << "red" << "blue" << "green" << "black";
46
47 QChart *chart = new QChart();
48 chart->legend()->hide();
49
50 #ifdef USE_LOG_AXIS
51 QLogValueAxis *axisX = new QLogValueAxis;
52 QLogValueAxis *axisY = new QLogValueAxis;
53 #else
54 QValueAxis *axisX = new QValueAxis;
55 QValueAxis *axisY = new QValueAxis;
56 #endif
57
58 chart->addAxis(axisX, Qt::AlignBottom);
59 chart->addAxis(axisY, Qt::AlignLeft);
60
61 const int seriesCount = 10;
62 #ifdef DONT_USE_GL_SERIES
63 const int pointCount = 100;
64 chart->setTitle("Unaccelerated Series");
65 #else
66 const int pointCount = 10000;
67 chart->setTitle("OpenGL Accelerated Series");
68 #endif
69
70 QList<QXYSeries *> seriesList;
71 for (int i = 0; i < seriesCount; i++) {
72 QXYSeries *series = 0;
73 int colorIndex = i % colors.size();
74 if (i % 2) {
75 series = new QScatterSeries;
76 QScatterSeries *scatter = static_cast<QScatterSeries *>(series);
77 scatter->setColor(QColor(colors.at(colorIndex)));
78 scatter->setMarkerSize(qreal(colorIndex + 2) / 2.0);
79 // Scatter pen doesn't have affect in OpenGL drawing, but if you disable OpenGL drawing
80 // this makes the marker border visible and gives comparable marker size to OpenGL
81 // scatter points.
82 scatter->setPen(QPen("black"));
83 } else {
84 series = new QLineSeries;
85 series->setPen(QPen(QBrush(QColor(colors.at(colorIndex))),
86 qreal(colorIndex + 2) / 2.0));
87 }
88 seriesList.append(series);
89 #ifdef DONT_USE_GL_SERIES
90 series->setUseOpenGL(false);
91 #else
92 //![1]
93 series->setUseOpenGL(true);
94 //![1]
95 #endif
96 chart->addSeries(series);
97 series->attachAxis(axisX);
98 series->attachAxis(axisY);
99 }
100
101 if (axisX->type() == QAbstractAxis::AxisTypeLogValue)
102 axisX->setRange(0.1, 20.0);
103 else
104 axisX->setRange(0, 20.0);
105
106 if (axisY->type() == QAbstractAxis::AxisTypeLogValue)
107 axisY->setRange(0.1, 10.0);
108 else
109 axisY->setRange(0, 10.0);
110
111 #ifdef ADD_SIMPLE_SERIES
112 QLineSeries *simpleRasterSeries = new QLineSeries;
113 *simpleRasterSeries << QPointF(0.001, 0.001)
114 << QPointF(2.5, 8.0)
115 << QPointF(5.0, 4.0)
116 << QPointF(7.5, 9.0)
117 << QPointF(10.0, 0.001)
118 << QPointF(12.5, 2.0)
119 << QPointF(15.0, 1.0)
120 << QPointF(17.5, 6.0)
121 << QPointF(20.0, 10.0);
122 simpleRasterSeries->setUseOpenGL(false);
123 simpleRasterSeries->setPen(QPen(QBrush("magenta"), 8));
124 chart->addSeries(simpleRasterSeries);
125 simpleRasterSeries->attachAxis(axisX);
126 simpleRasterSeries->attachAxis(axisY);
127
128 QLineSeries *simpleGLSeries = new QLineSeries;
129 simpleGLSeries->setUseOpenGL(true);
130 simpleGLSeries->setPen(QPen(QBrush("black"), 2));
131 simpleGLSeries->replace(simpleRasterSeries->points());
132 chart->addSeries(simpleGLSeries);
133 simpleGLSeries->attachAxis(axisX);
134 simpleGLSeries->attachAxis(axisY);
135 #endif
136
137 QChartView *chartView = new QChartView(chart);
138
139 QMainWindow window;
140 window.setCentralWidget(chartView);
141 window.resize(600, 400);
142 window.show();
143
144 DataSource dataSource;
145 dataSource.generateData(seriesCount, 10, pointCount);
146
147 QLabel *fpsLabel = new QLabel(&window);
148 QLabel *countLabel = new QLabel(&window);
149 QString countText = QStringLiteral("Total point count: %1");
150 countLabel->setText(countText.arg(pointCount * seriesCount));
151 countLabel->resize(window.width(), countLabel->height());
152 fpsLabel->move(10,2);
153 fpsLabel->raise();
154 fpsLabel->show();
155 countLabel->move(10, 14);
156 fpsLabel->raise();
157 countLabel->show();
158
159 // We can get more than one changed event per frame, so do async update.
160 // This also allows the application to be responsive.
161 QObject::connect(chart->scene(), &QGraphicsScene::changed,
162 &dataSource, &DataSource::handleSceneChanged);
163
164 dataSource.startUpdates(seriesList, fpsLabel);
165
166 return a.exec();
167 }
@@ -0,0 +1,9
1 !include( ../examples.pri ) {
2 error( "Couldn't find the examples.pri file!" )
3 }
4
5 TARGET = openglseries
6 SOURCES += main.cpp \
7 datasource.cpp
8 HEADERS += datasource.h
9
1 NO CONTENT: new file 100644, binary diff hidden
@@ -0,0 +1,37
1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd
4 ** All rights reserved.
5 ** For any questions to The Qt Company, please use contact form at http://qt.io
6 **
7 ** This file is part of the Qt Charts module.
8 **
9 ** Licensees holding valid commercial license for Qt may use this file in
10 ** accordance with the Qt License Agreement provided with the Software
11 ** or, alternatively, in accordance with the terms contained in a written
12 ** agreement between you and The Qt Company.
13 **
14 ** If you have questions regarding the use of this file, please use
15 ** contact form at http://qt.io
16 **
17 ****************************************************************************/
18
19 /*!
20 \example openglseries
21 \title OpenGL Accelerated Series Example
22 \ingroup qtcharts_examples
23
24 \brief The example shows how to enable OpenGL acceleration for QLineSeries and QScatterSeries.
25
26 \image examples_openglseries.png
27
28 To create an OpenGL accelerated series, all you have to do compared to a regular
29 series is to set QAbstractSeries::useOpenGL property to \c{true}:
30
31 \snippet openglseries/main.cpp 1
32
33 This makes the chart to instantiate a transparent QOpenGLWidget that is used to draw
34 the accelerated series on top of the chart.
35
36 \note The OpenGL acceleration is only supported for QLineSeries and QScatterSeries.
37 */
@@ -0,0 +1,222
1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd
4 ** All rights reserved.
5 ** For any questions to The Qt Company, please use contact form at http://qt.io
6 **
7 ** This file is part of the Qt Charts module.
8 **
9 ** Licensees holding valid commercial license for Qt may use this file in
10 ** accordance with the Qt License Agreement provided with the Software
11 ** or, alternatively, in accordance with the terms contained in a written
12 ** agreement between you and The Qt Company.
13 **
14 ** If you have questions regarding the use of this file, please use
15 ** contact form at http://qt.io
16 **
17 ****************************************************************************/
18
19 #ifndef QT_NO_OPENGL
20
21 #include "private/glwidget_p.h"
22 #include "private/glxyseriesdata_p.h"
23 #include <QtGui/QOpenGLShaderProgram>
24 #include <QtGui/QOpenGLContext>
25 #include <QtGui/QOpenGLBuffer>
26
27 //#define QDEBUG_TRACE_GL_FPS
28 #ifdef QDEBUG_TRACE_GL_FPS
29 # include <QElapsedTimer>
30 #endif
31
32 QT_CHARTS_BEGIN_NAMESPACE
33
34 GLWidget::GLWidget(GLXYSeriesDataManager *xyDataManager, QWidget *parent)
35 : QOpenGLWidget(parent),
36 m_program(0),
37 m_shaderAttribLoc(-1),
38 m_colorUniformLoc(-1),
39 m_minUniformLoc(-1),
40 m_deltaUniformLoc(-1),
41 m_pointSizeUniformLoc(-1),
42 m_xyDataManager(xyDataManager)
43 {
44 setAttribute(Qt::WA_TranslucentBackground);
45 setAttribute(Qt::WA_AlwaysStackOnTop);
46 setAttribute(Qt::WA_TransparentForMouseEvents);
47
48 QSurfaceFormat surfaceFormat;
49 surfaceFormat.setDepthBufferSize(0);
50 surfaceFormat.setStencilBufferSize(0);
51 surfaceFormat.setRedBufferSize(8);
52 surfaceFormat.setGreenBufferSize(8);
53 surfaceFormat.setBlueBufferSize(8);
54 surfaceFormat.setAlphaBufferSize(8);
55 surfaceFormat.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
56 surfaceFormat.setRenderableType(QSurfaceFormat::DefaultRenderableType);
57 setFormat(surfaceFormat);
58
59 connect(xyDataManager, &GLXYSeriesDataManager::seriesRemoved,
60 this, &GLWidget::cleanXYSeriesResources);
61 }
62
63 GLWidget::~GLWidget()
64 {
65 cleanup();
66 }
67
68 void GLWidget::cleanup()
69 {
70 makeCurrent();
71
72 delete m_program;
73 m_program = 0;
74
75 foreach (QOpenGLBuffer *buffer, m_seriesBufferMap.values())
76 delete buffer;
77 m_seriesBufferMap.clear();
78
79 doneCurrent();
80 }
81
82 void GLWidget::cleanXYSeriesResources(const QXYSeries *series)
83 {
84 makeCurrent();
85 if (series) {
86 delete m_seriesBufferMap.take(series);
87 } else {
88 // Null series means all series were removed
89 foreach (QOpenGLBuffer *buffer, m_seriesBufferMap.values())
90 delete buffer;
91 m_seriesBufferMap.clear();
92 }
93 doneCurrent();
94 }
95
96 static const char *vertexSource =
97 "attribute highp vec2 points;\n"
98 "uniform highp vec2 min;\n"
99 "uniform highp vec2 delta;\n"
100 "uniform highp float pointSize;\n"
101 "void main() {\n"
102 " vec2 normalPoint = vec2(-1, -1) + ((points - min) / delta);\n"
103 " gl_Position = vec4(normalPoint, 0, 1);\n"
104 " gl_PointSize = pointSize;\n"
105 "}";
106 static const char *fragmentSource =
107 "uniform highp vec3 color;\n"
108 "void main() {\n"
109 " gl_FragColor = vec4(color,1);\n"
110 "}\n";
111
112 void GLWidget::initializeGL()
113 {
114 connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &GLWidget::cleanup);
115
116 initializeOpenGLFunctions();
117 glClearColor(0, 0, 0, 0);
118
119 m_program = new QOpenGLShaderProgram;
120 m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexSource);
121 m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentSource);
122 m_program->bindAttributeLocation("points", 0);
123 m_program->link();
124
125 m_program->bind();
126 m_colorUniformLoc = m_program->uniformLocation("color");
127 m_minUniformLoc = m_program->uniformLocation("min");
128 m_deltaUniformLoc = m_program->uniformLocation("delta");
129 m_pointSizeUniformLoc = m_program->uniformLocation("pointSize");
130
131
132 // Create a vertex array object. In OpenGL ES 2.0 and OpenGL 2.x
133 // implementations this is optional and support may not be present
134 // at all. Nonetheless the below code works in all cases and makes
135 // sure there is a VAO when one is needed.
136 m_vao.create();
137 QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
138
139 glEnableVertexAttribArray(0);
140
141 glDisable(GL_DEPTH_TEST);
142 glDisable(GL_STENCIL_TEST);
143
144 #if !defined(QT_OPENGL_ES_2)
145 if (!QOpenGLContext::currentContext()->isOpenGLES()) {
146 // Make it possible to change point primitive size and use textures with them in
147 // the shaders. These are implicitly enabled in ES2.
148 glEnable(GL_PROGRAM_POINT_SIZE);
149 }
150 #endif
151
152 m_program->release();
153 }
154
155 void GLWidget::paintGL()
156 {
157 glClear(GL_COLOR_BUFFER_BIT);
158
159 QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
160 m_program->bind();
161
162 GLXYDataMapIterator i(m_xyDataManager->dataMap());
163 while (i.hasNext()) {
164 i.next();
165 QOpenGLBuffer *vbo = m_seriesBufferMap.value(i.key());
166 GLXYSeriesData *data = i.value();
167
168 m_program->setUniformValue(m_colorUniformLoc, data->color);
169 m_program->setUniformValue(m_minUniformLoc, data->min);
170 m_program->setUniformValue(m_deltaUniformLoc, data->delta);
171
172 if (!vbo) {
173 vbo = new QOpenGLBuffer;
174 m_seriesBufferMap.insert(i.key(), vbo);
175 vbo->create();
176 }
177 vbo->bind();
178 if (data->dirty) {
179 vbo->allocate(data->array.constData(), data->array.count() * sizeof(GLfloat));
180 data->dirty = false;
181 }
182
183 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
184 if (data->type == QAbstractSeries::SeriesTypeLine) {
185 glLineWidth(data->width);
186 glDrawArrays(GL_LINE_STRIP, 0, data->array.size() / 2);
187 } else { // Scatter
188 m_program->setUniformValue(m_pointSizeUniformLoc, data->width);
189 glDrawArrays(GL_POINTS, 0, data->array.size() / 2);
190 }
191 vbo->release();
192 }
193
194 #ifdef QDEBUG_TRACE_GL_FPS
195 static QElapsedTimer stopWatch;
196 static int frameCount = -1;
197 if (frameCount == -1) {
198 stopWatch.start();
199 frameCount = 0;
200 }
201 frameCount++;
202 int elapsed = stopWatch.elapsed();
203 if (elapsed >= 1000) {
204 elapsed = stopWatch.restart();
205 qreal fps = qreal(0.1 * int(10000.0 * (qreal(frameCount) / qreal(elapsed))));
206 qDebug() << "FPS:" << fps;
207 frameCount = 0;
208 }
209 #endif
210
211 m_program->release();
212 }
213
214 void GLWidget::resizeGL(int w, int h)
215 {
216 Q_UNUSED(w)
217 Q_UNUSED(h)
218 }
219
220 QT_CHARTS_END_NAMESPACE
221
222 #endif
@@ -0,0 +1,80
1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd
4 ** All rights reserved.
5 ** For any questions to The Qt Company, please use contact form at http://qt.io
6 **
7 ** This file is part of the Qt Charts module.
8 **
9 ** Licensees holding valid commercial license for Qt may use this file in
10 ** accordance with the Qt License Agreement provided with the Software
11 ** or, alternatively, in accordance with the terms contained in a written
12 ** agreement between you and The Qt Company.
13 **
14 ** If you have questions regarding the use of this file, please use
15 ** contact form at http://qt.io
16 **
17 ****************************************************************************/
18
19 // W A R N I N G
20 // -------------
21 //
22 // This file is not part of the Qt Chart API. It exists purely as an
23 // implementation detail. This header file may change from version to
24 // version without notice, or even be removed.
25 //
26 // We mean it.
27
28 #ifndef GLWIDGET_H
29 #define GLWIDGET_H
30
31 #ifndef QT_NO_OPENGL
32
33 #include <QtWidgets/QOpenGLWidget>
34 #include <QtGui/QOpenGLFunctions>
35 #include <QtGui/QOpenGLVertexArrayObject>
36 #include <QtCore/QHash>
37 #include <QtCharts/QAbstractSeries>
38 #include <QtCharts/QXYSeries>
39
40 QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram)
41
42 class QOpenGLBuffer;
43
44 QT_CHARTS_BEGIN_NAMESPACE
45
46 class GLXYSeriesDataManager;
47
48 class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions
49 {
50 Q_OBJECT
51
52 public:
53 GLWidget(GLXYSeriesDataManager *xyDataManager, QWidget *parent = 0);
54 ~GLWidget();
55
56 public Q_SLOTS:
57 void cleanup();
58 void cleanXYSeriesResources(const QXYSeries *series);
59
60 protected:
61 void initializeGL() Q_DECL_OVERRIDE;
62 void paintGL() Q_DECL_OVERRIDE;
63 void resizeGL(int width, int height) Q_DECL_OVERRIDE;
64
65 private:
66 QOpenGLShaderProgram *m_program;
67 int m_shaderAttribLoc;
68 int m_colorUniformLoc;
69 int m_minUniformLoc;
70 int m_deltaUniformLoc;
71 int m_pointSizeUniformLoc;
72 QOpenGLVertexArrayObject m_vao;
73
74 QHash<const QAbstractSeries *, QOpenGLBuffer *> m_seriesBufferMap;
75 GLXYSeriesDataManager *m_xyDataManager;
76 };
77
78 QT_CHARTS_END_NAMESPACE
79 #endif
80 #endif
@@ -0,0 +1,147
1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd
4 ** All rights reserved.
5 ** For any questions to The Qt Company, please use contact form at http://qt.io
6 **
7 ** This file is part of the Qt Charts module.
8 **
9 ** Licensees holding valid commercial license for Qt may use this file in
10 ** accordance with the Qt License Agreement provided with the Software
11 ** or, alternatively, in accordance with the terms contained in a written
12 ** agreement between you and The Qt Company.
13 **
14 ** If you have questions regarding the use of this file, please use
15 ** contact form at http://qt.io
16 **
17 ****************************************************************************/
18
19 #include "private/glxyseriesdata_p.h"
20 #include "private/abstractdomain_p.h"
21 #include <QtCharts/QScatterSeries>
22
23 QT_CHARTS_BEGIN_NAMESPACE
24
25 GLXYSeriesDataManager::GLXYSeriesDataManager(QObject *parent)
26 : QObject(parent),
27 m_mapDirty(false)
28 {
29 }
30
31 GLXYSeriesDataManager::~GLXYSeriesDataManager()
32 {
33 cleanup();
34 }
35
36 void GLXYSeriesDataManager::setPoints(QXYSeries *series, const AbstractDomain *domain)
37 {
38 GLXYSeriesData *data = m_seriesDataMap.value(series);
39 if (!data) {
40 data = new GLXYSeriesData;
41 data->type = series->type();
42 QColor sc;
43 if (data->type == QAbstractSeries::SeriesTypeScatter) {
44 QScatterSeries *scatter = static_cast<QScatterSeries *>(series);
45 data->width = float(scatter->markerSize());
46 sc = scatter->color(); // Scatter overwrites color property
47 } else {
48 data->width = float(series->pen().widthF());
49 sc = series->color();
50 }
51 data->color = QVector3D(float(sc.redF()), float(sc.greenF()), float(sc.blueF()));
52 connect(series, &QXYSeries::penChanged, this,
53 &GLXYSeriesDataManager::handleSeriesPenChange);
54 connect(series, &QXYSeries::useOpenGLChanged, this,
55 &GLXYSeriesDataManager::handleSeriesOpenGLChange);
56 m_seriesDataMap.insert(series, data);
57 m_mapDirty = true;
58 }
59 QVector<float> &array = data->array;
60
61 bool logAxis = false;
62 foreach (QAbstractAxis* axis, series->attachedAxes()) {
63 if (axis->type() == QAbstractAxis::AxisTypeLogValue) {
64 logAxis = true;
65 break;
66 }
67 }
68
69 int count = series->count();
70 int index = 0;
71 array.resize(count * 2);
72 if (logAxis) {
73 // Use domain to resolve geometry points. Not as fast as shaders, but simpler that way
74 QVector<QPointF> geometryPoints = domain->calculateGeometryPoints(series->pointsVector());
75 const float height = domain->size().height();
76 if (geometryPoints.size()) {
77 for (int i = 0; i < count; i++) {
78 const QPointF &point = geometryPoints.at(i);
79 array[index++] = float(point.x());
80 array[index++] = float(height - point.y());
81 }
82 } else {
83 // If there are invalid log values, geometry points generation fails
84 for (int i = 0; i < count; i++) {
85 array[index++] = 0.0f;
86 array[index++] = 0.0f;
87 }
88 }
89 data->min = QVector2D(0, 0);
90 data->delta = QVector2D(domain->size().width() / 2.0f, domain->size().height() / 2.0f);
91 } else {
92 // Regular value axes, so we can do the math easily on shaders.
93 QVector<QPointF> seriesPoints = series->pointsVector();
94 for (int i = 0; i < count; i++) {
95 const QPointF &point = seriesPoints.at(i);
96 array[index++] = float(point.x());
97 array[index++] = float(point.y());
98 }
99 data->min = QVector2D(domain->minX(), domain->minY());
100 data->delta = QVector2D((domain->maxX() - domain->minX()) / 2.0f,
101 (domain->maxY() - domain->minY()) / 2.0f);
102 }
103 data->dirty = true;
104 }
105
106 void GLXYSeriesDataManager::removeSeries(const QXYSeries *series)
107 {
108 GLXYSeriesData *data = m_seriesDataMap.take(series);
109 if (data) {
110 disconnect(series, 0, this, 0);
111 delete data;
112 emit seriesRemoved(series);
113 m_mapDirty = true;
114 }
115 }
116
117 void GLXYSeriesDataManager::cleanup()
118 {
119 foreach (GLXYSeriesData *data, m_seriesDataMap.values())
120 delete data;
121 m_seriesDataMap.clear();
122 m_mapDirty = true;
123 // Signal all series removal by using zero as parameter
124 emit seriesRemoved(0);
125 }
126
127 void GLXYSeriesDataManager::handleSeriesPenChange()
128 {
129 QXYSeries *series = qobject_cast<QXYSeries *>(sender());
130 if (series) {
131 GLXYSeriesData *data = m_seriesDataMap.value(series);
132 if (data) {
133 QColor sc = series->color();
134 data->color = QVector3D(float(sc.redF()), float(sc.greenF()), float(sc.blueF()));
135 data->width = float(series->pen().widthF());
136 }
137 }
138 }
139
140 void GLXYSeriesDataManager::handleSeriesOpenGLChange()
141 {
142 QXYSeries *series = qobject_cast<QXYSeries *>(sender());
143 if (!series->useOpenGL())
144 removeSeries(series);
145 }
146
147 QT_CHARTS_END_NAMESPACE
@@ -0,0 +1,91
1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd
4 ** All rights reserved.
5 ** For any questions to The Qt Company, please use contact form at http://qt.io
6 **
7 ** This file is part of the Qt Charts module.
8 **
9 ** Licensees holding valid commercial license for Qt may use this file in
10 ** accordance with the Qt License Agreement provided with the Software
11 ** or, alternatively, in accordance with the terms contained in a written
12 ** agreement between you and The Qt Company.
13 **
14 ** If you have questions regarding the use of this file, please use
15 ** contact form at http://qt.io
16 **
17 ****************************************************************************/
18
19 // W A R N I N G
20 // -------------
21 //
22 // This file is not part of the Qt Chart API. It exists purely as an
23 // implementation detail. This header file may change from version to
24 // version without notice, or even be removed.
25 //
26 // We mean it.
27
28 #ifndef GLXYSERIESDATA_H
29 #define GLXYSERIESDATA_H
30
31 #include <QtCore/QMap>
32 #include <QtCharts/QAbstractSeries>
33 #include <QtCharts/QXYSeries>
34 #include <QtGui/QVector3D>
35 #include <QtGui/QVector2D>
36
37 QT_CHARTS_BEGIN_NAMESPACE
38
39 class AbstractDomain;
40
41 struct GLXYSeriesData {
42 QVector<float> array;
43 bool dirty;
44 QVector3D color;
45 float width;
46 QAbstractSeries::SeriesType type;
47 QVector2D min;
48 QVector2D delta;
49 };
50
51 typedef QMap<const QXYSeries *, GLXYSeriesData *> GLXYDataMap;
52 typedef QMapIterator<const QXYSeries *, GLXYSeriesData *> GLXYDataMapIterator;
53
54 class GLXYSeriesDataManager : public QObject
55 {
56 Q_OBJECT
57
58 public:
59 GLXYSeriesDataManager(QObject *parent = 0);
60 ~GLXYSeriesDataManager();
61
62 void setPoints(QXYSeries *series, const AbstractDomain *domain);
63
64 void removeSeries(const QXYSeries *series);
65
66 GLXYDataMap &dataMap() { return m_seriesDataMap; }
67
68 // These functions are needed by qml side, so they must be inline
69 bool mapDirty() const { return m_mapDirty; }
70 void clearAllDirty() {
71 m_mapDirty = false;
72 foreach (GLXYSeriesData *data, m_seriesDataMap.values())
73 data->dirty = false;
74 }
75
76 public Q_SLOTS:
77 void cleanup();
78 void handleSeriesPenChange();
79 void handleSeriesOpenGLChange();
80
81 Q_SIGNALS:
82 void seriesRemoved(const QXYSeries *series);
83
84 private:
85 GLXYDataMap m_seriesDataMap;
86 bool m_mapDirty;
87 };
88
89 QT_CHARTS_END_NAMESPACE
90
91 #endif
@@ -0,0 +1,78
1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd
4 ** All rights reserved.
5 ** For any questions to The Qt Company, please use contact form at http://qt.io
6 **
7 ** This file is part of the Qt Charts module.
8 **
9 ** Licensees holding valid commercial license for Qt may use this file in
10 ** accordance with the Qt License Agreement provided with the Software
11 ** or, alternatively, in accordance with the terms contained in a written
12 ** agreement between you and The Qt Company.
13 **
14 ** If you have questions regarding the use of this file, please use
15 ** contact form at http://qt.io
16 **
17 ****************************************************************************/
18
19 #include "declarativechartnode.h"
20 #include "declarativerendernode.h"
21 #include <QtGui/QOpenGLContext>
22 #include <QtGui/QOpenGLFunctions>
23 #include <QtGui/QOpenGLFramebufferObjectFormat>
24 #include <QtGui/QOpenGLFramebufferObject>
25 #include <QOpenGLShaderProgram>
26 #include <QtGui/QOpenGLBuffer>
27
28 QT_CHARTS_BEGIN_NAMESPACE
29
30 // This node handles displaying of the chart itself
31 DeclarativeChartNode::DeclarativeChartNode(QQuickWindow *window) :
32 QSGSimpleTextureNode(),
33 m_texture(0),
34 m_window(window),
35 m_textureOptions(0),
36 m_textureSize(1, 1),
37 m_glRenderNode(0)
38 {
39 initializeOpenGLFunctions();
40
41 // Our texture node must have a texture, so use a default one pixel texture
42 GLuint defaultTexture = 0;
43 glGenTextures(1, &defaultTexture);
44 glBindTexture(GL_TEXTURE_2D, defaultTexture);
45 uchar buf[4] = { 0, 0, 0, 0 };
46 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &buf);
47
48 QQuickWindow::CreateTextureOptions defaultTextureOptions = QQuickWindow::CreateTextureOptions(
49 QQuickWindow::TextureHasAlphaChannel | QQuickWindow::TextureOwnsGLTexture);
50 m_texture = m_window->createTextureFromId(defaultTexture, QSize(1, 1), defaultTextureOptions);
51
52 setTexture(m_texture);
53 setFiltering(QSGTexture::Linear);
54
55 // Create child node for rendering GL graphics
56 m_glRenderNode = new DeclarativeRenderNode(m_window);
57 m_glRenderNode->setFlag(OwnedByParent);
58 appendChildNode(m_glRenderNode);
59 m_glRenderNode->setRect(0, 0, 0, 0); // Hide child node by default
60 }
61
62 DeclarativeChartNode::~DeclarativeChartNode()
63 {
64 delete m_texture;
65 }
66
67 // Must be called on render thread and in context
68 void DeclarativeChartNode::createTextureFromImage(const QImage &chartImage)
69 {
70 if (chartImage.size() != m_textureSize)
71 m_textureSize = chartImage.size();
72
73 delete m_texture;
74 m_texture = m_window->createTextureFromImage(chartImage, m_textureOptions);
75 setTexture(m_texture);
76 }
77
78 QT_CHARTS_END_NAMESPACE
@@ -0,0 +1,50
1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd
4 ** All rights reserved.
5 ** For any questions to The Qt Company, please use contact form at http://qt.io
6 **
7 ** This file is part of the Qt Charts module.
8 **
9 ** Licensees holding valid commercial license for Qt may use this file in
10 ** accordance with the Qt License Agreement provided with the Software
11 ** or, alternatively, in accordance with the terms contained in a written
12 ** agreement between you and The Qt Company.
13 **
14 ** If you have questions regarding the use of this file, please use
15 ** contact form at http://qt.io
16 **
17 ****************************************************************************/
18
19 #ifndef DECLARATIVECHARTNODE_P_H
20 #define DECLARATIVECHARTNODE_P_H
21
22 #include <QtCharts/QChartGlobal>
23 #include <QtQuick/QSGSimpleTextureNode>
24 #include <QtQuick/QQuickWindow>
25 #include <QtGui/QOpenGLFunctions>
26
27 QT_CHARTS_BEGIN_NAMESPACE
28
29 class DeclarativeRenderNode;
30
31 class DeclarativeChartNode : public QSGSimpleTextureNode, QOpenGLFunctions
32 {
33 public:
34 DeclarativeChartNode(QQuickWindow *window);
35 ~DeclarativeChartNode();
36
37 void createTextureFromImage(const QImage &chartImage);
38 DeclarativeRenderNode *glRenderNode() const { return m_glRenderNode; }
39
40 private:
41 QSGTexture *m_texture;
42 QQuickWindow *m_window;
43 QQuickWindow::CreateTextureOptions m_textureOptions;
44 QSize m_textureSize;
45 DeclarativeRenderNode *m_glRenderNode;
46 };
47
48 QT_CHARTS_END_NAMESPACE
49
50 #endif // DECLARATIVECHARTNODE_P_H
@@ -0,0 +1,317
1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd
4 ** All rights reserved.
5 ** For any questions to The Qt Company, please use contact form at http://qt.io
6 **
7 ** This file is part of the Qt Charts module.
8 **
9 ** Licensees holding valid commercial license for Qt may use this file in
10 ** accordance with the Qt License Agreement provided with the Software
11 ** or, alternatively, in accordance with the terms contained in a written
12 ** agreement between you and The Qt Company.
13 **
14 ** If you have questions regarding the use of this file, please use
15 ** contact form at http://qt.io
16 **
17 ****************************************************************************/
18
19 #include "declarativerendernode.h"
20
21 #include <QtGui/QOpenGLContext>
22 #include <QtGui/QOpenGLFunctions>
23 #include <QtGui/QOpenGLFramebufferObjectFormat>
24 #include <QtGui/QOpenGLFramebufferObject>
25 #include <QOpenGLShaderProgram>
26 #include <QtGui/QOpenGLBuffer>
27
28 //#define QDEBUG_TRACE_GL_FPS
29 #ifdef QDEBUG_TRACE_GL_FPS
30 # include <QElapsedTimer>
31 #endif
32
33 QT_CHARTS_BEGIN_NAMESPACE
34
35 // This node draws the xy series data on a transparent background using OpenGL.
36 // It is used as a child node of the chart node.
37 DeclarativeRenderNode::DeclarativeRenderNode(QQuickWindow *window) :
38 QObject(),
39 QSGSimpleTextureNode(),
40 m_texture(0),
41 m_window(window),
42 m_textureOptions(QQuickWindow::TextureHasAlphaChannel),
43 m_textureSize(1, 1),
44 m_recreateFbo(false),
45 m_fbo(0),
46 m_program(0),
47 m_shaderAttribLoc(-1),
48 m_colorUniformLoc(-1),
49 m_minUniformLoc(-1),
50 m_deltaUniformLoc(-1),
51 m_pointSizeUniformLoc(-1),
52 m_renderNeeded(true)
53 {
54 initializeOpenGLFunctions();
55
56 // Our texture node must have a texture, so use a default one pixel texture
57 GLuint defaultTexture = 0;
58 glGenTextures(1, &defaultTexture);
59 glBindTexture(GL_TEXTURE_2D, defaultTexture);
60 uchar buf[4] = { 0, 0, 0, 0 };
61 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &buf);
62
63 QQuickWindow::CreateTextureOptions defaultTextureOptions = QQuickWindow::CreateTextureOptions(
64 QQuickWindow::TextureHasAlphaChannel | QQuickWindow::TextureOwnsGLTexture);
65 m_texture = m_window->createTextureFromId(defaultTexture, QSize(1, 1), defaultTextureOptions);
66
67 setTexture(m_texture);
68 setFiltering(QSGTexture::Linear);
69 setTextureCoordinatesTransform(QSGSimpleTextureNode::MirrorVertically);
70 }
71
72 DeclarativeRenderNode::~DeclarativeRenderNode()
73 {
74 delete m_texture;
75 delete m_fbo;
76
77 delete m_program;
78 m_program = 0;
79
80 cleanXYSeriesResources(0);
81 }
82
83 static const char *vertexSource =
84 "attribute highp vec2 points;\n"
85 "uniform highp vec2 min;\n"
86 "uniform highp vec2 delta;\n"
87 "uniform highp float pointSize;\n"
88 "void main() {\n"
89 " vec2 normalPoint = vec2(-1, -1) + ((points - min) / delta);\n"
90 " gl_Position = vec4(normalPoint, 0, 1);\n"
91 " gl_PointSize = pointSize;\n"
92 "}";
93 static const char *fragmentSource =
94 "uniform highp vec3 color;\n"
95 "void main() {\n"
96 " gl_FragColor = vec4(color,1);\n"
97 "}\n";
98
99 // Must be called on render thread and in context
100 void DeclarativeRenderNode::initGL()
101 {
102 recreateFBO();
103
104 m_program = new QOpenGLShaderProgram;
105 m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexSource);
106 m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentSource);
107 m_program->bindAttributeLocation("points", 0);
108 m_program->link();
109
110 m_program->bind();
111 m_colorUniformLoc = m_program->uniformLocation("color");
112 m_minUniformLoc = m_program->uniformLocation("min");
113 m_deltaUniformLoc = m_program->uniformLocation("delta");
114 m_pointSizeUniformLoc = m_program->uniformLocation("pointSize");
115
116 // Create a vertex array object. In OpenGL ES 2.0 and OpenGL 2.x
117 // implementations this is optional and support may not be present
118 // at all. Nonetheless the below code works in all cases and makes
119 // sure there is a VAO when one is needed.
120 m_vao.create();
121 QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
122
123 #if !defined(QT_OPENGL_ES_2)
124 if (!QOpenGLContext::currentContext()->isOpenGLES()) {
125 // Make it possible to change point primitive size and use textures with them in
126 // the shaders. These are implicitly enabled in ES2.
127 // Qt Quick doesn't change these flags, so it should be safe to just enable them
128 // at initialization.
129 glEnable(GL_PROGRAM_POINT_SIZE);
130 }
131 #endif
132
133 m_program->release();
134 }
135
136 void DeclarativeRenderNode::recreateFBO()
137 {
138 QOpenGLFramebufferObjectFormat fboFormat;
139 fboFormat.setAttachment(QOpenGLFramebufferObject::NoAttachment);
140 delete m_fbo;
141 m_fbo = new QOpenGLFramebufferObject(m_textureSize.width(),
142 m_textureSize.height(),
143 fboFormat);
144
145 delete m_texture;
146 m_texture = m_window->createTextureFromId(m_fbo->texture(), m_textureSize, m_textureOptions);
147 setTexture(m_texture);
148
149 m_recreateFbo = false;
150 }
151
152 // Must be called on render thread and in context
153 void DeclarativeRenderNode::setTextureSize(const QSize &size)
154 {
155 m_textureSize = size;
156 m_recreateFbo = true;
157 m_renderNeeded = true;
158 }
159
160 // Must be called on render thread while gui thread is blocked, and in context
161 void DeclarativeRenderNode::setSeriesData(bool mapDirty, const GLXYDataMap &dataMap)
162 {
163 if (mapDirty) {
164 // Series have changed, recreate map, but utilize old data where feasible
165 GLXYDataMap oldMap = m_xyDataMap;
166 m_xyDataMap.clear();
167
168 GLXYDataMapIterator i(dataMap);
169 while (i.hasNext()) {
170 i.next();
171 GLXYSeriesData *data = oldMap.take(i.key());
172 const GLXYSeriesData *newData = i.value();
173 if (!data || newData->dirty) {
174 data = new GLXYSeriesData;
175 data->array = newData->array;
176 data->color = newData->color;
177 data->dirty = newData->dirty;
178 data->width = newData->width;
179 data->type = newData->type;
180 data->min = newData->min;
181 data->delta = newData->delta;
182 }
183 m_xyDataMap.insert(i.key(), data);
184 }
185 // Delete remaining old data
186 i = oldMap;
187 while (i.hasNext()) {
188 i.next();
189 delete i.value();
190 cleanXYSeriesResources(i.key());
191 }
192 } else {
193 // Series have not changed, so just copy dirty data over
194 GLXYDataMapIterator i(dataMap);
195 while (i.hasNext()) {
196 i.next();
197 const GLXYSeriesData *newData = i.value();
198 if (i.value()->dirty) {
199 GLXYSeriesData *data = m_xyDataMap.value(i.key());
200 if (data) {
201 data->array = newData->array;
202 data->color = newData->color;
203 data->dirty = newData->dirty;
204 data->width = newData->width;
205 data->type = newData->type;
206 data->min = newData->min;
207 data->delta = newData->delta;
208 }
209 }
210 }
211 }
212 markDirty(DirtyMaterial);
213 m_renderNeeded = true;
214 }
215
216 void DeclarativeRenderNode::renderGL()
217 {
218 glClearColor(0, 0, 0, 0);
219
220 QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
221 m_program->bind();
222 m_fbo->bind();
223
224 glClear(GL_COLOR_BUFFER_BIT);
225 glEnableVertexAttribArray(0);
226
227 glViewport(0, 0, m_textureSize.width(), m_textureSize.height());
228
229 GLXYDataMapIterator i(m_xyDataMap);
230 while (i.hasNext()) {
231 i.next();
232 QOpenGLBuffer *vbo = m_seriesBufferMap.value(i.key());
233 GLXYSeriesData *data = i.value();
234
235 m_program->setUniformValue(m_colorUniformLoc, data->color);
236 m_program->setUniformValue(m_minUniformLoc, data->min);
237 m_program->setUniformValue(m_deltaUniformLoc, data->delta);
238
239 if (!vbo) {
240 vbo = new QOpenGLBuffer;
241 m_seriesBufferMap.insert(i.key(), vbo);
242 vbo->create();
243 }
244 vbo->bind();
245 if (data->dirty) {
246 vbo->allocate(data->array.constData(), data->array.count() * sizeof(GLfloat));
247 data->dirty = false;
248 }
249
250 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
251 if (data->type == QAbstractSeries::SeriesTypeLine) {
252 glLineWidth(data->width);
253 glDrawArrays(GL_LINE_STRIP, 0, data->array.size() / 2);
254 } else { // Scatter
255 m_program->setUniformValue(m_pointSizeUniformLoc, data->width);
256 glDrawArrays(GL_POINTS, 0, data->array.size() / 2);
257 }
258 vbo->release();
259 }
260
261 #ifdef QDEBUG_TRACE_GL_FPS
262 static QElapsedTimer stopWatch;
263 static int frameCount = -1;
264 if (frameCount == -1) {
265 stopWatch.start();
266 frameCount = 0;
267 }
268 frameCount++;
269 int elapsed = stopWatch.elapsed();
270 if (elapsed >= 1000) {
271 elapsed = stopWatch.restart();
272 qreal fps = qreal(0.1 * int(10000.0 * (qreal(frameCount) / qreal(elapsed))));
273 qDebug() << "FPS:" << fps;
274 frameCount = 0;
275 }
276 #endif
277
278 markDirty(DirtyMaterial);
279 m_window->resetOpenGLState();
280 }
281
282 // Must be called on render thread as response to beforeRendering signal
283 void DeclarativeRenderNode::render()
284 {
285 if (m_renderNeeded) {
286 if (m_xyDataMap.size()) {
287 if (!m_program)
288 initGL();
289 if (m_recreateFbo)
290 recreateFBO();
291 renderGL();
292 } else {
293 if (rect() != QRectF()) {
294 glClearColor(0, 0, 0, 0);
295 m_fbo->bind();
296 glClear(GL_COLOR_BUFFER_BIT);
297
298 // If last series was removed, zero out the node rect
299 setRect(QRectF());
300 }
301 }
302 m_renderNeeded = false;
303 }
304 }
305
306 void DeclarativeRenderNode::cleanXYSeriesResources(const QXYSeries *series)
307 {
308 if (series) {
309 delete m_seriesBufferMap.take(series);
310 } else {
311 foreach (QOpenGLBuffer *buffer, m_seriesBufferMap.values())
312 delete buffer;
313 m_seriesBufferMap.clear();
314 }
315 }
316
317 QT_CHARTS_END_NAMESPACE
@@ -0,0 +1,74
1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd
4 ** All rights reserved.
5 ** For any questions to The Qt Company, please use contact form at http://qt.io
6 **
7 ** This file is part of the Qt Charts module.
8 **
9 ** Licensees holding valid commercial license for Qt may use this file in
10 ** accordance with the Qt License Agreement provided with the Software
11 ** or, alternatively, in accordance with the terms contained in a written
12 ** agreement between you and The Qt Company.
13 **
14 ** If you have questions regarding the use of this file, please use
15 ** contact form at http://qt.io
16 **
17 ****************************************************************************/
18
19 #ifndef DECLARATIVERENDERNODE_P_H
20 #define DECLARATIVERENDERNODE_P_H
21
22 #include <QtCharts/QChartGlobal>
23 #include <private/glxyseriesdata_p.h>
24 #include <QtQuick/QSGSimpleTextureNode>
25 #include <QtQuick/QQuickWindow>
26 #include <QtGui/QOpenGLVertexArrayObject>
27 #include <QtGui/QOpenGLFunctions>
28
29 class QOpenGLFramebufferObject;
30 class QOpenGLBuffer;
31
32 QT_CHARTS_BEGIN_NAMESPACE
33
34 class DeclarativeRenderNode : public QObject, public QSGSimpleTextureNode, QOpenGLFunctions
35 {
36 Q_OBJECT
37 public:
38 DeclarativeRenderNode(QQuickWindow *window);
39 ~DeclarativeRenderNode();
40
41 void initGL();
42 QSize textureSize() const { return m_textureSize; }
43 void setTextureSize(const QSize &size);
44 void setSeriesData(bool mapDirty, const GLXYDataMap &dataMap);
45
46 public Q_SLOTS:
47 void render();
48
49 private:
50 void renderGL();
51 void recreateFBO();
52 void cleanXYSeriesResources(const QXYSeries *series);
53
54 QSGTexture *m_texture;
55 QQuickWindow *m_window;
56 QQuickWindow::CreateTextureOptions m_textureOptions;
57 QSize m_textureSize;
58 bool m_recreateFbo;
59 GLXYDataMap m_xyDataMap;
60 QOpenGLFramebufferObject *m_fbo;
61 QOpenGLShaderProgram *m_program;
62 int m_shaderAttribLoc;
63 int m_colorUniformLoc;
64 int m_minUniformLoc;
65 int m_deltaUniformLoc;
66 int m_pointSizeUniformLoc;
67 QOpenGLVertexArrayObject m_vao;
68 QHash<const QAbstractSeries *, QOpenGLBuffer *> m_seriesBufferMap;
69 bool m_renderNeeded;
70 };
71
72 QT_CHARTS_END_NAMESPACE
73
74 #endif // DECLARATIVERENDERNODE_P_H
@@ -35,12 +35,12 qint64 XYSeriesIODevice::readData(char * data, qint64 maxSize)
35 35 qint64 XYSeriesIODevice::writeData(const char * data, qint64 maxSize)
36 36 {
37 37 qint64 range = 2000;
38 QList<QPointF> oldPoints = m_series->points();
39 QList<QPointF> points;
38 QVector<QPointF> oldPoints = m_series->pointsVector();
39 QVector<QPointF> points;
40 40 int resolution = 4;
41 41
42 42 if (oldPoints.count() < range) {
43 points = m_series->points();
43 points = m_series->pointsVector();
44 44 } else {
45 45 for (int i = maxSize/resolution; i < oldPoints.count(); i++)
46 46 points.append(QPointF(i - maxSize/resolution, oldPoints.at(i).y()));
@@ -50,7 +50,13 qtHaveModule(quick) {
50 50 qtHaveModule(multimedia) {
51 51 SUBDIRS += audio
52 52 } else {
53 message("QtMultimedia library not available. Some examples are disabled")
53 message("QtMultimedia library not available. Some examples are disabled.")
54 }
55
56 contains(QT_CONFIG, opengl) {
57 SUBDIRS += openglseries
58 } else {
59 message("OpenGL not available. Some examples are disabled.")
54 60 }
55 61
56 62 !linux-arm*: {
@@ -189,7 +189,7 QChart *ThemeWidget::createAreaChart() const
189 189 for (int j(0); j < m_dataTable[i].count(); j++) {
190 190 Data data = m_dataTable[i].at(j);
191 191 if (lowerSeries) {
192 const QList<QPointF>& points = lowerSeries->points();
192 const QVector<QPointF>& points = lowerSeries->pointsVector();
193 193 upperSeries->append(QPointF(j, points[i].y() + data.first.y()));
194 194 } else {
195 195 upperSeries->append(QPointF(j, data.first.y()));
@@ -16,10 +16,11
16 16 **
17 17 ****************************************************************************/
18 18
19 import QtQuick 2.0
19 import QtQuick 2.1
20 20 import QtQuick.Layouts 1.0
21 21
22 22 ColumnLayout {
23 property alias openGLButton: openGLButton
23 24 spacing: 8
24 25 Layout.fillHeight: true
25 26 signal animationsEnabled(bool enabled)
@@ -27,6 +28,7 ColumnLayout {
27 28 signal refreshRateChanged(variant rate);
28 29 signal signalSourceChanged(string source, int signalCount, int sampleCount);
29 30 signal antialiasingEnabled(bool enabled)
31 signal openGlChanged(bool enabled)
30 32
31 33 Text {
32 34 text: "Scope"
@@ -35,6 +37,14 ColumnLayout {
35 37 }
36 38
37 39 MultiButton {
40 id: openGLButton
41 text: "OpenGL: "
42 items: ["false", "true"]
43 currentSelection: 0
44 onSelectionChanged: openGlChanged(currentSelection == 1);
45 }
46
47 MultiButton {
38 48 text: "Graph: "
39 49 items: ["line", "spline", "scatter"]
40 50 currentSelection: 0
@@ -17,13 +17,18
17 17 ****************************************************************************/
18 18
19 19 import QtQuick 2.0
20 import QtCharts 2.0
20 import QtCharts 2.1
21 21
22 22 //![1]
23 23 ChartView {
24 24 id: chartView
25 25 animationOptions: ChartView.NoAnimation
26 26 theme: ChartView.ChartThemeDark
27 property bool openGL: false
28 onOpenGLChanged: {
29 series("signal 1").useOpenGL = openGL;
30 series("signal 2").useOpenGL = openGL;
31 }
27 32
28 33 ValueAxis {
29 34 id: axisY1
@@ -40,7 +45,7 ChartView {
40 45 ValueAxis {
41 46 id: axisX
42 47 min: 0
43 max: 1000
48 max: 1024
44 49 }
45 50
46 51 LineSeries {
@@ -48,12 +53,14 ChartView {
48 53 name: "signal 1"
49 54 axisX: axisX
50 55 axisY: axisY1
56 useOpenGL: chartView.openGL
51 57 }
52 58 LineSeries {
53 59 id: lineSeries2
54 60 name: "signal 2"
55 61 axisX: axisX
56 62 axisYRight: axisY2
63 useOpenGL: chartView.openGL
57 64 }
58 65 //![1]
59 66
@@ -78,18 +85,28 ChartView {
78 85 // but the series have their own y-axes to make it possible to control the y-offset
79 86 // of the "signal sources".
80 87 if (type == "line") {
81 chartView.createSeries(ChartView.SeriesTypeLine, "signal 1", axisX, axisY1);
82 chartView.createSeries(ChartView.SeriesTypeLine, "signal 2", axisX, axisY2);
88 var series1 = chartView.createSeries(ChartView.SeriesTypeLine, "signal 1",
89 axisX, axisY1);
90 series1.useOpenGL = chartView.openGL
91
92 var series2 = chartView.createSeries(ChartView.SeriesTypeLine, "signal 2",
93 axisX, axisY2);
94 series2.useOpenGL = chartView.openGL
83 95 } else if (type == "spline") {
84 96 chartView.createSeries(ChartView.SeriesTypeSpline, "signal 1", axisX, axisY1);
85 97 chartView.createSeries(ChartView.SeriesTypeSpline, "signal 2", axisX, axisY2);
86 98 } else {
87 var series1 = chartView.createSeries(ChartView.SeriesTypeScatter, "signal 1", axisX, axisY1);
99 var series1 = chartView.createSeries(ChartView.SeriesTypeScatter, "signal 1",
100 axisX, axisY1);
88 101 series1.markerSize = 3;
89 102 series1.borderColor = "transparent";
90 var series2 = chartView.createSeries(ChartView.SeriesTypeScatter, "signal 2", axisX, axisY2);
103 series1.useOpenGL = chartView.openGL
104
105 var series2 = chartView.createSeries(ChartView.SeriesTypeScatter, "signal 2",
106 axisX, axisY2);
91 107 series2.markerSize = 3;
92 108 series2.borderColor = "transparent";
109 series2.useOpenGL = chartView.openGL
93 110 }
94 111 }
95 112
@@ -21,8 +21,8 import QtQuick 2.0
21 21 //![1]
22 22 Rectangle {
23 23 id: main
24 width: 400
25 height: 300
24 width: 600
25 height: 400
26 26 color: "#404040"
27 27
28 28 ControlPanel {
@@ -39,11 +39,22 Rectangle {
39 39 dataSource.generateData(0, signalCount, sampleCount);
40 40 else
41 41 dataSource.generateData(1, signalCount, sampleCount);
42 scopeView.axisX().max = sampleCount;
42 43 }
43 44 onAnimationsEnabled: scopeView.setAnimations(enabled);
44 onSeriesTypeChanged: scopeView.changeSeriesType(type);
45 onSeriesTypeChanged: {
46 scopeView.changeSeriesType(type);
47 if (type === "spline") {
48 controlPanel.openGLButton.currentSelection = 0;
49 controlPanel.openGLButton.enabled = false;
50 scopeView.openGL = false;
51 } else {
52 controlPanel.openGLButton.enabled = true;
53 }
54 }
45 55 onRefreshRateChanged: scopeView.changeRefreshRate(rate);
46 56 onAntialiasingEnabled: scopeView.antialiasing = enabled;
57 onOpenGlChanged: scopeView.openGL = enabled;
47 58 }
48 59
49 60 //![2]
@@ -56,4 +67,5 Rectangle {
56 67 height: main.height
57 68 }
58 69 //![2]
70
59 71 }
@@ -345,7 +345,7 QT_CHARTS_BEGIN_NAMESPACE
345 345 \sa pointLabelsVisible
346 346 */
347 347 /*!
348 \fn void QAreaSeries::pointLabelsClippintChanged(bool clipping)
348 \fn void QAreaSeries::pointLabelsClippingChanged(bool clipping)
349 349 The clipping of the data point labels is changed to \a clipping.
350 350 */
351 351 /*!
@@ -395,8 +395,10 QAbstractSeries::SeriesType QAreaSeries::type() const
395 395 void QAreaSeries::setUpperSeries(QLineSeries *series)
396 396 {
397 397 Q_D(QAreaSeries);
398 if (d->m_upperSeries != series)
398 if (d->m_upperSeries != series) {
399 series->d_ptr->setBlockOpenGL(true);
399 400 d->m_upperSeries = series;
401 }
400 402 }
401 403
402 404 QLineSeries *QAreaSeries::upperSeries() const
@@ -411,6 +413,7 QLineSeries *QAreaSeries::upperSeries() const
411 413 void QAreaSeries::setLowerSeries(QLineSeries *series)
412 414 {
413 415 Q_D(QAreaSeries);
416 series->d_ptr->setBlockOpenGL(true);
414 417 d->m_lowerSeries = series;
415 418 }
416 419
@@ -624,7 +627,7 void QAreaSeriesPrivate::initializeDomain()
624 627 QLineSeries *lowerSeries = q->lowerSeries();
625 628
626 629 if (upperSeries) {
627 const QList<QPointF>& points = upperSeries->points();
630 const QVector<QPointF> &points = upperSeries->pointsVector();
628 631
629 632 for (int i = 0; i < points.count(); i++) {
630 633 qreal x = points[i].x();
@@ -636,8 +639,7 void QAreaSeriesPrivate::initializeDomain()
636 639 }
637 640 }
638 641 if (lowerSeries) {
639
640 const QList<QPointF>& points = lowerSeries->points();
642 const QVector<QPointF> &points = lowerSeries->pointsVector();
641 643
642 644 for (int i = 0; i < points.count(); i++) {
643 645 qreal x = points[i].x();
@@ -883,7 +883,7 bool QAbstractAxis::isVisible() const
883 883 }
884 884
885 885 /*!
886 Sets axis, shades, labels and grid lines to be visible.
886 Sets axis, shades, labels and grid lines visibility to \a visible.
887 887 */
888 888 void QAbstractAxis::setVisible(bool visible)
889 889 {
@@ -546,7 +546,7 QColor QBarSet::borderColor()
546 546 }
547 547
548 548 /*!
549 Sets the color of pen for this bar set.
549 Sets the \a color of pen for this bar set.
550 550 */
551 551 void QBarSet::setBorderColor(QColor color)
552 552 {
@@ -567,7 +567,7 QColor QBarSet::labelColor()
567 567 }
568 568
569 569 /*!
570 Sets the color of labels for this bar set.
570 Sets the \a color of labels for this bar set.
571 571 */
572 572 void QBarSet::setLabelColor(QColor color)
573 573 {
@@ -38,6 +38,7
38 38 #include <private/xlogypolardomain_p.h>
39 39 #include <private/logxypolardomain_p.h>
40 40 #include <private/logxlogypolardomain_p.h>
41 #include <private/glxyseriesdata_p.h>
41 42
42 43 #ifndef QT_ON_ARM
43 44 #include <QtCharts/QDateTimeAxis>
@@ -47,7 +48,8 QT_CHARTS_BEGIN_NAMESPACE
47 48
48 49 ChartDataSet::ChartDataSet(QChart *chart)
49 50 : QObject(chart),
50 m_chart(chart)
51 m_chart(chart),
52 m_glXYSeriesDataManager(new GLXYSeriesDataManager(this))
51 53 {
52 54
53 55 }
@@ -77,6 +79,8 void ChartDataSet::addSeries(QAbstractSeries *series)
77 79 qWarning() << QObject::tr("Can not add series. Series type is not supported by a polar chart.");
78 80 return;
79 81 }
82 // Disable OpenGL for series in polar charts
83 series->setUseOpenGL(false);
80 84 series->d_ptr->setDomain(new XYPolarDomain());
81 85 // Set the correct domain for upper and lower series too
82 86 if (series->type() == QAbstractSeries::SeriesTypeArea) {
@@ -157,6 +161,10 void ChartDataSet::removeSeries(QAbstractSeries *series)
157 161 series->d_ptr->setDomain(new XYDomain());
158 162 series->setParent(0);
159 163 series->d_ptr->m_chart = 0;
164
165 QXYSeries *xySeries = qobject_cast<QXYSeries *>(series);
166 if (xySeries)
167 m_glXYSeriesDataManager->removeSeries(xySeries);
160 168 }
161 169
162 170 /*
@@ -37,6 +37,7 QT_CHARTS_BEGIN_NAMESPACE
37 37
38 38 class QAbstractAxis;
39 39 class ChartPresenter;
40 class GLXYSeriesDataManager;
40 41
41 42 class QT_CHARTS_AUTOTEST_EXPORT ChartDataSet : public QObject
42 43 {
@@ -67,6 +68,8 public:
67 68 QPointF mapToValue(const QPointF &position, QAbstractSeries *series = 0);
68 69 QPointF mapToPosition(const QPointF &value, QAbstractSeries *series = 0);
69 70
71 GLXYSeriesDataManager *glXYSeriesDataManager() { return m_glXYSeriesDataManager; }
72
70 73 Q_SIGNALS:
71 74 void axisAdded(QAbstractAxis* axis);
72 75 void axisRemoved(QAbstractAxis* axis);
@@ -85,6 +88,7 private:
85 88 QList<QAbstractSeries *> m_seriesList;
86 89 QList<QAbstractAxis *> m_axisList;
87 90 QChart* m_chart;
91 GLXYSeriesDataManager *m_glXYSeriesDataManager;
88 92 };
89 93
90 94 QT_CHARTS_END_NAMESPACE
@@ -19,13 +19,15
19 19 #include <private/chartelement_p.h>
20 20 #include <private/chartpresenter_p.h>
21 21 #include <private/abstractdomain_p.h>
22 #include <private/chartdataset_p.h>
22 23
23 24 QT_CHARTS_BEGIN_NAMESPACE
24 25
25 26 ChartElement::ChartElement(QGraphicsItem* item):
26 27 QGraphicsObject(item),
27 28 m_presenter(0),
28 m_themeManager(0)
29 m_themeManager(0),
30 m_dataSet(0)
29 31 {
30 32
31 33 }
@@ -50,4 +52,14 ChartThemeManager* ChartElement::themeManager() const
50 52 return m_themeManager;
51 53 }
52 54
55 void ChartElement::setDataSet(ChartDataSet *dataSet)
56 {
57 m_dataSet = dataSet;
58 }
59
60 ChartDataSet *ChartElement::dataSet() const
61 {
62 return m_dataSet;
63 }
64
53 65 QT_CHARTS_END_NAMESPACE
@@ -40,6 +40,7 class ChartPresenter;
40 40 class ChartAnimation;
41 41 class ChartThemeManager;
42 42 class AbstractDomain;
43 class ChartDataSet;
43 44
44 45 class ChartElement: public QGraphicsObject
45 46 {
@@ -52,10 +53,13 public:
52 53 ChartPresenter *presenter() const;
53 54 virtual void setThemeManager(ChartThemeManager *manager);
54 55 ChartThemeManager* themeManager() const;
56 virtual void setDataSet(ChartDataSet *dataSet);
57 ChartDataSet *dataSet() const;
55 58
56 59 private:
57 60 ChartPresenter *m_presenter;
58 61 ChartThemeManager *m_themeManager;
62 ChartDataSet *m_dataSet;
59 63 };
60 64
61 65 QT_CHARTS_END_NAMESPACE
@@ -46,6 +46,10 ChartPresenter::ChartPresenter(QChart *chart, QChart::ChartType type)
46 46 m_plotAreaBackground(0),
47 47 m_title(0),
48 48 m_localizeNumbers(false)
49 #ifndef QT_NO_OPENGL
50 , m_glWidget(0)
51 , m_glUseWidget(true)
52 #endif
49 53 {
50 54 if (type == QChart::ChartTypeCartesian)
51 55 m_layout = new CartesianChartLayout(this);
@@ -56,7 +60,9 ChartPresenter::ChartPresenter(QChart *chart, QChart::ChartType type)
56 60
57 61 ChartPresenter::~ChartPresenter()
58 62 {
59
63 #ifndef QT_NO_OPENGL
64 delete m_glWidget.data();
65 #endif
60 66 }
61 67
62 68 void ChartPresenter::setGeometry(const QRectF rect)
@@ -67,6 +73,10 void ChartPresenter::setGeometry(const QRectF rect)
67 73 chart->domain()->setSize(rect.size());
68 74 chart->setPos(rect.topLeft());
69 75 }
76 #ifndef QT_NO_OPENGL
77 if (!m_glWidget.isNull())
78 m_glWidget->setGeometry(m_rect.toRect());
79 #endif
70 80 emit plotAreaChanged(m_rect);
71 81 }
72 82 }
@@ -108,6 +118,7 void ChartPresenter::handleSeriesAdded(QAbstractSeries *series)
108 118 ChartItem *chart = series->d_ptr->chartItem();
109 119 chart->setPresenter(this);
110 120 chart->setThemeManager(m_chart->d_ptr->m_themeManager);
121 chart->setDataSet(m_chart->d_ptr->m_dataset);
111 122 chart->domain()->setSize(m_rect.size());
112 123 chart->setPos(m_rect.topLeft());
113 124 chart->handleDomainUpdated(); //this could be moved to intializeGraphics when animator is refactored
@@ -531,6 +542,28 QString ChartPresenter::numberToString(int value)
531 542 return QString::number(value);
532 543 }
533 544
545 void ChartPresenter::ensureGLWidget()
546 {
547 #ifndef QT_NO_OPENGL
548 // GLWidget pointer is wrapped in QPointer as its parent is not in our control, and therefore
549 // can potentially get deleted unexpectedly.
550 if (m_glWidget.isNull() && m_glUseWidget && m_chart->scene()) {
551 QObject *parent = m_chart->scene()->parent();
552 while (parent) {
553 QWidget *parentWidget = qobject_cast<QWidget *>(parent);
554 if (parentWidget) {
555 m_glWidget = new GLWidget(m_chart->d_ptr->m_dataset->glXYSeriesDataManager(),
556 parentWidget);
557 m_glWidget->setGeometry(m_rect.toRect());
558 m_glWidget->show();
559 break;
560 }
561 parent = parent->parent();
562 }
563 }
564 #endif
565 }
566
534 567 #include "moc_chartpresenter_p.cpp"
535 568
536 569 QT_CHARTS_END_NAMESPACE
@@ -30,9 +30,11
30 30
31 31 #include <QtCharts/QChartGlobal>
32 32 #include <QtCharts/QChart> //because of QChart::ChartThemeId
33 #include <private/glwidget_p.h>
33 34 #include <QtCore/QRectF>
34 35 #include <QtCore/QMargins>
35 36 #include <QtCore/QLocale>
37 #include <QtCore/QPointer>
36 38
37 39 QT_CHARTS_BEGIN_NAMESPACE
38 40
@@ -160,6 +162,9 public:
160 162 QString numberToString(double value, char f = 'g', int prec = 6);
161 163 QString numberToString(int value);
162 164
165 void ensureGLWidget();
166 void glSetUseWidget(bool enable) { m_glUseWidget = enable; }
167
163 168 private:
164 169 void createBackgroundItem();
165 170 void createPlotAreaBackgroundItem();
@@ -192,6 +197,10 private:
192 197 QRectF m_rect;
193 198 bool m_localizeNumbers;
194 199 QLocale m_locale;
200 #ifndef QT_NO_OPENGL
201 QPointer<GLWidget> m_glWidget;
202 #endif
203 bool m_glUseWidget;
195 204 };
196 205
197 206 QT_CHARTS_END_NAMESPACE
@@ -32,6 +32,9 SOURCES += \
32 32 $$PWD/scroller.cpp \
33 33 $$PWD/charttitle.cpp \
34 34 $$PWD/qpolarchart.cpp
35
36 contains(QT_CONFIG, opengl): SOURCES += $$PWD/glwidget.cpp
37
35 38 PRIVATE_HEADERS += \
36 39 $$PWD/chartdataset_p.h \
37 40 $$PWD/chartitem_p.h \
@@ -46,6 +49,9 PRIVATE_HEADERS += \
46 49 $$PWD/qabstractseries_p.h \
47 50 $$PWD/charttitle_p.h \
48 51 $$PWD/charthelpers_p.h
52
53 contains(QT_CONFIG, opengl): PRIVATE_HEADERS += $$PWD/glwidget_p.h
54
49 55 PUBLIC_HEADERS += \
50 56 $$PWD/qchart.h \
51 57 $$PWD/qchartglobal.h \
@@ -92,7 +92,7 public:
92 92
93 93 virtual QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const = 0;
94 94 virtual QPointF calculateDomainPoint(const QPointF &point) const = 0;
95 virtual QVector<QPointF> calculateGeometryPoints(const QList<QPointF> &vector) const = 0;
95 virtual QVector<QPointF> calculateGeometryPoints(const QVector<QPointF> &vector) const = 0;
96 96
97 97 virtual bool attachAxis(QAbstractAxis *axis);
98 98 virtual bool detachAxis(QAbstractAxis *axis);
@@ -162,7 +162,7 QPointF LogXLogYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) c
162 162 return QPointF(x, y);
163 163 }
164 164
165 QVector<QPointF> LogXLogYDomain::calculateGeometryPoints(const QList<QPointF> &vector) const
165 QVector<QPointF> LogXLogYDomain::calculateGeometryPoints(const QVector<QPointF> &vector) const
166 166 {
167 167 const qreal deltaX = m_size.width() / qAbs(m_logRightX - m_logLeftX);
168 168 const qreal deltaY = m_size.height() / qAbs(m_logRightY - m_logLeftY);
@@ -54,7 +54,7 public:
54 54
55 55 QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const;
56 56 QPointF calculateDomainPoint(const QPointF &point) const;
57 QVector<QPointF> calculateGeometryPoints(const QList<QPointF> &vector) const;
57 QVector<QPointF> calculateGeometryPoints(const QVector<QPointF> &vector) const;
58 58
59 59 bool attachAxis(QAbstractAxis *axis);
60 60 bool detachAxis(QAbstractAxis *axis);
@@ -146,7 +146,7 QPointF LogXYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) cons
146 146 return QPointF(x, y);
147 147 }
148 148
149 QVector<QPointF> LogXYDomain::calculateGeometryPoints(const QList<QPointF> &vector) const
149 QVector<QPointF> LogXYDomain::calculateGeometryPoints(const QVector<QPointF> &vector) const
150 150 {
151 151 const qreal deltaX = m_size.width() / (m_logRightX - m_logLeftX);
152 152 const qreal deltaY = m_size.height() / (m_maxY - m_minY);
@@ -54,7 +54,7 public:
54 54
55 55 QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const;
56 56 QPointF calculateDomainPoint(const QPointF &point) const;
57 QVector<QPointF> calculateGeometryPoints(const QList<QPointF> &vector) const;
57 QVector<QPointF> calculateGeometryPoints(const QVector<QPointF> &vector) const;
58 58
59 59 bool attachAxis(QAbstractAxis *axis);
60 60 bool detachAxis(QAbstractAxis *axis);
@@ -53,7 +53,7 QPointF PolarDomain::calculateGeometryPoint(const QPointF &point, bool &ok) cons
53 53 }
54 54 }
55 55
56 QVector<QPointF> PolarDomain::calculateGeometryPoints(const QList<QPointF> &vector) const
56 QVector<QPointF> PolarDomain::calculateGeometryPoints(const QVector<QPointF> &vector) const
57 57 {
58 58 QVector<QPointF> result;
59 59 result.resize(vector.count());
@@ -43,7 +43,7 public:
43 43 void setSize(const QSizeF &size);
44 44
45 45 QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const;
46 QVector<QPointF> calculateGeometryPoints(const QList<QPointF> &vector) const;
46 QVector<QPointF> calculateGeometryPoints(const QVector<QPointF> &vector) const;
47 47
48 48 virtual qreal toAngularCoordinate(qreal value, bool &ok) const = 0;
49 49 virtual qreal toRadialCoordinate(qreal value, bool &ok) const = 0;
@@ -146,7 +146,7 QPointF XLogYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) cons
146 146 return QPointF(x, y);
147 147 }
148 148
149 QVector<QPointF> XLogYDomain::calculateGeometryPoints(const QList<QPointF> &vector) const
149 QVector<QPointF> XLogYDomain::calculateGeometryPoints(const QVector<QPointF> &vector) const
150 150 {
151 151 const qreal deltaX = m_size.width() / (m_maxX - m_minX);
152 152 const qreal deltaY = m_size.height() / qAbs(m_logRightY - m_logLeftY);
@@ -54,7 +54,7 public:
54 54
55 55 QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const;
56 56 QPointF calculateDomainPoint(const QPointF &point) const;
57 QVector<QPointF> calculateGeometryPoints(const QList<QPointF> &vector) const;
57 QVector<QPointF> calculateGeometryPoints(const QVector<QPointF> &vector) const;
58 58
59 59 bool attachAxis(QAbstractAxis *axis);
60 60 bool detachAxis(QAbstractAxis *axis);
@@ -144,7 +144,7 QPointF XYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) const
144 144 return QPointF(x, y);
145 145 }
146 146
147 QVector<QPointF> XYDomain::calculateGeometryPoints(const QList<QPointF> &vector) const
147 QVector<QPointF> XYDomain::calculateGeometryPoints(const QVector<QPointF> &vector) const
148 148 {
149 149 const qreal deltaX = m_size.width() / (m_maxX - m_minX);
150 150 const qreal deltaY = m_size.height() / (m_maxY - m_minY);
@@ -54,7 +54,7 public:
54 54
55 55 QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const;
56 56 QPointF calculateDomainPoint(const QPointF &point) const;
57 QVector<QPointF> calculateGeometryPoints(const QList<QPointF> &vector) const;
57 QVector<QPointF> calculateGeometryPoints(const QVector<QPointF> &vector) const;
58 58 };
59 59
60 60 QT_CHARTS_END_NAMESPACE
@@ -70,6 +70,17 QPainterPath LineChartItem::shape() const
70 70
71 71 void LineChartItem::updateGeometry()
72 72 {
73 static const QRectF dummyRect = QRectF(0.0, 0.0, 0.001, 0.001);
74 if (m_series->useOpenGL()) {
75 // Fake a miniscule region, so we trigger changed signal.
76 if (m_rect.width() != dummyRect.width()) {
77 prepareGeometryChange();
78 m_rect = dummyRect;
79 }
80 update();
81 return;
82 }
83
73 84 // Store the points to a local variable so that the old line gets properly cleared
74 85 // when animation starts.
75 86 m_linePoints = geometryPoints();
@@ -345,6 +356,9 void LineChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt
345 356 Q_UNUSED(widget)
346 357 Q_UNUSED(option)
347 358
359 if (m_series->useOpenGL())
360 return;
361
348 362 QRectF clipRect = QRectF(QPointF(0, 0), domain()->size());
349 363
350 364 painter->save();
@@ -135,6 +135,69 QT_CHARTS_BEGIN_NAMESPACE
135 135 */
136 136
137 137 /*!
138 \property QAbstractSeries::useOpenGL
139 \brief Specifies whether or not the series drawing is accelerated with OpenGL.
140
141 Drawing series with OpenGL is supported only for QLineSeries and QScatterSeries.
142 Line series used as edge series for a QAreaSeries cannot use OpenGL acceleration.
143 When a chart contains any series that are drawn with OpenGL, a transparent QOpenGLWidget
144 is created on top of the chart plot area. Specified series are not drawn on the underlying
145 QGraphicsView, but are instead drawn on the created QOpenGLWidget.
146
147 Performance gained from using OpenGL to accelerate series drawing depends on the underlying
148 hardware, but in most cases it is significant. For example, on a standard desktop computer,
149 enabling OpenGL acceleration for a series typically allows rendering at least hundred times
150 more points without reduction on the frame rate.
151 Chart size also has less effect on the frame rate.
152
153 The OpenGL acceleration of series drawing is meant for use cases that need fast drawing of
154 large numbers of points. It is optimized for efficiency, and therefore the series using
155 it lack support for some features available to non-accelerated series.
156
157 There are the following restrictions imposed on charts and series when using OpenGL
158 acceleration:
159
160 \list
161 \li Series animations are not supported for accelerated series.
162 \li Antialiasing is not supported for accelerated series.
163 \li Pen styles and marker shapes are ignored for accelerated series.
164 Only solid lines and plain scatter dots are supported.
165 The scatter dots may be circular or rectangular, depending on the underlying graphics
166 hardware and drivers.
167 \li Polar charts are not supported for accelerated series.
168 \li Since the accelerated series are drawn on top of the entire graphics view, they get drawn
169 on top of any other graphics items that you may have on top chart in the same scene.
170 \li To enable QOpenGLWidget to be partially transparent, it needs to be stacked on top of
171 all other widgets. This means you cannot have other widgets partially covering the
172 chart.
173 \endlist
174
175 The default value is \c{false}.
176 */
177 /*!
178 \qmlproperty bool AbstractSeries::useOpenGL
179 Specifies whether or not the series is drawn with OpenGL.
180
181 Drawing series with OpenGL is supported only for LineSeries and ScatterSeries.
182
183 For more details, see QAbstractSeries::useOpenGL documentation. QML applications have similar
184 restrictions as those listed in QAbstractSeries::useOpenGL documentation,
185 except there is no restriction about covering the ChartView partially with other
186 items due to different rendering mechanism.
187
188 The default value is \c{false}.
189 */
190
191 /*!
192 \fn void QAbstractSeries::useOpenGLChanged()
193 Emitted when the useOpenGL property value changes.
194 */
195 /*!
196 \qmlsignal AbstractSeries::onUseOpenGLChanged()
197 Emitted when the useOpenGL property value changes.
198 */
199
200 /*!
138 201 \internal
139 202 \brief Constructs QAbstractSeries object with \a parent.
140 203 */
@@ -192,6 +255,28 void QAbstractSeries::setOpacity(qreal opacity)
192 255 }
193 256 }
194 257
258 void QAbstractSeries::setUseOpenGL(bool enable)
259 {
260 #ifdef QT_NO_OPENGL
261 Q_UNUSED(enable)
262 #else
263 bool polarChart = d_ptr->m_chart && d_ptr->m_chart->chartType() == QChart::ChartTypePolar;
264 bool supportedSeries = (type() == SeriesTypeLine || type() == SeriesTypeScatter);
265 if ((!enable || !d_ptr->m_blockOpenGL)
266 && supportedSeries
267 && enable != d_ptr->m_useOpenGL
268 && (!enable || !polarChart)) {
269 d_ptr->m_useOpenGL = enable;
270 emit useOpenGLChanged();
271 }
272 #endif
273 }
274
275 bool QAbstractSeries::useOpenGL() const
276 {
277 return d_ptr->m_useOpenGL;
278 }
279
195 280 /*!
196 281 \brief Returns the chart where series belongs to.
197 282
@@ -274,7 +359,9 QAbstractSeriesPrivate::QAbstractSeriesPrivate(QAbstractSeries *q)
274 359 m_item(0),
275 360 m_domain(new XYDomain()),
276 361 m_visible(true),
277 m_opacity(1.0)
362 m_opacity(1.0),
363 m_useOpenGL(false),
364 m_blockOpenGL(false)
278 365 {
279 366 }
280 367
@@ -354,6 +441,15 bool QAbstractSeriesPrivate::reverseYAxis()
354 441 return reverseYAxis;
355 442 }
356 443
444 // This function can be used to explicitly block OpenGL use from some otherwise supported series,
445 // such as the line series used as edge series of an area series.
446 void QAbstractSeriesPrivate::setBlockOpenGL(bool enable)
447 {
448 m_blockOpenGL = enable;
449 if (enable)
450 q_ptr->setUseOpenGL(false);
451 }
452
357 453 #include "moc_qabstractseries.cpp"
358 454 #include "moc_qabstractseries_p.cpp"
359 455
@@ -36,6 +36,7 class QT_CHARTS_EXPORT QAbstractSeries : public QObject
36 36 Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged)
37 37 Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged)
38 38 Q_PROPERTY(SeriesType type READ type)
39 Q_PROPERTY(bool useOpenGL READ useOpenGL WRITE setUseOpenGL NOTIFY useOpenGLChanged)
39 40 Q_ENUMS(SeriesType)
40 41
41 42 public:
@@ -67,6 +68,8 public:
67 68 bool isVisible() const;
68 69 qreal opacity() const;
69 70 void setOpacity(qreal opacity);
71 void setUseOpenGL(bool enable = true);
72 bool useOpenGL() const;
70 73
71 74 QChart *chart() const;
72 75
@@ -81,6 +84,7 Q_SIGNALS:
81 84 void nameChanged();
82 85 void visibleChanged();
83 86 void opacityChanged();
87 void useOpenGLChanged();
84 88
85 89 protected:
86 90 QScopedPointer<QAbstractSeriesPrivate> d_ptr;
@@ -89,6 +93,7 protected:
89 93 friend class ChartThemeManager;
90 94 friend class QLegendPrivate;
91 95 friend class DeclarativeChart;
96 friend class QAreaSeries;
92 97 };
93 98
94 99 QT_CHARTS_END_NAMESPACE
@@ -81,6 +81,8 public:
81 81 bool reverseXAxis();
82 82 bool reverseYAxis();
83 83
84 void setBlockOpenGL(bool enable);
85
84 86 Q_SIGNALS:
85 87 void countChanged();
86 88
@@ -96,6 +98,8 private:
96 98 bool m_visible;
97 99 qreal m_opacity;
98 100 ChartPresenter *m_presenter;
101 bool m_useOpenGL;
102 bool m_blockOpenGL;
99 103
100 104 friend class QAbstractSeries;
101 105 friend class ChartDataSet;
@@ -130,6 +130,18 void ScatterChartItem::markerDoubleClicked(QGraphicsItem *marker)
130 130
131 131 void ScatterChartItem::updateGeometry()
132 132 {
133 static const QRectF dummyRect = QRectF(0.0, 0.0, 0.001, 0.001);
134 if (m_series->useOpenGL()) {
135 if (m_items.childItems().count())
136 deletePoints(m_items.childItems().count());
137 // Fake a miniscule region, so we trigger changed signal.
138 if (m_rect.width() != dummyRect.width()) {
139 prepareGeometryChange();
140 m_rect = dummyRect;
141 }
142 update();
143 return;
144 }
133 145
134 146 const QVector<QPointF>& points = geometryPoints();
135 147
@@ -199,6 +211,9 void ScatterChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *
199 211 Q_UNUSED(option)
200 212 Q_UNUSED(widget)
201 213
214 if (m_series->useOpenGL())
215 return;
216
202 217 QRectF clipRect = QRectF(QPointF(0, 0), domain()->size());
203 218
204 219 painter->save();
@@ -238,7 +238,7 QT_CHARTS_BEGIN_NAMESPACE
238 238 \sa pointLabelsVisible
239 239 */
240 240 /*!
241 \fn void QXYSeries::pointLabelsClippintChanged(bool clipping)
241 \fn void QXYSeries::pointLabelsClippingChanged(bool clipping)
242 242 The clipping of the data point labels is changed to \a clipping.
243 243 */
244 244 /*!
@@ -398,6 +398,11 QT_CHARTS_BEGIN_NAMESPACE
398 398 */
399 399
400 400 /*!
401 \fn void QXYSeries::penChanged(const QPen &pen)
402 \brief Signal is emitted when the line pen has changed to \a pen.
403 */
404
405 /*!
401 406 \fn void QXYSeriesPrivate::updated()
402 407 \brief \internal
403 408 */
@@ -539,7 +544,7 void QXYSeries::replace(int index, const QPointF &newPoint)
539 544 \note This is much faster than replacing data points one by one,
540 545 or first clearing all data, and then appending the new data. Emits QXYSeries::pointsReplaced()
541 546 when the points have been replaced. However, note that using the overload that takes
542 \c{QVector<QPointF>} as parameter is slightly faster than using this overload.
547 \c{QVector<QPointF>} as parameter is faster than using this overload.
543 548 \sa pointsReplaced()
544 549 */
545 550 void QXYSeries::replace(QList<QPointF> points)
@@ -634,7 +639,8 void QXYSeries::clear()
634 639 }
635 640
636 641 /*!
637 Returns list of points in the series.
642 Returns the points in the series as a list.
643 Use QXYSeries::pointsVector() for better performance.
638 644 */
639 645 QList<QPointF> QXYSeries::points() const
640 646 {
@@ -643,6 +649,16 QList<QPointF> QXYSeries::points() const
643 649 }
644 650
645 651 /*!
652 Returns the points in the series as a vector.
653 This is more efficient that calling QXYSeries::points();
654 */
655 QVector<QPointF> QXYSeries::pointsVector() const
656 {
657 Q_D(const QXYSeries);
658 return d->m_points;
659 }
660
661 /*!
646 662 Returns point at \a index in internal points vector.
647 663 */
648 664 const QPointF &QXYSeries::at(int index) const
@@ -675,6 +691,7 void QXYSeries::setPen(const QPen &pen)
675 691 emit d->updated();
676 692 if (emitColorChanged)
677 693 emit colorChanged(pen.color());
694 emit penChanged(pen);
678 695 }
679 696 }
680 697
@@ -864,7 +881,7 void QXYSeriesPrivate::initializeDomain()
864 881
865 882 Q_Q(QXYSeries);
866 883
867 const QList<QPointF>& points = q->points();
884 const QVector<QPointF> &points = q->pointsVector();
868 885
869 886 if (!points.isEmpty()) {
870 887 minX = points[0].x();
@@ -65,6 +65,7 public:
65 65
66 66 int count() const;
67 67 QList<QPointF> points() const;
68 QVector<QPointF> pointsVector() const;
68 69 const QPointF &at(int index) const;
69 70
70 71 QXYSeries &operator << (const QPointF &point);
@@ -117,6 +118,7 Q_SIGNALS:
117 118 void pointLabelsColorChanged(const QColor &color);
118 119 void pointLabelsClippingChanged(bool clipping);
119 120 void pointsRemoved(int index, int count);
121 void penChanged(const QPen &pen);
120 122
121 123 private:
122 124 Q_DECLARE_PRIVATE(QXYSeries)
@@ -21,6 +21,8
21 21 #include <private/qxyseries_p.h>
22 22 #include <private/chartpresenter_p.h>
23 23 #include <private/abstractdomain_p.h>
24 #include <private/chartdataset_p.h>
25 #include <private/glxyseriesdata_p.h>
24 26 #include <QtCharts/QXYModelMapper>
25 27 #include <private/qabstractaxis_p.h>
26 28 #include <QtGui/QPainter>
@@ -106,6 +108,13 void XYChart::updateChart(QVector<QPointF> &oldPoints, QVector<QPointF> &newPoin
106 108 }
107 109 }
108 110
111 void XYChart::updateGlChart()
112 {
113 presenter()->ensureGLWidget();
114 dataSet()->glXYSeriesDataManager()->setPoints(m_series, domain());
115 updateGeometry();
116 }
117
109 118 //handlers
110 119
111 120 void XYChart::handlePointAdded(int index)
@@ -113,20 +122,23 void XYChart::handlePointAdded(int index)
113 122 Q_ASSERT(index < m_series->count());
114 123 Q_ASSERT(index >= 0);
115 124
116 QVector<QPointF> points;
117
118 if (m_dirty || m_points.isEmpty()) {
119 points = domain()->calculateGeometryPoints(m_series->points());
125 if (m_series->useOpenGL()) {
126 updateGlChart();
120 127 } else {
121 points = m_points;
122 QPointF point = domain()->calculateGeometryPoint(m_series->points()[index], m_validData);
123 if (!m_validData)
124 m_points.clear();
125 else
126 points.insert(index, point);
128 QVector<QPointF> points;
129 if (m_dirty || m_points.isEmpty()) {
130 points = domain()->calculateGeometryPoints(m_series->pointsVector());
131 } else {
132 points = m_points;
133 QPointF point = domain()->calculateGeometryPoint(m_series->pointsVector().at(index),
134 m_validData);
135 if (!m_validData)
136 m_points.clear();
137 else
138 points.insert(index, point);
139 }
140 updateChart(m_points, points, index);
127 141 }
128
129 updateChart(m_points, points, index);
130 142 }
131 143
132 144 void XYChart::handlePointRemoved(int index)
@@ -134,16 +146,18 void XYChart::handlePointRemoved(int index)
134 146 Q_ASSERT(index <= m_series->count());
135 147 Q_ASSERT(index >= 0);
136 148
137 QVector<QPointF> points;
138
139 if (m_dirty || m_points.isEmpty()) {
140 points = domain()->calculateGeometryPoints(m_series->points());
149 if (m_series->useOpenGL()) {
150 updateGlChart();
141 151 } else {
142 points = m_points;
143 points.remove(index);
152 QVector<QPointF> points;
153 if (m_dirty || m_points.isEmpty()) {
154 points = domain()->calculateGeometryPoints(m_series->pointsVector());
155 } else {
156 points = m_points;
157 points.remove(index);
158 }
159 updateChart(m_points, points, index);
144 160 }
145
146 updateChart(m_points, points, index);
147 161 }
148 162
149 163 void XYChart::handlePointsRemoved(int index, int count)
@@ -151,16 +165,18 void XYChart::handlePointsRemoved(int index, int count)
151 165 Q_ASSERT(index <= m_series->count());
152 166 Q_ASSERT(index >= 0);
153 167
154 QVector<QPointF> points;
155
156 if (m_dirty || m_points.isEmpty()) {
157 points = domain()->calculateGeometryPoints(m_series->points());
168 if (m_series->useOpenGL()) {
169 updateGlChart();
158 170 } else {
159 points = m_points;
160 points.remove(index, count);
171 QVector<QPointF> points;
172 if (m_dirty || m_points.isEmpty()) {
173 points = domain()->calculateGeometryPoints(m_series->pointsVector());
174 } else {
175 points = m_points;
176 points.remove(index, count);
177 }
178 updateChart(m_points, points, index);
161 179 }
162
163 updateChart(m_points, points, index);
164 180 }
165 181
166 182 void XYChart::handlePointReplaced(int index)
@@ -168,34 +184,45 void XYChart::handlePointReplaced(int index)
168 184 Q_ASSERT(index < m_series->count());
169 185 Q_ASSERT(index >= 0);
170 186
171 QVector<QPointF> points;
172
173 if (m_dirty || m_points.isEmpty()) {
174 points = domain()->calculateGeometryPoints(m_series->points());
187 if (m_series->useOpenGL()) {
188 updateGlChart();
175 189 } else {
176 QPointF point = domain()->calculateGeometryPoint(m_series->points()[index], m_validData);
177 if (!m_validData)
178 m_points.clear();
179 points = m_points;
180 if (m_validData)
181 points.replace(index, point);
190 QVector<QPointF> points;
191 if (m_dirty || m_points.isEmpty()) {
192 points = domain()->calculateGeometryPoints(m_series->pointsVector());
193 } else {
194 QPointF point = domain()->calculateGeometryPoint(m_series->pointsVector().at(index),
195 m_validData);
196 if (!m_validData)
197 m_points.clear();
198 points = m_points;
199 if (m_validData)
200 points.replace(index, point);
201 }
202 updateChart(m_points, points, index);
182 203 }
183
184 updateChart(m_points, points, index);
185 204 }
186 205
187 206 void XYChart::handlePointsReplaced()
188 207 {
189 // All the points were replaced -> recalculate
190 QVector<QPointF> points = domain()->calculateGeometryPoints(m_series->points());
191 updateChart(m_points, points, -1);
208 if (m_series->useOpenGL()) {
209 updateGlChart();
210 } else {
211 // All the points were replaced -> recalculate
212 QVector<QPointF> points = domain()->calculateGeometryPoints(m_series->pointsVector());
213 updateChart(m_points, points, -1);
214 }
192 215 }
193 216
194 217 void XYChart::handleDomainUpdated()
195 218 {
196 if (isEmpty()) return;
197 QVector<QPointF> points = domain()->calculateGeometryPoints(m_series->points());
198 updateChart(m_points, points);
219 if (m_series->useOpenGL()) {
220 updateGlChart();
221 } else {
222 if (isEmpty()) return;
223 QVector<QPointF> points = domain()->calculateGeometryPoints(m_series->pointsVector());
224 updateChart(m_points, points);
225 }
199 226 }
200 227
201 228 bool XYChart::isEmpty()
@@ -6,12 +6,14 SOURCES += \
6 6 $$PWD/qxyseries.cpp \
7 7 $$PWD/qxymodelmapper.cpp \
8 8 $$PWD/qvxymodelmapper.cpp \
9 $$PWD/qhxymodelmapper.cpp
9 $$PWD/qhxymodelmapper.cpp \
10 $$PWD/glxyseriesdata.cpp
10 11
11 12 PRIVATE_HEADERS += \
12 13 $$PWD/xychart_p.h \
13 14 $$PWD/qxyseries_p.h \
14 $$PWD/qxymodelmapper_p.h
15 $$PWD/qxymodelmapper_p.h \
16 $$PWD/glxyseriesdata_p.h
15 17
16 18 PUBLIC_HEADERS += \
17 19 $$PWD/qxyseries.h \
@@ -76,6 +76,7 Q_SIGNALS:
76 76
77 77 protected:
78 78 virtual void updateChart(QVector<QPointF> &oldPoints, QVector<QPointF> &newPoints, int index = -1);
79 virtual void updateGlChart();
79 80
80 81 private:
81 82 inline bool isEmpty();
@@ -33,7 +33,9 SOURCES += \
33 33 declarativemargins.cpp \
34 34 declarativeaxes.cpp \
35 35 declarativepolarchart.cpp \
36 declarativeboxplotseries.cpp
36 declarativeboxplotseries.cpp \
37 declarativechartnode.cpp \
38 declarativerendernode.cpp
37 39
38 40 HEADERS += \
39 41 declarativechart.h \
@@ -49,7 +51,9 HEADERS += \
49 51 declarativemargins.h \
50 52 declarativeaxes.h \
51 53 declarativepolarchart.h \
52 declarativeboxplotseries.h
54 declarativeboxplotseries.h \
55 declarativechartnode.h \
56 declarativerendernode.h
53 57
54 58 OTHER_FILES = qmldir
55 59
@@ -25,6 +25,8
25 25 #include "declarativesplineseries.h"
26 26 #include "declarativeboxplotseries.h"
27 27 #include "declarativescatterseries.h"
28 #include "declarativechartnode.h"
29 #include "declarativerendernode.h"
28 30 #include <QtCharts/QBarCategoryAxis>
29 31 #include <QtCharts/QValueAxis>
30 32 #include <QtCharts/QLogValueAxis>
@@ -34,6 +36,7
34 36 #include <private/chartdataset_p.h>
35 37 #include "declarativeaxes.h"
36 38 #include <private/qchart_p.h>
39 #include <private/chartpresenter_p.h>
37 40 #include <QtCharts/QPolarChart>
38 41
39 42 #ifndef QT_ON_ARM
@@ -88,6 +91,7 QT_CHARTS_BEGIN_NAMESPACE
88 91 /*!
89 92 \qmlproperty easing ChartView::animationEasingCurve
90 93 The easing curve of the animation for the chart.
94 */
91 95
92 96 /*!
93 97 \qmlproperty Font ChartView::titleFont
@@ -312,34 +316,42 QT_CHARTS_BEGIN_NAMESPACE
312 316 */
313 317
314 318 DeclarativeChart::DeclarativeChart(QQuickItem *parent)
315 : QQuickPaintedItem(parent)
319 : QQuickItem(parent)
316 320 {
317 321 initChart(QChart::ChartTypeCartesian);
318 322 }
319 323
320 324 DeclarativeChart::DeclarativeChart(QChart::ChartType type, QQuickItem *parent)
321 : QQuickPaintedItem(parent)
325 : QQuickItem(parent)
322 326 {
323 327 initChart(type);
324 328 }
325 329
326 330 void DeclarativeChart::initChart(QChart::ChartType type)
327 331 {
328 m_currentSceneImage = 0;
332 m_sceneImage = 0;
333 m_sceneImageDirty = false;
329 334 m_guiThreadId = QThread::currentThreadId();
330 335 m_paintThreadId = 0;
331 336 m_updatePending = false;
332 337
338 setFlag(ItemHasContents, true);
339
333 340 if (type == QChart::ChartTypePolar)
334 341 m_chart = new QPolarChart();
335 342 else
336 343 m_chart = new QChart();
337 344
345 m_chart->d_ptr->m_presenter->glSetUseWidget(false);
346 m_glXYDataManager = m_chart->d_ptr->m_dataset->glXYSeriesDataManager();
347
338 348 m_scene = new QGraphicsScene(this);
339 349 m_scene->addItem(m_chart);
340 350
341 351 setAntialiasing(QQuickItem::antialiasing());
342 connect(m_scene, SIGNAL(changed(QList<QRectF>)), this, SLOT(sceneChanged(QList<QRectF>)));
352 connect(m_scene, &QGraphicsScene::changed, this, &DeclarativeChart::sceneChanged);
353 connect(this, &DeclarativeChart::needRender, this, &DeclarativeChart::renderScene,
354 Qt::QueuedConnection);
343 355 connect(this, SIGNAL(antialiasingChanged(bool)), this, SLOT(handleAntialiasingChanged(bool)));
344 356
345 357 setAcceptedMouseButtons(Qt::AllButtons);
@@ -377,10 +389,7 void DeclarativeChart::changeMargins(int top, int bottom, int left, int right)
377 389 DeclarativeChart::~DeclarativeChart()
378 390 {
379 391 delete m_chart;
380 m_sceneImageLock.lock();
381 delete m_currentSceneImage;
382 m_currentSceneImage = 0;
383 m_sceneImageLock.unlock();
392 delete m_sceneImage;
384 393 }
385 394
386 395 void DeclarativeChart::childEvent(QChildEvent *event)
@@ -493,19 +502,79 void DeclarativeChart::geometryChanged(const QRectF &newGeometry, const QRectF &
493 502 QQuickItem::geometryChanged(newGeometry, oldGeometry);
494 503 }
495 504
496 void DeclarativeChart::sceneChanged(QList<QRectF> region)
505 QSGNode *DeclarativeChart::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
497 506 {
498 Q_UNUSED(region);
507 DeclarativeChartNode *node = static_cast<DeclarativeChartNode *>(oldNode);
499 508
500 if (m_guiThreadId == m_paintThreadId) {
501 // Rendering in gui thread, no need for shenannigans, just update
502 update();
503 } else {
504 // Multi-threaded rendering, need to ensure scene is actually rendered in gui thread
505 if (!m_updatePending) {
509 if (!node) {
510 node = new DeclarativeChartNode(window());
511 connect(window(), &QQuickWindow::beforeRendering,
512 node->glRenderNode(), &DeclarativeRenderNode::render);
513 }
514
515 const QRectF &bRect = boundingRect();
516
517 // Update GL data
518 if (m_glXYDataManager->dataMap().size() || m_glXYDataManager->mapDirty()) {
519 const QRectF &plotArea = m_chart->plotArea();
520 const QSizeF &chartAreaSize = m_chart->size();
521
522 // We can't use chart's plot area directly, as graphicscene has some internal minimum size
523 const qreal normalizedX = plotArea.x() / chartAreaSize.width();
524 const qreal normalizedY = plotArea.y() / chartAreaSize.height();
525 const qreal normalizedWidth = plotArea.width() / chartAreaSize.width();
526 const qreal normalizedHeight = plotArea.height() / chartAreaSize.height();
527
528 QRectF adjustedPlotArea(normalizedX * bRect.width(),
529 normalizedY * bRect.height(),
530 normalizedWidth * bRect.width(),
531 normalizedHeight * bRect.height());
532
533 const QSize &adjustedPlotSize = adjustedPlotArea.size().toSize();
534 if (adjustedPlotSize != node->glRenderNode()->textureSize())
535 node->glRenderNode()->setTextureSize(adjustedPlotSize);
536
537 node->glRenderNode()->setRect(adjustedPlotArea);
538 node->glRenderNode()->setSeriesData(m_glXYDataManager->mapDirty(),
539 m_glXYDataManager->dataMap());
540
541 // Clear dirty flags from original xy data
542 m_glXYDataManager->clearAllDirty();
543 }
544
545 // Copy chart (if dirty) to chart node
546 if (m_sceneImageDirty) {
547 node->createTextureFromImage(*m_sceneImage);
548 m_sceneImageDirty = false;
549 }
550
551 node->setRect(bRect);
552
553 return node;
554 }
555
556 void DeclarativeChart::sceneChanged(QList<QRectF> region)
557 {
558 const int count = region.size();
559 const qreal limitSize = 0.01;
560 if (count && !m_updatePending) {
561 qreal totalSize = 0.0;
562 for (int i = 0; i < count; i++) {
563 const QRectF &reg = region.at(i);
564 totalSize += (reg.height() * reg.width());
565 if (totalSize >= limitSize)
566 break;
567 }
568 // Ignore region updates that change less than small fraction of a pixel, as there is
569 // little point regenerating the image in these cases. These are typically cases
570 // where OpenGL series are drawn to otherwise static chart.
571 if (totalSize >= limitSize) {
506 572 m_updatePending = true;
507 573 // Do async render to avoid some unnecessary renders.
508 QTimer::singleShot(0, this, SLOT(renderScene()));
574 emit needRender();
575 } else {
576 // We do want to call update to trigger possible gl series updates.
577 update();
509 578 }
510 579 }
511 580 }
@@ -513,45 +582,22 void DeclarativeChart::sceneChanged(QList<QRectF> region)
513 582 void DeclarativeChart::renderScene()
514 583 {
515 584 m_updatePending = false;
516 m_sceneImageLock.lock();
517 delete m_currentSceneImage;
518 m_currentSceneImage = new QImage(m_chart->size().toSize(), QImage::Format_ARGB32);
519 m_currentSceneImage->fill(Qt::transparent);
520 QPainter painter(m_currentSceneImage);
521 if (antialiasing())
522 painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
523 QRect renderRect(QPoint(0, 0), m_chart->size().toSize());
524 m_scene->render(&painter, renderRect, renderRect);
525 m_sceneImageLock.unlock();
526
527 update();
528 }
529
530 void DeclarativeChart::paint(QPainter *painter)
531 {
532 if (!m_paintThreadId) {
533 m_paintThreadId = QThread::currentThreadId();
534 if (m_guiThreadId == m_paintThreadId) {
535 // No need for scene image in single threaded rendering, so delete
536 // the one that got made by default before the rendering type was
537 // detected.
538 delete m_currentSceneImage;
539 m_currentSceneImage = 0;
540 }
585 m_sceneImageDirty = true;
586 QSize chartSize = m_chart->size().toSize();
587 if (!m_sceneImage || chartSize != m_sceneImage->size()) {
588 delete m_sceneImage;
589 m_sceneImage = new QImage(chartSize, QImage::Format_ARGB32);
590 m_sceneImage->fill(Qt::transparent);
541 591 }
542 592
543 if (m_guiThreadId == m_paintThreadId) {
544 QRectF renderRect(QPointF(0, 0), m_chart->size());
545 m_scene->render(painter, renderRect, renderRect);
546 } else {
547 m_sceneImageLock.lock();
548 if (m_currentSceneImage) {
549 QRect imageRect(QPoint(0, 0), m_currentSceneImage->size());
550 QRect itemRect(QPoint(0, 0), QSize(width(), height()));
551 painter->drawImage(itemRect, *m_currentSceneImage, imageRect);
552 }
553 m_sceneImageLock.unlock();
593 QPainter painter(m_sceneImage);
594 if (antialiasing()) {
595 painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing
596 | QPainter::SmoothPixmapTransform);
554 597 }
598 QRect renderRect(QPoint(0, 0), chartSize);
599 m_scene->render(&painter, renderRect, renderRect);
600 update();
555 601 }
556 602
557 603 void DeclarativeChart::mousePressEvent(QMouseEvent *event)
@@ -19,11 +19,11
19 19 #ifndef DECLARATIVECHART_H
20 20 #define DECLARATIVECHART_H
21 21
22 #include <private/glxyseriesdata_p.h>
23
22 24 #include <QtCore/QtGlobal>
23 25 #include <QtQuick/QQuickItem>
24 #include <QtQuick/QQuickPaintedItem>
25 26 #include <QtWidgets/QGraphicsScene>
26 #include <QtCore/QMutex>
27 27
28 28 #include <QtCharts/QChart>
29 29 #include <QtCore/QLocale>
@@ -34,7 +34,7 class DeclarativeMargins;
34 34 class Domain;
35 35 class DeclarativeAxes;
36 36
37 class DeclarativeChart : public QQuickPaintedItem
37 class DeclarativeChart : public QQuickItem
38 38 {
39 39 Q_OBJECT
40 40 Q_PROPERTY(Theme theme READ theme WRITE setTheme)
@@ -102,7 +102,7 public: // From parent classes
102 102 void childEvent(QChildEvent *event);
103 103 void componentComplete();
104 104 void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry);
105 void paint(QPainter *painter);
105 QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *);
106 106 protected:
107 107 void mousePressEvent(QMouseEvent *event);
108 108 void mouseReleaseEvent(QMouseEvent *event);
@@ -199,6 +199,7 Q_SIGNALS:
199 199 Q_REVISION(4) void localeChanged();
200 200 Q_REVISION(5) void animationDurationChanged(int msecs);
201 201 Q_REVISION(5) void animationEasingCurveChanged(QEasingCurve curve);
202 void needRender();
202 203
203 204 private Q_SLOTS:
204 205 void changeMargins(int top, int bottom, int left, int right);
@@ -227,12 +228,13 private:
227 228 QPoint m_lastMouseMoveScreenPoint;
228 229 Qt::MouseButton m_mousePressButton;
229 230 Qt::MouseButtons m_mousePressButtons;
230 QMutex m_sceneImageLock;
231 QImage *m_currentSceneImage;
231 QImage *m_sceneImage;
232 bool m_sceneImageDirty;
232 233 bool m_updatePending;
233 234 Qt::HANDLE m_paintThreadId;
234 235 Qt::HANDLE m_guiThreadId;
235 236 DeclarativeMargins *m_margins;
237 GLXYSeriesDataManager *m_glXYDataManager;
236 238 };
237 239
238 240 QT_CHARTS_END_NAMESPACE
@@ -19,8 +19,8
19 19 #include "../qxyseries/tst_qxyseries.h"
20 20 #include <QtCharts/QLineSeries>
21 21
22
23 22 Q_DECLARE_METATYPE(QList<QPointF>)
23 Q_DECLARE_METATYPE(QVector<QPointF>)
24 24
25 25 class tst_QLineSeries : public tst_QXYSeries
26 26 {
@@ -76,6 +76,7 void tst_QLineSeries::qlineseries()
76 76 QCOMPARE(series.count(),0);
77 77 QCOMPARE(series.brush(), QBrush());
78 78 QCOMPARE(series.points(), QList<QPointF>());
79 QCOMPARE(series.pointsVector(), QVector<QPointF>());
79 80 QCOMPARE(series.pen(), QPen());
80 81 QCOMPARE(series.pointsVisible(), false);
81 82 QCOMPARE(series.pointLabelsVisible(), false);
@@ -20,6 +20,7
20 20 #include <QtCharts/QScatterSeries>
21 21
22 22 Q_DECLARE_METATYPE(QList<QPointF>)
23 Q_DECLARE_METATYPE(QVector<QPointF>)
23 24
24 25 class tst_QScatterSeries : public tst_QXYSeries
25 26 {
@@ -75,6 +76,7 void tst_QScatterSeries::qscatterseries()
75 76 QCOMPARE(series.count(),0);
76 77 QCOMPARE(series.brush(), QBrush());
77 78 QCOMPARE(series.points(), QList<QPointF>());
79 QCOMPARE(series.pointsVector(), QVector<QPointF>());
78 80 QCOMPARE(series.pen(), QPen());
79 81 QCOMPARE(series.pointsVisible(), false);
80 82
@@ -20,6 +20,7
20 20 #include <QtCharts/QSplineSeries>
21 21
22 22 Q_DECLARE_METATYPE(QList<QPointF>)
23 Q_DECLARE_METATYPE(QVector<QPointF>)
23 24
24 25 class tst_QSplineSeries : public tst_QXYSeries
25 26 {
@@ -73,6 +74,7 void tst_QSplineSeries::qsplineseries()
73 74 QCOMPARE(series.count(),0);
74 75 QCOMPARE(series.brush(), QBrush());
75 76 QCOMPARE(series.points(), QList<QPointF>());
77 QCOMPARE(series.pointsVector(), QVector<QPointF>());
76 78 QCOMPARE(series.pen(), QPen());
77 79 QCOMPARE(series.pointsVisible(), false);
78 80
@@ -198,6 +198,7 void tst_QXYSeries::append_raw()
198 198 TRY_COMPARE(spy0.count(), 0);
199 199 TRY_COMPARE(addedSpy.count(), points.count());
200 200 QCOMPARE(m_series->points(), points);
201 QCOMPARE(m_series->pointsVector(), points.toVector());
201 202
202 203 // Process events between appends
203 204 foreach (const QPointF &point, otherPoints) {
General Comments 0
You need to be logged in to leave comments. Login now