1 | /****************************************************************************
|
---|
2 | **
|
---|
3 | ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
---|
4 | ** Contact: Qt Software Information ([email protected])
|
---|
5 | **
|
---|
6 | ** This file is part of the QtOpenGL module of the Qt Toolkit.
|
---|
7 | **
|
---|
8 | ** $QT_BEGIN_LICENSE:LGPL$
|
---|
9 | ** Commercial Usage
|
---|
10 | ** Licensees holding valid Qt Commercial licenses may use this file in
|
---|
11 | ** accordance with the Qt Commercial License Agreement provided with the
|
---|
12 | ** Software or, alternatively, in accordance with the terms contained in
|
---|
13 | ** a written agreement between you and Nokia.
|
---|
14 | **
|
---|
15 | ** GNU Lesser General Public License Usage
|
---|
16 | ** Alternatively, this file may be used under the terms of the GNU Lesser
|
---|
17 | ** General Public License version 2.1 as published by the Free Software
|
---|
18 | ** Foundation and appearing in the file LICENSE.LGPL included in the
|
---|
19 | ** packaging of this file. Please review the following information to
|
---|
20 | ** ensure the GNU Lesser General Public License version 2.1 requirements
|
---|
21 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
---|
22 | **
|
---|
23 | ** In addition, as a special exception, Nokia gives you certain
|
---|
24 | ** additional rights. These rights are described in the Nokia Qt LGPL
|
---|
25 | ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
|
---|
26 | ** package.
|
---|
27 | **
|
---|
28 | ** GNU General Public License Usage
|
---|
29 | ** Alternatively, this file may be used under the terms of the GNU
|
---|
30 | ** General Public License version 3.0 as published by the Free Software
|
---|
31 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
---|
32 | ** packaging of this file. Please review the following information to
|
---|
33 | ** ensure the GNU General Public License version 3.0 requirements will be
|
---|
34 | ** met: http://www.gnu.org/copyleft/gpl.html.
|
---|
35 | **
|
---|
36 | ** If you are unsure which license is appropriate for your use, please
|
---|
37 | ** contact the sales department at [email protected].
|
---|
38 | ** $QT_END_LICENSE$
|
---|
39 | **
|
---|
40 | ****************************************************************************/
|
---|
41 |
|
---|
42 | /*
|
---|
43 | When the active program changes, we need to update it's uniforms.
|
---|
44 | We could track state for each program and only update stale uniforms
|
---|
45 | - Could lead to lots of overhead if there's a lot of programs
|
---|
46 | We could update all the uniforms when the program changes
|
---|
47 | - Could end up updating lots of uniforms which don't need updating
|
---|
48 |
|
---|
49 | Updating uniforms should be cheap, so the overhead of updating up-to-date
|
---|
50 | uniforms should be minimal. It's also less complex.
|
---|
51 |
|
---|
52 | Things which _may_ cause a different program to be used:
|
---|
53 | - Change in brush/pen style
|
---|
54 | - Change in painter opacity
|
---|
55 | - Change in composition mode
|
---|
56 |
|
---|
57 | Whenever we set a mode on the shader manager - it needs to tell us if it had
|
---|
58 | to switch to a different program.
|
---|
59 |
|
---|
60 | The shader manager should only switch when we tell it to. E.g. if we set a new
|
---|
61 | brush style and then switch to transparent painter, we only want it to compile
|
---|
62 | and use the correct program when we really need it.
|
---|
63 | */
|
---|
64 |
|
---|
65 |
|
---|
66 | #include "qpaintengineex_opengl2_p.h"
|
---|
67 |
|
---|
68 | #include <string.h> //for memcpy
|
---|
69 | #include <qmath.h>
|
---|
70 |
|
---|
71 | #include <private/qgl_p.h>
|
---|
72 | #include <private/qmath_p.h>
|
---|
73 | #include <private/qpaintengineex_p.h>
|
---|
74 | #include <QPaintEngine>
|
---|
75 | #include <private/qpainter_p.h>
|
---|
76 | #include <private/qfontengine_p.h>
|
---|
77 | #include <private/qtextureglyphcache_p.h>
|
---|
78 |
|
---|
79 | #include "qglgradientcache_p.h"
|
---|
80 | #include "qglpexshadermanager_p.h"
|
---|
81 | #include "qgl2pexvertexarray_p.h"
|
---|
82 |
|
---|
83 |
|
---|
84 | extern QImage qt_imageForBrush(int brushStyle, bool invert); //in qbrush.cpp
|
---|
85 |
|
---|
86 |
|
---|
87 | #include <QDebug>
|
---|
88 |
|
---|
89 |
|
---|
90 | static const GLuint QT_VERTEX_COORDS_ATTR = 0;
|
---|
91 | static const GLuint QT_TEXTURE_COORDS_ATTR = 1;
|
---|
92 | static const GLuint QT_BRUSH_TEXTURE_UNIT = 0;
|
---|
93 |
|
---|
94 | class QGL2PaintEngineExPrivate : public QPaintEngineExPrivate
|
---|
95 | {
|
---|
96 | Q_DECLARE_PUBLIC(QGL2PaintEngineEx)
|
---|
97 | public:
|
---|
98 | QGL2PaintEngineExPrivate(QGL2PaintEngineEx *q_ptr) :
|
---|
99 | q(q_ptr),
|
---|
100 | width(0), height(0),
|
---|
101 | ctx(0),
|
---|
102 | currentBrush( &(q->state()->brush) ),
|
---|
103 | inverseScale(1),
|
---|
104 | shaderManager(0)
|
---|
105 | { }
|
---|
106 |
|
---|
107 | ~QGL2PaintEngineExPrivate();
|
---|
108 |
|
---|
109 | void updateBrushTexture();
|
---|
110 | void updateBrushUniforms();
|
---|
111 | void updateMatrix();
|
---|
112 | void updateCompositionMode();
|
---|
113 | void updateTextureFilter(GLenum target, GLenum wrapMode, bool smoothPixmapTransform);
|
---|
114 |
|
---|
115 | void setBrush(const QBrush* brush);
|
---|
116 |
|
---|
117 | void drawTexture(const QGLRect& dest, const QGLRect& src, int txtWidth, int txtHeight);
|
---|
118 |
|
---|
119 | void fill(const QVectorPath &path);
|
---|
120 | void drawOutline(const QVectorPath& path);
|
---|
121 |
|
---|
122 | void drawVertexArrays(QGL2PEXVertexArray& vertexArray, GLenum primitive);
|
---|
123 | // ^ draws whatever is in the vertex array
|
---|
124 | void composite(const QGLRect& boundingRect);
|
---|
125 | // ^ Composites the bounding rect onto dest buffer
|
---|
126 | void fillStencilWithVertexArray(QGL2PEXVertexArray& vertexArray, bool useWindingFill);
|
---|
127 | // ^ Calls drawVertexArrays to render into stencil buffer
|
---|
128 | void cleanStencilBuffer(const QGLRect& area);
|
---|
129 |
|
---|
130 | void prepareForDraw();
|
---|
131 |
|
---|
132 | inline void useSimpleShader();
|
---|
133 | inline QColor premultiplyColor(QColor c, GLfloat opacity);
|
---|
134 |
|
---|
135 | QGL2PaintEngineEx* q;
|
---|
136 |
|
---|
137 | //### Move into QGLDrawable
|
---|
138 | int width, height;
|
---|
139 | QGLContext* ctx;
|
---|
140 |
|
---|
141 | // Dirty flags
|
---|
142 | bool matrixDirty; // Implies matrix uniforms are also dirty
|
---|
143 | bool compositionModeDirty;
|
---|
144 | bool brushTextureDirty;
|
---|
145 | bool brushUniformsDirty;
|
---|
146 | bool simpleShaderMatrixUniformDirty;
|
---|
147 | bool brushShaderMatrixUniformDirty;
|
---|
148 | bool imageShaderMatrixUniformDirty;
|
---|
149 | bool textShaderMatrixUniformDirty;
|
---|
150 | bool stencilBuferDirty;
|
---|
151 |
|
---|
152 | const QBrush* currentBrush; // May not be the state's brush!
|
---|
153 |
|
---|
154 | GLfloat inverseScale;
|
---|
155 |
|
---|
156 | QGL2PEXVertexArray pathVertexArray;
|
---|
157 |
|
---|
158 | GLfloat pmvMatrix[4][4];
|
---|
159 |
|
---|
160 | QGLPEXShaderManager* shaderManager;
|
---|
161 |
|
---|
162 | // Clipping & state stuff stolen from QOpenGLPaintEngine:
|
---|
163 | void updateDepthClip();
|
---|
164 | uint use_system_clip : 1;
|
---|
165 | };
|
---|
166 |
|
---|
167 |
|
---|
168 | ////////////////////////////////// Private Methods //////////////////////////////////////////
|
---|
169 |
|
---|
170 | QGL2PaintEngineExPrivate::~QGL2PaintEngineExPrivate()
|
---|
171 | {
|
---|
172 | if (shaderManager) {
|
---|
173 | delete shaderManager;
|
---|
174 | shaderManager = 0;
|
---|
175 | }
|
---|
176 | }
|
---|
177 |
|
---|
178 | void QGL2PaintEngineExPrivate::updateTextureFilter(GLenum target, GLenum wrapMode, bool smoothPixmapTransform)
|
---|
179 | {
|
---|
180 | glActiveTexture(QT_BRUSH_TEXTURE_UNIT);
|
---|
181 |
|
---|
182 | if (smoothPixmapTransform) {
|
---|
183 | glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
---|
184 | glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
---|
185 | } else {
|
---|
186 | glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
---|
187 | glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
---|
188 | }
|
---|
189 | glTexParameterf(target, GL_TEXTURE_WRAP_S, wrapMode);
|
---|
190 | glTexParameterf(target, GL_TEXTURE_WRAP_T, wrapMode);
|
---|
191 | }
|
---|
192 |
|
---|
193 |
|
---|
194 | QColor QGL2PaintEngineExPrivate::premultiplyColor(QColor c, GLfloat opacity)
|
---|
195 | {
|
---|
196 | uint alpha = qRound(c.alpha() * opacity);
|
---|
197 | return QColor ( ((c.red() * alpha + 128) >> 8),
|
---|
198 | ((c.green() * alpha + 128) >> 8),
|
---|
199 | ((c.blue() * alpha + 128) >> 8),
|
---|
200 | alpha);
|
---|
201 | }
|
---|
202 |
|
---|
203 |
|
---|
204 | void QGL2PaintEngineExPrivate::setBrush(const QBrush* brush)
|
---|
205 | {
|
---|
206 | currentBrush = brush;
|
---|
207 | brushTextureDirty = true;
|
---|
208 | brushUniformsDirty = true;
|
---|
209 | shaderManager->setBrushStyle(currentBrush->style());
|
---|
210 | shaderManager->setAffineOnlyBrushTransform(currentBrush->transform().isAffine());
|
---|
211 | }
|
---|
212 |
|
---|
213 |
|
---|
214 | // Unless this gets used elsewhere, it's probably best to merge it into fillStencilWithVertexArray
|
---|
215 | void QGL2PaintEngineExPrivate::useSimpleShader()
|
---|
216 | {
|
---|
217 | shaderManager->simpleShader()->use();
|
---|
218 |
|
---|
219 | if (matrixDirty)
|
---|
220 | updateMatrix();
|
---|
221 |
|
---|
222 | if (simpleShaderMatrixUniformDirty) {
|
---|
223 | shaderManager->simpleShader()->uniforms()[QLatin1String("pmvMatrix")] = pmvMatrix;
|
---|
224 | simpleShaderMatrixUniformDirty = false;
|
---|
225 | }
|
---|
226 | }
|
---|
227 |
|
---|
228 |
|
---|
229 | Q_GLOBAL_STATIC(QGLGradientCache, qt_opengl_gradient_cache)
|
---|
230 |
|
---|
231 | void QGL2PaintEngineExPrivate::updateBrushTexture()
|
---|
232 | {
|
---|
233 | // qDebug("QGL2PaintEngineExPrivate::updateBrushTexture()");
|
---|
234 | Qt::BrushStyle style = currentBrush->style();
|
---|
235 |
|
---|
236 | if ( (style >= Qt::Dense1Pattern) && (style <= Qt::DiagCrossPattern) ) {
|
---|
237 | // Get the image data for the pattern
|
---|
238 | QImage texImage = qt_imageForBrush(style, true);
|
---|
239 |
|
---|
240 | glActiveTexture(QT_BRUSH_TEXTURE_UNIT);
|
---|
241 | ctx->d_func()->bindTexture(texImage, GL_TEXTURE_2D, GL_RGBA, true);
|
---|
242 | updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, true);
|
---|
243 | }
|
---|
244 | else if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern) {
|
---|
245 | // Gradiant brush: All the gradiants use the same texture
|
---|
246 |
|
---|
247 | const QGradient* g = currentBrush->gradient();
|
---|
248 |
|
---|
249 | // We apply global opacity in the fragment shaders, so we always pass 1.0
|
---|
250 | // for opacity to the cache.
|
---|
251 | GLuint texId = qt_opengl_gradient_cache()->getBuffer(*g, 1.0, ctx);
|
---|
252 |
|
---|
253 | if (g->spread() == QGradient::RepeatSpread || g->type() == QGradient::ConicalGradient)
|
---|
254 | updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, true);
|
---|
255 | else if (g->spread() == QGradient::ReflectSpread)
|
---|
256 | updateTextureFilter(GL_TEXTURE_2D, GL_MIRRORED_REPEAT_IBM, true);
|
---|
257 | else
|
---|
258 | updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, true);
|
---|
259 |
|
---|
260 | glBindTexture(GL_TEXTURE_2D, texId);
|
---|
261 | }
|
---|
262 | else if (style == Qt::TexturePattern) {
|
---|
263 | const QPixmap& texPixmap = currentBrush->texture();
|
---|
264 |
|
---|
265 | glActiveTexture(QT_BRUSH_TEXTURE_UNIT);
|
---|
266 | ctx->d_func()->bindTexture(texPixmap, GL_TEXTURE_2D, GL_RGBA, true);
|
---|
267 | updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, true);
|
---|
268 | }
|
---|
269 | brushTextureDirty = false;
|
---|
270 | }
|
---|
271 |
|
---|
272 |
|
---|
273 | void QGL2PaintEngineExPrivate::updateBrushUniforms()
|
---|
274 | {
|
---|
275 | // qDebug("QGL2PaintEngineExPrivate::updateBrushUniforms()");
|
---|
276 | Qt::BrushStyle style = currentBrush->style();
|
---|
277 |
|
---|
278 | if (style == Qt::NoBrush)
|
---|
279 | return;
|
---|
280 |
|
---|
281 | GLfloat opacity = 1.0;
|
---|
282 | if (q->state()->opacity < 0.99f)
|
---|
283 | opacity = (GLfloat)q->state()->opacity;
|
---|
284 | bool setOpacity = true;
|
---|
285 |
|
---|
286 | QTransform brushQTransform = currentBrush->transform();
|
---|
287 |
|
---|
288 | if (style == Qt::SolidPattern) {
|
---|
289 | QColor col = premultiplyColor(currentBrush->color(), opacity);
|
---|
290 | shaderManager->brushShader()->uniforms()[QLatin1String("fragmentColor")] = col;
|
---|
291 | setOpacity = false;
|
---|
292 | }
|
---|
293 | else {
|
---|
294 | // All other brushes have a transform and thus need the translation point:
|
---|
295 | QPointF translationPoint;
|
---|
296 |
|
---|
297 | if (style <= Qt::DiagCrossPattern) {
|
---|
298 | translationPoint = q->state()->brushOrigin;
|
---|
299 |
|
---|
300 | QColor col = premultiplyColor(currentBrush->color(), opacity);
|
---|
301 |
|
---|
302 | shaderManager->brushShader()->uniforms()[QLatin1String("patternColor")] = col;
|
---|
303 | setOpacity = false; //So code below doesn't try to set the opacity uniform
|
---|
304 |
|
---|
305 | QGLVec2 halfViewportSize = { width*0.5, height*0.5 };
|
---|
306 | shaderManager->brushShader()->uniforms()[QLatin1String("halfViewportSize")] = halfViewportSize;
|
---|
307 | }
|
---|
308 | else if (style == Qt::LinearGradientPattern) {
|
---|
309 | const QLinearGradient *g = static_cast<const QLinearGradient *>(currentBrush->gradient());
|
---|
310 |
|
---|
311 | QPointF realStart = g->start();
|
---|
312 | QPointF realFinal = g->finalStop();
|
---|
313 | translationPoint = realStart;
|
---|
314 |
|
---|
315 | QPointF l = realFinal - realStart;
|
---|
316 |
|
---|
317 | // ###
|
---|
318 | QGLVec3 linearData = {
|
---|
319 | l.x(),
|
---|
320 | l.y(),
|
---|
321 | 1.0f / (l.x() * l.x() + l.y() * l.y())
|
---|
322 | };
|
---|
323 |
|
---|
324 | shaderManager->brushShader()->uniforms()[QLatin1String("linearData")] = linearData;
|
---|
325 |
|
---|
326 | QGLVec2 halfViewportSize = { width*0.5, height*0.5 };
|
---|
327 | shaderManager->brushShader()->uniforms()[QLatin1String("halfViewportSize")] = halfViewportSize;
|
---|
328 | }
|
---|
329 | else if (style == Qt::ConicalGradientPattern) {
|
---|
330 | const QConicalGradient *g = static_cast<const QConicalGradient *>(currentBrush->gradient());
|
---|
331 | translationPoint = g->center();
|
---|
332 |
|
---|
333 | GLfloat angle = -(g->angle() * 2 * Q_PI) / 360.0;
|
---|
334 |
|
---|
335 | shaderManager->brushShader()->uniforms()[QLatin1String("angle")] = angle;
|
---|
336 |
|
---|
337 | QGLVec2 halfViewportSize = { width*0.5, height*0.5 };
|
---|
338 | shaderManager->brushShader()->uniforms()[QLatin1String("halfViewportSize")] = halfViewportSize;
|
---|
339 | }
|
---|
340 | else if (style == Qt::RadialGradientPattern) {
|
---|
341 | const QRadialGradient *g = static_cast<const QRadialGradient *>(currentBrush->gradient());
|
---|
342 | QPointF realCenter = g->center();
|
---|
343 | QPointF realFocal = g->focalPoint();
|
---|
344 | qreal realRadius = g->radius();
|
---|
345 | translationPoint = realFocal;
|
---|
346 |
|
---|
347 | QPointF fmp = realCenter - realFocal;
|
---|
348 | shaderManager->brushShader()->uniforms()[QLatin1String("fmp")] = fmp;
|
---|
349 |
|
---|
350 | GLfloat fmp2_m_radius2 = -fmp.x() * fmp.x() - fmp.y() * fmp.y() + realRadius*realRadius;
|
---|
351 | shaderManager->brushShader()->uniforms()[QLatin1String("fmp2_m_radius2")] = fmp2_m_radius2;
|
---|
352 |
|
---|
353 | shaderManager->brushShader()->uniforms()[QLatin1String("inverse_2_fmp2_m_radius2")] =
|
---|
354 | GLfloat(1.0 / (2.0*fmp2_m_radius2));
|
---|
355 |
|
---|
356 | QGLVec2 halfViewportSize = { width*0.5, height*0.5 };
|
---|
357 | shaderManager->brushShader()->uniforms()[QLatin1String("halfViewportSize")] = halfViewportSize;
|
---|
358 | }
|
---|
359 | else if (style == Qt::TexturePattern) {
|
---|
360 | translationPoint = q->state()->brushOrigin;
|
---|
361 |
|
---|
362 | const QPixmap& texPixmap = currentBrush->texture();
|
---|
363 |
|
---|
364 | QSizeF invertedTextureSize( 1.0 / texPixmap.width(), 1.0 / texPixmap.height() );
|
---|
365 | shaderManager->brushShader()->uniforms()[QLatin1String("invertedTextureSize")] = invertedTextureSize;
|
---|
366 |
|
---|
367 | QGLVec2 halfViewportSize = { width*0.5, height*0.5 };
|
---|
368 | shaderManager->brushShader()->uniforms()[QLatin1String("halfViewportSize")] = halfViewportSize;
|
---|
369 | }
|
---|
370 | else
|
---|
371 | qWarning("QGL2PaintEngineEx: Unimplemented fill style");
|
---|
372 |
|
---|
373 | QTransform translate(1, 0, 0, 1, -translationPoint.x(), -translationPoint.y());
|
---|
374 | QTransform gl_to_qt(1, 0, 0, -1, 0, height);
|
---|
375 | QTransform inv_matrix = gl_to_qt * (brushQTransform * q->state()->matrix).inverted() * translate;
|
---|
376 |
|
---|
377 | shaderManager->brushShader()->uniforms()[QLatin1String("brushTransform")] = inv_matrix;
|
---|
378 | shaderManager->brushShader()->uniforms()[QLatin1String("brushTexture")] = QT_BRUSH_TEXTURE_UNIT;
|
---|
379 |
|
---|
380 | if (setOpacity)
|
---|
381 | shaderManager->brushShader()->uniforms()[QLatin1String("opacity")] = opacity;
|
---|
382 | }
|
---|
383 | brushUniformsDirty = false;
|
---|
384 | }
|
---|
385 |
|
---|
386 |
|
---|
387 | // This assumes the shader manager has already setup the correct shader program
|
---|
388 | void QGL2PaintEngineExPrivate::updateMatrix()
|
---|
389 | {
|
---|
390 | // qDebug("QGL2PaintEngineExPrivate::updateMatrix()");
|
---|
391 |
|
---|
392 | // We setup the Projection matrix to be the equivilant of glOrtho(0, w, h, 0, -1, 1):
|
---|
393 | GLfloat P[4][4] = {
|
---|
394 | {2.0/width, 0.0, 0.0, -1.0},
|
---|
395 | {0.0, -2.0/height, 0.0, 1.0},
|
---|
396 | {0.0, 0.0, -1.0, 0.0},
|
---|
397 | {0.0, 0.0, 0.0, 1.0}
|
---|
398 | };
|
---|
399 |
|
---|
400 | // Use the (3x3) transform for the Model~View matrix:
|
---|
401 | const QTransform& transform = q->state()->matrix;
|
---|
402 | GLfloat MV[4][4] = {
|
---|
403 | {transform.m11(), transform.m21(), 0.0, transform.dx() + 0.5},
|
---|
404 | {transform.m12(), transform.m22(), 0.0, transform.dy() + 0.5},
|
---|
405 | {0.0, 0.0, 1.0, 0.0},
|
---|
406 | {transform.m13(), transform.m23(), 0.0, transform.m33()}
|
---|
407 | };
|
---|
408 |
|
---|
409 | // NOTE: OpenGL ES works with column-major matrices, so when we multiply the matrices,
|
---|
410 | // we also transpose them ready for GL.
|
---|
411 | for (int row = 0; row < 4; ++row) {
|
---|
412 | for (int col = 0; col < 4; ++col) {
|
---|
413 | pmvMatrix[col][row] = 0.0;
|
---|
414 | for (int n = 0; n < 4; ++n)
|
---|
415 | pmvMatrix[col][row] += P[row][n] * MV[n][col];
|
---|
416 | }
|
---|
417 | }
|
---|
418 |
|
---|
419 | // 1/10000 == 0.0001, so we have good enough res to cover curves
|
---|
420 | // that span the entire widget...
|
---|
421 | inverseScale = qMax(1 / qMax( qMax(qAbs(transform.m11()), qAbs(transform.m22())),
|
---|
422 | qMax(qAbs(transform.m12()), qAbs(transform.m21())) ),
|
---|
423 | qreal(0.0001));
|
---|
424 |
|
---|
425 | matrixDirty = false;
|
---|
426 |
|
---|
427 | // The actual data has been updated so both shader program's uniforms need updating
|
---|
428 | simpleShaderMatrixUniformDirty = true;
|
---|
429 | brushShaderMatrixUniformDirty = true;
|
---|
430 | imageShaderMatrixUniformDirty = true;
|
---|
431 | textShaderMatrixUniformDirty = true;
|
---|
432 | }
|
---|
433 |
|
---|
434 |
|
---|
435 | void QGL2PaintEngineExPrivate::updateCompositionMode()
|
---|
436 | {
|
---|
437 | // NOTE: The entire paint engine works on pre-multiplied data - which is why some of these
|
---|
438 | // composition modes look odd.
|
---|
439 | // qDebug() << "QGL2PaintEngineExPrivate::updateCompositionMode() - Setting GL composition mode for " << q->state()->composition_mode;
|
---|
440 | switch(q->state()->composition_mode) {
|
---|
441 | case QPainter::CompositionMode_SourceOver:
|
---|
442 | glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
---|
443 | break;
|
---|
444 | case QPainter::CompositionMode_DestinationOver:
|
---|
445 | glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);
|
---|
446 | break;
|
---|
447 | case QPainter::CompositionMode_Clear:
|
---|
448 | glBlendFunc(GL_ZERO, GL_ZERO);
|
---|
449 | break;
|
---|
450 | case QPainter::CompositionMode_Source:
|
---|
451 | glBlendFunc(GL_ONE, GL_ZERO);
|
---|
452 | break;
|
---|
453 | case QPainter::CompositionMode_Destination:
|
---|
454 | glBlendFunc(GL_ZERO, GL_ONE);
|
---|
455 | break;
|
---|
456 | case QPainter::CompositionMode_SourceIn:
|
---|
457 | glBlendFunc(GL_DST_ALPHA, GL_ZERO);
|
---|
458 | break;
|
---|
459 | case QPainter::CompositionMode_DestinationIn:
|
---|
460 | glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
|
---|
461 | break;
|
---|
462 | case QPainter::CompositionMode_SourceOut:
|
---|
463 | glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ZERO);
|
---|
464 | break;
|
---|
465 | case QPainter::CompositionMode_DestinationOut:
|
---|
466 | glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
|
---|
467 | break;
|
---|
468 | case QPainter::CompositionMode_SourceAtop:
|
---|
469 | glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
---|
470 | break;
|
---|
471 | case QPainter::CompositionMode_DestinationAtop:
|
---|
472 | glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA);
|
---|
473 | break;
|
---|
474 | case QPainter::CompositionMode_Xor:
|
---|
475 | glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
---|
476 | break;
|
---|
477 | case QPainter::CompositionMode_Plus:
|
---|
478 | glBlendFunc(GL_ONE, GL_ONE);
|
---|
479 | break;
|
---|
480 | default:
|
---|
481 | qWarning("Unsupported composition mode");
|
---|
482 | break;
|
---|
483 | }
|
---|
484 |
|
---|
485 | compositionModeDirty = false;
|
---|
486 | }
|
---|
487 |
|
---|
488 |
|
---|
489 | void QGL2PaintEngineExPrivate::drawTexture(const QGLRect& dest, const QGLRect& src, int txtWidth, int txtHeight)
|
---|
490 | {
|
---|
491 | // qDebug("QGL2PaintEngineExPrivate::drawImage()");
|
---|
492 |
|
---|
493 | // We have a shader specifically for drawPixmap/drawImage...
|
---|
494 | shaderManager->imageShader()->use();
|
---|
495 |
|
---|
496 | updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, false);
|
---|
497 |
|
---|
498 | if (compositionModeDirty)
|
---|
499 | updateCompositionMode();
|
---|
500 |
|
---|
501 | if (matrixDirty)
|
---|
502 | updateMatrix();
|
---|
503 |
|
---|
504 | if (imageShaderMatrixUniformDirty) {
|
---|
505 | shaderManager->imageShader()->uniforms()[QLatin1String("pmvMatrix")] = pmvMatrix;
|
---|
506 | imageShaderMatrixUniformDirty = false;
|
---|
507 | }
|
---|
508 |
|
---|
509 | shaderManager->imageShader()->uniforms()[QLatin1String("textureSampler")] = QT_BRUSH_TEXTURE_UNIT;
|
---|
510 |
|
---|
511 | // if (q->state()->opacity < 0.99f)
|
---|
512 | shaderManager->imageShader()->uniforms()[QLatin1String("opacity")] = (GLfloat)q->state()->opacity;
|
---|
513 |
|
---|
514 | GLfloat vertexCoords[] = {
|
---|
515 | dest.left, dest.top,
|
---|
516 | dest.left, dest.bottom,
|
---|
517 | dest.right, dest.bottom,
|
---|
518 | dest.right, dest.top
|
---|
519 | };
|
---|
520 |
|
---|
521 | glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
|
---|
522 | glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexCoords);
|
---|
523 |
|
---|
524 | GLfloat dx = 1.0 / txtWidth;
|
---|
525 | GLfloat dy = 1.0 / txtHeight;
|
---|
526 |
|
---|
527 | QGLRect srcTextureRect(src.left*dx, 1.0 - src.top*dy, src.right*dx, 1.0 - src.bottom*dy);
|
---|
528 |
|
---|
529 | GLfloat textureCoords[] = {
|
---|
530 | srcTextureRect.left, srcTextureRect.top,
|
---|
531 | srcTextureRect.left, srcTextureRect.bottom,
|
---|
532 | srcTextureRect.right, srcTextureRect.bottom,
|
---|
533 | srcTextureRect.right, srcTextureRect.top
|
---|
534 | };
|
---|
535 |
|
---|
536 | glEnableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
|
---|
537 | glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, textureCoords);
|
---|
538 |
|
---|
539 | glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
---|
540 |
|
---|
541 | glDisableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
|
---|
542 | glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
|
---|
543 | }
|
---|
544 |
|
---|
545 |
|
---|
546 | void QGL2PaintEngineExPrivate::drawOutline(const QVectorPath& path)
|
---|
547 | {
|
---|
548 | // qDebug("QGL2PaintEngineExPrivate::drawOutline()");
|
---|
549 | if (matrixDirty)
|
---|
550 | updateMatrix();
|
---|
551 |
|
---|
552 | pathVertexArray.clear();
|
---|
553 | pathVertexArray.addPath(path, inverseScale);
|
---|
554 |
|
---|
555 | if (path.hasImplicitClose()) {
|
---|
556 | // Close the path's outline
|
---|
557 | pathVertexArray.lineToArray(path.points()[0], path.points()[1]);
|
---|
558 | pathVertexArray.stops().last() += 1;
|
---|
559 | }
|
---|
560 |
|
---|
561 | prepareForDraw();
|
---|
562 | drawVertexArrays(pathVertexArray, GL_LINE_STRIP);
|
---|
563 | }
|
---|
564 |
|
---|
565 |
|
---|
566 | // Assumes everything is configured for the brush you want to use
|
---|
567 | void QGL2PaintEngineExPrivate::fill(const QVectorPath& path)
|
---|
568 | {
|
---|
569 | if (matrixDirty)
|
---|
570 | updateMatrix();
|
---|
571 |
|
---|
572 | const QPointF* const points = reinterpret_cast<const QPointF*>(path.points());
|
---|
573 |
|
---|
574 |
|
---|
575 | // Check to see if there's any hints
|
---|
576 | if (path.shape() == QVectorPath::RectangleHint) {
|
---|
577 | QGLRect rect(points[0].x(), points[0].y(), points[2].x(), points[2].y());
|
---|
578 | prepareForDraw();
|
---|
579 | composite(rect);
|
---|
580 | }
|
---|
581 | else if (path.shape() == QVectorPath::EllipseHint) {
|
---|
582 | pathVertexArray.clear();
|
---|
583 | pathVertexArray.addPath(path, inverseScale);
|
---|
584 | prepareForDraw();
|
---|
585 | drawVertexArrays(pathVertexArray, GL_TRIANGLE_FAN);
|
---|
586 | }
|
---|
587 | else {
|
---|
588 | // The path is too complicated & needs the stencil technique
|
---|
589 | pathVertexArray.clear();
|
---|
590 | pathVertexArray.addPath(path, inverseScale);
|
---|
591 |
|
---|
592 | fillStencilWithVertexArray(pathVertexArray, path.hasWindingFill());
|
---|
593 |
|
---|
594 | // Stencil the brush onto the dest buffer
|
---|
595 | glStencilFunc(GL_NOTEQUAL, 0, 0xFFFF); // Pass if stencil buff value != 0
|
---|
596 | glEnable(GL_STENCIL_TEST);
|
---|
597 | prepareForDraw();
|
---|
598 | composite(pathVertexArray.boundingRect());
|
---|
599 | glDisable(GL_STENCIL_TEST);
|
---|
600 |
|
---|
601 | cleanStencilBuffer(pathVertexArray.boundingRect());
|
---|
602 | }
|
---|
603 | }
|
---|
604 |
|
---|
605 |
|
---|
606 | void QGL2PaintEngineExPrivate::fillStencilWithVertexArray(QGL2PEXVertexArray& vertexArray, bool useWindingFill)
|
---|
607 | {
|
---|
608 | // qDebug("QGL2PaintEngineExPrivate::fillStencilWithVertexArray()");
|
---|
609 | if (stencilBuferDirty) {
|
---|
610 | // Clear the stencil buffer to zeros
|
---|
611 | glDisable(GL_STENCIL_TEST);
|
---|
612 | glStencilMask(0xFFFF); // Enable writing to stencil buffer, otherwise glClear wont do anything.
|
---|
613 | glClearStencil(0); // Clear to zero
|
---|
614 | glClear(GL_STENCIL_BUFFER_BIT);
|
---|
615 | stencilBuferDirty = false;
|
---|
616 | }
|
---|
617 |
|
---|
618 | glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // Disable color writes
|
---|
619 | glStencilMask(0xFFFF); // Enable stencil writes
|
---|
620 | glStencilFunc(GL_ALWAYS, 0, 0xFFFF); // Always pass the stencil test
|
---|
621 |
|
---|
622 | // Setup the stencil op:
|
---|
623 | if (useWindingFill) {
|
---|
624 | glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP); // Inc. for front-facing triangle
|
---|
625 | glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP); //Dec. for back-facing "holes"
|
---|
626 | } else
|
---|
627 | glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT); // Simply invert the stencil bit
|
---|
628 |
|
---|
629 | // No point in using a fancy gradiant shader for writing into the stencil buffer!
|
---|
630 | useSimpleShader();
|
---|
631 |
|
---|
632 | glEnable(GL_STENCIL_TEST); // For some reason, this has to happen _after_ the simple shader is use()'d
|
---|
633 | glDisable(GL_BLEND);
|
---|
634 |
|
---|
635 | // Draw the vertecies into the stencil buffer:
|
---|
636 | drawVertexArrays(vertexArray, GL_TRIANGLE_FAN);
|
---|
637 |
|
---|
638 | // Enable color writes & disable stencil writes
|
---|
639 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
---|
640 | glStencilMask(0);
|
---|
641 | }
|
---|
642 |
|
---|
643 | void QGL2PaintEngineExPrivate::cleanStencilBuffer(const QGLRect& area)
|
---|
644 | {
|
---|
645 | // qDebug("QGL2PaintEngineExPrivate::cleanStencilBuffer()");
|
---|
646 | useSimpleShader();
|
---|
647 |
|
---|
648 | GLfloat rectVerts[] = {
|
---|
649 | area.left, area.top,
|
---|
650 | area.left, area.bottom,
|
---|
651 | area.right, area.bottom,
|
---|
652 | area.right, area.top
|
---|
653 | };
|
---|
654 |
|
---|
655 | glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
|
---|
656 | glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, rectVerts);
|
---|
657 |
|
---|
658 | glEnable(GL_STENCIL_TEST);
|
---|
659 | glStencilFunc(GL_ALWAYS, 0, 0xFFFF); // Always pass the stencil test
|
---|
660 |
|
---|
661 | glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // Disable color writes
|
---|
662 | glStencilMask(0xFFFF); // Enable writing to stencil buffer
|
---|
663 | glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); // Write 0's to stencil buffer
|
---|
664 |
|
---|
665 | glDisable(GL_BLEND);
|
---|
666 |
|
---|
667 | glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
---|
668 |
|
---|
669 | glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
|
---|
670 |
|
---|
671 | // Enable color writes & disable stencil writes
|
---|
672 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
---|
673 | glStencilMask(0);
|
---|
674 | glDisable(GL_STENCIL_TEST);
|
---|
675 | }
|
---|
676 |
|
---|
677 | void QGL2PaintEngineExPrivate::prepareForDraw()
|
---|
678 | {
|
---|
679 | if (brushTextureDirty)
|
---|
680 | updateBrushTexture();
|
---|
681 |
|
---|
682 | if (compositionModeDirty)
|
---|
683 | updateCompositionMode();
|
---|
684 |
|
---|
685 | if (shaderManager->useCorrectShaderProg()) {
|
---|
686 | // The shader program has changed so mark all uniforms as dirty:
|
---|
687 | brushUniformsDirty = true;
|
---|
688 | brushShaderMatrixUniformDirty = true;
|
---|
689 | }
|
---|
690 |
|
---|
691 | if (brushUniformsDirty)
|
---|
692 | updateBrushUniforms();
|
---|
693 |
|
---|
694 | if (brushShaderMatrixUniformDirty) {
|
---|
695 | shaderManager->brushShader()->uniforms()[QLatin1String("pmvMatrix")] = pmvMatrix;
|
---|
696 | brushShaderMatrixUniformDirty = false;
|
---|
697 | }
|
---|
698 |
|
---|
699 | if ((q->state()->opacity < 0.99f) || !currentBrush->isOpaque())
|
---|
700 | glEnable(GL_BLEND);
|
---|
701 | else
|
---|
702 | glDisable(GL_BLEND);
|
---|
703 | }
|
---|
704 |
|
---|
705 | void QGL2PaintEngineExPrivate::composite(const QGLRect& boundingRect)
|
---|
706 | {
|
---|
707 | // Setup a vertex array for the bounding rect:
|
---|
708 | GLfloat rectVerts[] = {
|
---|
709 | boundingRect.left, boundingRect.top,
|
---|
710 | boundingRect.left, boundingRect.bottom,
|
---|
711 | boundingRect.right, boundingRect.bottom,
|
---|
712 | boundingRect.right, boundingRect.top
|
---|
713 | };
|
---|
714 |
|
---|
715 | glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
|
---|
716 | glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, rectVerts);
|
---|
717 |
|
---|
718 | glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
---|
719 |
|
---|
720 | glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
|
---|
721 | }
|
---|
722 |
|
---|
723 | // Draws the vertex array as a set of <vertexArrayStops.size()> triangle fans.
|
---|
724 | void QGL2PaintEngineExPrivate::drawVertexArrays(QGL2PEXVertexArray& vertexArray, GLenum primitive)
|
---|
725 | {
|
---|
726 | // Now setup the pointer to the vertex array:
|
---|
727 | glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
|
---|
728 | glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexArray.data());
|
---|
729 |
|
---|
730 | int previousStop = 0;
|
---|
731 | foreach(int stop, vertexArray.stops()) {
|
---|
732 | /*
|
---|
733 | qDebug("Drawing triangle fan for vertecies %d -> %d:", previousStop, stop-1);
|
---|
734 | for (int i=previousStop; i<stop; ++i)
|
---|
735 | qDebug(" %02d: [%.2f, %.2f]", i, vertexArray.data()[i].x, vertexArray.data()[i].y);
|
---|
736 | */
|
---|
737 | glDrawArrays(primitive, previousStop, stop - previousStop);
|
---|
738 | previousStop = stop;
|
---|
739 | }
|
---|
740 | glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
|
---|
741 | }
|
---|
742 |
|
---|
743 |
|
---|
744 |
|
---|
745 |
|
---|
746 |
|
---|
747 | /////////////////////////////////// Public Methods //////////////////////////////////////////
|
---|
748 |
|
---|
749 | QGL2PaintEngineEx::QGL2PaintEngineEx()
|
---|
750 | : QPaintEngineEx(*(new QGL2PaintEngineExPrivate(this)))
|
---|
751 | {
|
---|
752 | qDebug("QGL2PaintEngineEx::QGL2PaintEngineEx()");
|
---|
753 |
|
---|
754 | }
|
---|
755 |
|
---|
756 | QGL2PaintEngineEx::~QGL2PaintEngineEx()
|
---|
757 | {
|
---|
758 | }
|
---|
759 |
|
---|
760 | void QGL2PaintEngineEx::fill(const QVectorPath &path, const QBrush &brush)
|
---|
761 | {
|
---|
762 | Q_D(QGL2PaintEngineEx);
|
---|
763 |
|
---|
764 | QTime startTime = QTime::currentTime();
|
---|
765 |
|
---|
766 | d->setBrush(&brush);
|
---|
767 | d->fill(path);
|
---|
768 | d->setBrush(&(state()->brush)); // reset back to the state's brush
|
---|
769 | }
|
---|
770 |
|
---|
771 | void QGL2PaintEngineEx::stroke(const QVectorPath &path, const QPen &pen)
|
---|
772 | {
|
---|
773 | Q_D(QGL2PaintEngineEx);
|
---|
774 |
|
---|
775 | if (pen.style() == Qt::NoPen)
|
---|
776 | return;
|
---|
777 |
|
---|
778 | if ( (pen.isCosmetic() && (pen.style() == Qt::SolidLine)) && (pen.widthF() < 2.5f) )
|
---|
779 | {
|
---|
780 | // We only handle solid, cosmetic pens with a width of 1 pixel
|
---|
781 | const QBrush& brush = pen.brush();
|
---|
782 | d->setBrush(&brush);
|
---|
783 |
|
---|
784 | if (pen.widthF() < 0.01f)
|
---|
785 | glLineWidth(1.0);
|
---|
786 | else
|
---|
787 | glLineWidth(pen.widthF());
|
---|
788 |
|
---|
789 | d->drawOutline(path);
|
---|
790 | d->setBrush(&(state()->brush));
|
---|
791 | } else
|
---|
792 | return QPaintEngineEx::stroke(path, pen);
|
---|
793 |
|
---|
794 | }
|
---|
795 |
|
---|
796 | void QGL2PaintEngineEx::penChanged()
|
---|
797 | {
|
---|
798 | // qDebug("QGL2PaintEngineEx::penChanged() not implemented!");
|
---|
799 | }
|
---|
800 |
|
---|
801 |
|
---|
802 | void QGL2PaintEngineEx::brushChanged()
|
---|
803 | {
|
---|
804 | // qDebug("QGL2PaintEngineEx::brushChanged()");
|
---|
805 | Q_D(QGL2PaintEngineEx);
|
---|
806 | d->setBrush(&(state()->brush));
|
---|
807 | }
|
---|
808 |
|
---|
809 | void QGL2PaintEngineEx::brushOriginChanged()
|
---|
810 | {
|
---|
811 | // qDebug("QGL2PaintEngineEx::brushOriginChanged()");
|
---|
812 | Q_D(QGL2PaintEngineEx);
|
---|
813 | d->brushUniformsDirty = true;
|
---|
814 | }
|
---|
815 |
|
---|
816 | void QGL2PaintEngineEx::opacityChanged()
|
---|
817 | {
|
---|
818 | // qDebug("QGL2PaintEngineEx::opacityChanged()");
|
---|
819 | Q_D(QGL2PaintEngineEx);
|
---|
820 |
|
---|
821 | Q_ASSERT(d->shaderManager);
|
---|
822 | d->shaderManager->setUseGlobalOpacity(state()->opacity > 0.999);
|
---|
823 | d->brushUniformsDirty = true;
|
---|
824 | }
|
---|
825 |
|
---|
826 | void QGL2PaintEngineEx::compositionModeChanged()
|
---|
827 | {
|
---|
828 | // qDebug("QGL2PaintEngineEx::compositionModeChanged()");
|
---|
829 | Q_D(QGL2PaintEngineEx);
|
---|
830 | d->compositionModeDirty = true;
|
---|
831 | }
|
---|
832 |
|
---|
833 | void QGL2PaintEngineEx::renderHintsChanged()
|
---|
834 | {
|
---|
835 | // qDebug("QGL2PaintEngineEx::renderHintsChanged() not implemented!");
|
---|
836 | }
|
---|
837 |
|
---|
838 | void QGL2PaintEngineEx::transformChanged()
|
---|
839 | {
|
---|
840 | Q_D(QGL2PaintEngineEx);
|
---|
841 | d->matrixDirty = true;
|
---|
842 | }
|
---|
843 |
|
---|
844 |
|
---|
845 | void QGL2PaintEngineEx::drawPixmap(const QRectF& dest, const QPixmap & pixmap, const QRectF & src)
|
---|
846 | {
|
---|
847 | Q_D(QGL2PaintEngineEx);
|
---|
848 | glActiveTexture(QT_BRUSH_TEXTURE_UNIT);
|
---|
849 |
|
---|
850 | d->ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA, true);
|
---|
851 |
|
---|
852 | //FIXME: we should use hasAlpha() instead, but that's SLOW at the moment
|
---|
853 | if ((state()->opacity < 0.99f) || pixmap.hasAlphaChannel())
|
---|
854 | glEnable(GL_BLEND);
|
---|
855 | else
|
---|
856 | glDisable(GL_BLEND);
|
---|
857 |
|
---|
858 | d->drawTexture(dest, src, pixmap.width(), pixmap.height());
|
---|
859 | }
|
---|
860 |
|
---|
861 | void QGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, const QRectF& src,
|
---|
862 | Qt::ImageConversionFlags)
|
---|
863 | {
|
---|
864 | Q_D(QGL2PaintEngineEx);
|
---|
865 | glActiveTexture(QT_BRUSH_TEXTURE_UNIT);
|
---|
866 | d->ctx->d_func()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, true);
|
---|
867 |
|
---|
868 | if ((state()->opacity < 0.99f) || image.hasAlphaChannel())
|
---|
869 | glEnable(GL_BLEND);
|
---|
870 | else
|
---|
871 | glDisable(GL_BLEND);
|
---|
872 |
|
---|
873 | d->drawTexture(dest, src, image.width(), image.height());
|
---|
874 | }
|
---|
875 |
|
---|
876 | void QGL2PaintEngineEx::drawTextItem(const QPointF &p, const QTextItem &textItem)
|
---|
877 | {
|
---|
878 | QOpenGLPaintEngineState *s = state();
|
---|
879 |
|
---|
880 | const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
|
---|
881 |
|
---|
882 | bool drawCached = true;
|
---|
883 |
|
---|
884 | if (state()->pen.brush().style() != Qt::SolidPattern)
|
---|
885 | drawCached = false;
|
---|
886 |
|
---|
887 | if (s->matrix.type() > QTransform::TxTranslate)
|
---|
888 | drawCached = false;
|
---|
889 |
|
---|
890 | // don't try to cache huge fonts
|
---|
891 | if (ti.fontEngine->fontDef.pixelSize * qSqrt(s->matrix.determinant()) >= 64)
|
---|
892 | drawCached = false;
|
---|
893 |
|
---|
894 | if (drawCached) {
|
---|
895 | drawCachedGlyphs(p, ti);
|
---|
896 | return;
|
---|
897 | }
|
---|
898 |
|
---|
899 | QPaintEngineEx::drawTextItem(p, ti);
|
---|
900 | }
|
---|
901 |
|
---|
902 | void QGL2PaintEngineEx::drawCachedGlyphs(const QPointF &p, const QTextItemInt &ti)
|
---|
903 | {
|
---|
904 | Q_D(QGL2PaintEngineEx);
|
---|
905 | QOpenGLPaintEngineState *s = state();
|
---|
906 |
|
---|
907 | QVarLengthArray<QFixedPoint> positions;
|
---|
908 | QVarLengthArray<glyph_t> glyphs;
|
---|
909 | QTransform matrix;
|
---|
910 | matrix.translate(p.x(), p.y());
|
---|
911 | ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
|
---|
912 |
|
---|
913 | QFontEngineGlyphCache::Type glyphType = ti.fontEngine->glyphFormat >= 0
|
---|
914 | ? QFontEngineGlyphCache::Type(ti.fontEngine->glyphFormat)
|
---|
915 | : QFontEngineGlyphCache::Raster_A8;
|
---|
916 |
|
---|
917 | QImageTextureGlyphCache *cache =
|
---|
918 | (QImageTextureGlyphCache *) ti.fontEngine->glyphCache(glyphType, s->matrix);
|
---|
919 | if (!cache) {
|
---|
920 | cache = new QImageTextureGlyphCache(glyphType, s->matrix);
|
---|
921 | ti.fontEngine->setGlyphCache(glyphType, cache);
|
---|
922 | }
|
---|
923 |
|
---|
924 | cache->populate(ti, glyphs, positions);
|
---|
925 |
|
---|
926 | const QImage &image = cache->image();
|
---|
927 | int margin = cache->glyphMargin();
|
---|
928 |
|
---|
929 | glActiveTexture(QT_BRUSH_TEXTURE_UNIT);
|
---|
930 | d->ctx->d_func()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, true);
|
---|
931 |
|
---|
932 | glEnable(GL_BLEND);
|
---|
933 |
|
---|
934 | d->shaderManager->textShader()->use();
|
---|
935 | d->updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, false);
|
---|
936 |
|
---|
937 | if (d->compositionModeDirty)
|
---|
938 | d->updateCompositionMode();
|
---|
939 |
|
---|
940 | if (d->matrixDirty)
|
---|
941 | d->updateMatrix();
|
---|
942 |
|
---|
943 | if (d->textShaderMatrixUniformDirty) {
|
---|
944 | d->shaderManager->textShader()->uniforms()[QLatin1String("pmvMatrix")] = d->pmvMatrix;
|
---|
945 | d->textShaderMatrixUniformDirty = false;
|
---|
946 | }
|
---|
947 |
|
---|
948 | d->shaderManager->textShader()->uniforms()[QLatin1String("textureSampler")] = QT_BRUSH_TEXTURE_UNIT;
|
---|
949 | QColor col = d->premultiplyColor(state()->pen.color(), (GLfloat)state()->opacity);
|
---|
950 | d->shaderManager->textShader()->uniforms()[QLatin1String("fragmentColor")] = col;
|
---|
951 |
|
---|
952 | GLfloat dx = 1.0 / image.width();
|
---|
953 | GLfloat dy = 1.0 / image.height();
|
---|
954 |
|
---|
955 | glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
|
---|
956 | glEnableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
|
---|
957 | for (int i=0; i<glyphs.size(); ++i) {
|
---|
958 | const QTextureGlyphCache::Coord &c = cache->coords.value(glyphs[i]);
|
---|
959 | int x = positions[i].x.toInt() + c.baseLineX - margin;
|
---|
960 | int y = positions[i].y.toInt() - c.baseLineY - margin;
|
---|
961 |
|
---|
962 | QGLRect dest = QRectF(x, y, c.w, c.h);
|
---|
963 | QGLRect src = QRectF(c.x, c.y, c.w, c.h);
|
---|
964 |
|
---|
965 | GLfloat vertexCoords[] = {
|
---|
966 | dest.left, dest.top,
|
---|
967 | dest.left, dest.bottom,
|
---|
968 | dest.right, dest.bottom,
|
---|
969 | dest.right, dest.top
|
---|
970 | };
|
---|
971 |
|
---|
972 | glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexCoords);
|
---|
973 |
|
---|
974 | QGLRect srcTextureRect(src.left*dx, 1.0 - src.top*dy, src.right*dx, 1.0 - src.bottom*dy);
|
---|
975 |
|
---|
976 | GLfloat textureCoords[] = {
|
---|
977 | srcTextureRect.left, srcTextureRect.top,
|
---|
978 | srcTextureRect.left, srcTextureRect.bottom,
|
---|
979 | srcTextureRect.right, srcTextureRect.bottom,
|
---|
980 | srcTextureRect.right, srcTextureRect.top
|
---|
981 | };
|
---|
982 |
|
---|
983 | glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, textureCoords);
|
---|
984 |
|
---|
985 | glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
---|
986 | }
|
---|
987 | glDisableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
|
---|
988 | glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
|
---|
989 | }
|
---|
990 |
|
---|
991 | bool QGL2PaintEngineEx::begin(QPaintDevice *pdev)
|
---|
992 | {
|
---|
993 | Q_D(QGL2PaintEngineEx);
|
---|
994 |
|
---|
995 | // qDebug("QGL2PaintEngineEx::begin()");
|
---|
996 |
|
---|
997 | QGLWidget* widget = static_cast<QGLWidget*>(pdev);
|
---|
998 | d->ctx = const_cast<QGLContext*>(widget->context());
|
---|
999 | d->ctx->makeCurrent();
|
---|
1000 | d->width = widget->width();
|
---|
1001 | d->height = widget->height();
|
---|
1002 |
|
---|
1003 | if (!d->shaderManager)
|
---|
1004 | d->shaderManager = new QGLPEXShaderManager(d->ctx);
|
---|
1005 |
|
---|
1006 | glViewport(0, 0, d->width, d->height);
|
---|
1007 |
|
---|
1008 | // glClearColor(0.0, 1.0, 0.0, 1.0);
|
---|
1009 | // glClear(GL_COLOR_BUFFER_BIT);
|
---|
1010 | // d->ctx->swapBuffers();
|
---|
1011 | // qDebug("You should see green now");
|
---|
1012 | // sleep(5);
|
---|
1013 |
|
---|
1014 | d->brushTextureDirty = true;
|
---|
1015 | d->brushUniformsDirty = true;
|
---|
1016 | d->matrixDirty = true;
|
---|
1017 | d->compositionModeDirty = true;
|
---|
1018 | d->stencilBuferDirty = true;
|
---|
1019 |
|
---|
1020 | d->use_system_clip = !systemClip().isEmpty();
|
---|
1021 |
|
---|
1022 | glDisable(GL_DEPTH_TEST);
|
---|
1023 |
|
---|
1024 | return true;
|
---|
1025 | }
|
---|
1026 |
|
---|
1027 | bool QGL2PaintEngineEx::end()
|
---|
1028 | {
|
---|
1029 | Q_D(QGL2PaintEngineEx);
|
---|
1030 | d->ctx->swapBuffers();
|
---|
1031 | return false;
|
---|
1032 | }
|
---|
1033 |
|
---|
1034 |
|
---|
1035 | /////////////////////////////////// State/Clipping stolen from QOpenGLPaintEngine //////////////////////////////////////////
|
---|
1036 |
|
---|
1037 | void QGL2PaintEngineEx::clipEnabledChanged()
|
---|
1038 | {
|
---|
1039 | Q_D(QGL2PaintEngineEx);
|
---|
1040 |
|
---|
1041 | d->updateDepthClip();
|
---|
1042 | }
|
---|
1043 |
|
---|
1044 | void QGL2PaintEngineEx::clip(const QVectorPath &path, Qt::ClipOperation op)
|
---|
1045 | {
|
---|
1046 | // qDebug("QGL2PaintEngineEx::clip()");
|
---|
1047 | const qreal *points = path.points();
|
---|
1048 | const QPainterPath::ElementType *types = path.elements();
|
---|
1049 | if (!types && path.shape() == QVectorPath::RectangleHint) {
|
---|
1050 | QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
|
---|
1051 | updateClipRegion(QRegion(r.toRect()), op);
|
---|
1052 | return;
|
---|
1053 | }
|
---|
1054 |
|
---|
1055 | QPainterPath p;
|
---|
1056 | if (types) {
|
---|
1057 | int id = 0;
|
---|
1058 | for (int i=0; i<path.elementCount(); ++i) {
|
---|
1059 | switch(types[i]) {
|
---|
1060 | case QPainterPath::MoveToElement:
|
---|
1061 | p.moveTo(QPointF(points[id], points[id+1]));
|
---|
1062 | id+=2;
|
---|
1063 | break;
|
---|
1064 | case QPainterPath::LineToElement:
|
---|
1065 | p.lineTo(QPointF(points[id], points[id+1]));
|
---|
1066 | id+=2;
|
---|
1067 | break;
|
---|
1068 | case QPainterPath::CurveToElement: {
|
---|
1069 | QPointF p1(points[id], points[id+1]);
|
---|
1070 | QPointF p2(points[id+2], points[id+3]);
|
---|
1071 | QPointF p3(points[id+4], points[id+5]);
|
---|
1072 | p.cubicTo(p1, p2, p3);
|
---|
1073 | id+=6;
|
---|
1074 | break;
|
---|
1075 | }
|
---|
1076 | case QPainterPath::CurveToDataElement:
|
---|
1077 | ;
|
---|
1078 | break;
|
---|
1079 | }
|
---|
1080 | }
|
---|
1081 | } else if (!path.isEmpty()) {
|
---|
1082 | p.moveTo(QPointF(points[0], points[1]));
|
---|
1083 | int id = 2;
|
---|
1084 | for (int i=1; i<path.elementCount(); ++i) {
|
---|
1085 | p.lineTo(QPointF(points[id], points[id+1]));
|
---|
1086 | id+=2;
|
---|
1087 | }
|
---|
1088 | }
|
---|
1089 | if (path.hints() & QVectorPath::WindingFill)
|
---|
1090 | p.setFillRule(Qt::WindingFill);
|
---|
1091 |
|
---|
1092 | updateClipRegion(QRegion(p.toFillPolygon().toPolygon(), p.fillRule()), op);
|
---|
1093 | return;
|
---|
1094 | }
|
---|
1095 |
|
---|
1096 | void QGL2PaintEngineEx::updateClipRegion(const QRegion &clipRegion, Qt::ClipOperation op)
|
---|
1097 | {
|
---|
1098 | // qDebug("QGL2PaintEngineEx::updateClipRegion()");
|
---|
1099 | Q_D(QGL2PaintEngineEx);
|
---|
1100 |
|
---|
1101 | QRegion sysClip = systemClip();
|
---|
1102 | if (op == Qt::NoClip && !d->use_system_clip) {
|
---|
1103 | state()->hasClipping = false;
|
---|
1104 | state()->clipRegion = QRegion();
|
---|
1105 | d->updateDepthClip();
|
---|
1106 | return;
|
---|
1107 | }
|
---|
1108 |
|
---|
1109 | bool isScreenClip = false;
|
---|
1110 | if (!d->use_system_clip) {
|
---|
1111 | QVector<QRect> untransformedRects = clipRegion.rects();
|
---|
1112 |
|
---|
1113 | if (untransformedRects.size() == 1) {
|
---|
1114 | QPainterPath path;
|
---|
1115 | path.addRect(untransformedRects[0]);
|
---|
1116 | //path = d->matrix.map(path);
|
---|
1117 | path = state()->matrix.map(path);
|
---|
1118 |
|
---|
1119 | // if (path.contains(QRectF(QPointF(), d->drawable.size())))
|
---|
1120 | // isScreenClip = true;
|
---|
1121 | if (path.contains(QRectF(0.0, 0.0, d->width, d->height)))
|
---|
1122 | isScreenClip = true;
|
---|
1123 | }
|
---|
1124 | }
|
---|
1125 |
|
---|
1126 | // QRegion region = isScreenClip ? QRegion() : clipRegion * d->matrix;
|
---|
1127 | QRegion region = isScreenClip ? QRegion() : clipRegion * state()->matrix;
|
---|
1128 | switch (op) {
|
---|
1129 | case Qt::NoClip:
|
---|
1130 | if (!d->use_system_clip)
|
---|
1131 | break;
|
---|
1132 | state()->clipRegion = sysClip;
|
---|
1133 | break;
|
---|
1134 | case Qt::IntersectClip:
|
---|
1135 | if (isScreenClip)
|
---|
1136 | return;
|
---|
1137 | if (state()->hasClipping) {
|
---|
1138 | state()->clipRegion &= region;
|
---|
1139 | break;
|
---|
1140 | }
|
---|
1141 | // fall through
|
---|
1142 | case Qt::ReplaceClip:
|
---|
1143 | if (d->use_system_clip && !sysClip.isEmpty())
|
---|
1144 | state()->clipRegion = region & sysClip;
|
---|
1145 | else
|
---|
1146 | state()->clipRegion = region;
|
---|
1147 | break;
|
---|
1148 | case Qt::UniteClip:
|
---|
1149 | state()->clipRegion |= region;
|
---|
1150 | if (d->use_system_clip && !sysClip.isEmpty())
|
---|
1151 | state()->clipRegion &= sysClip;
|
---|
1152 | break;
|
---|
1153 | default:
|
---|
1154 | break;
|
---|
1155 | }
|
---|
1156 |
|
---|
1157 | if (isScreenClip) {
|
---|
1158 | state()->hasClipping = false;
|
---|
1159 | state()->clipRegion = QRegion();
|
---|
1160 | } else {
|
---|
1161 | state()->hasClipping = op != Qt::NoClip || d->use_system_clip;
|
---|
1162 | }
|
---|
1163 |
|
---|
1164 | if (state()->hasClipping && state()->clipRegion.rects().size() == 1)
|
---|
1165 | state()->fastClip = state()->clipRegion.rects().at(0);
|
---|
1166 | else
|
---|
1167 | state()->fastClip = QRect();
|
---|
1168 |
|
---|
1169 | d->updateDepthClip();
|
---|
1170 | }
|
---|
1171 |
|
---|
1172 |
|
---|
1173 | void QGL2PaintEngineExPrivate::updateDepthClip()
|
---|
1174 | {
|
---|
1175 | // qDebug("QGL2PaintEngineExPrivate::updateDepthClip()");
|
---|
1176 |
|
---|
1177 | Q_Q(QGL2PaintEngineEx);
|
---|
1178 |
|
---|
1179 | glDisable(GL_DEPTH_TEST);
|
---|
1180 | glDisable(GL_SCISSOR_TEST);
|
---|
1181 |
|
---|
1182 | if (!q->state()->hasClipping)
|
---|
1183 | return;
|
---|
1184 |
|
---|
1185 | QRect fastClip;
|
---|
1186 | if (q->state()->clipEnabled) {
|
---|
1187 | fastClip = q->state()->fastClip;
|
---|
1188 | } else if (use_system_clip && q->systemClip().rects().count() == 1) {
|
---|
1189 | fastClip = q->systemClip().rects().at(0);
|
---|
1190 | }
|
---|
1191 |
|
---|
1192 | if (!fastClip.isEmpty()) {
|
---|
1193 | glEnable(GL_SCISSOR_TEST);
|
---|
1194 |
|
---|
1195 | const int left = fastClip.left();
|
---|
1196 | const int width = fastClip.width();
|
---|
1197 | const int bottom = height - (fastClip.bottom() + 1);
|
---|
1198 | const int height = fastClip.height();
|
---|
1199 |
|
---|
1200 | glScissor(left, bottom, width, height);
|
---|
1201 | return;
|
---|
1202 | }
|
---|
1203 |
|
---|
1204 | glClearDepthf(0x0);
|
---|
1205 | glDepthMask(true);
|
---|
1206 | glClear(GL_DEPTH_BUFFER_BIT);
|
---|
1207 | glClearDepthf(0x1);
|
---|
1208 |
|
---|
1209 | const QVector<QRect> rects = q->state()->clipEnabled ? q->state()->clipRegion.rects() : q->systemClip().rects();
|
---|
1210 | glEnable(GL_SCISSOR_TEST);
|
---|
1211 | for (int i = 0; i < rects.size(); ++i) {
|
---|
1212 | QRect rect = rects.at(i);
|
---|
1213 |
|
---|
1214 | const int left = rect.left();
|
---|
1215 | const int width = rect.width();
|
---|
1216 | const int bottom = height - (rect.bottom() + 1);
|
---|
1217 | const int height = rect.height();
|
---|
1218 |
|
---|
1219 | glScissor(left, bottom, width, height);
|
---|
1220 |
|
---|
1221 | glClear(GL_DEPTH_BUFFER_BIT);
|
---|
1222 | }
|
---|
1223 | glDisable(GL_SCISSOR_TEST);
|
---|
1224 |
|
---|
1225 | glDepthMask(false);
|
---|
1226 | glDepthFunc(GL_LEQUAL);
|
---|
1227 | glEnable(GL_DEPTH_TEST);
|
---|
1228 | }
|
---|
1229 |
|
---|
1230 |
|
---|
1231 |
|
---|
1232 | void QGL2PaintEngineEx::setState(QPainterState *s)
|
---|
1233 | {
|
---|
1234 | // qDebug("QGL2PaintEngineEx::setState()");
|
---|
1235 |
|
---|
1236 | Q_D(QGL2PaintEngineEx);
|
---|
1237 | QPaintEngineEx::setState(s);
|
---|
1238 |
|
---|
1239 | d->updateDepthClip();
|
---|
1240 |
|
---|
1241 | d->matrixDirty = true;
|
---|
1242 | d->compositionModeDirty = true;
|
---|
1243 | d->brushTextureDirty = true;
|
---|
1244 | d->brushUniformsDirty = true;
|
---|
1245 | d->simpleShaderMatrixUniformDirty = true;
|
---|
1246 | d->brushShaderMatrixUniformDirty = true;
|
---|
1247 | d->imageShaderMatrixUniformDirty = true;
|
---|
1248 | d->textShaderMatrixUniformDirty = true;
|
---|
1249 | }
|
---|
1250 |
|
---|
1251 | QPainterState *QGL2PaintEngineEx::createState(QPainterState *orig) const
|
---|
1252 | {
|
---|
1253 | QOpenGLPaintEngineState *s;
|
---|
1254 | if (!orig)
|
---|
1255 | s = new QOpenGLPaintEngineState();
|
---|
1256 | else
|
---|
1257 | s = new QOpenGLPaintEngineState(*static_cast<QOpenGLPaintEngineState *>(orig));
|
---|
1258 |
|
---|
1259 | return s;
|
---|
1260 | }
|
---|
1261 |
|
---|
1262 | QOpenGLPaintEngineState::QOpenGLPaintEngineState(QOpenGLPaintEngineState &other)
|
---|
1263 | : QPainterState(other)
|
---|
1264 | {
|
---|
1265 | clipRegion = other.clipRegion;
|
---|
1266 | hasClipping = other.hasClipping;
|
---|
1267 | fastClip = other.fastClip;
|
---|
1268 | }
|
---|
1269 |
|
---|
1270 | QOpenGLPaintEngineState::QOpenGLPaintEngineState()
|
---|
1271 | {
|
---|
1272 | hasClipping = false;
|
---|
1273 | }
|
---|
1274 |
|
---|
1275 | QOpenGLPaintEngineState::~QOpenGLPaintEngineState()
|
---|
1276 | {
|
---|
1277 | }
|
---|
1278 |
|
---|