##// 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
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 qint64 XYSeriesIODevice::writeData(const char * data, qint64 maxSize)
35 qint64 XYSeriesIODevice::writeData(const char * data, qint64 maxSize)
36 {
36 {
37 qint64 range = 2000;
37 qint64 range = 2000;
38 QList<QPointF> oldPoints = m_series->points();
38 QVector<QPointF> oldPoints = m_series->pointsVector();
39 QList<QPointF> points;
39 QVector<QPointF> points;
40 int resolution = 4;
40 int resolution = 4;
41
41
42 if (oldPoints.count() < range) {
42 if (oldPoints.count() < range) {
43 points = m_series->points();
43 points = m_series->pointsVector();
44 } else {
44 } else {
45 for (int i = maxSize/resolution; i < oldPoints.count(); i++)
45 for (int i = maxSize/resolution; i < oldPoints.count(); i++)
46 points.append(QPointF(i - maxSize/resolution, oldPoints.at(i).y()));
46 points.append(QPointF(i - maxSize/resolution, oldPoints.at(i).y()));
@@ -50,7 +50,13 qtHaveModule(quick) {
50 qtHaveModule(multimedia) {
50 qtHaveModule(multimedia) {
51 SUBDIRS += audio
51 SUBDIRS += audio
52 } else {
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 !linux-arm*: {
62 !linux-arm*: {
@@ -189,7 +189,7 QChart *ThemeWidget::createAreaChart() const
189 for (int j(0); j < m_dataTable[i].count(); j++) {
189 for (int j(0); j < m_dataTable[i].count(); j++) {
190 Data data = m_dataTable[i].at(j);
190 Data data = m_dataTable[i].at(j);
191 if (lowerSeries) {
191 if (lowerSeries) {
192 const QList<QPointF>& points = lowerSeries->points();
192 const QVector<QPointF>& points = lowerSeries->pointsVector();
193 upperSeries->append(QPointF(j, points[i].y() + data.first.y()));
193 upperSeries->append(QPointF(j, points[i].y() + data.first.y()));
194 } else {
194 } else {
195 upperSeries->append(QPointF(j, data.first.y()));
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 import QtQuick.Layouts 1.0
20 import QtQuick.Layouts 1.0
21
21
22 ColumnLayout {
22 ColumnLayout {
23 property alias openGLButton: openGLButton
23 spacing: 8
24 spacing: 8
24 Layout.fillHeight: true
25 Layout.fillHeight: true
25 signal animationsEnabled(bool enabled)
26 signal animationsEnabled(bool enabled)
@@ -27,6 +28,7 ColumnLayout {
27 signal refreshRateChanged(variant rate);
28 signal refreshRateChanged(variant rate);
28 signal signalSourceChanged(string source, int signalCount, int sampleCount);
29 signal signalSourceChanged(string source, int signalCount, int sampleCount);
29 signal antialiasingEnabled(bool enabled)
30 signal antialiasingEnabled(bool enabled)
31 signal openGlChanged(bool enabled)
30
32
31 Text {
33 Text {
32 text: "Scope"
34 text: "Scope"
@@ -35,6 +37,14 ColumnLayout {
35 }
37 }
36
38
37 MultiButton {
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 text: "Graph: "
48 text: "Graph: "
39 items: ["line", "spline", "scatter"]
49 items: ["line", "spline", "scatter"]
40 currentSelection: 0
50 currentSelection: 0
@@ -17,13 +17,18
17 ****************************************************************************/
17 ****************************************************************************/
18
18
19 import QtQuick 2.0
19 import QtQuick 2.0
20 import QtCharts 2.0
20 import QtCharts 2.1
21
21
22 //![1]
22 //![1]
23 ChartView {
23 ChartView {
24 id: chartView
24 id: chartView
25 animationOptions: ChartView.NoAnimation
25 animationOptions: ChartView.NoAnimation
26 theme: ChartView.ChartThemeDark
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 ValueAxis {
33 ValueAxis {
29 id: axisY1
34 id: axisY1
@@ -40,7 +45,7 ChartView {
40 ValueAxis {
45 ValueAxis {
41 id: axisX
46 id: axisX
42 min: 0
47 min: 0
43 max: 1000
48 max: 1024
44 }
49 }
45
50
46 LineSeries {
51 LineSeries {
@@ -48,12 +53,14 ChartView {
48 name: "signal 1"
53 name: "signal 1"
49 axisX: axisX
54 axisX: axisX
50 axisY: axisY1
55 axisY: axisY1
56 useOpenGL: chartView.openGL
51 }
57 }
52 LineSeries {
58 LineSeries {
53 id: lineSeries2
59 id: lineSeries2
54 name: "signal 2"
60 name: "signal 2"
55 axisX: axisX
61 axisX: axisX
56 axisYRight: axisY2
62 axisYRight: axisY2
63 useOpenGL: chartView.openGL
57 }
64 }
58 //![1]
65 //![1]
59
66
@@ -78,18 +85,28 ChartView {
78 // but the series have their own y-axes to make it possible to control the y-offset
85 // but the series have their own y-axes to make it possible to control the y-offset
79 // of the "signal sources".
86 // of the "signal sources".
80 if (type == "line") {
87 if (type == "line") {
81 chartView.createSeries(ChartView.SeriesTypeLine, "signal 1", axisX, axisY1);
88 var series1 = chartView.createSeries(ChartView.SeriesTypeLine, "signal 1",
82 chartView.createSeries(ChartView.SeriesTypeLine, "signal 2", axisX, axisY2);
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 } else if (type == "spline") {
95 } else if (type == "spline") {
84 chartView.createSeries(ChartView.SeriesTypeSpline, "signal 1", axisX, axisY1);
96 chartView.createSeries(ChartView.SeriesTypeSpline, "signal 1", axisX, axisY1);
85 chartView.createSeries(ChartView.SeriesTypeSpline, "signal 2", axisX, axisY2);
97 chartView.createSeries(ChartView.SeriesTypeSpline, "signal 2", axisX, axisY2);
86 } else {
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 series1.markerSize = 3;
101 series1.markerSize = 3;
89 series1.borderColor = "transparent";
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 series2.markerSize = 3;
107 series2.markerSize = 3;
92 series2.borderColor = "transparent";
108 series2.borderColor = "transparent";
109 series2.useOpenGL = chartView.openGL
93 }
110 }
94 }
111 }
95
112
@@ -21,8 +21,8 import QtQuick 2.0
21 //![1]
21 //![1]
22 Rectangle {
22 Rectangle {
23 id: main
23 id: main
24 width: 400
24 width: 600
25 height: 300
25 height: 400
26 color: "#404040"
26 color: "#404040"
27
27
28 ControlPanel {
28 ControlPanel {
@@ -39,11 +39,22 Rectangle {
39 dataSource.generateData(0, signalCount, sampleCount);
39 dataSource.generateData(0, signalCount, sampleCount);
40 else
40 else
41 dataSource.generateData(1, signalCount, sampleCount);
41 dataSource.generateData(1, signalCount, sampleCount);
42 scopeView.axisX().max = sampleCount;
42 }
43 }
43 onAnimationsEnabled: scopeView.setAnimations(enabled);
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 onRefreshRateChanged: scopeView.changeRefreshRate(rate);
55 onRefreshRateChanged: scopeView.changeRefreshRate(rate);
46 onAntialiasingEnabled: scopeView.antialiasing = enabled;
56 onAntialiasingEnabled: scopeView.antialiasing = enabled;
57 onOpenGlChanged: scopeView.openGL = enabled;
47 }
58 }
48
59
49 //![2]
60 //![2]
@@ -56,4 +67,5 Rectangle {
56 height: main.height
67 height: main.height
57 }
68 }
58 //![2]
69 //![2]
70
59 }
71 }
@@ -345,7 +345,7 QT_CHARTS_BEGIN_NAMESPACE
345 \sa pointLabelsVisible
345 \sa pointLabelsVisible
346 */
346 */
347 /*!
347 /*!
348 \fn void QAreaSeries::pointLabelsClippintChanged(bool clipping)
348 \fn void QAreaSeries::pointLabelsClippingChanged(bool clipping)
349 The clipping of the data point labels is changed to \a clipping.
349 The clipping of the data point labels is changed to \a clipping.
350 */
350 */
351 /*!
351 /*!
@@ -395,9 +395,11 QAbstractSeries::SeriesType QAreaSeries::type() const
395 void QAreaSeries::setUpperSeries(QLineSeries *series)
395 void QAreaSeries::setUpperSeries(QLineSeries *series)
396 {
396 {
397 Q_D(QAreaSeries);
397 Q_D(QAreaSeries);
398 if (d->m_upperSeries != series)
398 if (d->m_upperSeries != series) {
399 series->d_ptr->setBlockOpenGL(true);
399 d->m_upperSeries = series;
400 d->m_upperSeries = series;
400 }
401 }
402 }
401
403
402 QLineSeries *QAreaSeries::upperSeries() const
404 QLineSeries *QAreaSeries::upperSeries() const
403 {
405 {
@@ -411,6 +413,7 QLineSeries *QAreaSeries::upperSeries() const
411 void QAreaSeries::setLowerSeries(QLineSeries *series)
413 void QAreaSeries::setLowerSeries(QLineSeries *series)
412 {
414 {
413 Q_D(QAreaSeries);
415 Q_D(QAreaSeries);
416 series->d_ptr->setBlockOpenGL(true);
414 d->m_lowerSeries = series;
417 d->m_lowerSeries = series;
415 }
418 }
416
419
@@ -624,7 +627,7 void QAreaSeriesPrivate::initializeDomain()
624 QLineSeries *lowerSeries = q->lowerSeries();
627 QLineSeries *lowerSeries = q->lowerSeries();
625
628
626 if (upperSeries) {
629 if (upperSeries) {
627 const QList<QPointF>& points = upperSeries->points();
630 const QVector<QPointF> &points = upperSeries->pointsVector();
628
631
629 for (int i = 0; i < points.count(); i++) {
632 for (int i = 0; i < points.count(); i++) {
630 qreal x = points[i].x();
633 qreal x = points[i].x();
@@ -636,8 +639,7 void QAreaSeriesPrivate::initializeDomain()
636 }
639 }
637 }
640 }
638 if (lowerSeries) {
641 if (lowerSeries) {
639
642 const QVector<QPointF> &points = lowerSeries->pointsVector();
640 const QList<QPointF>& points = lowerSeries->points();
641
643
642 for (int i = 0; i < points.count(); i++) {
644 for (int i = 0; i < points.count(); i++) {
643 qreal x = points[i].x();
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 void QAbstractAxis::setVisible(bool visible)
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 void QBarSet::setBorderColor(QColor color)
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 void QBarSet::setLabelColor(QColor color)
572 void QBarSet::setLabelColor(QColor color)
573 {
573 {
@@ -38,6 +38,7
38 #include <private/xlogypolardomain_p.h>
38 #include <private/xlogypolardomain_p.h>
39 #include <private/logxypolardomain_p.h>
39 #include <private/logxypolardomain_p.h>
40 #include <private/logxlogypolardomain_p.h>
40 #include <private/logxlogypolardomain_p.h>
41 #include <private/glxyseriesdata_p.h>
41
42
42 #ifndef QT_ON_ARM
43 #ifndef QT_ON_ARM
43 #include <QtCharts/QDateTimeAxis>
44 #include <QtCharts/QDateTimeAxis>
@@ -47,7 +48,8 QT_CHARTS_BEGIN_NAMESPACE
47
48
48 ChartDataSet::ChartDataSet(QChart *chart)
49 ChartDataSet::ChartDataSet(QChart *chart)
49 : QObject(chart),
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 qWarning() << QObject::tr("Can not add series. Series type is not supported by a polar chart.");
79 qWarning() << QObject::tr("Can not add series. Series type is not supported by a polar chart.");
78 return;
80 return;
79 }
81 }
82 // Disable OpenGL for series in polar charts
83 series->setUseOpenGL(false);
80 series->d_ptr->setDomain(new XYPolarDomain());
84 series->d_ptr->setDomain(new XYPolarDomain());
81 // Set the correct domain for upper and lower series too
85 // Set the correct domain for upper and lower series too
82 if (series->type() == QAbstractSeries::SeriesTypeArea) {
86 if (series->type() == QAbstractSeries::SeriesTypeArea) {
@@ -157,6 +161,10 void ChartDataSet::removeSeries(QAbstractSeries *series)
157 series->d_ptr->setDomain(new XYDomain());
161 series->d_ptr->setDomain(new XYDomain());
158 series->setParent(0);
162 series->setParent(0);
159 series->d_ptr->m_chart = 0;
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 class QAbstractAxis;
38 class QAbstractAxis;
39 class ChartPresenter;
39 class ChartPresenter;
40 class GLXYSeriesDataManager;
40
41
41 class QT_CHARTS_AUTOTEST_EXPORT ChartDataSet : public QObject
42 class QT_CHARTS_AUTOTEST_EXPORT ChartDataSet : public QObject
42 {
43 {
@@ -67,6 +68,8 public:
67 QPointF mapToValue(const QPointF &position, QAbstractSeries *series = 0);
68 QPointF mapToValue(const QPointF &position, QAbstractSeries *series = 0);
68 QPointF mapToPosition(const QPointF &value, QAbstractSeries *series = 0);
69 QPointF mapToPosition(const QPointF &value, QAbstractSeries *series = 0);
69
70
71 GLXYSeriesDataManager *glXYSeriesDataManager() { return m_glXYSeriesDataManager; }
72
70 Q_SIGNALS:
73 Q_SIGNALS:
71 void axisAdded(QAbstractAxis* axis);
74 void axisAdded(QAbstractAxis* axis);
72 void axisRemoved(QAbstractAxis* axis);
75 void axisRemoved(QAbstractAxis* axis);
@@ -85,6 +88,7 private:
85 QList<QAbstractSeries *> m_seriesList;
88 QList<QAbstractSeries *> m_seriesList;
86 QList<QAbstractAxis *> m_axisList;
89 QList<QAbstractAxis *> m_axisList;
87 QChart* m_chart;
90 QChart* m_chart;
91 GLXYSeriesDataManager *m_glXYSeriesDataManager;
88 };
92 };
89
93
90 QT_CHARTS_END_NAMESPACE
94 QT_CHARTS_END_NAMESPACE
@@ -19,13 +19,15
19 #include <private/chartelement_p.h>
19 #include <private/chartelement_p.h>
20 #include <private/chartpresenter_p.h>
20 #include <private/chartpresenter_p.h>
21 #include <private/abstractdomain_p.h>
21 #include <private/abstractdomain_p.h>
22 #include <private/chartdataset_p.h>
22
23
23 QT_CHARTS_BEGIN_NAMESPACE
24 QT_CHARTS_BEGIN_NAMESPACE
24
25
25 ChartElement::ChartElement(QGraphicsItem* item):
26 ChartElement::ChartElement(QGraphicsItem* item):
26 QGraphicsObject(item),
27 QGraphicsObject(item),
27 m_presenter(0),
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 return m_themeManager;
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 QT_CHARTS_END_NAMESPACE
65 QT_CHARTS_END_NAMESPACE
@@ -40,6 +40,7 class ChartPresenter;
40 class ChartAnimation;
40 class ChartAnimation;
41 class ChartThemeManager;
41 class ChartThemeManager;
42 class AbstractDomain;
42 class AbstractDomain;
43 class ChartDataSet;
43
44
44 class ChartElement: public QGraphicsObject
45 class ChartElement: public QGraphicsObject
45 {
46 {
@@ -52,10 +53,13 public:
52 ChartPresenter *presenter() const;
53 ChartPresenter *presenter() const;
53 virtual void setThemeManager(ChartThemeManager *manager);
54 virtual void setThemeManager(ChartThemeManager *manager);
54 ChartThemeManager* themeManager() const;
55 ChartThemeManager* themeManager() const;
56 virtual void setDataSet(ChartDataSet *dataSet);
57 ChartDataSet *dataSet() const;
55
58
56 private:
59 private:
57 ChartPresenter *m_presenter;
60 ChartPresenter *m_presenter;
58 ChartThemeManager *m_themeManager;
61 ChartThemeManager *m_themeManager;
62 ChartDataSet *m_dataSet;
59 };
63 };
60
64
61 QT_CHARTS_END_NAMESPACE
65 QT_CHARTS_END_NAMESPACE
@@ -46,6 +46,10 ChartPresenter::ChartPresenter(QChart *chart, QChart::ChartType type)
46 m_plotAreaBackground(0),
46 m_plotAreaBackground(0),
47 m_title(0),
47 m_title(0),
48 m_localizeNumbers(false)
48 m_localizeNumbers(false)
49 #ifndef QT_NO_OPENGL
50 , m_glWidget(0)
51 , m_glUseWidget(true)
52 #endif
49 {
53 {
50 if (type == QChart::ChartTypeCartesian)
54 if (type == QChart::ChartTypeCartesian)
51 m_layout = new CartesianChartLayout(this);
55 m_layout = new CartesianChartLayout(this);
@@ -56,7 +60,9 ChartPresenter::ChartPresenter(QChart *chart, QChart::ChartType type)
56
60
57 ChartPresenter::~ChartPresenter()
61 ChartPresenter::~ChartPresenter()
58 {
62 {
59
63 #ifndef QT_NO_OPENGL
64 delete m_glWidget.data();
65 #endif
60 }
66 }
61
67
62 void ChartPresenter::setGeometry(const QRectF rect)
68 void ChartPresenter::setGeometry(const QRectF rect)
@@ -67,6 +73,10 void ChartPresenter::setGeometry(const QRectF rect)
67 chart->domain()->setSize(rect.size());
73 chart->domain()->setSize(rect.size());
68 chart->setPos(rect.topLeft());
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 emit plotAreaChanged(m_rect);
80 emit plotAreaChanged(m_rect);
71 }
81 }
72 }
82 }
@@ -108,6 +118,7 void ChartPresenter::handleSeriesAdded(QAbstractSeries *series)
108 ChartItem *chart = series->d_ptr->chartItem();
118 ChartItem *chart = series->d_ptr->chartItem();
109 chart->setPresenter(this);
119 chart->setPresenter(this);
110 chart->setThemeManager(m_chart->d_ptr->m_themeManager);
120 chart->setThemeManager(m_chart->d_ptr->m_themeManager);
121 chart->setDataSet(m_chart->d_ptr->m_dataset);
111 chart->domain()->setSize(m_rect.size());
122 chart->domain()->setSize(m_rect.size());
112 chart->setPos(m_rect.topLeft());
123 chart->setPos(m_rect.topLeft());
113 chart->handleDomainUpdated(); //this could be moved to intializeGraphics when animator is refactored
124 chart->handleDomainUpdated(); //this could be moved to intializeGraphics when animator is refactored
@@ -531,6 +542,28 QString ChartPresenter::numberToString(int value)
531 return QString::number(value);
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 #include "moc_chartpresenter_p.cpp"
567 #include "moc_chartpresenter_p.cpp"
535
568
536 QT_CHARTS_END_NAMESPACE
569 QT_CHARTS_END_NAMESPACE
@@ -30,9 +30,11
30
30
31 #include <QtCharts/QChartGlobal>
31 #include <QtCharts/QChartGlobal>
32 #include <QtCharts/QChart> //because of QChart::ChartThemeId
32 #include <QtCharts/QChart> //because of QChart::ChartThemeId
33 #include <private/glwidget_p.h>
33 #include <QtCore/QRectF>
34 #include <QtCore/QRectF>
34 #include <QtCore/QMargins>
35 #include <QtCore/QMargins>
35 #include <QtCore/QLocale>
36 #include <QtCore/QLocale>
37 #include <QtCore/QPointer>
36
38
37 QT_CHARTS_BEGIN_NAMESPACE
39 QT_CHARTS_BEGIN_NAMESPACE
38
40
@@ -160,6 +162,9 public:
160 QString numberToString(double value, char f = 'g', int prec = 6);
162 QString numberToString(double value, char f = 'g', int prec = 6);
161 QString numberToString(int value);
163 QString numberToString(int value);
162
164
165 void ensureGLWidget();
166 void glSetUseWidget(bool enable) { m_glUseWidget = enable; }
167
163 private:
168 private:
164 void createBackgroundItem();
169 void createBackgroundItem();
165 void createPlotAreaBackgroundItem();
170 void createPlotAreaBackgroundItem();
@@ -192,6 +197,10 private:
192 QRectF m_rect;
197 QRectF m_rect;
193 bool m_localizeNumbers;
198 bool m_localizeNumbers;
194 QLocale m_locale;
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 QT_CHARTS_END_NAMESPACE
206 QT_CHARTS_END_NAMESPACE
@@ -32,6 +32,9 SOURCES += \
32 $$PWD/scroller.cpp \
32 $$PWD/scroller.cpp \
33 $$PWD/charttitle.cpp \
33 $$PWD/charttitle.cpp \
34 $$PWD/qpolarchart.cpp
34 $$PWD/qpolarchart.cpp
35
36 contains(QT_CONFIG, opengl): SOURCES += $$PWD/glwidget.cpp
37
35 PRIVATE_HEADERS += \
38 PRIVATE_HEADERS += \
36 $$PWD/chartdataset_p.h \
39 $$PWD/chartdataset_p.h \
37 $$PWD/chartitem_p.h \
40 $$PWD/chartitem_p.h \
@@ -46,6 +49,9 PRIVATE_HEADERS += \
46 $$PWD/qabstractseries_p.h \
49 $$PWD/qabstractseries_p.h \
47 $$PWD/charttitle_p.h \
50 $$PWD/charttitle_p.h \
48 $$PWD/charthelpers_p.h
51 $$PWD/charthelpers_p.h
52
53 contains(QT_CONFIG, opengl): PRIVATE_HEADERS += $$PWD/glwidget_p.h
54
49 PUBLIC_HEADERS += \
55 PUBLIC_HEADERS += \
50 $$PWD/qchart.h \
56 $$PWD/qchart.h \
51 $$PWD/qchartglobal.h \
57 $$PWD/qchartglobal.h \
@@ -92,7 +92,7 public:
92
92
93 virtual QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const = 0;
93 virtual QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const = 0;
94 virtual QPointF calculateDomainPoint(const QPointF &point) const = 0;
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 virtual bool attachAxis(QAbstractAxis *axis);
97 virtual bool attachAxis(QAbstractAxis *axis);
98 virtual bool detachAxis(QAbstractAxis *axis);
98 virtual bool detachAxis(QAbstractAxis *axis);
@@ -162,7 +162,7 QPointF LogXLogYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) c
162 return QPointF(x, y);
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 const qreal deltaX = m_size.width() / qAbs(m_logRightX - m_logLeftX);
167 const qreal deltaX = m_size.width() / qAbs(m_logRightX - m_logLeftX);
168 const qreal deltaY = m_size.height() / qAbs(m_logRightY - m_logLeftY);
168 const qreal deltaY = m_size.height() / qAbs(m_logRightY - m_logLeftY);
@@ -54,7 +54,7 public:
54
54
55 QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const;
55 QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const;
56 QPointF calculateDomainPoint(const QPointF &point) const;
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 bool attachAxis(QAbstractAxis *axis);
59 bool attachAxis(QAbstractAxis *axis);
60 bool detachAxis(QAbstractAxis *axis);
60 bool detachAxis(QAbstractAxis *axis);
@@ -146,7 +146,7 QPointF LogXYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) cons
146 return QPointF(x, y);
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 const qreal deltaX = m_size.width() / (m_logRightX - m_logLeftX);
151 const qreal deltaX = m_size.width() / (m_logRightX - m_logLeftX);
152 const qreal deltaY = m_size.height() / (m_maxY - m_minY);
152 const qreal deltaY = m_size.height() / (m_maxY - m_minY);
@@ -54,7 +54,7 public:
54
54
55 QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const;
55 QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const;
56 QPointF calculateDomainPoint(const QPointF &point) const;
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 bool attachAxis(QAbstractAxis *axis);
59 bool attachAxis(QAbstractAxis *axis);
60 bool detachAxis(QAbstractAxis *axis);
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 QVector<QPointF> result;
58 QVector<QPointF> result;
59 result.resize(vector.count());
59 result.resize(vector.count());
@@ -43,7 +43,7 public:
43 void setSize(const QSizeF &size);
43 void setSize(const QSizeF &size);
44
44
45 QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const;
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 virtual qreal toAngularCoordinate(qreal value, bool &ok) const = 0;
48 virtual qreal toAngularCoordinate(qreal value, bool &ok) const = 0;
49 virtual qreal toRadialCoordinate(qreal value, bool &ok) const = 0;
49 virtual qreal toRadialCoordinate(qreal value, bool &ok) const = 0;
@@ -146,7 +146,7 QPointF XLogYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) cons
146 return QPointF(x, y);
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 const qreal deltaX = m_size.width() / (m_maxX - m_minX);
151 const qreal deltaX = m_size.width() / (m_maxX - m_minX);
152 const qreal deltaY = m_size.height() / qAbs(m_logRightY - m_logLeftY);
152 const qreal deltaY = m_size.height() / qAbs(m_logRightY - m_logLeftY);
@@ -54,7 +54,7 public:
54
54
55 QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const;
55 QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const;
56 QPointF calculateDomainPoint(const QPointF &point) const;
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 bool attachAxis(QAbstractAxis *axis);
59 bool attachAxis(QAbstractAxis *axis);
60 bool detachAxis(QAbstractAxis *axis);
60 bool detachAxis(QAbstractAxis *axis);
@@ -144,7 +144,7 QPointF XYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) const
144 return QPointF(x, y);
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 const qreal deltaX = m_size.width() / (m_maxX - m_minX);
149 const qreal deltaX = m_size.width() / (m_maxX - m_minX);
150 const qreal deltaY = m_size.height() / (m_maxY - m_minY);
150 const qreal deltaY = m_size.height() / (m_maxY - m_minY);
@@ -54,7 +54,7 public:
54
54
55 QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const;
55 QPointF calculateGeometryPoint(const QPointF &point, bool &ok) const;
56 QPointF calculateDomainPoint(const QPointF &point) const;
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 QT_CHARTS_END_NAMESPACE
60 QT_CHARTS_END_NAMESPACE
@@ -70,6 +70,17 QPainterPath LineChartItem::shape() const
70
70
71 void LineChartItem::updateGeometry()
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 // Store the points to a local variable so that the old line gets properly cleared
84 // Store the points to a local variable so that the old line gets properly cleared
74 // when animation starts.
85 // when animation starts.
75 m_linePoints = geometryPoints();
86 m_linePoints = geometryPoints();
@@ -345,6 +356,9 void LineChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt
345 Q_UNUSED(widget)
356 Q_UNUSED(widget)
346 Q_UNUSED(option)
357 Q_UNUSED(option)
347
358
359 if (m_series->useOpenGL())
360 return;
361
348 QRectF clipRect = QRectF(QPointF(0, 0), domain()->size());
362 QRectF clipRect = QRectF(QPointF(0, 0), domain()->size());
349
363
350 painter->save();
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 \internal
201 \internal
139 \brief Constructs QAbstractSeries object with \a parent.
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 \brief Returns the chart where series belongs to.
281 \brief Returns the chart where series belongs to.
197
282
@@ -274,7 +359,9 QAbstractSeriesPrivate::QAbstractSeriesPrivate(QAbstractSeries *q)
274 m_item(0),
359 m_item(0),
275 m_domain(new XYDomain()),
360 m_domain(new XYDomain()),
276 m_visible(true),
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 return reverseYAxis;
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 #include "moc_qabstractseries.cpp"
453 #include "moc_qabstractseries.cpp"
358 #include "moc_qabstractseries_p.cpp"
454 #include "moc_qabstractseries_p.cpp"
359
455
@@ -36,6 +36,7 class QT_CHARTS_EXPORT QAbstractSeries : public QObject
36 Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged)
36 Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged)
37 Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged)
37 Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged)
38 Q_PROPERTY(SeriesType type READ type)
38 Q_PROPERTY(SeriesType type READ type)
39 Q_PROPERTY(bool useOpenGL READ useOpenGL WRITE setUseOpenGL NOTIFY useOpenGLChanged)
39 Q_ENUMS(SeriesType)
40 Q_ENUMS(SeriesType)
40
41
41 public:
42 public:
@@ -67,6 +68,8 public:
67 bool isVisible() const;
68 bool isVisible() const;
68 qreal opacity() const;
69 qreal opacity() const;
69 void setOpacity(qreal opacity);
70 void setOpacity(qreal opacity);
71 void setUseOpenGL(bool enable = true);
72 bool useOpenGL() const;
70
73
71 QChart *chart() const;
74 QChart *chart() const;
72
75
@@ -81,6 +84,7 Q_SIGNALS:
81 void nameChanged();
84 void nameChanged();
82 void visibleChanged();
85 void visibleChanged();
83 void opacityChanged();
86 void opacityChanged();
87 void useOpenGLChanged();
84
88
85 protected:
89 protected:
86 QScopedPointer<QAbstractSeriesPrivate> d_ptr;
90 QScopedPointer<QAbstractSeriesPrivate> d_ptr;
@@ -89,6 +93,7 protected:
89 friend class ChartThemeManager;
93 friend class ChartThemeManager;
90 friend class QLegendPrivate;
94 friend class QLegendPrivate;
91 friend class DeclarativeChart;
95 friend class DeclarativeChart;
96 friend class QAreaSeries;
92 };
97 };
93
98
94 QT_CHARTS_END_NAMESPACE
99 QT_CHARTS_END_NAMESPACE
@@ -81,6 +81,8 public:
81 bool reverseXAxis();
81 bool reverseXAxis();
82 bool reverseYAxis();
82 bool reverseYAxis();
83
83
84 void setBlockOpenGL(bool enable);
85
84 Q_SIGNALS:
86 Q_SIGNALS:
85 void countChanged();
87 void countChanged();
86
88
@@ -96,6 +98,8 private:
96 bool m_visible;
98 bool m_visible;
97 qreal m_opacity;
99 qreal m_opacity;
98 ChartPresenter *m_presenter;
100 ChartPresenter *m_presenter;
101 bool m_useOpenGL;
102 bool m_blockOpenGL;
99
103
100 friend class QAbstractSeries;
104 friend class QAbstractSeries;
101 friend class ChartDataSet;
105 friend class ChartDataSet;
@@ -130,6 +130,18 void ScatterChartItem::markerDoubleClicked(QGraphicsItem *marker)
130
130
131 void ScatterChartItem::updateGeometry()
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 const QVector<QPointF>& points = geometryPoints();
146 const QVector<QPointF>& points = geometryPoints();
135
147
@@ -199,6 +211,9 void ScatterChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *
199 Q_UNUSED(option)
211 Q_UNUSED(option)
200 Q_UNUSED(widget)
212 Q_UNUSED(widget)
201
213
214 if (m_series->useOpenGL())
215 return;
216
202 QRectF clipRect = QRectF(QPointF(0, 0), domain()->size());
217 QRectF clipRect = QRectF(QPointF(0, 0), domain()->size());
203
218
204 painter->save();
219 painter->save();
@@ -238,7 +238,7 QT_CHARTS_BEGIN_NAMESPACE
238 \sa pointLabelsVisible
238 \sa pointLabelsVisible
239 */
239 */
240 /*!
240 /*!
241 \fn void QXYSeries::pointLabelsClippintChanged(bool clipping)
241 \fn void QXYSeries::pointLabelsClippingChanged(bool clipping)
242 The clipping of the data point labels is changed to \a clipping.
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 \fn void QXYSeriesPrivate::updated()
406 \fn void QXYSeriesPrivate::updated()
402 \brief \internal
407 \brief \internal
403 */
408 */
@@ -539,7 +544,7 void QXYSeries::replace(int index, const QPointF &newPoint)
539 \note This is much faster than replacing data points one by one,
544 \note This is much faster than replacing data points one by one,
540 or first clearing all data, and then appending the new data. Emits QXYSeries::pointsReplaced()
545 or first clearing all data, and then appending the new data. Emits QXYSeries::pointsReplaced()
541 when the points have been replaced. However, note that using the overload that takes
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 \sa pointsReplaced()
548 \sa pointsReplaced()
544 */
549 */
545 void QXYSeries::replace(QList<QPointF> points)
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 QList<QPointF> QXYSeries::points() const
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 Returns point at \a index in internal points vector.
662 Returns point at \a index in internal points vector.
647 */
663 */
648 const QPointF &QXYSeries::at(int index) const
664 const QPointF &QXYSeries::at(int index) const
@@ -675,6 +691,7 void QXYSeries::setPen(const QPen &pen)
675 emit d->updated();
691 emit d->updated();
676 if (emitColorChanged)
692 if (emitColorChanged)
677 emit colorChanged(pen.color());
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 Q_Q(QXYSeries);
882 Q_Q(QXYSeries);
866
883
867 const QList<QPointF>& points = q->points();
884 const QVector<QPointF> &points = q->pointsVector();
868
885
869 if (!points.isEmpty()) {
886 if (!points.isEmpty()) {
870 minX = points[0].x();
887 minX = points[0].x();
@@ -65,6 +65,7 public:
65
65
66 int count() const;
66 int count() const;
67 QList<QPointF> points() const;
67 QList<QPointF> points() const;
68 QVector<QPointF> pointsVector() const;
68 const QPointF &at(int index) const;
69 const QPointF &at(int index) const;
69
70
70 QXYSeries &operator << (const QPointF &point);
71 QXYSeries &operator << (const QPointF &point);
@@ -117,6 +118,7 Q_SIGNALS:
117 void pointLabelsColorChanged(const QColor &color);
118 void pointLabelsColorChanged(const QColor &color);
118 void pointLabelsClippingChanged(bool clipping);
119 void pointLabelsClippingChanged(bool clipping);
119 void pointsRemoved(int index, int count);
120 void pointsRemoved(int index, int count);
121 void penChanged(const QPen &pen);
120
122
121 private:
123 private:
122 Q_DECLARE_PRIVATE(QXYSeries)
124 Q_DECLARE_PRIVATE(QXYSeries)
@@ -21,6 +21,8
21 #include <private/qxyseries_p.h>
21 #include <private/qxyseries_p.h>
22 #include <private/chartpresenter_p.h>
22 #include <private/chartpresenter_p.h>
23 #include <private/abstractdomain_p.h>
23 #include <private/abstractdomain_p.h>
24 #include <private/chartdataset_p.h>
25 #include <private/glxyseriesdata_p.h>
24 #include <QtCharts/QXYModelMapper>
26 #include <QtCharts/QXYModelMapper>
25 #include <private/qabstractaxis_p.h>
27 #include <private/qabstractaxis_p.h>
26 #include <QtGui/QPainter>
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 //handlers
118 //handlers
110
119
111 void XYChart::handlePointAdded(int index)
120 void XYChart::handlePointAdded(int index)
@@ -113,90 +122,108 void XYChart::handlePointAdded(int index)
113 Q_ASSERT(index < m_series->count());
122 Q_ASSERT(index < m_series->count());
114 Q_ASSERT(index >= 0);
123 Q_ASSERT(index >= 0);
115
124
125 if (m_series->useOpenGL()) {
126 updateGlChart();
127 } else {
116 QVector<QPointF> points;
128 QVector<QPointF> points;
117
118 if (m_dirty || m_points.isEmpty()) {
129 if (m_dirty || m_points.isEmpty()) {
119 points = domain()->calculateGeometryPoints(m_series->points());
130 points = domain()->calculateGeometryPoints(m_series->pointsVector());
120 } else {
131 } else {
121 points = m_points;
132 points = m_points;
122 QPointF point = domain()->calculateGeometryPoint(m_series->points()[index], m_validData);
133 QPointF point = domain()->calculateGeometryPoint(m_series->pointsVector().at(index),
134 m_validData);
123 if (!m_validData)
135 if (!m_validData)
124 m_points.clear();
136 m_points.clear();
125 else
137 else
126 points.insert(index, point);
138 points.insert(index, point);
127 }
139 }
128
129 updateChart(m_points, points, index);
140 updateChart(m_points, points, index);
130 }
141 }
142 }
131
143
132 void XYChart::handlePointRemoved(int index)
144 void XYChart::handlePointRemoved(int index)
133 {
145 {
134 Q_ASSERT(index <= m_series->count());
146 Q_ASSERT(index <= m_series->count());
135 Q_ASSERT(index >= 0);
147 Q_ASSERT(index >= 0);
136
148
149 if (m_series->useOpenGL()) {
150 updateGlChart();
151 } else {
137 QVector<QPointF> points;
152 QVector<QPointF> points;
138
139 if (m_dirty || m_points.isEmpty()) {
153 if (m_dirty || m_points.isEmpty()) {
140 points = domain()->calculateGeometryPoints(m_series->points());
154 points = domain()->calculateGeometryPoints(m_series->pointsVector());
141 } else {
155 } else {
142 points = m_points;
156 points = m_points;
143 points.remove(index);
157 points.remove(index);
144 }
158 }
145
146 updateChart(m_points, points, index);
159 updateChart(m_points, points, index);
147 }
160 }
161 }
148
162
149 void XYChart::handlePointsRemoved(int index, int count)
163 void XYChart::handlePointsRemoved(int index, int count)
150 {
164 {
151 Q_ASSERT(index <= m_series->count());
165 Q_ASSERT(index <= m_series->count());
152 Q_ASSERT(index >= 0);
166 Q_ASSERT(index >= 0);
153
167
168 if (m_series->useOpenGL()) {
169 updateGlChart();
170 } else {
154 QVector<QPointF> points;
171 QVector<QPointF> points;
155
156 if (m_dirty || m_points.isEmpty()) {
172 if (m_dirty || m_points.isEmpty()) {
157 points = domain()->calculateGeometryPoints(m_series->points());
173 points = domain()->calculateGeometryPoints(m_series->pointsVector());
158 } else {
174 } else {
159 points = m_points;
175 points = m_points;
160 points.remove(index, count);
176 points.remove(index, count);
161 }
177 }
162
163 updateChart(m_points, points, index);
178 updateChart(m_points, points, index);
164 }
179 }
180 }
165
181
166 void XYChart::handlePointReplaced(int index)
182 void XYChart::handlePointReplaced(int index)
167 {
183 {
168 Q_ASSERT(index < m_series->count());
184 Q_ASSERT(index < m_series->count());
169 Q_ASSERT(index >= 0);
185 Q_ASSERT(index >= 0);
170
186
187 if (m_series->useOpenGL()) {
188 updateGlChart();
189 } else {
171 QVector<QPointF> points;
190 QVector<QPointF> points;
172
173 if (m_dirty || m_points.isEmpty()) {
191 if (m_dirty || m_points.isEmpty()) {
174 points = domain()->calculateGeometryPoints(m_series->points());
192 points = domain()->calculateGeometryPoints(m_series->pointsVector());
175 } else {
193 } else {
176 QPointF point = domain()->calculateGeometryPoint(m_series->points()[index], m_validData);
194 QPointF point = domain()->calculateGeometryPoint(m_series->pointsVector().at(index),
195 m_validData);
177 if (!m_validData)
196 if (!m_validData)
178 m_points.clear();
197 m_points.clear();
179 points = m_points;
198 points = m_points;
180 if (m_validData)
199 if (m_validData)
181 points.replace(index, point);
200 points.replace(index, point);
182 }
201 }
183
184 updateChart(m_points, points, index);
202 updateChart(m_points, points, index);
185 }
203 }
204 }
186
205
187 void XYChart::handlePointsReplaced()
206 void XYChart::handlePointsReplaced()
188 {
207 {
208 if (m_series->useOpenGL()) {
209 updateGlChart();
210 } else {
189 // All the points were replaced -> recalculate
211 // All the points were replaced -> recalculate
190 QVector<QPointF> points = domain()->calculateGeometryPoints(m_series->points());
212 QVector<QPointF> points = domain()->calculateGeometryPoints(m_series->pointsVector());
191 updateChart(m_points, points, -1);
213 updateChart(m_points, points, -1);
192 }
214 }
215 }
193
216
194 void XYChart::handleDomainUpdated()
217 void XYChart::handleDomainUpdated()
195 {
218 {
219 if (m_series->useOpenGL()) {
220 updateGlChart();
221 } else {
196 if (isEmpty()) return;
222 if (isEmpty()) return;
197 QVector<QPointF> points = domain()->calculateGeometryPoints(m_series->points());
223 QVector<QPointF> points = domain()->calculateGeometryPoints(m_series->pointsVector());
198 updateChart(m_points, points);
224 updateChart(m_points, points);
199 }
225 }
226 }
200
227
201 bool XYChart::isEmpty()
228 bool XYChart::isEmpty()
202 {
229 {
@@ -6,12 +6,14 SOURCES += \
6 $$PWD/qxyseries.cpp \
6 $$PWD/qxyseries.cpp \
7 $$PWD/qxymodelmapper.cpp \
7 $$PWD/qxymodelmapper.cpp \
8 $$PWD/qvxymodelmapper.cpp \
8 $$PWD/qvxymodelmapper.cpp \
9 $$PWD/qhxymodelmapper.cpp
9 $$PWD/qhxymodelmapper.cpp \
10 $$PWD/glxyseriesdata.cpp
10
11
11 PRIVATE_HEADERS += \
12 PRIVATE_HEADERS += \
12 $$PWD/xychart_p.h \
13 $$PWD/xychart_p.h \
13 $$PWD/qxyseries_p.h \
14 $$PWD/qxyseries_p.h \
14 $$PWD/qxymodelmapper_p.h
15 $$PWD/qxymodelmapper_p.h \
16 $$PWD/glxyseriesdata_p.h
15
17
16 PUBLIC_HEADERS += \
18 PUBLIC_HEADERS += \
17 $$PWD/qxyseries.h \
19 $$PWD/qxyseries.h \
@@ -76,6 +76,7 Q_SIGNALS:
76
76
77 protected:
77 protected:
78 virtual void updateChart(QVector<QPointF> &oldPoints, QVector<QPointF> &newPoints, int index = -1);
78 virtual void updateChart(QVector<QPointF> &oldPoints, QVector<QPointF> &newPoints, int index = -1);
79 virtual void updateGlChart();
79
80
80 private:
81 private:
81 inline bool isEmpty();
82 inline bool isEmpty();
@@ -33,7 +33,9 SOURCES += \
33 declarativemargins.cpp \
33 declarativemargins.cpp \
34 declarativeaxes.cpp \
34 declarativeaxes.cpp \
35 declarativepolarchart.cpp \
35 declarativepolarchart.cpp \
36 declarativeboxplotseries.cpp
36 declarativeboxplotseries.cpp \
37 declarativechartnode.cpp \
38 declarativerendernode.cpp
37
39
38 HEADERS += \
40 HEADERS += \
39 declarativechart.h \
41 declarativechart.h \
@@ -49,7 +51,9 HEADERS += \
49 declarativemargins.h \
51 declarativemargins.h \
50 declarativeaxes.h \
52 declarativeaxes.h \
51 declarativepolarchart.h \
53 declarativepolarchart.h \
52 declarativeboxplotseries.h
54 declarativeboxplotseries.h \
55 declarativechartnode.h \
56 declarativerendernode.h
53
57
54 OTHER_FILES = qmldir
58 OTHER_FILES = qmldir
55
59
@@ -25,6 +25,8
25 #include "declarativesplineseries.h"
25 #include "declarativesplineseries.h"
26 #include "declarativeboxplotseries.h"
26 #include "declarativeboxplotseries.h"
27 #include "declarativescatterseries.h"
27 #include "declarativescatterseries.h"
28 #include "declarativechartnode.h"
29 #include "declarativerendernode.h"
28 #include <QtCharts/QBarCategoryAxis>
30 #include <QtCharts/QBarCategoryAxis>
29 #include <QtCharts/QValueAxis>
31 #include <QtCharts/QValueAxis>
30 #include <QtCharts/QLogValueAxis>
32 #include <QtCharts/QLogValueAxis>
@@ -34,6 +36,7
34 #include <private/chartdataset_p.h>
36 #include <private/chartdataset_p.h>
35 #include "declarativeaxes.h"
37 #include "declarativeaxes.h"
36 #include <private/qchart_p.h>
38 #include <private/qchart_p.h>
39 #include <private/chartpresenter_p.h>
37 #include <QtCharts/QPolarChart>
40 #include <QtCharts/QPolarChart>
38
41
39 #ifndef QT_ON_ARM
42 #ifndef QT_ON_ARM
@@ -88,6 +91,7 QT_CHARTS_BEGIN_NAMESPACE
88 /*!
91 /*!
89 \qmlproperty easing ChartView::animationEasingCurve
92 \qmlproperty easing ChartView::animationEasingCurve
90 The easing curve of the animation for the chart.
93 The easing curve of the animation for the chart.
94 */
91
95
92 /*!
96 /*!
93 \qmlproperty Font ChartView::titleFont
97 \qmlproperty Font ChartView::titleFont
@@ -312,34 +316,42 QT_CHARTS_BEGIN_NAMESPACE
312 */
316 */
313
317
314 DeclarativeChart::DeclarativeChart(QQuickItem *parent)
318 DeclarativeChart::DeclarativeChart(QQuickItem *parent)
315 : QQuickPaintedItem(parent)
319 : QQuickItem(parent)
316 {
320 {
317 initChart(QChart::ChartTypeCartesian);
321 initChart(QChart::ChartTypeCartesian);
318 }
322 }
319
323
320 DeclarativeChart::DeclarativeChart(QChart::ChartType type, QQuickItem *parent)
324 DeclarativeChart::DeclarativeChart(QChart::ChartType type, QQuickItem *parent)
321 : QQuickPaintedItem(parent)
325 : QQuickItem(parent)
322 {
326 {
323 initChart(type);
327 initChart(type);
324 }
328 }
325
329
326 void DeclarativeChart::initChart(QChart::ChartType type)
330 void DeclarativeChart::initChart(QChart::ChartType type)
327 {
331 {
328 m_currentSceneImage = 0;
332 m_sceneImage = 0;
333 m_sceneImageDirty = false;
329 m_guiThreadId = QThread::currentThreadId();
334 m_guiThreadId = QThread::currentThreadId();
330 m_paintThreadId = 0;
335 m_paintThreadId = 0;
331 m_updatePending = false;
336 m_updatePending = false;
332
337
338 setFlag(ItemHasContents, true);
339
333 if (type == QChart::ChartTypePolar)
340 if (type == QChart::ChartTypePolar)
334 m_chart = new QPolarChart();
341 m_chart = new QPolarChart();
335 else
342 else
336 m_chart = new QChart();
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 m_scene = new QGraphicsScene(this);
348 m_scene = new QGraphicsScene(this);
339 m_scene->addItem(m_chart);
349 m_scene->addItem(m_chart);
340
350
341 setAntialiasing(QQuickItem::antialiasing());
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 connect(this, SIGNAL(antialiasingChanged(bool)), this, SLOT(handleAntialiasingChanged(bool)));
355 connect(this, SIGNAL(antialiasingChanged(bool)), this, SLOT(handleAntialiasingChanged(bool)));
344
356
345 setAcceptedMouseButtons(Qt::AllButtons);
357 setAcceptedMouseButtons(Qt::AllButtons);
@@ -377,10 +389,7 void DeclarativeChart::changeMargins(int top, int bottom, int left, int right)
377 DeclarativeChart::~DeclarativeChart()
389 DeclarativeChart::~DeclarativeChart()
378 {
390 {
379 delete m_chart;
391 delete m_chart;
380 m_sceneImageLock.lock();
392 delete m_sceneImage;
381 delete m_currentSceneImage;
382 m_currentSceneImage = 0;
383 m_sceneImageLock.unlock();
384 }
393 }
385
394
386 void DeclarativeChart::childEvent(QChildEvent *event)
395 void DeclarativeChart::childEvent(QChildEvent *event)
@@ -493,65 +502,102 void DeclarativeChart::geometryChanged(const QRectF &newGeometry, const QRectF &
493 QQuickItem::geometryChanged(newGeometry, oldGeometry);
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) {
509 if (!node) {
501 // Rendering in gui thread, no need for shenannigans, just update
510 node = new DeclarativeChartNode(window());
502 update();
511 connect(window(), &QQuickWindow::beforeRendering,
503 } else {
512 node->glRenderNode(), &DeclarativeRenderNode::render);
504 // Multi-threaded rendering, need to ensure scene is actually rendered in gui thread
505 if (!m_updatePending) {
506 m_updatePending = true;
507 // Do async render to avoid some unnecessary renders.
508 QTimer::singleShot(0, this, SLOT(renderScene()));
509 }
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();
510 }
543 }
544
545 // Copy chart (if dirty) to chart node
546 if (m_sceneImageDirty) {
547 node->createTextureFromImage(*m_sceneImage);
548 m_sceneImageDirty = false;
511 }
549 }
512
550
513 void DeclarativeChart::renderScene()
551 node->setRect(bRect);
514 {
515 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
552
527 update();
553 return node;
528 }
554 }
529
555
530 void DeclarativeChart::paint(QPainter *painter)
556 void DeclarativeChart::sceneChanged(QList<QRectF> region)
531 {
557 {
532 if (!m_paintThreadId) {
558 const int count = region.size();
533 m_paintThreadId = QThread::currentThreadId();
559 const qreal limitSize = 0.01;
534 if (m_guiThreadId == m_paintThreadId) {
560 if (count && !m_updatePending) {
535 // No need for scene image in single threaded rendering, so delete
561 qreal totalSize = 0.0;
536 // the one that got made by default before the rendering type was
562 for (int i = 0; i < count; i++) {
537 // detected.
563 const QRectF &reg = region.at(i);
538 delete m_currentSceneImage;
564 totalSize += (reg.height() * reg.width());
539 m_currentSceneImage = 0;
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) {
572 m_updatePending = true;
573 // Do async render to avoid some unnecessary renders.
574 emit needRender();
575 } else {
576 // We do want to call update to trigger possible gl series updates.
577 update();
578 }
540 }
579 }
541 }
580 }
542
581
543 if (m_guiThreadId == m_paintThreadId) {
582 void DeclarativeChart::renderScene()
544 QRectF renderRect(QPointF(0, 0), m_chart->size());
583 {
545 m_scene->render(painter, renderRect, renderRect);
584 m_updatePending = false;
546 } else {
585 m_sceneImageDirty = true;
547 m_sceneImageLock.lock();
586 QSize chartSize = m_chart->size().toSize();
548 if (m_currentSceneImage) {
587 if (!m_sceneImage || chartSize != m_sceneImage->size()) {
549 QRect imageRect(QPoint(0, 0), m_currentSceneImage->size());
588 delete m_sceneImage;
550 QRect itemRect(QPoint(0, 0), QSize(width(), height()));
589 m_sceneImage = new QImage(chartSize, QImage::Format_ARGB32);
551 painter->drawImage(itemRect, *m_currentSceneImage, imageRect);
590 m_sceneImage->fill(Qt::transparent);
552 }
591 }
553 m_sceneImageLock.unlock();
592
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 void DeclarativeChart::mousePressEvent(QMouseEvent *event)
603 void DeclarativeChart::mousePressEvent(QMouseEvent *event)
@@ -19,11 +19,11
19 #ifndef DECLARATIVECHART_H
19 #ifndef DECLARATIVECHART_H
20 #define DECLARATIVECHART_H
20 #define DECLARATIVECHART_H
21
21
22 #include <private/glxyseriesdata_p.h>
23
22 #include <QtCore/QtGlobal>
24 #include <QtCore/QtGlobal>
23 #include <QtQuick/QQuickItem>
25 #include <QtQuick/QQuickItem>
24 #include <QtQuick/QQuickPaintedItem>
25 #include <QtWidgets/QGraphicsScene>
26 #include <QtWidgets/QGraphicsScene>
26 #include <QtCore/QMutex>
27
27
28 #include <QtCharts/QChart>
28 #include <QtCharts/QChart>
29 #include <QtCore/QLocale>
29 #include <QtCore/QLocale>
@@ -34,7 +34,7 class DeclarativeMargins;
34 class Domain;
34 class Domain;
35 class DeclarativeAxes;
35 class DeclarativeAxes;
36
36
37 class DeclarativeChart : public QQuickPaintedItem
37 class DeclarativeChart : public QQuickItem
38 {
38 {
39 Q_OBJECT
39 Q_OBJECT
40 Q_PROPERTY(Theme theme READ theme WRITE setTheme)
40 Q_PROPERTY(Theme theme READ theme WRITE setTheme)
@@ -102,7 +102,7 public: // From parent classes
102 void childEvent(QChildEvent *event);
102 void childEvent(QChildEvent *event);
103 void componentComplete();
103 void componentComplete();
104 void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry);
104 void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry);
105 void paint(QPainter *painter);
105 QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *);
106 protected:
106 protected:
107 void mousePressEvent(QMouseEvent *event);
107 void mousePressEvent(QMouseEvent *event);
108 void mouseReleaseEvent(QMouseEvent *event);
108 void mouseReleaseEvent(QMouseEvent *event);
@@ -199,6 +199,7 Q_SIGNALS:
199 Q_REVISION(4) void localeChanged();
199 Q_REVISION(4) void localeChanged();
200 Q_REVISION(5) void animationDurationChanged(int msecs);
200 Q_REVISION(5) void animationDurationChanged(int msecs);
201 Q_REVISION(5) void animationEasingCurveChanged(QEasingCurve curve);
201 Q_REVISION(5) void animationEasingCurveChanged(QEasingCurve curve);
202 void needRender();
202
203
203 private Q_SLOTS:
204 private Q_SLOTS:
204 void changeMargins(int top, int bottom, int left, int right);
205 void changeMargins(int top, int bottom, int left, int right);
@@ -227,12 +228,13 private:
227 QPoint m_lastMouseMoveScreenPoint;
228 QPoint m_lastMouseMoveScreenPoint;
228 Qt::MouseButton m_mousePressButton;
229 Qt::MouseButton m_mousePressButton;
229 Qt::MouseButtons m_mousePressButtons;
230 Qt::MouseButtons m_mousePressButtons;
230 QMutex m_sceneImageLock;
231 QImage *m_sceneImage;
231 QImage *m_currentSceneImage;
232 bool m_sceneImageDirty;
232 bool m_updatePending;
233 bool m_updatePending;
233 Qt::HANDLE m_paintThreadId;
234 Qt::HANDLE m_paintThreadId;
234 Qt::HANDLE m_guiThreadId;
235 Qt::HANDLE m_guiThreadId;
235 DeclarativeMargins *m_margins;
236 DeclarativeMargins *m_margins;
237 GLXYSeriesDataManager *m_glXYDataManager;
236 };
238 };
237
239
238 QT_CHARTS_END_NAMESPACE
240 QT_CHARTS_END_NAMESPACE
@@ -19,8 +19,8
19 #include "../qxyseries/tst_qxyseries.h"
19 #include "../qxyseries/tst_qxyseries.h"
20 #include <QtCharts/QLineSeries>
20 #include <QtCharts/QLineSeries>
21
21
22
23 Q_DECLARE_METATYPE(QList<QPointF>)
22 Q_DECLARE_METATYPE(QList<QPointF>)
23 Q_DECLARE_METATYPE(QVector<QPointF>)
24
24
25 class tst_QLineSeries : public tst_QXYSeries
25 class tst_QLineSeries : public tst_QXYSeries
26 {
26 {
@@ -76,6 +76,7 void tst_QLineSeries::qlineseries()
76 QCOMPARE(series.count(),0);
76 QCOMPARE(series.count(),0);
77 QCOMPARE(series.brush(), QBrush());
77 QCOMPARE(series.brush(), QBrush());
78 QCOMPARE(series.points(), QList<QPointF>());
78 QCOMPARE(series.points(), QList<QPointF>());
79 QCOMPARE(series.pointsVector(), QVector<QPointF>());
79 QCOMPARE(series.pen(), QPen());
80 QCOMPARE(series.pen(), QPen());
80 QCOMPARE(series.pointsVisible(), false);
81 QCOMPARE(series.pointsVisible(), false);
81 QCOMPARE(series.pointLabelsVisible(), false);
82 QCOMPARE(series.pointLabelsVisible(), false);
@@ -20,6 +20,7
20 #include <QtCharts/QScatterSeries>
20 #include <QtCharts/QScatterSeries>
21
21
22 Q_DECLARE_METATYPE(QList<QPointF>)
22 Q_DECLARE_METATYPE(QList<QPointF>)
23 Q_DECLARE_METATYPE(QVector<QPointF>)
23
24
24 class tst_QScatterSeries : public tst_QXYSeries
25 class tst_QScatterSeries : public tst_QXYSeries
25 {
26 {
@@ -75,6 +76,7 void tst_QScatterSeries::qscatterseries()
75 QCOMPARE(series.count(),0);
76 QCOMPARE(series.count(),0);
76 QCOMPARE(series.brush(), QBrush());
77 QCOMPARE(series.brush(), QBrush());
77 QCOMPARE(series.points(), QList<QPointF>());
78 QCOMPARE(series.points(), QList<QPointF>());
79 QCOMPARE(series.pointsVector(), QVector<QPointF>());
78 QCOMPARE(series.pen(), QPen());
80 QCOMPARE(series.pen(), QPen());
79 QCOMPARE(series.pointsVisible(), false);
81 QCOMPARE(series.pointsVisible(), false);
80
82
@@ -20,6 +20,7
20 #include <QtCharts/QSplineSeries>
20 #include <QtCharts/QSplineSeries>
21
21
22 Q_DECLARE_METATYPE(QList<QPointF>)
22 Q_DECLARE_METATYPE(QList<QPointF>)
23 Q_DECLARE_METATYPE(QVector<QPointF>)
23
24
24 class tst_QSplineSeries : public tst_QXYSeries
25 class tst_QSplineSeries : public tst_QXYSeries
25 {
26 {
@@ -73,6 +74,7 void tst_QSplineSeries::qsplineseries()
73 QCOMPARE(series.count(),0);
74 QCOMPARE(series.count(),0);
74 QCOMPARE(series.brush(), QBrush());
75 QCOMPARE(series.brush(), QBrush());
75 QCOMPARE(series.points(), QList<QPointF>());
76 QCOMPARE(series.points(), QList<QPointF>());
77 QCOMPARE(series.pointsVector(), QVector<QPointF>());
76 QCOMPARE(series.pen(), QPen());
78 QCOMPARE(series.pen(), QPen());
77 QCOMPARE(series.pointsVisible(), false);
79 QCOMPARE(series.pointsVisible(), false);
78
80
@@ -198,6 +198,7 void tst_QXYSeries::append_raw()
198 TRY_COMPARE(spy0.count(), 0);
198 TRY_COMPARE(spy0.count(), 0);
199 TRY_COMPARE(addedSpy.count(), points.count());
199 TRY_COMPARE(addedSpy.count(), points.count());
200 QCOMPARE(m_series->points(), points);
200 QCOMPARE(m_series->points(), points);
201 QCOMPARE(m_series->pointsVector(), points.toVector());
201
202
202 // Process events between appends
203 // Process events between appends
203 foreach (const QPointF &point, otherPoints) {
204 foreach (const QPointF &point, otherPoints) {
General Comments 0
You need to be logged in to leave comments. Login now