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

Last change on this file since 606 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) {
1697 addOffset = false;
1698 matrixDirty = true;
1699 }
1700
1701 if (snapToPixelGrid) {
1702 snapToPixelGrid = false;
1703 matrixDirty = true;
1704 }
1705
1706 bool allOpaque = true;
1707
1708 for (int i = 0; i < dataCount; ++i) {
1709 qreal s = 0;
1710 qreal c = 1;
1711 if (drawingData[i].rotation != 0) {
1712 s = qFastSin(drawingData[i].rotation * Q_PI / 180);
1713 c = qFastCos(drawingData[i].rotation * Q_PI / 180);
1714 }
1715
1716 qreal right = 0.5 * drawingData[i].scaleX * drawingData[i].source.width();
1717 qreal bottom = 0.5 * drawingData[i].scaleY * drawingData[i].source.height();
1718 QGLPoint bottomRight(right * c - bottom * s, right * s + bottom * c);
1719 QGLPoint bottomLeft(-right * c - bottom * s, -right * s + bottom * c);
1720
1721 vertexCoordinateArray.lineToArray(bottomRight.x + drawingData[i].point.x(), bottomRight.y + drawingData[i].point.y());
1722 vertexCoordinateArray.lineToArray(-bottomLeft.x + drawingData[i].point.x(), -bottomLeft.y + drawingData[i].point.y());
1723 vertexCoordinateArray.lineToArray(-bottomRight.x + drawingData[i].point.x(), -bottomRight.y + drawingData[i].point.y());
1724 vertexCoordinateArray.lineToArray(-bottomRight.x + drawingData[i].point.x(), -bottomRight.y + drawingData[i].point.y());
1725 vertexCoordinateArray.lineToArray(bottomLeft.x + drawingData[i].point.x(), bottomLeft.y + drawingData[i].point.y());
1726 vertexCoordinateArray.lineToArray(bottomRight.x + drawingData[i].point.x(), bottomRight.y + drawingData[i].point.y());
1727
1728 QGLRect src(drawingData[i].source.left() * dx, drawingData[i].source.top() * dy,
1729 drawingData[i].source.right() * dx, drawingData[i].source.bottom() * dy);
1730
1731 textureCoordinateArray.lineToArray(src.right, src.bottom);
1732 textureCoordinateArray.lineToArray(src.right, src.top);
1733 textureCoordinateArray.lineToArray(src.left, src.top);
1734 textureCoordinateArray.lineToArray(src.left, src.top);
1735 textureCoordinateArray.lineToArray(src.left, src.bottom);
1736 textureCoordinateArray.lineToArray(src.right, src.bottom);
1737
1738 qreal opacity = drawingData[i].opacity * q->state()->opacity;
1739 opacityArray << opacity << opacity << opacity << opacity << opacity << opacity;
1740 allOpaque &= (opacity >= 0.99f);
1741 }
1742
1743 glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT);
1744 QGLTexture *texture = ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA,
1745 QGLContext::InternalBindOption
1746 | QGLContext::CanFlipNativePixmapBindOption);
1747
1748 if (texture->options & QGLContext::InvertedYBindOption) {
1749 // Flip texture y-coordinate.
1750 QGLPoint *data = textureCoordinateArray.data();
1751 for (int i = 0; i < 6 * dataCount; ++i)
1752 data[i].y = 1 - data[i].y;
1753 }
1754
1755 transferMode(ImageArrayDrawingMode);
1756
1757 bool isBitmap = pixmap.isQBitmap();
1758 bool isOpaque = !isBitmap && (!pixmap.hasAlphaChannel() || (hints & QDrawPixmaps::OpaqueHint)) && allOpaque;
1759
1760 updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE,
1761 q->state()->renderHints & QPainter::SmoothPixmapTransform, texture->id);
1762
1763 // Setup for texture drawing
1764 currentBrush = noBrush;
1765 shaderManager->setSrcPixelType(isBitmap ? QGLEngineShaderManager::PatternSrc : QGLEngineShaderManager::ImageSrc);
1766 if (prepareForDraw(isOpaque))
1767 shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::ImageTexture), QT_IMAGE_TEXTURE_UNIT);
1768
1769 if (isBitmap) {
1770 QColor col = qt_premultiplyColor(q->state()->pen.color(), (GLfloat)q->state()->opacity);
1771 shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col);
1772 }
1773
1774 glDrawArrays(GL_TRIANGLES, 0, 6 * dataCount);
1775}
1776
1777bool QGL2PaintEngineEx::begin(QPaintDevice *pdev)
1778{
1779 Q_D(QGL2PaintEngineEx);
1780
1781// qDebug("QGL2PaintEngineEx::begin()");
1782 if (pdev->devType() == QInternal::OpenGL)
1783 d->device = static_cast<QGLPaintDevice*>(pdev);
1784 else
1785 d->device = QGLPaintDevice::getDevice(pdev);
1786
1787 if (!d->device)
1788 return false;
1789
1790 d->ctx = d->device->context();
1791 d->ctx->d_ptr->active_engine = this;
1792
1793 const QSize sz = d->device->size();
1794 d->width = sz.width();
1795 d->height = sz.height();
1796 d->mode = BrushDrawingMode;
1797 d->brushTextureDirty = true;
1798 d->brushUniformsDirty = true;
1799 d->matrixDirty = true;
1800 d->compositionModeDirty = true;
1801 d->opacityUniformDirty = true;
1802 d->needsSync = true;
1803 d->useSystemClip = !systemClip().isEmpty();
1804 d->currentBrush = QBrush();
1805
1806 d->dirtyStencilRegion = QRect(0, 0, d->width, d->height);
1807 d->stencilClean = true;
1808
1809 // Calling begin paint should make the correct context current. So, any
1810 // code which calls into GL or otherwise needs a current context *must*
1811 // go after beginPaint:
1812 d->device->beginPaint();
1813
1814#if !defined(QT_OPENGL_ES_2)
1815 bool success = qt_resolve_version_2_0_functions(d->ctx)
1816 && qt_resolve_buffer_extensions(d->ctx);
1817 Q_ASSERT(success);
1818 Q_UNUSED(success);
1819#endif
1820
1821 d->shaderManager = new QGLEngineShaderManager(d->ctx);
1822
1823 glDisable(GL_STENCIL_TEST);
1824 glDisable(GL_DEPTH_TEST);
1825 glDisable(GL_SCISSOR_TEST);
1826
1827#if !defined(QT_OPENGL_ES_2)
1828 glDisable(GL_MULTISAMPLE);
1829#endif
1830
1831 d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
1832
1833#if !defined(QT_OPENGL_ES_2)
1834#if defined(Q_WS_WIN)
1835 if (qt_cleartype_enabled)
1836#endif
1837 d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
1838#endif
1839
1840#if defined(QT_OPENGL_ES_2)
1841 // OpenGL ES can't switch MSAA off, so if the gl paint device is
1842 // multisampled, it's always multisampled.
1843 d->multisamplingAlwaysEnabled = d->device->format().sampleBuffers();
1844#else
1845 d->multisamplingAlwaysEnabled = false;
1846#endif
1847
1848 return true;
1849}
1850
1851bool QGL2PaintEngineEx::end()
1852{
1853 Q_D(QGL2PaintEngineEx);
1854 QGLContext *ctx = d->ctx;
1855
1856 glUseProgram(0);
1857 d->transferMode(BrushDrawingMode);
1858 d->device->endPaint();
1859
1860#if defined(Q_WS_X11)
1861 // On some (probably all) drivers, deleting an X pixmap which has been bound to a texture
1862 // before calling glFinish/swapBuffers renders garbage. Presumably this is because X deletes
1863 // the pixmap behind the driver's back before it's had a chance to use it. To fix this, we
1864 // reference all QPixmaps which have been bound to stop them being deleted and only deref
1865 // them here, after swapBuffers, where they can be safely deleted.
1866 ctx->d_func()->boundPixmaps.clear();
1867#endif
1868 d->ctx->d_ptr->active_engine = 0;
1869
1870 d->resetGLState();
1871
1872 delete d->shaderManager;
1873 d->shaderManager = 0;
1874
1875#ifdef QT_OPENGL_CACHE_AS_VBOS
1876 if (!d->unusedVBOSToClean.isEmpty()) {
1877 glDeleteBuffers(d->unusedVBOSToClean.size(), d->unusedVBOSToClean.constData());
1878 d->unusedVBOSToClean.clear();
1879 }
1880#endif
1881
1882 return false;
1883}
1884
1885void QGL2PaintEngineEx::ensureActive()
1886{
1887 Q_D(QGL2PaintEngineEx);
1888 QGLContext *ctx = d->ctx;
1889
1890 if (isActive() && ctx->d_ptr->active_engine != this) {
1891 ctx->d_ptr->active_engine = this;
1892 d->needsSync = true;
1893 }
1894
1895 d->device->ensureActiveTarget();
1896
1897 if (d->needsSync) {
1898 d->transferMode(BrushDrawingMode);
1899 glViewport(0, 0, d->width, d->height);
1900 d->needsSync = false;
1901 d->shaderManager->setDirty();
1902 setState(state());
1903 }
1904}
1905
1906void QGL2PaintEngineExPrivate::updateClipScissorTest()
1907{
1908 Q_Q(QGL2PaintEngineEx);
1909 if (q->state()->clipTestEnabled) {
1910 glEnable(GL_STENCIL_TEST);
1911 glStencilFunc(GL_LEQUAL, q->state()->currentClip, ~GL_STENCIL_HIGH_BIT);
1912 } else {
1913 glDisable(GL_STENCIL_TEST);
1914 glStencilFunc(GL_ALWAYS, 0, 0xff);
1915 }
1916
1917#ifdef QT_GL_NO_SCISSOR_TEST
1918 currentScissorBounds = QRect(0, 0, width, height);
1919#else
1920 QRect bounds = q->state()->rectangleClip;
1921 if (!q->state()->clipEnabled) {
1922 if (useSystemClip)
1923 bounds = systemClip.boundingRect();
1924 else
1925 bounds = QRect(0, 0, width, height);
1926 } else {
1927 if (useSystemClip)
1928 bounds = bounds.intersected(systemClip.boundingRect());
1929 else
1930 bounds = bounds.intersected(QRect(0, 0, width, height));
1931 }
1932
1933 currentScissorBounds = bounds;
1934
1935 if (bounds == QRect(0, 0, width, height)) {
1936 glDisable(GL_SCISSOR_TEST);
1937 } else {
1938 glEnable(GL_SCISSOR_TEST);
1939 setScissor(bounds);
1940 }
1941#endif
1942}
1943
1944void QGL2PaintEngineExPrivate::setScissor(const QRect &rect)
1945{
1946 const int left = rect.left();
1947 const int width = rect.width();
1948 const int bottom = height - (rect.top() + rect.height());
1949 const int height = rect.height();
1950
1951 glScissor(left, bottom, width, height);
1952}
1953
1954void QGL2PaintEngineEx::clipEnabledChanged()
1955{
1956 Q_D(QGL2PaintEngineEx);
1957
1958 state()->clipChanged = true;
1959
1960 if (painter()->hasClipping())
1961 d->regenerateClip();
1962 else
1963 d->systemStateChanged();
1964}
1965
1966void QGL2PaintEngineExPrivate::clearClip(uint value)
1967{
1968 dirtyStencilRegion -= currentScissorBounds;
1969
1970 glStencilMask(0xff);
1971 glClearStencil(value);
1972 glClear(GL_STENCIL_BUFFER_BIT);
1973 glStencilMask(0x0);
1974
1975 q->state()->needsClipBufferClear = false;
1976}
1977
1978void QGL2PaintEngineExPrivate::writeClip(const QVectorPath &path, uint value)
1979{
1980 transferMode(BrushDrawingMode);
1981
1982 if (addOffset) {
1983 addOffset = false;
1984 matrixDirty = true;
1985 }
1986 if (snapToPixelGrid) {
1987 snapToPixelGrid = false;
1988 matrixDirty = true;
1989 }
1990
1991 if (matrixDirty)
1992 updateMatrix();
1993
1994 stencilClean = false;
1995
1996 const bool singlePass = !path.hasWindingFill()
1997 && (((q->state()->currentClip == maxClip - 1) && q->state()->clipTestEnabled)
1998 || q->state()->needsClipBufferClear);
1999 const uint referenceClipValue = q->state()->needsClipBufferClear ? 1 : q->state()->currentClip;
2000
2001 if (q->state()->needsClipBufferClear)
2002 clearClip(1);
2003
2004 if (path.isEmpty()) {
2005 glEnable(GL_STENCIL_TEST);
2006 glStencilFunc(GL_LEQUAL, value, ~GL_STENCIL_HIGH_BIT);
2007 return;
2008 }
2009
2010 if (q->state()->clipTestEnabled)
2011 glStencilFunc(GL_LEQUAL, q->state()->currentClip, ~GL_STENCIL_HIGH_BIT);
2012 else
2013 glStencilFunc(GL_ALWAYS, 0, 0xff);
2014
2015 vertexCoordinateArray.clear();
2016 vertexCoordinateArray.addPath(path, inverseScale, false);
2017
2018 if (!singlePass)
2019 fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill());
2020
2021 glColorMask(false, false, false, false);
2022 glEnable(GL_STENCIL_TEST);
2023 useSimpleShader();
2024
2025 if (singlePass) {
2026 // Under these conditions we can set the new stencil value in a single
2027 // pass, by using the current value and the "new value" as the toggles
2028
2029 glStencilFunc(GL_LEQUAL, referenceClipValue, ~GL_STENCIL_HIGH_BIT);
2030 glStencilOp(GL_KEEP, GL_INVERT, GL_INVERT);
2031 glStencilMask(value ^ referenceClipValue);
2032
2033 drawVertexArrays(vertexCoordinateArray, GL_TRIANGLE_FAN);
2034 } else {
2035 glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
2036 glStencilMask(0xff);
2037
2038 if (!q->state()->clipTestEnabled && path.hasWindingFill()) {
2039 // Pass when any clip bit is set, set high bit
2040 glStencilFunc(GL_NOTEQUAL, GL_STENCIL_HIGH_BIT, ~GL_STENCIL_HIGH_BIT);
2041 composite(vertexCoordinateArray.boundingRect());
2042 }
2043
2044 // Pass when high bit is set, replace stencil value with new clip value
2045 glStencilFunc(GL_NOTEQUAL, value, GL_STENCIL_HIGH_BIT);
2046
2047 composite(vertexCoordinateArray.boundingRect());
2048 }
2049
2050 glStencilFunc(GL_LEQUAL, value, ~GL_STENCIL_HIGH_BIT);
2051 glStencilMask(0);
2052
2053 glColorMask(true, true, true, true);
2054}
2055
2056void QGL2PaintEngineEx::clip(const QVectorPath &path, Qt::ClipOperation op)
2057{
2058// qDebug("QGL2PaintEngineEx::clip()");
2059 Q_D(QGL2PaintEngineEx);
2060
2061 state()->clipChanged = true;
2062
2063 ensureActive();
2064
2065 if (op == Qt::ReplaceClip) {
2066 op = Qt::IntersectClip;
2067 if (d->hasClipOperations()) {
2068 d->systemStateChanged();
2069 state()->canRestoreClip = false;
2070 }
2071 }
2072
2073#ifndef QT_GL_NO_SCISSOR_TEST
2074 if (!path.isEmpty() && op == Qt::IntersectClip && (path.shape() == QVectorPath::RectangleHint)) {
2075 const QPointF* const points = reinterpret_cast<const QPointF*>(path.points());
2076 QRectF rect(points[0], points[2]);
2077
2078 if (state()->matrix.type() <= QTransform::TxScale) {
2079 state()->rectangleClip = state()->rectangleClip.intersected(state()->matrix.mapRect(rect).toRect());
2080 d->updateClipScissorTest();
2081 return;
2082 }
2083 }
2084#endif
2085
2086 const QRect pathRect = state()->matrix.mapRect(path.controlPointRect()).toAlignedRect();
2087
2088 switch (op) {
2089 case Qt::NoClip:
2090 if (d->useSystemClip) {
2091 state()->clipTestEnabled = true;
2092 state()->currentClip = 1;
2093 } else {
2094 state()->clipTestEnabled = false;
2095 }
2096 state()->rectangleClip = QRect(0, 0, d->width, d->height);
2097 state()->canRestoreClip = false;
2098 d->updateClipScissorTest();
2099 break;
2100 case Qt::IntersectClip:
2101 state()->rectangleClip = state()->rectangleClip.intersected(pathRect);
2102 d->updateClipScissorTest();
2103 d->resetClipIfNeeded();
2104 ++d->maxClip;
2105 d->writeClip(path, d->maxClip);
2106 state()->currentClip = d->maxClip;
2107 state()->clipTestEnabled = true;
2108 break;
2109 case Qt::UniteClip: {
2110 d->resetClipIfNeeded();
2111 ++d->maxClip;
2112 if (state()->rectangleClip.isValid()) {
2113 QPainterPath path;
2114 path.addRect(state()->rectangleClip);
2115
2116 // flush the existing clip rectangle to the depth buffer
2117 d->writeClip(qtVectorPathForPath(state()->matrix.inverted().map(path)), d->maxClip);
2118 }
2119
2120 state()->clipTestEnabled = false;
2121#ifndef QT_GL_NO_SCISSOR_TEST
2122 QRect oldRectangleClip = state()->rectangleClip;
2123
2124 state()->rectangleClip = state()->rectangleClip.united(pathRect);
2125 d->updateClipScissorTest();
2126
2127 QRegion extendRegion = QRegion(state()->rectangleClip) - oldRectangleClip;
2128
2129 if (!extendRegion.isEmpty()) {
2130 QPainterPath extendPath;
2131 extendPath.addRegion(extendRegion);
2132
2133 // first clear the depth buffer in the extended region
2134 d->writeClip(qtVectorPathForPath(state()->matrix.inverted().map(extendPath)), 0);
2135 }
2136#endif
2137 // now write the clip path
2138 d->writeClip(path, d->maxClip);
2139 state()->canRestoreClip = false;
2140 state()->currentClip = d->maxClip;
2141 state()->clipTestEnabled = true;
2142 break;
2143 }
2144 default:
2145 break;
2146 }
2147}
2148
2149void QGL2PaintEngineExPrivate::regenerateClip()
2150{
2151 systemStateChanged();
2152 replayClipOperations();
2153}
2154
2155void QGL2PaintEngineExPrivate::systemStateChanged()
2156{
2157 Q_Q(QGL2PaintEngineEx);
2158
2159 q->state()->clipChanged = true;
2160
2161 if (systemClip.isEmpty()) {
2162 useSystemClip = false;
2163 } else {
2164 if (q->paintDevice()->devType() == QInternal::Widget && currentClipWidget) {
2165 QWidgetPrivate *widgetPrivate = qt_widget_private(currentClipWidget->window());
2166 useSystemClip = widgetPrivate->extra && widgetPrivate->extra->inRenderWithPainter;
2167 } else {
2168 useSystemClip = true;
2169 }
2170 }
2171
2172 q->state()->clipTestEnabled = false;
2173 q->state()->needsClipBufferClear = true;
2174
2175 q->state()->currentClip = 1;
2176 maxClip = 1;
2177
2178 q->state()->rectangleClip = useSystemClip ? systemClip.boundingRect() : QRect(0, 0, width, height);
2179 updateClipScissorTest();
2180
2181 if (systemClip.rectCount() == 1) {
2182 if (systemClip.boundingRect() == QRect(0, 0, width, height))
2183 useSystemClip = false;
2184#ifndef QT_GL_NO_SCISSOR_TEST
2185 // scissoring takes care of the system clip
2186 return;
2187#endif
2188 }
2189
2190 if (useSystemClip) {
2191 clearClip(0);
2192
2193 QPainterPath path;
2194 path.addRegion(systemClip);
2195
2196 q->state()->currentClip = 0;
2197 writeClip(qtVectorPathForPath(q->state()->matrix.inverted().map(path)), 1);
2198 q->state()->currentClip = 1;
2199 q->state()->clipTestEnabled = true;
2200 }
2201}
2202
2203void QGL2PaintEngineEx::setState(QPainterState *new_state)
2204{
2205 // qDebug("QGL2PaintEngineEx::setState()");
2206
2207 Q_D(QGL2PaintEngineEx);
2208
2209 QOpenGL2PaintEngineState *s = static_cast<QOpenGL2PaintEngineState *>(new_state);
2210 QOpenGL2PaintEngineState *old_state = state();
2211
2212 QPaintEngineEx::setState(s);
2213
2214 if (s->isNew) {
2215 // Newly created state object. The call to setState()
2216 // will either be followed by a call to begin(), or we are
2217 // setting the state as part of a save().
2218 s->isNew = false;
2219 return;
2220 }
2221
2222 // Setting the state as part of a restore().
2223
2224 if (old_state == s || old_state->renderHintsChanged)
2225 renderHintsChanged();
2226
2227 if (old_state == s || old_state->matrixChanged) {
2228 d->matrixDirty = true;
2229 d->simpleShaderMatrixUniformDirty = true;
2230 d->shaderMatrixUniformDirty = true;
2231 }
2232
2233 if (old_state == s || old_state->compositionModeChanged)
2234 d->compositionModeDirty = true;
2235
2236 if (old_state == s || old_state->opacityChanged)
2237 d->opacityUniformDirty = true;
2238
2239 if (old_state == s || old_state->clipChanged) {
2240 if (old_state && old_state != s && old_state->canRestoreClip) {
2241 d->updateClipScissorTest();
2242 glDepthFunc(GL_LEQUAL);
2243 } else {
2244 d->regenerateClip();
2245 }
2246 }
2247}
2248
2249QPainterState *QGL2PaintEngineEx::createState(QPainterState *orig) const
2250{
2251 if (orig)
2252 const_cast<QGL2PaintEngineEx *>(this)->ensureActive();
2253
2254 QOpenGL2PaintEngineState *s;
2255 if (!orig)
2256 s = new QOpenGL2PaintEngineState();
2257 else
2258 s = new QOpenGL2PaintEngineState(*static_cast<QOpenGL2PaintEngineState *>(orig));
2259
2260 s->matrixChanged = false;
2261 s->compositionModeChanged = false;
2262 s->opacityChanged = false;
2263 s->renderHintsChanged = false;
2264 s->clipChanged = false;
2265
2266 return s;
2267}
2268
2269QOpenGL2PaintEngineState::QOpenGL2PaintEngineState(QOpenGL2PaintEngineState &other)
2270 : QPainterState(other)
2271{
2272 isNew = true;
2273 needsClipBufferClear = other.needsClipBufferClear;
2274 clipTestEnabled = other.clipTestEnabled;
2275 currentClip = other.currentClip;
2276 canRestoreClip = other.canRestoreClip;
2277 rectangleClip = other.rectangleClip;
2278}
2279
2280QOpenGL2PaintEngineState::QOpenGL2PaintEngineState()
2281{
2282 isNew = true;
2283 needsClipBufferClear = true;
2284 clipTestEnabled = false;
2285 canRestoreClip = true;
2286}
2287
2288QOpenGL2PaintEngineState::~QOpenGL2PaintEngineState()
2289{
2290}
2291
2292QT_END_NAMESPACE
2293
2294#include "qpaintengineex_opengl2.moc"
Note: See TracBrowser for help on using the repository browser.