source: trunk/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp@ 638

Last change on this file since 638 was 561, checked in by Dmitry A. Kuminov, 16 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 80.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtOpenGL module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this 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 have questions regarding the use of this file, please contact
37** Nokia 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// #define QT_OPENGL_CACHE_AS_VBOS
66
67#include "qpaintengineex_opengl2_p.h"
68
69#include <string.h> //for memcpy
70#include <qmath.h>
71
72#include <private/qgl_p.h>
73#include <private/qmath_p.h>
74#include <private/qpaintengineex_p.h>
75#include <QPaintEngine>
76#include <private/qpainter_p.h>
77#include <private/qfontengine_p.h>
78#include <private/qtextureglyphcache_p.h>
79#include <private/qpixmapdata_gl_p.h>
80#include <private/qdatabuffer_p.h>
81
82#include "qglgradientcache_p.h"
83#include "qglengineshadermanager_p.h"
84#include "qgl2pexvertexarray_p.h"
85
86#include "qtriangulatingstroker_p.h"
87
88#include <QDebug>
89
90QT_BEGIN_NAMESPACE
91
92//#define QT_GL_NO_SCISSOR_TEST
93
94static const GLuint GL_STENCIL_HIGH_BIT = 0x80;
95static const GLuint QT_BRUSH_TEXTURE_UNIT = 0;
96static const GLuint QT_IMAGE_TEXTURE_UNIT = 0; //Can be the same as brush texture unit
97static const GLuint QT_MASK_TEXTURE_UNIT = 1;
98static const GLuint QT_BACKGROUND_TEXTURE_UNIT = 2;
99
100#ifdef Q_WS_WIN
101extern Q_GUI_EXPORT bool qt_cleartype_enabled;
102#endif
103
104class QGLTextureGlyphCache : public QObject, public QTextureGlyphCache
105{
106 Q_OBJECT
107public:
108 QGLTextureGlyphCache(QGLContext *context, QFontEngineGlyphCache::Type type, const QTransform &matrix);
109 ~QGLTextureGlyphCache();
110
111 virtual void createTextureData(int width, int height);
112 virtual void resizeTextureData(int width, int height);
113 virtual void fillTexture(const Coord &c, glyph_t glyph);
114 virtual int glyphMargin() const;
115
116 inline GLuint texture() const { return m_texture; }
117
118 inline int width() const { return m_width; }
119 inline int height() const { return m_height; }
120
121 inline void setPaintEnginePrivate(QGL2PaintEngineExPrivate *p) { pex = p; }
122
123
124public Q_SLOTS:
125 void contextDestroyed(const QGLContext *context) {
126 if (context == ctx) {
127 QList<const QGLContext *> shares = qgl_share_reg()->shares(ctx);
128 if (shares.isEmpty()) {
129 glDeleteFramebuffers(1, &m_fbo);
130 if (m_width || m_height)
131 glDeleteTextures(1, &m_texture);
132 ctx = 0;
133 } else {
134 // since the context holding the texture is shared, and
135 // about to be destroyed, we have to transfer ownership
136 // of the texture to one of the share contexts
137 ctx = const_cast<QGLContext *>((ctx == shares.at(0)) ? shares.at(1) : shares.at(0));
138 }
139 }
140 }
141
142private:
143 QGLContext *ctx;
144
145 QGL2PaintEngineExPrivate *pex;
146
147 GLuint m_texture;
148 GLuint m_fbo;
149
150 int m_width;
151 int m_height;
152
153 QGLShaderProgram *m_program;
154};
155
156QGLTextureGlyphCache::QGLTextureGlyphCache(QGLContext *context, QFontEngineGlyphCache::Type type, const QTransform &matrix)
157 : QTextureGlyphCache(type, matrix)
158 , ctx(context)
159 , m_width(0)
160 , m_height(0)
161{
162 glGenFramebuffers(1, &m_fbo);
163 connect(QGLSignalProxy::instance(), SIGNAL(aboutToDestroyContext(const QGLContext*)),
164 SLOT(contextDestroyed(const QGLContext*)));
165}
166
167QGLTextureGlyphCache::~QGLTextureGlyphCache()
168{
169 if (ctx) {
170 QGLShareContextScope scope(ctx);
171 glDeleteFramebuffers(1, &m_fbo);
172
173 if (m_width || m_height)
174 glDeleteTextures(1, &m_texture);
175 }
176}
177
178void QGLTextureGlyphCache::createTextureData(int width, int height)
179{
180 glGenTextures(1, &m_texture);
181 glBindTexture(GL_TEXTURE_2D, m_texture);
182
183 m_width = width;
184 m_height = height;
185
186 QVarLengthArray<uchar> data(width * height);
187 for (int i = 0; i < data.size(); ++i)
188 data[i] = 0;
189
190 if (m_type == QFontEngineGlyphCache::Raster_RGBMask)
191 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, &data[0]);
192 else
193 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, &data[0]);
194
195 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
196 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
197}
198
199void QGLTextureGlyphCache::resizeTextureData(int width, int height)
200{
201 // ### the QTextureGlyphCache API needs to be reworked to allow
202 // ### resizeTextureData to fail
203
204 int oldWidth = m_width;
205 int oldHeight = m_height;
206
207 GLuint oldTexture = m_texture;
208 createTextureData(width, height);
209
210 glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_fbo);
211
212 GLuint tmp_texture;
213 glGenTextures(1, &tmp_texture);
214 glBindTexture(GL_TEXTURE_2D, tmp_texture);
215 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, oldWidth, oldHeight, 0,
216 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
217 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
218 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
219 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
220 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
221 glBindTexture(GL_TEXTURE_2D, 0);
222 glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
223 GL_TEXTURE_2D, tmp_texture, 0);
224
225 glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT);
226 glBindTexture(GL_TEXTURE_2D, oldTexture);
227
228 pex->transferMode(BrushDrawingMode);
229
230 glDisable(GL_STENCIL_TEST);
231 glDisable(GL_DEPTH_TEST);
232 glDisable(GL_SCISSOR_TEST);
233 glDisable(GL_BLEND);
234
235 glViewport(0, 0, oldWidth, oldHeight);
236
237 float vertexCoordinateArray[] = { -1, -1, 1, -1, 1, 1, -1, 1 };
238 float textureCoordinateArray[] = { 0, 0, 1, 0, 1, 1, 0, 1 };
239
240 glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
241 glEnableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
242
243 glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexCoordinateArray);
244 glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, textureCoordinateArray);
245
246 pex->shaderManager->blitProgram()->bind();
247 pex->shaderManager->blitProgram()->setUniformValue("imageTexture", QT_IMAGE_TEXTURE_UNIT);
248 pex->shaderManager->setDirty();
249
250 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
251
252 glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
253 glDisableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
254
255 glBindTexture(GL_TEXTURE_2D, m_texture);
256
257#ifdef QT_OPENGL_ES_2
258 QDataBuffer<uchar> buffer(4*oldWidth*oldHeight);
259 buffer.resize(4*oldWidth*oldHeight);
260 glReadPixels(0, 0, oldWidth, oldHeight, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
261
262 // do an in-place conversion from GL_RGBA to GL_ALPHA
263 for (int i=0; i<oldWidth*oldHeight; ++i)
264 buffer.data()[i] = buffer.at(4*i + 3);
265
266 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, oldWidth, oldHeight,
267 GL_ALPHA, GL_UNSIGNED_BYTE, buffer.data());
268#else
269 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, oldWidth, oldHeight);
270#endif
271
272 glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
273 GL_RENDERBUFFER_EXT, 0);
274 glDeleteTextures(1, &tmp_texture);
275 glDeleteTextures(1, &oldTexture);
276
277 glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo);
278
279 glViewport(0, 0, pex->width, pex->height);
280 pex->updateClipScissorTest();
281}
282
283void QGLTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph)
284{
285 QImage mask = textureMapForGlyph(glyph);
286 const int maskWidth = mask.width();
287 const int maskHeight = mask.height();
288
289 if (mask.format() == QImage::Format_Mono) {
290 mask = mask.convertToFormat(QImage::Format_Indexed8);
291 for (int y = 0; y < maskHeight; ++y) {
292 uchar *src = (uchar *) mask.scanLine(y);
293 for (int x = 0; x < maskWidth; ++x)
294 src[x] = -src[x]; // convert 0 and 1 into 0 and 255
295 }
296 }
297
298
299 glBindTexture(GL_TEXTURE_2D, m_texture);
300 if (mask.format() == QImage::Format_RGB32) {
301 glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, maskWidth, maskHeight, GL_BGRA, GL_UNSIGNED_BYTE, mask.bits());
302 } else {
303#ifdef QT_OPENGL_ES2
304 glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, maskWidth, maskHeight, GL_ALPHA, GL_UNSIGNED_BYTE, mask.bits());
305#else
306 // glTexSubImage2D() might cause some garbage to appear in the texture if the mask width is
307 // not a multiple of four bytes. The bug appeared on a computer with 32-bit Windows Vista
308 // and nVidia GeForce 8500GT. GL_UNPACK_ALIGNMENT is set to four bytes, 'mask' has a
309 // multiple of four bytes per line, and most of the glyph shows up correctly in the
310 // texture, which makes me think that this is a driver bug.
311 // One workaround is to make sure the mask width is a multiple of four bytes, for instance
312 // by converting it to a format with four bytes per pixel. Another is to copy one line at a
313 // time.
314
315 for (int i = 0; i < maskHeight; ++i)
316 glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y + i, maskWidth, 1, GL_ALPHA, GL_UNSIGNED_BYTE, mask.scanLine(i));
317#endif
318 }
319}
320
321int QGLTextureGlyphCache::glyphMargin() const
322{
323#if defined(Q_WS_MAC)
324 return 2;
325#elif defined (Q_WS_X11)
326 return 0;
327#else
328 return m_type == QFontEngineGlyphCache::Raster_RGBMask ? 2 : 0;
329#endif
330}
331
332extern QImage qt_imageForBrush(int brushStyle, bool invert);
333
334////////////////////////////////// Private Methods //////////////////////////////////////////
335
336QGL2PaintEngineExPrivate::~QGL2PaintEngineExPrivate()
337{
338 delete shaderManager;
339
340 while (pathCaches.size()) {
341 QVectorPath::CacheEntry *e = *(pathCaches.constBegin());
342 e->cleanup(e->engine, e->data);
343 e->data = 0;
344 e->engine = 0;
345 }
346}
347
348void QGL2PaintEngineExPrivate::updateTextureFilter(GLenum target, GLenum wrapMode, bool smoothPixmapTransform, GLuint id)
349{
350// glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT); //### Is it always this texture unit?
351 if (id != GLuint(-1) && id == lastTextureUsed)
352 return;
353
354 lastTextureUsed = id;
355
356 if (smoothPixmapTransform) {
357 glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
358 glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
359 } else {
360 glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
361 glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
362 }
363 glTexParameterf(target, GL_TEXTURE_WRAP_S, wrapMode);
364 glTexParameterf(target, GL_TEXTURE_WRAP_T, wrapMode);
365}
366
367
368inline QColor qt_premultiplyColor(QColor c, GLfloat opacity)
369{
370 qreal alpha = c.alphaF() * opacity;
371 c.setAlphaF(alpha);
372 c.setRedF(c.redF() * alpha);
373 c.setGreenF(c.greenF() * alpha);
374 c.setBlueF(c.blueF() * alpha);
375 return c;
376}
377
378
379void QGL2PaintEngineExPrivate::setBrush(const QBrush& brush)
380{
381 if (qbrush_fast_equals(currentBrush, brush))
382 return;
383
384 const Qt::BrushStyle newStyle = qbrush_style(brush);
385 Q_ASSERT(newStyle != Qt::NoBrush);
386
387 currentBrush = brush;
388 brushUniformsDirty = true; // All brushes have at least one uniform
389
390 if (newStyle > Qt::SolidPattern)
391 brushTextureDirty = true;
392
393 if (currentBrush.style() == Qt::TexturePattern
394 && qHasPixmapTexture(brush) && brush.texture().isQBitmap())
395 {
396 shaderManager->setSrcPixelType(QGLEngineShaderManager::TextureSrcWithPattern);
397 } else {
398 shaderManager->setSrcPixelType(newStyle);
399 }
400 shaderManager->optimiseForBrushTransform(currentBrush.transform().type());
401}
402
403
404void QGL2PaintEngineExPrivate::useSimpleShader()
405{
406 shaderManager->simpleProgram()->bind();
407 shaderManager->setDirty();
408
409 if (matrixDirty)
410 updateMatrix();
411
412 if (simpleShaderMatrixUniformDirty) {
413 const GLuint location = shaderManager->simpleProgram()->uniformLocation("pmvMatrix");
414 glUniformMatrix3fv(location, 1, GL_FALSE, (GLfloat*)pmvMatrix);
415 simpleShaderMatrixUniformDirty = false;
416 }
417}
418
419void QGL2PaintEngineExPrivate::updateBrushTexture()
420{
421 Q_Q(QGL2PaintEngineEx);
422// qDebug("QGL2PaintEngineExPrivate::updateBrushTexture()");
423 Qt::BrushStyle style = currentBrush.style();
424
425 if ( (style >= Qt::Dense1Pattern) && (style <= Qt::DiagCrossPattern) ) {
426 // Get the image data for the pattern
427 QImage texImage = qt_imageForBrush(style, false);
428
429 glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT);
430 ctx->d_func()->bindTexture(texImage, GL_TEXTURE_2D, GL_RGBA, true, QGLContext::InternalBindOption);
431 updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, q->state()->renderHints & QPainter::SmoothPixmapTransform);
432 }
433 else if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern) {
434 // Gradiant brush: All the gradiants use the same texture
435
436 const QGradient* g = currentBrush.gradient();
437
438 // We apply global opacity in the fragment shaders, so we always pass 1.0
439 // for opacity to the cache.
440 GLuint texId = QGL2GradientCache::cacheForContext(ctx)->getBuffer(*g, 1.0);
441
442 glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT);
443 glBindTexture(GL_TEXTURE_2D, texId);
444
445 if (g->spread() == QGradient::RepeatSpread || g->type() == QGradient::ConicalGradient)
446 updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, q->state()->renderHints & QPainter::SmoothPixmapTransform);
447 else if (g->spread() == QGradient::ReflectSpread)
448 updateTextureFilter(GL_TEXTURE_2D, GL_MIRRORED_REPEAT_IBM, q->state()->renderHints & QPainter::SmoothPixmapTransform);
449 else
450 updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, q->state()->renderHints & QPainter::SmoothPixmapTransform);
451 }
452 else if (style == Qt::TexturePattern) {
453 const QPixmap& texPixmap = currentBrush.texture();
454
455 glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT);
456 QGLTexture *tex = ctx->d_func()->bindTexture(texPixmap, GL_TEXTURE_2D, GL_RGBA, QGLContext::InternalBindOption);
457 updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, q->state()->renderHints & QPainter::SmoothPixmapTransform);
458 textureInvertedY = tex->options & QGLContext::InvertedYBindOption ? -1 : 1;
459 }
460 brushTextureDirty = false;
461}
462
463
464void QGL2PaintEngineExPrivate::updateBrushUniforms()
465{
466// qDebug("QGL2PaintEngineExPrivate::updateBrushUniforms()");
467 Qt::BrushStyle style = currentBrush.style();
468
469 if (style == Qt::NoBrush)
470 return;
471
472 QTransform brushQTransform = currentBrush.transform();
473
474 if (style == Qt::SolidPattern) {
475 QColor col = qt_premultiplyColor(currentBrush.color(), (GLfloat)q->state()->opacity);
476 shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::FragmentColor), col);
477 }
478 else {
479 // All other brushes have a transform and thus need the translation point:
480 QPointF translationPoint;
481
482 if (style <= Qt::DiagCrossPattern) {
483 QColor col = qt_premultiplyColor(currentBrush.color(), (GLfloat)q->state()->opacity);
484
485 shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col);
486
487 QVector2D halfViewportSize(width*0.5, height*0.5);
488 shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::HalfViewportSize), halfViewportSize);
489 }
490 else if (style == Qt::LinearGradientPattern) {
491 const QLinearGradient *g = static_cast<const QLinearGradient *>(currentBrush.gradient());
492
493 QPointF realStart = g->start();
494 QPointF realFinal = g->finalStop();
495 translationPoint = realStart;
496
497 QPointF l = realFinal - realStart;
498
499 QVector3D linearData(
500 l.x(),
501 l.y(),
502 1.0f / (l.x() * l.x() + l.y() * l.y())
503 );
504
505 shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::LinearData), linearData);
506
507 QVector2D halfViewportSize(width*0.5, height*0.5);
508 shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::HalfViewportSize), halfViewportSize);
509 }
510 else if (style == Qt::ConicalGradientPattern) {
511 const QConicalGradient *g = static_cast<const QConicalGradient *>(currentBrush.gradient());
512 translationPoint = g->center();
513
514 GLfloat angle = -(g->angle() * 2 * Q_PI) / 360.0;
515
516 shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Angle), angle);
517
518 QVector2D halfViewportSize(width*0.5, height*0.5);
519 shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::HalfViewportSize), halfViewportSize);
520 }
521 else if (style == Qt::RadialGradientPattern) {
522 const QRadialGradient *g = static_cast<const QRadialGradient *>(currentBrush.gradient());
523 QPointF realCenter = g->center();
524 QPointF realFocal = g->focalPoint();
525 qreal realRadius = g->radius();
526 translationPoint = realFocal;
527
528 QPointF fmp = realCenter - realFocal;
529 shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Fmp), fmp);
530
531 GLfloat fmp2_m_radius2 = -fmp.x() * fmp.x() - fmp.y() * fmp.y() + realRadius*realRadius;
532 shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Fmp2MRadius2), fmp2_m_radius2);
533 shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Inverse2Fmp2MRadius2),
534 GLfloat(1.0 / (2.0*fmp2_m_radius2)));
535
536 QVector2D halfViewportSize(width*0.5, height*0.5);
537 shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::HalfViewportSize), halfViewportSize);
538 }
539 else if (style == Qt::TexturePattern) {
540 const QPixmap& texPixmap = currentBrush.texture();
541
542 if (qHasPixmapTexture(currentBrush) && currentBrush.texture().isQBitmap()) {
543 QColor col = qt_premultiplyColor(currentBrush.color(), (GLfloat)q->state()->opacity);
544 shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col);
545 }
546
547 QSizeF invertedTextureSize(1.0 / texPixmap.width(), 1.0 / texPixmap.height());
548 shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::InvertedTextureSize), invertedTextureSize);
549
550 QVector2D halfViewportSize(width*0.5, height*0.5);
551 shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::HalfViewportSize), halfViewportSize);
552 }
553 else
554 qWarning("QGL2PaintEngineEx: Unimplemented fill style");
555
556 const QPointF &brushOrigin = q->state()->brushOrigin;
557 QTransform matrix = q->state()->matrix;
558 matrix.translate(brushOrigin.x(), brushOrigin.y());
559
560 QTransform translate(1, 0, 0, 1, -translationPoint.x(), -translationPoint.y());
561 QTransform gl_to_qt(1, 0, 0, -1, 0, height);
562 QTransform inv_matrix;
563 if (style == Qt::TexturePattern && textureInvertedY == -1)
564 inv_matrix = gl_to_qt * (QTransform(1, 0, 0, -1, 0, currentBrush.texture().height()) * brushQTransform * matrix).inverted() * translate;
565 else
566 inv_matrix = gl_to_qt * (brushQTransform * matrix).inverted() * translate;
567
568 shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::BrushTransform), inv_matrix);
569 shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::BrushTexture), QT_BRUSH_TEXTURE_UNIT);
570 }
571 brushUniformsDirty = false;
572}
573
574
575// This assumes the shader manager has already setup the correct shader program
576void QGL2PaintEngineExPrivate::updateMatrix()
577{
578// qDebug("QGL2PaintEngineExPrivate::updateMatrix()");
579
580 const QTransform& transform = q->state()->matrix;
581
582 // The projection matrix converts from Qt's coordinate system to GL's coordinate system
583 // * GL's viewport is 2x2, Qt's is width x height
584 // * GL has +y -> -y going from bottom -> top, Qt is the other way round
585 // * GL has [0,0] in the center, Qt has it in the top-left
586 //
587 // This results in the Projection matrix below, which is multiplied by the painter's
588 // transformation matrix, as shown below:
589 //
590 // Projection Matrix Painter Transform
591 // ------------------------------------------------ ------------------------
592 // | 2.0 / width | 0.0 | -1.0 | | m11 | m21 | dx |
593 // | 0.0 | -2.0 / height | 1.0 | * | m12 | m22 | dy |
594 // | 0.0 | 0.0 | 1.0 | | m13 | m23 | m33 |
595 // ------------------------------------------------ ------------------------
596 //
597 // NOTE: The resultant matrix is also transposed, as GL expects column-major matracies
598
599 const GLfloat wfactor = 2.0f / width;
600 const GLfloat hfactor = -2.0f / height;
601 GLfloat dx = transform.dx();
602 GLfloat dy = transform.dy();
603
604 // Non-integer translates can have strange effects for some rendering operations such as
605 // anti-aliased text rendering. In such cases, we snap the translate to the pixel grid.
606 if (snapToPixelGrid && transform.type() == QTransform::TxTranslate) {
607 // 0.50 needs to rounded down to 0.0 for consistency with raster engine:
608 dx = ceilf(dx - 0.5f);
609 dy = ceilf(dy - 0.5f);
610 }
611
612 if (addOffset) {
613 dx += 0.49f;
614 dy += 0.49f;
615 }
616
617 pmvMatrix[0][0] = (wfactor * transform.m11()) - transform.m13();
618 pmvMatrix[1][0] = (wfactor * transform.m21()) - transform.m23();
619 pmvMatrix[2][0] = (wfactor * dx) - transform.m33();
620 pmvMatrix[0][1] = (hfactor * transform.m12()) + transform.m13();
621 pmvMatrix[1][1] = (hfactor * transform.m22()) + transform.m23();
622 pmvMatrix[2][1] = (hfactor * dy) + transform.m33();
623 pmvMatrix[0][2] = transform.m13();
624 pmvMatrix[1][2] = transform.m23();
625 pmvMatrix[2][2] = transform.m33();
626
627 // 1/10000 == 0.0001, so we have good enough res to cover curves
628 // that span the entire widget...
629 inverseScale = qMax(1 / qMax( qMax(qAbs(transform.m11()), qAbs(transform.m22())),
630 qMax(qAbs(transform.m12()), qAbs(transform.m21())) ),
631 qreal(0.0001));
632
633 matrixDirty = false;
634
635 // The actual data has been updated so both shader program's uniforms need updating
636 simpleShaderMatrixUniformDirty = true;
637 shaderMatrixUniformDirty = true;
638
639 dasher.setInvScale(inverseScale);
640 stroker.setInvScale(inverseScale);
641}
642
643
644void QGL2PaintEngineExPrivate::updateCompositionMode()
645{
646 // NOTE: The entire paint engine works on pre-multiplied data - which is why some of these
647 // composition modes look odd.
648// qDebug() << "QGL2PaintEngineExPrivate::updateCompositionMode() - Setting GL composition mode for " << q->state()->composition_mode;
649 switch(q->state()->composition_mode) {
650 case QPainter::CompositionMode_SourceOver:
651 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
652 break;
653 case QPainter::CompositionMode_DestinationOver:
654 glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);
655 break;
656 case QPainter::CompositionMode_Clear:
657 glBlendFunc(GL_ZERO, GL_ZERO);
658 break;
659 case QPainter::CompositionMode_Source:
660 glBlendFunc(GL_ONE, GL_ZERO);
661 break;
662 case QPainter::CompositionMode_Destination:
663 glBlendFunc(GL_ZERO, GL_ONE);
664 break;
665 case QPainter::CompositionMode_SourceIn:
666 glBlendFunc(GL_DST_ALPHA, GL_ZERO);
667 break;
668 case QPainter::CompositionMode_DestinationIn:
669 glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
670 break;
671 case QPainter::CompositionMode_SourceOut:
672 glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ZERO);
673 break;
674 case QPainter::CompositionMode_DestinationOut:
675 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
676 break;
677 case QPainter::CompositionMode_SourceAtop:
678 glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
679 break;
680 case QPainter::CompositionMode_DestinationAtop:
681 glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA);
682 break;
683 case QPainter::CompositionMode_Xor:
684 glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
685 break;
686 case QPainter::CompositionMode_Plus:
687 glBlendFunc(GL_ONE, GL_ONE);
688 break;
689 default:
690 qWarning("Unsupported composition mode");
691 break;
692 }
693
694 compositionModeDirty = false;
695}
696
697static inline void setCoords(GLfloat *coords, const QGLRect &rect)
698{
699 coords[0] = rect.left;
700 coords[1] = rect.top;
701 coords[2] = rect.right;
702 coords[3] = rect.top;
703 coords[4] = rect.right;
704 coords[5] = rect.bottom;
705 coords[6] = rect.left;
706 coords[7] = rect.bottom;
707}
708
709void QGL2PaintEngineExPrivate::drawTexture(const QGLRect& dest, const QGLRect& src, const QSize &textureSize, bool opaque, bool pattern)
710{
711 // Setup for texture drawing
712 currentBrush = noBrush;
713 shaderManager->setSrcPixelType(pattern ? QGLEngineShaderManager::PatternSrc : QGLEngineShaderManager::ImageSrc);
714
715 if (addOffset) {
716 addOffset = false;
717 matrixDirty = true;
718 }
719
720 if (snapToPixelGrid) {
721 snapToPixelGrid = false;
722 matrixDirty = true;
723 }
724
725 if (prepareForDraw(opaque))
726 shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::ImageTexture), QT_IMAGE_TEXTURE_UNIT);
727
728 if (pattern) {
729 QColor col = qt_premultiplyColor(q->state()->pen.color(), (GLfloat)q->state()->opacity);
730 shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col);
731 }
732
733 GLfloat dx = 1.0 / textureSize.width();
734 GLfloat dy = 1.0 / textureSize.height();
735
736 QGLRect srcTextureRect(src.left*dx, src.top*dy, src.right*dx, src.bottom*dy);
737
738 setCoords(staticVertexCoordinateArray, dest);
739 setCoords(staticTextureCoordinateArray, srcTextureRect);
740
741 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
742}
743
744void QGL2PaintEngineEx::beginNativePainting()
745{
746 Q_D(QGL2PaintEngineEx);
747 ensureActive();
748 d->transferMode(BrushDrawingMode);
749
750 QGLContext *ctx = d->ctx;
751 glUseProgram(0);
752
753#ifndef QT_OPENGL_ES_2
754 // be nice to people who mix OpenGL 1.x code with QPainter commands
755 // by setting modelview and projection matrices to mirror the GL 1
756 // paint engine
757 const QTransform& mtx = state()->matrix;
758
759 float mv_matrix[4][4] =
760 {
761 { mtx.m11(), mtx.m12(), 0, mtx.m13() },
762 { mtx.m21(), mtx.m22(), 0, mtx.m23() },
763 { 0, 0, 1, 0 },
764 { mtx.dx(), mtx.dy(), 0, mtx.m33() }
765 };
766
767 const QSize sz = d->device->size();
768
769 glMatrixMode(GL_PROJECTION);
770 glLoadIdentity();
771 glOrtho(0, sz.width(), sz.height(), 0, -999999, 999999);
772
773 glMatrixMode(GL_MODELVIEW);
774 glLoadMatrixf(&mv_matrix[0][0]);
775#else
776 Q_UNUSED(ctx);
777#endif
778
779 d->lastTextureUsed = GLuint(-1);
780 d->dirtyStencilRegion = QRect(0, 0, d->width, d->height);
781 d->resetGLState();
782
783 d->shaderManager->setDirty();
784
785 d->needsSync = true;
786}
787
788void QGL2PaintEngineExPrivate::resetGLState()
789{
790 glDisable(GL_BLEND);
791 glActiveTexture(GL_TEXTURE0);
792 glDisable(GL_STENCIL_TEST);
793 glDisable(GL_DEPTH_TEST);
794 glDisable(GL_SCISSOR_TEST);
795 glDepthMask(true);
796 glDepthFunc(GL_LESS);
797 glClearDepth(1);
798}
799
800void QGL2PaintEngineEx::endNativePainting()
801{
802 Q_D(QGL2PaintEngineEx);
803 d->needsSync = true;
804}
805
806void QGL2PaintEngineExPrivate::transferMode(EngineMode newMode)
807{
808 if (newMode == mode)
809 return;
810
811 if (mode == TextDrawingMode || mode == ImageDrawingMode || mode == ImageArrayDrawingMode) {
812 glDisableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
813 glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
814 glDisableVertexAttribArray(QT_OPACITY_ATTR);
815
816 lastTextureUsed = GLuint(-1);
817 }
818
819 if (newMode == TextDrawingMode) {
820 glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
821 glEnableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
822
823 glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexCoordinateArray.data());
824 glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, textureCoordinateArray.data());
825 }
826
827 if (newMode == ImageDrawingMode) {
828 glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
829 glEnableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
830
831 glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, staticVertexCoordinateArray);
832 glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, staticTextureCoordinateArray);
833 }
834
835 if (newMode == ImageArrayDrawingMode) {
836 glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
837 glEnableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
838 glEnableVertexAttribArray(QT_OPACITY_ATTR);
839
840 glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexCoordinateArray.data());
841 glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, textureCoordinateArray.data());
842 glVertexAttribPointer(QT_OPACITY_ATTR, 1, GL_FLOAT, GL_FALSE, 0, opacityArray.data());
843 }
844
845 // This needs to change when we implement high-quality anti-aliasing...
846 if (newMode != TextDrawingMode)
847 shaderManager->setMaskType(QGLEngineShaderManager::NoMask);
848
849 mode = newMode;
850}
851
852struct QGL2PEVectorPathCache
853{
854#ifdef QT_OPENGL_CACHE_AS_VBOS
855 GLuint vbo;
856#else
857 float *vertices;
858#endif
859 int vertexCount;
860 GLenum primitiveType;
861 qreal iscale;
862};
863
864void QGL2PaintEngineExPrivate::cleanupVectorPath(QPaintEngineEx *engine, void *data)
865{
866 QGL2PEVectorPathCache *c = (QGL2PEVectorPathCache *) data;
867#ifdef QT_OPENGL_CACHE_AS_VBOS
868 Q_ASSERT(engine->type() == QPaintEngine::OpenGL2);
869 static_cast<QGL2PaintEngineEx *>(engine)->d_func()->unusedVBOSToClean << c->vbo;
870#else
871 Q_UNUSED(engine);
872 qFree(c->vertices);
873#endif
874 delete c;
875}
876
877// Assumes everything is configured for the brush you want to use
878void QGL2PaintEngineExPrivate::fill(const QVectorPath& path)
879{
880 transferMode(BrushDrawingMode);
881
882 const QOpenGL2PaintEngineState *s = q->state();
883 const bool newAddOffset = !(s->renderHints & QPainter::Antialiasing) &&
884 (qbrush_style(currentBrush) == Qt::SolidPattern) &&
885 !multisamplingAlwaysEnabled;
886
887 if (addOffset != newAddOffset) {
888 addOffset = newAddOffset;
889 matrixDirty = true;
890 }
891
892 if (snapToPixelGrid) {
893 snapToPixelGrid = false;
894 matrixDirty = true;
895 }
896
897 // Might need to call updateMatrix to re-calculate inverseScale
898 if (matrixDirty)
899 updateMatrix();
900
901 const QPointF* const points = reinterpret_cast<const QPointF*>(path.points());
902
903 // Check to see if there's any hints
904 if (path.shape() == QVectorPath::RectangleHint) {
905 QGLRect rect(points[0].x(), points[0].y(), points[2].x(), points[2].y());
906 prepareForDraw(currentBrush.isOpaque());
907 composite(rect);
908 } else if (path.isConvex()) {
909
910 if (path.isCacheable()) {
911 QVectorPath::CacheEntry *data = path.lookupCacheData(q);
912 QGL2PEVectorPathCache *cache;
913
914 if (data) {
915 cache = (QGL2PEVectorPathCache *) data->data;
916 // Check if scale factor is exceeded for curved paths and generate curves if so...
917 if (path.isCurved()) {
918 qreal scaleFactor = cache->iscale / inverseScale;
919 if (scaleFactor < 0.5 || scaleFactor > 2.0) {
920#ifdef QT_OPENGL_CACHE_AS_VBOS
921 glDeleteBuffers(1, &cache->vbo);
922 cache->vbo = 0;
923#else
924 qFree(cache->vertices);
925#endif
926 cache->vertexCount = 0;
927 }
928 }
929 } else {
930 cache = new QGL2PEVectorPathCache;
931 cache->vertexCount = 0;
932 data = const_cast<QVectorPath &>(path).addCacheData(q, cache, cleanupVectorPath);
933 }
934
935 // Flatten the path at the current scale factor and fill it into the cache struct.
936 if (!cache->vertexCount) {
937 vertexCoordinateArray.clear();
938 vertexCoordinateArray.addPath(path, inverseScale, false);
939 int vertexCount = vertexCoordinateArray.vertexCount();
940 int floatSizeInBytes = vertexCount * 2 * sizeof(float);
941 cache->vertexCount = vertexCount;
942 cache->primitiveType = GL_TRIANGLE_FAN;
943 cache->iscale = inverseScale;
944#ifdef QT_OPENGL_CACHE_AS_VBOS
945 glGenBuffers(1, &cache->vbo);
946 glBindBuffer(GL_ARRAY_BUFFER, cache->vbo);
947 glBufferData(GL_ARRAY_BUFFER, floatSizeInBytes, vertexCoordinateArray.data(), GL_STATIC_DRAW);
948#else
949 cache->vertices = (float *) qMalloc(floatSizeInBytes);
950 memcpy(cache->vertices, vertexCoordinateArray.data(), floatSizeInBytes);
951#endif
952 }
953
954 prepareForDraw(currentBrush.isOpaque());
955 glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
956#ifdef QT_OPENGL_CACHE_AS_VBOS
957 glBindBuffer(GL_ARRAY_BUFFER, cache->vbo);
958 glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, false, 0, 0);
959#else
960 glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, false, 0, cache->vertices);
961#endif
962 glDrawArrays(cache->primitiveType, 0, cache->vertexCount);
963
964 } else {
965 // printf(" - Marking path as cachable...\n");
966 // Tag it for later so that if the same path is drawn twice, it is assumed to be static and thus cachable
967 // ### Remove before release...
968 static bool do_vectorpath_cache = qgetenv("QT_OPENGL_NO_PATH_CACHE").isEmpty();
969 if (do_vectorpath_cache)
970 path.makeCacheable();
971 vertexCoordinateArray.clear();
972 vertexCoordinateArray.addPath(path, inverseScale, false);
973 prepareForDraw(currentBrush.isOpaque());
974 drawVertexArrays(vertexCoordinateArray, GL_TRIANGLE_FAN);
975 }
976
977 } else {
978 // The path is too complicated & needs the stencil technique
979 vertexCoordinateArray.clear();
980 vertexCoordinateArray.addPath(path, inverseScale, false);
981
982 fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill());
983
984 glStencilMask(0xff);
985 glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
986
987 if (q->state()->clipTestEnabled) {
988 // Pass when high bit is set, replace stencil value with current clip
989 glStencilFunc(GL_NOTEQUAL, q->state()->currentClip, GL_STENCIL_HIGH_BIT);
990 } else if (path.hasWindingFill()) {
991 // Pass when any bit is set, replace stencil value with 0
992 glStencilFunc(GL_NOTEQUAL, 0, 0xff);
993 } else {
994 // Pass when high bit is set, replace stencil value with 0
995 glStencilFunc(GL_NOTEQUAL, 0, GL_STENCIL_HIGH_BIT);
996 }
997 prepareForDraw(currentBrush.isOpaque());
998
999 // Stencil the brush onto the dest buffer
1000 composite(vertexCoordinateArray.boundingRect());
1001 glStencilMask(0);
1002 updateClipScissorTest();
1003 }
1004}
1005
1006
1007void QGL2PaintEngineExPrivate::fillStencilWithVertexArray(const float *data,
1008 int count,
1009 int *stops,
1010 int stopCount,
1011 const QGLRect &bounds,
1012 StencilFillMode mode)
1013{
1014 Q_ASSERT(count || stops);
1015
1016// qDebug("QGL2PaintEngineExPrivate::fillStencilWithVertexArray()");
1017 glStencilMask(0xff); // Enable stencil writes
1018
1019 if (dirtyStencilRegion.intersects(currentScissorBounds)) {
1020 QVector<QRect> clearRegion = dirtyStencilRegion.intersected(currentScissorBounds).rects();
1021 glClearStencil(0); // Clear to zero
1022 for (int i = 0; i < clearRegion.size(); ++i) {
1023#ifndef QT_GL_NO_SCISSOR_TEST
1024 setScissor(clearRegion.at(i));
1025#endif
1026 glClear(GL_STENCIL_BUFFER_BIT);
1027 }
1028
1029 dirtyStencilRegion -= currentScissorBounds;
1030
1031#ifndef QT_GL_NO_SCISSOR_TEST
1032 updateClipScissorTest();
1033#endif
1034 }
1035
1036 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // Disable color writes
1037 useSimpleShader();
1038 glEnable(GL_STENCIL_TEST); // For some reason, this has to happen _after_ the simple shader is use()'d
1039
1040 if (mode == WindingFillMode) {
1041 Q_ASSERT(stops && !count);
1042 if (q->state()->clipTestEnabled) {
1043 // Flatten clip values higher than current clip, and set high bit to match current clip
1044 glStencilFunc(GL_LEQUAL, GL_STENCIL_HIGH_BIT | q->state()->currentClip, ~GL_STENCIL_HIGH_BIT);
1045 glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
1046 composite(bounds);
1047
1048 glStencilFunc(GL_EQUAL, GL_STENCIL_HIGH_BIT, GL_STENCIL_HIGH_BIT);
1049 } else if (!stencilClean) {
1050 // Clear stencil buffer within bounding rect
1051 glStencilFunc(GL_ALWAYS, 0, 0xff);
1052 glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
1053 composite(bounds);
1054 }
1055
1056 // Inc. for front-facing triangle
1057 glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_INCR_WRAP, GL_INCR_WRAP);
1058 // Dec. for back-facing "holes"
1059 glStencilOpSeparate(GL_BACK, GL_KEEP, GL_DECR_WRAP, GL_DECR_WRAP);
1060 glStencilMask(~GL_STENCIL_HIGH_BIT);
1061 drawVertexArrays(data, stops, stopCount, GL_TRIANGLE_FAN);
1062
1063 if (q->state()->clipTestEnabled) {
1064 // Clear high bit of stencil outside of path
1065 glStencilFunc(GL_EQUAL, q->state()->currentClip, ~GL_STENCIL_HIGH_BIT);
1066 glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
1067 glStencilMask(GL_STENCIL_HIGH_BIT);
1068 composite(bounds);
1069 }
1070 } else if (mode == OddEvenFillMode) {
1071 glStencilMask(GL_STENCIL_HIGH_BIT);
1072 glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT); // Simply invert the stencil bit
1073 drawVertexArrays(data, stops, stopCount, GL_TRIANGLE_FAN);
1074
1075 } else { // TriStripStrokeFillMode
1076 Q_ASSERT(count && !stops); // tristrips generated directly, so no vertexArray or stops
1077 glStencilMask(GL_STENCIL_HIGH_BIT);
1078#if 0
1079 glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT); // Simply invert the stencil bit
1080 glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
1081 glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, data);
1082 glDrawArrays(GL_TRIANGLE_STRIP, 0, count);
1083 glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
1084#else
1085
1086 glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
1087 if (q->state()->clipTestEnabled) {
1088 glStencilFunc(GL_LEQUAL, q->state()->currentClip | GL_STENCIL_HIGH_BIT,
1089 ~GL_STENCIL_HIGH_BIT);
1090 } else {
1091 glStencilFunc(GL_ALWAYS, GL_STENCIL_HIGH_BIT, 0xff);
1092 }
1093 glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
1094 glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, data);
1095 glDrawArrays(GL_TRIANGLE_STRIP, 0, count);
1096 glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
1097#endif
1098 }
1099
1100 // Enable color writes & disable stencil writes
1101 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1102}
1103
1104/*
1105 If the maximum value in the stencil buffer is GL_STENCIL_HIGH_BIT - 1,
1106 restore the stencil buffer to a pristine state. The current clip region
1107 is set to 1, and the rest to 0.
1108*/
1109void QGL2PaintEngineExPrivate::resetClipIfNeeded()
1110{
1111 if (maxClip != (GL_STENCIL_HIGH_BIT - 1))
1112 return;
1113
1114 Q_Q(QGL2PaintEngineEx);
1115
1116 useSimpleShader();
1117 glEnable(GL_STENCIL_TEST);
1118 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
1119
1120 QRectF bounds = q->state()->matrix.inverted().mapRect(QRectF(0, 0, width, height));
1121 QGLRect rect(bounds.left(), bounds.top(), bounds.right(), bounds.bottom());
1122
1123 // Set high bit on clip region
1124 glStencilFunc(GL_LEQUAL, q->state()->currentClip, 0xff);
1125 glStencilOp(GL_KEEP, GL_INVERT, GL_INVERT);
1126 glStencilMask(GL_STENCIL_HIGH_BIT);
1127 composite(rect);
1128
1129 // Reset clipping to 1 and everything else to zero
1130 glStencilFunc(GL_NOTEQUAL, 0x01, GL_STENCIL_HIGH_BIT);
1131 glStencilOp(GL_ZERO, GL_REPLACE, GL_REPLACE);
1132 glStencilMask(0xff);
1133 composite(rect);
1134
1135 q->state()->currentClip = 1;
1136 q->state()->canRestoreClip = false;
1137
1138 maxClip = 1;
1139
1140 glStencilMask(0x0);
1141 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1142}
1143
1144bool QGL2PaintEngineExPrivate::prepareForDraw(bool srcPixelsAreOpaque)
1145{
1146 if (brushTextureDirty && mode != ImageDrawingMode && mode != ImageArrayDrawingMode)
1147 updateBrushTexture();
1148
1149 if (compositionModeDirty)
1150 updateCompositionMode();
1151
1152 if (matrixDirty)
1153 updateMatrix();
1154
1155 const bool stateHasOpacity = q->state()->opacity < 0.99f;
1156 if (q->state()->composition_mode == QPainter::CompositionMode_Source
1157 || (q->state()->composition_mode == QPainter::CompositionMode_SourceOver
1158 && srcPixelsAreOpaque && !stateHasOpacity))
1159 {
1160 glDisable(GL_BLEND);
1161 } else {
1162 glEnable(GL_BLEND);
1163 }
1164
1165 QGLEngineShaderManager::OpacityMode opacityMode;
1166 if (mode == ImageArrayDrawingMode) {
1167 opacityMode = QGLEngineShaderManager::AttributeOpacity;
1168 } else {
1169 opacityMode = stateHasOpacity ? QGLEngineShaderManager::UniformOpacity
1170 : QGLEngineShaderManager::NoOpacity;
1171 if (stateHasOpacity && (mode != ImageDrawingMode)) {
1172 // Using a brush
1173 bool brushIsPattern = (currentBrush.style() >= Qt::Dense1Pattern) &&
1174 (currentBrush.style() <= Qt::DiagCrossPattern);
1175
1176 if ((currentBrush.style() == Qt::SolidPattern) || brushIsPattern)
1177 opacityMode = QGLEngineShaderManager::NoOpacity; // Global opacity handled by srcPixel shader
1178 }
1179 }
1180 shaderManager->setOpacityMode(opacityMode);
1181
1182 bool changed = shaderManager->useCorrectShaderProg();
1183 // If the shader program needs changing, we change it and mark all uniforms as dirty
1184 if (changed) {
1185 // The shader program has changed so mark all uniforms as dirty:
1186 brushUniformsDirty = true;
1187 shaderMatrixUniformDirty = true;
1188 opacityUniformDirty = true;
1189 }
1190
1191 if (brushUniformsDirty && mode != ImageDrawingMode && mode != ImageArrayDrawingMode)
1192 updateBrushUniforms();
1193
1194 if (shaderMatrixUniformDirty) {
1195 glUniformMatrix3fv(location(QGLEngineShaderManager::PmvMatrix), 1, GL_FALSE, (GLfloat*)pmvMatrix);
1196 shaderMatrixUniformDirty = false;
1197 }
1198
1199 if (opacityMode == QGLEngineShaderManager::UniformOpacity && opacityUniformDirty) {
1200 shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::GlobalOpacity), (GLfloat)q->state()->opacity);
1201 opacityUniformDirty = false;
1202 }
1203
1204 return changed;
1205}
1206
1207void QGL2PaintEngineExPrivate::composite(const QGLRect& boundingRect)
1208{
1209 // Setup a vertex array for the bounding rect:
1210 GLfloat rectVerts[] = {
1211 boundingRect.left, boundingRect.top,
1212 boundingRect.left, boundingRect.bottom,
1213 boundingRect.right, boundingRect.bottom,
1214 boundingRect.right, boundingRect.top
1215 };
1216
1217 glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
1218 glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, rectVerts);
1219
1220 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1221
1222 glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
1223}
1224
1225// Draws the vertex array as a set of <vertexArrayStops.size()> triangle fans.
1226void QGL2PaintEngineExPrivate::drawVertexArrays(const float *data, int *stops, int stopCount,
1227 GLenum primitive)
1228{
1229 // Now setup the pointer to the vertex array:
1230 glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
1231 glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, data);
1232
1233 int previousStop = 0;
1234 for (int i=0; i<stopCount; ++i) {
1235 int stop = stops[i];
1236/*
1237 qDebug("Drawing triangle fan for vertecies %d -> %d:", previousStop, stop-1);
1238 for (int i=previousStop; i<stop; ++i)
1239 qDebug(" %02d: [%.2f, %.2f]", i, vertexArray.data()[i].x, vertexArray.data()[i].y);
1240*/
1241 glDrawArrays(primitive, previousStop, stop - previousStop);
1242 previousStop = stop;
1243 }
1244 glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
1245}
1246
1247/////////////////////////////////// Public Methods //////////////////////////////////////////
1248
1249QGL2PaintEngineEx::QGL2PaintEngineEx()
1250 : QPaintEngineEx(*(new QGL2PaintEngineExPrivate(this)))
1251{
1252}
1253
1254QGL2PaintEngineEx::~QGL2PaintEngineEx()
1255{
1256}
1257
1258void QGL2PaintEngineEx::fill(const QVectorPath &path, const QBrush &brush)
1259{
1260 Q_D(QGL2PaintEngineEx);
1261
1262 if (qbrush_style(brush) == Qt::NoBrush)
1263 return;
1264 ensureActive();
1265 d->setBrush(brush);
1266 d->fill(path);
1267}
1268
1269extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
1270
1271
1272void QGL2PaintEngineEx::stroke(const QVectorPath &path, const QPen &pen)
1273{
1274 Q_D(QGL2PaintEngineEx);
1275
1276 const QBrush &penBrush = qpen_brush(pen);
1277 if (qpen_style(pen) == Qt::NoPen || qbrush_style(penBrush) == Qt::NoBrush)
1278 return;
1279
1280 QOpenGL2PaintEngineState *s = state();
1281 if (pen.isCosmetic() && !qt_scaleForTransform(s->transform(), 0)) {
1282 // QTriangulatingStroker class is not meant to support cosmetically sheared strokes.
1283 QPaintEngineEx::stroke(path, pen);
1284 return;
1285 }
1286
1287 ensureActive();
1288 d->setBrush(penBrush);
1289 d->stroke(path, pen);
1290}
1291
1292void QGL2PaintEngineExPrivate::stroke(const QVectorPath &path, const QPen &pen)
1293{
1294 const QOpenGL2PaintEngineState *s = q->state();
1295 const bool newAddOffset = !(s->renderHints & QPainter::Antialiasing) && !multisamplingAlwaysEnabled;
1296 if (addOffset != newAddOffset) {
1297 addOffset = newAddOffset;
1298 matrixDirty = true;
1299 }
1300
1301 if (snapToPixelGrid) {
1302 snapToPixelGrid = false;
1303 matrixDirty = true;
1304 }
1305
1306 const Qt::PenStyle penStyle = qpen_style(pen);
1307 const QBrush &penBrush = qpen_brush(pen);
1308 const bool opaque = penBrush.isOpaque() && s->opacity > 0.99;
1309
1310 transferMode(BrushDrawingMode);
1311
1312 // updateMatrix() is responsible for setting the inverse scale on
1313 // the strokers, so we need to call it here and not wait for
1314 // prepareForDraw() down below.
1315 updateMatrix();
1316
1317 if (penStyle == Qt::SolidLine) {
1318 stroker.process(path, pen);
1319
1320 } else { // Some sort of dash
1321 dasher.process(path, pen);
1322
1323 QVectorPath dashStroke(dasher.points(),
1324 dasher.elementCount(),
1325 dasher.elementTypes());
1326 stroker.process(dashStroke, pen);
1327 }
1328
1329 if (opaque) {
1330 prepareForDraw(opaque);
1331 glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
1332 glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, false, 0, stroker.vertices());
1333 glDrawArrays(GL_TRIANGLE_STRIP, 0, stroker.vertexCount() / 2);
1334
1335// QBrush b(Qt::green);
1336// d->setBrush(&b);
1337// d->prepareForDraw(true);
1338// glDrawArrays(GL_LINE_STRIP, 0, d->stroker.vertexCount() / 2);
1339
1340 glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
1341
1342 } else {
1343 qreal width = qpen_widthf(pen) / 2;
1344 if (width == 0)
1345 width = 0.5;
1346 qreal extra = pen.joinStyle() == Qt::MiterJoin
1347 ? qMax(pen.miterLimit() * width, width)
1348 : width;
1349
1350 if (pen.isCosmetic())
1351 extra = extra * inverseScale;
1352
1353 QRectF bounds = path.controlPointRect().adjusted(-extra, -extra, extra, extra);
1354
1355 fillStencilWithVertexArray(stroker.vertices(), stroker.vertexCount() / 2,
1356 0, 0, bounds, QGL2PaintEngineExPrivate::TriStripStrokeFillMode);
1357
1358 glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
1359
1360 // Pass when any bit is set, replace stencil value with 0
1361 glStencilFunc(GL_NOTEQUAL, 0, GL_STENCIL_HIGH_BIT);
1362 prepareForDraw(false);
1363
1364 // Stencil the brush onto the dest buffer
1365 composite(bounds);
1366
1367 glStencilMask(0);
1368
1369 updateClipScissorTest();
1370 }
1371}
1372
1373void QGL2PaintEngineEx::penChanged() { }
1374void QGL2PaintEngineEx::brushChanged() { }
1375void QGL2PaintEngineEx::brushOriginChanged() { }
1376
1377void QGL2PaintEngineEx::opacityChanged()
1378{
1379// qDebug("QGL2PaintEngineEx::opacityChanged()");
1380 Q_D(QGL2PaintEngineEx);
1381 state()->opacityChanged = true;
1382
1383 Q_ASSERT(d->shaderManager);
1384 d->brushUniformsDirty = true;
1385 d->opacityUniformDirty = true;
1386}
1387
1388void QGL2PaintEngineEx::compositionModeChanged()
1389{
1390// qDebug("QGL2PaintEngineEx::compositionModeChanged()");
1391 Q_D(QGL2PaintEngineEx);
1392 state()->compositionModeChanged = true;
1393 d->compositionModeDirty = true;
1394}
1395
1396void QGL2PaintEngineEx::renderHintsChanged()
1397{
1398 state()->renderHintsChanged = true;
1399
1400#if !defined(QT_OPENGL_ES_2)
1401 if ((state()->renderHints & QPainter::Antialiasing)
1402 || (state()->renderHints & QPainter::HighQualityAntialiasing))
1403 glEnable(GL_MULTISAMPLE);
1404 else
1405 glDisable(GL_MULTISAMPLE);
1406#endif
1407
1408 Q_D(QGL2PaintEngineEx);
1409 d->lastTextureUsed = GLuint(-1);
1410 d->brushTextureDirty = true;
1411// qDebug("QGL2PaintEngineEx::renderHintsChanged() not implemented!");
1412}
1413
1414void QGL2PaintEngineEx::transformChanged()
1415{
1416 Q_D(QGL2PaintEngineEx);
1417 d->matrixDirty = true;
1418 state()->matrixChanged = true;
1419}
1420
1421
1422void QGL2PaintEngineEx::drawPixmap(const QRectF& dest, const QPixmap & pixmap, const QRectF & src)
1423{
1424 Q_D(QGL2PaintEngineEx);
1425 ensureActive();
1426 d->transferMode(ImageDrawingMode);
1427
1428 QGLContext *ctx = d->ctx;
1429 glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT);
1430 QGLTexture *texture =
1431 ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA,
1432 QGLContext::InternalBindOption
1433 | QGLContext::CanFlipNativePixmapBindOption);
1434
1435 GLfloat top = texture->options & QGLContext::InvertedYBindOption ? (pixmap.height() - src.top()) : src.top();
1436 GLfloat bottom = texture->options & QGLContext::InvertedYBindOption ? (pixmap.height() - src.bottom()) : src.bottom();
1437 QGLRect srcRect(src.left(), top, src.right(), bottom);
1438
1439 bool isBitmap = pixmap.isQBitmap();
1440 bool isOpaque = !isBitmap && !pixmap.hasAlphaChannel();
1441
1442 d->updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE,
1443 state()->renderHints & QPainter::SmoothPixmapTransform, texture->id);
1444 d->drawTexture(dest, srcRect, pixmap.size(), isOpaque, isBitmap);
1445}
1446
1447void QGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, const QRectF& src,
1448 Qt::ImageConversionFlags)
1449{
1450 Q_D(QGL2PaintEngineEx);
1451 ensureActive();
1452 d->transferMode(ImageDrawingMode);
1453
1454 QGLContext *ctx = d->ctx;
1455 glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT);
1456 QGLTexture *texture = ctx->d_func()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, QGLContext::InternalBindOption);
1457 GLuint id = texture->id;
1458
1459 d->updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE,
1460 state()->renderHints & QPainter::SmoothPixmapTransform, id);
1461 d->drawTexture(dest, src, image.size(), !image.hasAlphaChannel());
1462}
1463
1464void QGL2PaintEngineEx::drawTexture(const QRectF &dest, GLuint textureId, const QSize &size, const QRectF &src)
1465{
1466 Q_D(QGL2PaintEngineEx);
1467 ensureActive();
1468 d->transferMode(ImageDrawingMode);
1469
1470#ifndef QT_OPENGL_ES_2
1471 QGLContext *ctx = d->ctx;
1472#endif
1473 glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT);
1474 glBindTexture(GL_TEXTURE_2D, textureId);
1475
1476 QGLRect srcRect(src.left(), src.bottom(), src.right(), src.top());
1477
1478 d->updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE,
1479 state()->renderHints & QPainter::SmoothPixmapTransform, textureId);
1480 d->drawTexture(dest, srcRect, size, false);
1481}
1482
1483void QGL2PaintEngineEx::drawTextItem(const QPointF &p, const QTextItem &textItem)
1484{
1485 Q_D(QGL2PaintEngineEx);
1486
1487 ensureActive();
1488 QOpenGL2PaintEngineState *s = state();
1489
1490 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
1491
1492 QTransform::TransformationType txtype = s->matrix.type();
1493
1494 float det = s->matrix.determinant();
1495 bool drawCached = txtype < QTransform::TxProject;
1496
1497 // don't try to cache huge fonts or vastly transformed fonts
1498 const qreal pixelSize = ti.fontEngine->fontDef.pixelSize;
1499 if (pixelSize * pixelSize * qAbs(det) >= 64 * 64 || det < 0.25f || det > 4.f)
1500 drawCached = false;
1501
1502 QFontEngineGlyphCache::Type glyphType = ti.fontEngine->glyphFormat >= 0
1503 ? QFontEngineGlyphCache::Type(ti.fontEngine->glyphFormat)
1504 : d->glyphCacheType;
1505
1506 if (txtype > QTransform::TxTranslate)
1507 glyphType = QFontEngineGlyphCache::Raster_A8;
1508
1509 if (glyphType == QFontEngineGlyphCache::Raster_RGBMask
1510 && state()->composition_mode != QPainter::CompositionMode_Source
1511 && state()->composition_mode != QPainter::CompositionMode_SourceOver)
1512 {
1513 drawCached = false;
1514 }
1515
1516 if (drawCached) {
1517 d->drawCachedGlyphs(p, glyphType, ti);
1518 return;
1519 }
1520
1521 QPaintEngineEx::drawTextItem(p, ti);
1522}
1523
1524void QGL2PaintEngineExPrivate::drawCachedGlyphs(const QPointF &p, QFontEngineGlyphCache::Type glyphType,
1525 const QTextItemInt &ti)
1526{
1527 Q_Q(QGL2PaintEngineEx);
1528
1529 QVarLengthArray<QFixedPoint> positions;
1530 QVarLengthArray<glyph_t> glyphs;
1531 QTransform matrix = QTransform::fromTranslate(p.x(), p.y());
1532 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
1533
1534 QGLTextureGlyphCache *cache =
1535 (QGLTextureGlyphCache *) ti.fontEngine->glyphCache(ctx, glyphType, QTransform());
1536
1537 if (!cache || cache->cacheType() != glyphType) {
1538 cache = new QGLTextureGlyphCache(ctx, glyphType, QTransform());
1539 ti.fontEngine->setGlyphCache(ctx, cache);
1540 }
1541
1542 cache->setPaintEnginePrivate(this);
1543 cache->populate(ti, glyphs, positions);
1544
1545 if (cache->width() == 0 || cache->height() == 0)
1546 return;
1547
1548 transferMode(TextDrawingMode);
1549
1550 int margin = cache->glyphMargin();
1551
1552 GLfloat dx = 1.0 / cache->width();
1553 GLfloat dy = 1.0 / cache->height();
1554
1555 QGLPoint *oldVertexCoordinateDataPtr = vertexCoordinateArray.data();
1556 QGLPoint *oldTextureCoordinateDataPtr = textureCoordinateArray.data();
1557
1558 vertexCoordinateArray.clear();
1559 textureCoordinateArray.clear();
1560
1561 for (int i=0; i<glyphs.size(); ++i) {
1562 const QTextureGlyphCache::Coord &c = cache->coords.value(glyphs[i]);
1563 int x = positions[i].x.toInt() + c.baseLineX - margin;
1564 int y = positions[i].y.toInt() - c.baseLineY - margin;
1565
1566 vertexCoordinateArray.addRect(QRectF(x, y, c.w, c.h));
1567 textureCoordinateArray.addRect(QRectF(c.x*dx, c.y*dy, c.w * dx, c.h * dy));
1568 }
1569
1570 if (vertexCoordinateArray.data() != oldVertexCoordinateDataPtr)
1571 glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexCoordinateArray.data());
1572 if (textureCoordinateArray.data() != oldTextureCoordinateDataPtr)
1573 glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, textureCoordinateArray.data());
1574
1575 if (addOffset) {
1576 addOffset = false;
1577 matrixDirty = true;
1578 }
1579 if (!snapToPixelGrid) {
1580 snapToPixelGrid = true;
1581 matrixDirty = true;
1582 }
1583
1584 QBrush pensBrush = q->state()->pen.brush();
1585 setBrush(pensBrush);
1586
1587 if (glyphType == QFontEngineGlyphCache::Raster_RGBMask) {
1588
1589 // Subpixel antialiasing without gamma correction
1590
1591 QPainter::CompositionMode compMode = q->state()->composition_mode;
1592 Q_ASSERT(compMode == QPainter::CompositionMode_Source
1593 || compMode == QPainter::CompositionMode_SourceOver);
1594
1595 shaderManager->setMaskType(QGLEngineShaderManager::SubPixelMaskPass1);
1596
1597 if (pensBrush.style() == Qt::SolidPattern) {
1598 // Solid patterns can get away with only one pass.
1599 QColor c = pensBrush.color();
1600 qreal oldOpacity = q->state()->opacity;
1601 if (compMode == QPainter::CompositionMode_Source) {
1602 c = qt_premultiplyColor(c, q->state()->opacity);
1603 q->state()->opacity = 1;
1604 opacityUniformDirty = true;
1605 }
1606
1607 compositionModeDirty = false; // I can handle this myself, thank you very much
1608 prepareForDraw(false); // Text always causes src pixels to be transparent
1609
1610 // prepareForDraw() have set the opacity on the current shader, so the opacity state can now be reset.
1611 if (compMode == QPainter::CompositionMode_Source) {
1612 q->state()->opacity = oldOpacity;
1613 opacityUniformDirty = true;
1614 }
1615
1616 glEnable(GL_BLEND);
1617 glBlendFunc(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_COLOR);
1618 glBlendColor(c.redF(), c.greenF(), c.blueF(), c.alphaF());
1619 } else {
1620 // Other brush styles need two passes.
1621
1622 qreal oldOpacity = q->state()->opacity;
1623 if (compMode == QPainter::CompositionMode_Source) {
1624 q->state()->opacity = 1;
1625 opacityUniformDirty = true;
1626 pensBrush = Qt::white;
1627 setBrush(pensBrush);
1628 }
1629
1630 compositionModeDirty = false; // I can handle this myself, thank you very much
1631 prepareForDraw(false); // Text always causes src pixels to be transparent
1632 glEnable(GL_BLEND);
1633 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1634
1635 glActiveTexture(GL_TEXTURE0 + QT_MASK_TEXTURE_UNIT);
1636 glBindTexture(GL_TEXTURE_2D, cache->texture());
1637 updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, false);
1638
1639 shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::MaskTexture), QT_MASK_TEXTURE_UNIT);
1640 glDrawArrays(GL_TRIANGLES, 0, 6 * glyphs.size());
1641
1642 shaderManager->setMaskType(QGLEngineShaderManager::SubPixelMaskPass2);
1643
1644 if (compMode == QPainter::CompositionMode_Source) {
1645 q->state()->opacity = oldOpacity;
1646 opacityUniformDirty = true;
1647 pensBrush = q->state()->pen.brush();
1648 setBrush(pensBrush);
1649 }
1650
1651 compositionModeDirty = false;
1652 prepareForDraw(false); // Text always causes src pixels to be transparent
1653 glEnable(GL_BLEND);
1654 glBlendFunc(GL_ONE, GL_ONE);
1655 }
1656 compositionModeDirty = true;
1657 } else {
1658 // Greyscale/mono glyphs
1659
1660 shaderManager->setMaskType(QGLEngineShaderManager::PixelMask);
1661 prepareForDraw(false); // Text always causes src pixels to be transparent
1662 }
1663 //### TODO: Gamma correction
1664
1665 glActiveTexture(GL_TEXTURE0 + QT_MASK_TEXTURE_UNIT);
1666 glBindTexture(GL_TEXTURE_2D, cache->texture());
1667 updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, false);
1668
1669 shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::MaskTexture), QT_MASK_TEXTURE_UNIT);
1670 glDrawArrays(GL_TRIANGLES, 0, 6 * glyphs.size());
1671}
1672
1673void QGL2PaintEngineEx::drawPixmaps(const QDrawPixmaps::Data *drawingData, int dataCount, const QPixmap &pixmap, QDrawPixmaps::DrawingHints hints)
1674{
1675 Q_D(QGL2PaintEngineEx);
1676 // Use fallback for extended composition modes.
1677 if (state()->composition_mode > QPainter::CompositionMode_Plus) {
1678 QPaintEngineEx::drawPixmaps(drawingData, dataCount, pixmap, hints);
1679 return;
1680 }
1681
1682 ensureActive();
1683 d->drawPixmaps(drawingData, dataCount, pixmap, hints);
1684}
1685
1686
1687void QGL2PaintEngineExPrivate::drawPixmaps(const QDrawPixmaps::Data *drawingData, int dataCount, const QPixmap &pixmap, QDrawPixmaps::DrawingHints hints)
1688{
1689 GLfloat dx = 1.0f / pixmap.size().width();
1690 GLfloat dy = 1.0f / pixmap.size().height();
1691
1692 vertexCoordinateArray.clear();
1693 textureCoordinateArray.clear();
1694 opacityArray.reset();
1695
1696 if (addOffset) {