source: trunk/src/opengl/qpaintengine_opengl.cpp@ 1056

Last change on this file since 1056 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 177.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 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#include <qdebug.h>
43#include <private/qfontengine_p.h>
44#include <qmath.h>
45#include <private/qmath_p.h>
46#include <private/qdrawhelper_p.h>
47#include <private/qpaintengine_p.h>
48#include "qapplication.h"
49#include "qbrush.h"
50#include "qgl.h"
51#include <private/qgl_p.h>
52#include <private/qglpaintdevice_p.h>
53#include <private/qpainter_p.h>
54#include "qmap.h"
55#include <private/qpaintengine_opengl_p.h>
56#include <private/qdatabuffer_p.h>
57#include "qpen.h"
58#include "qvarlengtharray.h"
59#include <private/qpainter_p.h>
60#include <private/qglpixelbuffer_p.h>
61#include <private/qbezier_p.h>
62#include <qglframebufferobject.h>
63#include <private/qstatictext_p.h>
64
65#include "private/qtessellator_p.h"
66
67#include "util/fragmentprograms_p.h"
68
69#ifdef Q_WS_QWS
70#include "private/qglwindowsurface_qws_p.h"
71#include "qwsmanager_qws.h"
72#include "private/qwsmanager_p.h"
73#endif
74
75#define QGL_FUNC_CONTEXT QGLContext *ctx = const_cast<QGLContext *>(device->context());
76
77#include <stdlib.h>
78#include "qpaintengine_opengl_p.h"
79
80QT_BEGIN_NAMESPACE
81
82Q_DECL_IMPORT extern QImage qt_imageForBrush(int brushStyle, bool invert); //in qbrush.cpp
83#ifdef QT_MAC_USE_COCOA
84extern void *qt_current_nsopengl_context(); // qgl_mac.mm
85#endif
86
87#define QREAL_MAX 9e100
88#define QREAL_MIN -9e100
89
90extern int qt_next_power_of_two(int v);
91
92#define DISABLE_DEBUG_ONCE
93
94//#define DEBUG_DISPLAY_MASK_TEXTURE
95
96#ifdef DISABLE_DEBUG_ONCE
97#define DEBUG_OVERRIDE(state) ;
98#define DEBUG_ONCE_STR(str) ;
99#define DEBUG_ONCE if (0)
100#else
101static int DEBUG_OVERRIDE_FLAG = 0;
102static bool DEBUG_TEMP_FLAG;
103#define DEBUG_OVERRIDE(state) { state ? ++DEBUG_OVERRIDE_FLAG : --DEBUG_OVERRIDE_FLAG; }
104#define DEBUG_ONCE if ((DEBUG_TEMP_FLAG = DEBUG_OVERRIDE_FLAG) && 0) ; else for (static int DEBUG_ONCE_FLAG = false; !DEBUG_ONCE_FLAG || DEBUG_TEMP_FLAG; DEBUG_ONCE_FLAG = true, DEBUG_TEMP_FLAG = false)
105#define DEBUG_ONCE_STR(str) DEBUG_ONCE qDebug() << (str);
106#endif
107
108#ifdef Q_WS_X11
109static bool qt_nvidiaFboNeedsFinish = false;
110#endif
111
112static inline void qt_glColor4ubv(unsigned char *col)
113{
114 glColor4f(col[0]/255.0f, col[1]/255.0f, col[2]/255.0f, col[3]/255.0f);
115}
116
117struct QT_PointF {
118 qreal x;
119 qreal y;
120};
121
122struct QGLTrapezoid
123{
124 QGLTrapezoid()
125 {}
126
127 QGLTrapezoid(qreal top_, qreal bottom_, qreal topLeftX_, qreal topRightX_, qreal bottomLeftX_, qreal bottomRightX_)
128 : top(top_),
129 bottom(bottom_),
130 topLeftX(topLeftX_),
131 topRightX(topRightX_),
132 bottomLeftX(bottomLeftX_),
133 bottomRightX(bottomRightX_)
134 {}
135
136 const QGLTrapezoid translated(const QPointF &delta) const;
137
138 qreal top;
139 qreal bottom;
140 qreal topLeftX;
141 qreal topRightX;
142 qreal bottomLeftX;
143 qreal bottomRightX;
144};
145
146const QGLTrapezoid QGLTrapezoid::translated(const QPointF &delta) const
147{
148 QGLTrapezoid trap(*this);
149 trap.top += delta.y();
150 trap.bottom += delta.y();
151 trap.topLeftX += delta.x();
152 trap.topRightX += delta.x();
153 trap.bottomLeftX += delta.x();
154 trap.bottomRightX += delta.x();
155 return trap;
156}
157
158
159class QOpenGLImmediateModeTessellator;
160class QGLMaskGenerator;
161class QGLOffscreen;
162
163class QGLMaskTextureCache
164{
165public:
166 void setOffscreenSize(const QSize &offscreenSize);
167 void setDrawableSize(const QSize &drawableSize);
168
169 struct CacheLocation {
170 QRect rect;
171 int channel;
172
173 QRect screen_rect;
174 };
175
176 struct CacheInfo {
177 inline CacheInfo(const QPainterPath &p, const QTransform &m, qreal w = -1) :
178 path(p), matrix(m), stroke_width(w), age(0) {}
179
180 QPainterPath path;
181 QTransform matrix;
182 qreal stroke_width;
183
184 CacheLocation loc;
185
186 int age;
187 };
188
189 struct QuadTreeNode {
190 quint64 key;
191
192 int largest_available_block;
193 int largest_used_block;
194 };
195
196 CacheLocation getMask(QGLMaskGenerator &maskGenerator, QOpenGLPaintEnginePrivate *engine);
197
198 typedef QMultiHash<quint64, CacheInfo> QGLTextureCacheHash;
199
200 enum {block_size = 64};
201
202 // throw out keys that are too old
203 void maintainCache();
204 void clearCache();
205
206private:
207 quint64 hash(const QPainterPath &p, const QTransform &m, qreal w);
208
209 void createMask(quint64 key, CacheInfo &info, QGLMaskGenerator &maskGenerator);
210
211 QSize offscreenSize;
212 QSize drawableSize;
213
214 QGLTextureCacheHash cache;
215
216 QVector<QuadTreeNode> occupied_quadtree[4];
217
218 void quadtreeUpdate(int channel, int node, int current_block_size);
219 void quadtreeAllocate(quint64 key, const QSize &size, QRect *rect, int *channel);
220
221 bool quadtreeFindAvailableLocation(const QSize &size, QRect *rect, int *channel);
222 void quadtreeFindExistingLocation(const QSize &size, QRect *rect, int *channel);
223
224 void quadtreeInsert(int channel, quint64 key, const QRect &rect, int node = 0);
225 void quadtreeClear(int channel, const QRect &rect, int node = 0);
226
227 int quadtreeBlocksize(int node);
228 QPoint quadtreeLocation(int node);
229
230 QOpenGLPaintEnginePrivate *engine;
231};
232
233Q_GLOBAL_STATIC(QGLMaskTextureCache, qt_mask_texture_cache)
234
235class QGLOffscreen : public QObject
236{
237 Q_OBJECT
238public:
239 QGLOffscreen()
240 : QObject(),
241 offscreen(0),
242 ctx(0),
243 mask_dim(0),
244 activated(false),
245 bound(false)
246 {
247 connect(QGLSignalProxy::instance(),
248 SIGNAL(aboutToDestroyContext(const QGLContext*)),
249 SLOT(cleanupGLContextRefs(const QGLContext*)));
250 }
251
252 inline void setDevice(QPaintDevice *pdev);
253
254 void begin();
255 void end();
256
257 inline void bind();
258 inline void release();
259
260 inline bool isBound() const;
261
262 inline QSize drawableSize() const;
263 inline QSize offscreenSize() const;
264
265 inline GLuint offscreenTexture() const;
266
267 QGLContext *context() const;
268
269 static bool isSupported();
270
271 inline void initialize();
272
273 inline bool isValid() const;
274
275public Q_SLOTS:
276 void cleanupGLContextRefs(const QGLContext *context) {
277 if (context == ctx) {
278 delete offscreen;
279 ctx = 0;
280 offscreen = 0;
281 mask_dim = 0;
282 }
283 }
284
285private:
286 QGLPaintDevice* device;
287
288 QGLFramebufferObject *offscreen;
289 QGLContext *ctx;
290
291 // dimensions of mask texture (square)
292 int mask_dim;
293 QSize last_failed_size;
294
295 bool drawable_fbo;
296
297 bool activated;
298 bool initialized;
299
300 bool bound;
301};
302
303inline void QGLOffscreen::setDevice(QPaintDevice *pdev)
304{
305 if (pdev->devType() == QInternal::OpenGL)
306 device = static_cast<QGLPaintDevice*>(pdev);
307 else
308 device = QGLPaintDevice::getDevice(pdev);
309
310 if (!device)
311 return;
312
313 drawable_fbo = (pdev->devType() == QInternal::FramebufferObject);
314}
315
316void QGLOffscreen::begin()
317{
318#ifndef QT_OPENGL_ES
319 initialized = false;
320
321 if (activated)
322 initialize();
323#endif
324}
325
326void QGLOffscreen::initialize()
327{
328#ifndef QT_OPENGL_ES
329 if (initialized)
330 return;
331
332 activated = true;
333 initialized = true;
334
335 int dim = qMax(2048, static_cast<int>(qt_next_power_of_two(qMax(device->size().width(), device->size().height()))));
336
337 bool shared_context = QGLContext::areSharing(device->context(), ctx);
338 bool would_fail = last_failed_size.isValid() &&
339 (device->size().width() >= last_failed_size.width() ||
340 device->size().height() >= last_failed_size.height());
341 bool needs_refresh = dim > mask_dim || !shared_context;
342
343 if (needs_refresh && !would_fail) {
344 DEBUG_ONCE qDebug() << "QGLOffscreen::initialize(): creating offscreen of size" << dim;
345 delete offscreen;
346 offscreen = new QGLFramebufferObject(dim, dim, GLenum(GL_TEXTURE_2D));
347 mask_dim = dim;
348
349 if (!offscreen->isValid()) {
350 qWarning("QGLOffscreen: Invalid offscreen fbo (size %dx%d)", mask_dim, mask_dim);
351 delete offscreen;
352 offscreen = 0;
353 mask_dim = 0;
354 last_failed_size = device->size();
355 }
356 }
357
358 qt_mask_texture_cache()->setOffscreenSize(offscreenSize());
359 qt_mask_texture_cache()->setDrawableSize(device->size());
360 ctx = device->context();
361#endif
362}
363
364inline bool QGLOffscreen::isValid() const
365{
366 return offscreen;
367}
368
369void QGLOffscreen::end()
370{
371 if (bound)
372 release();
373#ifdef DEBUG_DISPLAY_MASK_TEXTURE
374 glReadBuffer(GL_BACK);
375 glDrawBuffer(GL_BACK);
376 glMatrixMode(GL_MODELVIEW);
377 glLoadIdentity();
378 glColor4f(1, 1, 1, 1);
379 glDisable(GL_DEPTH_TEST);
380 glBlendFunc(GL_ONE, GL_ZERO);
381 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
382 glEnable(GL_TEXTURE_2D);
383 glBindTexture(GL_TEXTURE_2D, offscreen->texture());
384
385 glBegin(GL_QUADS);
386 glTexCoord2f(0.0, 1.0); glVertex2f(0.0, 0.0);
387 glTexCoord2f(1.0, 1.0); glVertex2f(drawable.size().width(), 0.0);
388 glTexCoord2f(1.0, 0.0); glVertex2f(drawable.size().width(), drawable.size().height());
389 glTexCoord2f(0.0, 0.0); glVertex2f(0.0, drawable.size().height());
390 glEnd();
391
392 glBindTexture(GL_TEXTURE_2D, 0);
393 glDisable(GL_TEXTURE_2D);
394#endif
395}
396
397inline void QGLOffscreen::bind()
398{
399#ifndef QT_OPENGL_ES
400 Q_ASSERT(initialized);
401
402 if (!offscreen || bound)
403 return;
404
405 DEBUG_ONCE qDebug() << "QGLOffscreen: binding offscreen";
406 offscreen->bind();
407
408 bound = true;
409
410 glViewport(0, 0, offscreenSize().width(), offscreenSize().height());
411
412 glMatrixMode(GL_PROJECTION);
413 glLoadIdentity();
414 glOrtho(0, offscreenSize().width(), offscreenSize().height(), 0, -999999, 999999);
415 glMatrixMode(GL_MODELVIEW);
416#endif
417}
418
419inline void QGLOffscreen::release()
420{
421#ifndef QT_OPENGL_ES
422 if (!offscreen || !bound)
423 return;
424
425#ifdef Q_WS_X11
426 // workaround for bug in nvidia driver versions 9x.xx
427 if (qt_nvidiaFboNeedsFinish)
428 glFinish();
429#endif
430
431 DEBUG_ONCE_STR("QGLOffscreen: releasing offscreen");
432
433 if (drawable_fbo)
434 device->ensureActiveTarget(); //###
435 else
436 offscreen->release();
437
438 QSize sz(device->size());
439 glViewport(0, 0, sz.width(), sz.height());
440
441 glMatrixMode(GL_PROJECTION);
442 glLoadIdentity();
443#ifndef QT_OPENGL_ES
444 glOrtho(0, sz.width(), sz.height(), 0, -999999, 999999);
445#else
446 glOrthof(0, sz.width(), sz.height(), 0, -999999, 999999);
447#endif
448 glMatrixMode(GL_MODELVIEW);
449
450 bound = false;
451#endif
452}
453
454inline bool QGLOffscreen::isBound() const
455{
456 return bound;
457}
458
459inline QSize QGLOffscreen::drawableSize() const
460{
461 return device->size();
462}
463
464inline QSize QGLOffscreen::offscreenSize() const
465{
466 return QSize(mask_dim, mask_dim);
467}
468
469inline GLuint QGLOffscreen::offscreenTexture() const
470{
471 return offscreen ? offscreen->texture() : 0;
472}
473
474inline QGLContext *QGLOffscreen::context() const
475{
476 return ctx;
477}
478
479bool QGLOffscreen::isSupported()
480{
481 return (QGLExtensions::glExtensions() & QGLExtensions::FramebufferObject); // for fbo
482}
483
484struct QDrawQueueItem
485{
486 QDrawQueueItem(qreal _opacity,
487 QBrush _brush,
488 const QPointF &_brush_origion,
489 QPainter::CompositionMode _composition_mode,
490 const QTransform &_matrix,
491 QGLMaskTextureCache::CacheLocation _location)
492 : opacity(_opacity),
493 brush(_brush),
494 brush_origin(_brush_origion),
495 composition_mode(_composition_mode),
496 matrix(_matrix),
497 location(_location) {}
498 qreal opacity;
499 QBrush brush;
500 QPointF brush_origin;
501 QPainter::CompositionMode composition_mode;
502
503 QTransform matrix;
504 QGLMaskTextureCache::CacheLocation location;
505};
506
507////////// GL program cache: start
508
509struct GLProgram {
510 int brush; // brush index or mask index
511 int mode; // composition mode index
512 bool mask;
513 GLuint program;
514};
515
516typedef QMultiHash<const QGLContext *, GLProgram> QGLProgramHash;
517
518class QGLProgramCache : public QObject
519{
520 Q_OBJECT
521public:
522 QGLProgramCache() {
523 // we have to know when a context is deleted so we can free
524 // any program handles it holds
525 connect(QGLSignalProxy::instance(), SIGNAL(aboutToDestroyContext(const QGLContext*)),
526 SLOT(cleanupPrograms(const QGLContext*)));
527
528 }
529 ~QGLProgramCache() {
530 // at this point the cache should contain 0 elements
531 // Q_ASSERT(program.size() == 0);
532 }
533
534 GLuint getProgram(const QGLContext *ctx, int brush, int mode, bool mask_mode)
535 {
536 // 1. see if we have an entry for the ctx context
537 QList<GLProgram> progs = programs.values(ctx);
538 for (int i=0; i<progs.size(); ++i) {
539 const GLProgram &prg = progs.at(i);
540 if (mask_mode) {
541 if (prg.mask && prg.brush == brush)
542 return prg.program;
543 } else {
544 if (!prg.mask && prg.brush == brush && prg.mode == mode)
545 return prg.program;
546 }
547 }
548
549 // 2. try to find a match in a shared context, and update the
550 // hash with the entry found
551 QList<const QGLContext *> contexts = programs.uniqueKeys();
552 for (int i=0; i<contexts.size(); ++i) {
553 const QGLContext *cx = contexts.at(i);
554 if (cx != ctx && QGLContext::areSharing(cx, ctx)) {
555 QList<GLProgram> progs = programs.values(cx);
556 for (int k=0; k<progs.size(); ++k) {
557 const GLProgram &prg = progs.at(k);
558 if (mask_mode) {
559 if (prg.mask && prg.brush == brush) {
560 programs.insert(ctx, prg);
561 return prg.program;
562 }
563 } else {
564 if (!prg.mask && prg.brush == brush && prg.mode == mode) {
565 programs.insert(ctx, prg);
566 return prg.program;
567 }
568 }
569 }
570 }
571 }
572
573 // 3. compile a new program and place it into the cache
574 // NB! assumes ctx is the current GL context
575 GLProgram prg;
576 prg.brush = brush;
577 prg.mode = mode;
578 prg.mask = mask_mode;
579 glGenProgramsARB(1, &prg.program);
580 glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, prg.program);
581 const char *src = mask_mode
582 ? mask_fragment_program_sources[brush]
583 : painter_fragment_program_sources[brush][mode];
584 // necessary for .NET 2002, apparently
585 const GLbyte *gl_src = reinterpret_cast<const GLbyte *>(src);
586
587 while (glGetError() != GL_NO_ERROR) {} // reset error state
588 glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB,
589 int(strlen(src)), gl_src);
590 if (glGetError() != GL_NO_ERROR) {
591// qDebug() << "QGLProgramCache: Unable to compile fragment program.";
592 glDeleteProgramsARB(1, &prg.program);
593 return 0;
594 }
595
596// qDebug() << "QGLProgramCache: Creating GL program:" << prg.program << hex << ctx;
597 programs.insert(ctx, prg);
598 return prg.program;
599 }
600
601public Q_SLOTS:
602 void cleanupPrograms(const QGLContext *context)
603 {
604 QGLProgramHash::iterator it = programs.begin();
605 while (it != programs.end()) {
606 if (it.key() == context) {
607 if (!context->isSharing()) {
608 // the ctx variable below is needed for the glDeleteProgramARB call
609 // since it is resolved from our extension system
610 // NB! assumes context is the current GL context
611 const QGLContext *ctx = context;
612 // qDebug() << "QGLProgramHash: Deleting GL program:" << it.value().program << hex << it.key();
613 glDeleteProgramsARB(1, &it.value().program);
614 }
615 it = programs.erase(it);
616 } else {
617 ++it;
618 }
619 }
620 }
621
622private:
623 QGLProgramHash programs;
624};
625
626Q_GLOBAL_STATIC(QGLProgramCache, qt_gl_program_cache)
627
628////////// GL program cache: end
629
630class QOpenGLPaintEnginePrivate;
631class QGLPrivateCleanup : public QObject
632{
633 Q_OBJECT
634public:
635 QGLPrivateCleanup(QOpenGLPaintEnginePrivate *priv)
636 : p(priv)
637 {
638 connect(QGLSignalProxy::instance(),
639 SIGNAL(aboutToDestroyContext(const QGLContext*)),
640 SLOT(cleanupGLContextRefs(const QGLContext*)));
641 }
642
643public Q_SLOTS:
644 void cleanupGLContextRefs(const QGLContext *context);
645
646private:
647 QOpenGLPaintEnginePrivate *p;
648};
649
650class QOpenGLPaintEnginePrivate : public QPaintEngineExPrivate
651{
652 Q_DECLARE_PUBLIC(QOpenGLPaintEngine)
653public:
654 QOpenGLPaintEnginePrivate()
655 : opacity(1)
656 , composition_mode(QPainter::CompositionMode_SourceOver)
657 , has_fast_pen(false)
658 , use_stencil_method(false)
659 , dirty_drawable_texture(false)
660 , has_stencil_face_ext(false)
661 , use_fragment_programs(false)
662 , high_quality_antialiasing(false)
663 , use_smooth_pixmap_transform(false)
664 , use_emulation(false)
665 , txop(QTransform::TxNone)
666 , inverseScale(1)
667 , moveToCount(0)
668 , last_created_state(0)
669 , shader_ctx(0)
670 , grad_palette(0)
671 , tess_points(0)
672 , drawable_texture(0)
673 , ref_cleaner(this)
674 {}
675
676 inline void setGLPen(const QColor &c) {
677 uint alpha = qRound(c.alpha() * opacity);
678 pen_color[0] = qt_div_255(c.red() * alpha);
679 pen_color[1] = qt_div_255(c.green() * alpha);
680 pen_color[2] = qt_div_255(c.blue() * alpha);
681 pen_color[3] = alpha;
682 }
683
684 inline void setGLBrush(const QColor &c) {
685 uint alpha = qRound(c.alpha() * opacity);
686 brush_color[0] = qt_div_255(c.red() * alpha);
687 brush_color[1] = qt_div_255(c.green() * alpha);
688 brush_color[2] = qt_div_255(c.blue() * alpha);
689 brush_color[3] = alpha;
690 }
691
692 inline void setGradientOps(const QBrush &brush, const QRectF &bounds);
693 void createGradientPaletteTexture(const QGradient& g);
694
695 void updateGradient(const QBrush &brush, const QRectF &bounds);
696
697 inline void lineToStencil(qreal x, qreal y);
698 inline void curveToStencil(const QPointF &cp1, const QPointF &cp2, const QPointF &ep);
699 void pathToVertexArrays(const QPainterPath &path);
700 void fillVertexArray(Qt::FillRule fillRule);
701 void drawVertexArrays();
702 void fillPath(const QPainterPath &path);
703 void fillPolygon_dev(const QPointF *polygonPoints, int pointCount,
704 Qt::FillRule fill);
705
706 void drawFastRect(const QRectF &rect);
707 void strokePath(const QPainterPath &path, bool use_cache);
708 void strokePathFastPen(const QPainterPath &path, bool needsResolving);
709 void strokeLines(const QPainterPath &path);
710
711 void updateDepthClip();
712 void systemStateChanged();
713
714 void cleanupGLContextRefs(const QGLContext *context) {
715 if (context == shader_ctx)
716 shader_ctx = 0;
717 }
718
719 inline void updateFastPen() {
720 qreal pen_width = cpen.widthF();
721 has_fast_pen =
722 ((pen_width == 0 || (pen_width <= 1 && matrix.type() <= QTransform::TxTranslate))
723 || cpen.isCosmetic())
724 && cpen.style() == Qt::SolidLine
725 && cpen.isSolid();
726
727 }
728
729 void disableClipping();
730 void enableClipping();
731 void ensureDrawableTexture();
732
733 QPen cpen;
734 QBrush cbrush;
735 Qt::BrushStyle brush_style;
736 QPointF brush_origin;
737 Qt::BrushStyle pen_brush_style;
738 qreal opacity;
739 QPainter::CompositionMode composition_mode;
740
741 Qt::BrushStyle current_style;
742
743 uint has_pen : 1;
744 uint has_brush : 1;
745 uint has_fast_pen : 1;
746 uint use_stencil_method : 1;
747 uint dirty_drawable_texture : 1;
748 uint has_stencil_face_ext : 1;
749 uint use_fragment_programs : 1;
750 uint high_quality_antialiasing : 1;
751 uint has_antialiasing : 1;
752 uint has_fast_composition_mode : 1;
753 uint use_smooth_pixmap_transform : 1;
754 uint use_system_clip : 1;
755 uint use_emulation : 1;
756
757 QRegion dirty_stencil;
758
759 void updateUseEmulation();
760
761 QTransform matrix;
762 GLubyte pen_color[4];
763 GLubyte brush_color[4];
764 QTransform::TransformationType txop;
765 QGLPaintDevice* device;
766 QGLOffscreen offscreen;
767
768 qreal inverseScale;
769
770 int moveToCount;
771 QPointF path_start;
772
773 bool isFastRect(const QRectF &r);
774
775 void drawImageAsPath(const QRectF &r, const QImage &img, const QRectF &sr);
776 void drawTiledImageAsPath(const QRectF &r, const QImage &img, qreal sx, qreal sy, const QPointF &offset);
777
778 void drawOffscreenPath(const QPainterPath &path);
779
780 void composite(const QRectF &rect, const QPoint &maskOffset = QPoint());
781 void composite(GLuint primitive, const GLfloat *vertexArray, int vertexCount, const QPoint &maskOffset = QPoint());
782
783 bool createFragmentPrograms();
784 void deleteFragmentPrograms();
785 void updateFragmentProgramData(int locations[]);
786
787 void cacheItemErased(int channel, const QRect &rect);
788
789 void addItem(const QGLMaskTextureCache::CacheLocation &location);
790 void drawItem(const QDrawQueueItem &item);
791 void flushDrawQueue();
792
793 void copyDrawable(const QRectF &rect);
794
795 void updateGLMatrix() const;
796
797 mutable QPainterState *last_created_state;
798
799 QGLContext *shader_ctx;
800 GLuint grad_palette;
801
802 GLuint painter_fragment_programs[num_fragment_brushes][num_fragment_composition_modes];
803 GLuint mask_fragment_programs[num_fragment_masks];
804
805 float inv_matrix_data[3][4];
806 float fmp_data[4];
807 float fmp2_m_radius2_data[4];
808 float angle_data[4];
809 float linear_data[4];
810
811 float porterduff_ab_data[4];
812 float porterduff_xyz_data[4];
813
814 float mask_offset_data[4];
815 float mask_channel_data[4];
816
817 FragmentBrushType fragment_brush;
818 FragmentCompositionModeType fragment_composition_mode;
819
820 void setPorterDuffData(float a, float b, float x, float y, float z);
821 void setInvMatrixData(const QTransform &inv_matrix);
822
823 qreal max_x;
824 qreal max_y;
825 qreal min_x;
826 qreal min_y;
827
828 QDataBuffer<QPointF> tess_points;
829 QVector<int> tess_points_stops;
830
831 GLdouble projection_matrix[4][4];
832
833#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_2)
834 GLfloat mv_matrix[4][4];
835#else
836 GLdouble mv_matrix[4][4];
837#endif
838
839 QList<QDrawQueueItem> drawQueue;
840
841 GLuint drawable_texture;
842 QSize drawable_texture_size;
843
844 int max_texture_size;
845
846 QGLPrivateCleanup ref_cleaner;
847 friend class QGLMaskTextureCache;
848};
849
850class QOpenGLCoordinateOffset
851{
852public:
853 QOpenGLCoordinateOffset(QOpenGLPaintEnginePrivate *d);
854 ~QOpenGLCoordinateOffset();
855
856 static void enableOffset(QOpenGLPaintEnginePrivate *d);
857 static void disableOffset(QOpenGLPaintEnginePrivate *d);
858
859private:
860 QOpenGLPaintEnginePrivate *d;
861};
862
863QOpenGLCoordinateOffset::QOpenGLCoordinateOffset(QOpenGLPaintEnginePrivate *d_)
864 : d(d_)
865{
866 enableOffset(d);
867}
868
869void QOpenGLCoordinateOffset::enableOffset(QOpenGLPaintEnginePrivate *d)
870{
871 if (!d->has_antialiasing) {
872 glMatrixMode(GL_MODELVIEW);
873 glPushMatrix();
874 d->mv_matrix[3][0] += 0.5;
875 d->mv_matrix[3][1] += 0.5;
876 d->updateGLMatrix();
877 }
878}
879
880QOpenGLCoordinateOffset::~QOpenGLCoordinateOffset()
881{
882 disableOffset(d);
883}
884
885void QOpenGLCoordinateOffset::disableOffset(QOpenGLPaintEnginePrivate *d)
886{
887 if (!d->has_antialiasing) {
888 glMatrixMode(GL_MODELVIEW);
889 glPopMatrix();
890 d->mv_matrix[3][0] -= 0.5;
891 d->mv_matrix[3][1] -= 0.5;
892 }
893}
894
895void QGLPrivateCleanup::cleanupGLContextRefs(const QGLContext *context)
896{
897 p->cleanupGLContextRefs(context);
898}
899
900
901static inline void updateTextureFilter(GLenum target, GLenum wrapMode, bool smoothPixmapTransform)
902{
903 if (smoothPixmapTransform) {
904 glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
905 glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
906 } else {
907 glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
908 glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
909 }
910 glTexParameterf(target, GL_TEXTURE_WRAP_S, wrapMode);
911 glTexParameterf(target, GL_TEXTURE_WRAP_T, wrapMode);
912}
913
914static inline QPainterPath strokeForPath(const QPainterPath &path, const QPen &cpen) {
915 QPainterPathStroker stroker;
916 if (cpen.style() == Qt::CustomDashLine)
917 stroker.setDashPattern(cpen.dashPattern());
918 else
919 stroker.setDashPattern(cpen.style());
920
921 stroker.setCapStyle(cpen.capStyle());
922 stroker.setJoinStyle(cpen.joinStyle());
923 stroker.setMiterLimit(cpen.miterLimit());
924 stroker.setDashOffset(cpen.dashOffset());
925
926 qreal width = cpen.widthF();
927 if (width == 0)
928 stroker.setWidth(1);
929 else
930 stroker.setWidth(width);
931
932 QPainterPath stroke = stroker.createStroke(path);
933 stroke.setFillRule(Qt::WindingFill);
934 return stroke;
935}
936
937class QGLStrokeCache
938{
939 struct CacheInfo
940 {
941 inline CacheInfo(QPainterPath p, QPainterPath sp, QPen stroke_pen) :
942 path(p), stroked_path(sp), pen(stroke_pen) {}
943 QPainterPath path;
944 QPainterPath stroked_path;
945 QPen pen;
946 };
947
948 typedef QMultiHash<quint64, CacheInfo> QGLStrokeTableHash;
949
950public:
951 inline QPainterPath getStrokedPath(const QPainterPath &path, const QPen &pen) {
952 quint64 hash_val = 0;
953
954 for (int i = 0; i < path.elementCount() && i <= 2; i++) {
955 hash_val += quint64(path.elementAt(i).x);
956 hash_val += quint64(path.elementAt(i).y);
957 }
958
959 QGLStrokeTableHash::const_iterator it = cache.constFind(hash_val);
960
961 if (it == cache.constEnd())
962 return addCacheElement(hash_val, path, pen);
963 else {
964 do {
965 const CacheInfo &cache_info = it.value();
966 if (cache_info.path == path && cache_info.pen == pen)
967 return cache_info.stroked_path;
968 ++it;
969 } while (it != cache.constEnd() && it.key() == hash_val);
970 // an exact match for this path was not found, create new cache element
971 return addCacheElement(hash_val, path, pen);
972 }
973 }
974
975protected:
976 inline int maxCacheSize() const { return 500; }
977 QPainterPath addCacheElement(quint64 hash_val, QPainterPath path, const QPen &pen) {
978 if (cache.size() == maxCacheSize()) {
979 int elem_to_remove = qrand() % maxCacheSize();
980 cache.remove(cache.keys()[elem_to_remove]); // may remove more than 1, but OK
981 }
982 QPainterPath stroke = strokeForPath(path, pen);
983 CacheInfo cache_entry(path, stroke, pen);
984 return cache.insert(hash_val, cache_entry).value().stroked_path;
985 }
986
987 QGLStrokeTableHash cache;
988};
989
990Q_GLOBAL_STATIC(QGLStrokeCache, qt_opengl_stroke_cache)
991
992class QGLGradientCache : public QObject
993{
994 Q_OBJECT
995 struct CacheInfo
996 {
997 inline CacheInfo(QGradientStops s, qreal op, QGradient::InterpolationMode mode) :
998 stops(s), opacity(op), interpolationMode(mode) {}
999
1000 GLuint texId;
1001 QGradientStops stops;
1002 qreal opacity;
1003 QGradient::InterpolationMode interpolationMode;
1004 };
1005
1006 typedef QMultiHash<quint64, CacheInfo> QGLGradientColorTableHash;
1007
1008public:
1009 QGLGradientCache() : QObject(), buffer_ctx(0)
1010 {
1011 connect(QGLSignalProxy::instance(),
1012 SIGNAL(aboutToDestroyContext(const QGLContext*)),
1013 SLOT(cleanupGLContextRefs(const QGLContext*)));
1014 }
1015
1016 inline GLuint getBuffer(const QGradient &gradient, qreal opacity, QGLContext *ctx) {
1017 if (buffer_ctx && !QGLContext::areSharing(buffer_ctx, ctx))
1018 cleanCache();
1019
1020 buffer_ctx = ctx;
1021
1022 quint64 hash_val = 0;
1023
1024 QGradientStops stops = gradient.stops();
1025 for (int i = 0; i < stops.size() && i <= 2; i++)
1026 hash_val += stops[i].second.rgba();
1027
1028 QGLGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
1029
1030 if (it == cache.constEnd())
1031 return addCacheElement(hash_val, gradient, opacity);
1032 else {
1033 do {
1034 const CacheInfo &cache_info = it.value();
1035 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode()) {
1036 return cache_info.texId;
1037 }
1038 ++it;
1039 } while (it != cache.constEnd() && it.key() == hash_val);
1040 // an exact match for these stops and opacity was not found, create new cache
1041 return addCacheElement(hash_val, gradient, opacity);
1042 }
1043 }
1044
1045 inline int paletteSize() const { return 1024; }
1046
1047protected:
1048 inline int maxCacheSize() const { return 60; }
1049 inline void generateGradientColorTable(const QGradient& g,
1050 uint *colorTable,
1051 int size, qreal opacity) const;
1052 GLuint addCacheElement(quint64 hash_val, const QGradient &gradient, qreal opacity) {
1053 if (cache.size() == maxCacheSize()) {
1054 int elem_to_remove = qrand() % maxCacheSize();
1055 quint64 key = cache.keys()[elem_to_remove];
1056
1057 // need to call glDeleteTextures on each removed cache entry:
1058 QGLGradientColorTableHash::const_iterator it = cache.constFind(key);
1059 do {
1060 glDeleteTextures(1, &it.value().texId);
1061 } while (++it != cache.constEnd() && it.key() == key);
1062 cache.remove(key); // may remove more than 1, but OK
1063 }
1064 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
1065 uint buffer[1024];
1066 generateGradientColorTable(gradient, buffer, paletteSize(), opacity);
1067 glGenTextures(1, &cache_entry.texId);
1068#ifndef QT_OPENGL_ES
1069 glBindTexture(GL_TEXTURE_1D, cache_entry.texId);
1070 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, paletteSize(),
1071 0, GL_BGRA, GL_UNSIGNED_BYTE, buffer);
1072#else
1073 // create 2D one-line texture instead. This requires an impl of manual GL_TEXGEN for all primitives
1074#endif
1075 return cache.insert(hash_val, cache_entry).value().texId;
1076 }
1077
1078 void cleanCache() {
1079 QGLShareContextScope scope(buffer_ctx);
1080 QGLGradientColorTableHash::const_iterator it = cache.constBegin();
1081 for (; it != cache.constEnd(); ++it) {
1082 const CacheInfo &cache_info = it.value();
1083 glDeleteTextures(1, &cache_info.texId);
1084 }
1085 cache.clear();
1086 }
1087
1088 QGLGradientColorTableHash cache;
1089
1090 QGLContext *buffer_ctx;
1091
1092public Q_SLOTS:
1093 void cleanupGLContextRefs(const QGLContext *context) {
1094 if (context == buffer_ctx) {
1095 cleanCache();
1096 buffer_ctx = 0;
1097 }
1098 }
1099};
1100
1101static inline uint endianColor(uint c)
1102{
1103#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
1104 return c;
1105#else
1106 return ( (c << 24) & 0xff000000)
1107 | ((c >> 24) & 0x000000ff)
1108 | ((c << 8) & 0x00ff0000)
1109 | ((c >> 8) & 0x0000ff00);
1110#endif // Q_BYTE_ORDER
1111}
1112
1113void QGLGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, qreal opacity) const
1114{
1115 int pos = 0;
1116 QGradientStops s = gradient.stops();
1117 QVector<uint> colors(s.size());
1118
1119 for (int i = 0; i < s.size(); ++i)
1120 colors[i] = s[i].second.rgba();
1121
1122 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
1123
1124 uint alpha = qRound(opacity * 256);
1125 uint current_color = ARGB_COMBINE_ALPHA(colors[0], alpha);
1126 qreal incr = 1.0 / qreal(size);
1127 qreal fpos = 1.5 * incr;
1128 colorTable[pos++] = endianColor(PREMUL(current_color));
1129
1130 while (fpos <= s.first().first) {
1131 colorTable[pos] = colorTable[pos - 1];
1132 pos++;
1133 fpos += incr;
1134 }
1135
1136 if (colorInterpolation)
1137 current_color = PREMUL(current_color);
1138
1139 for (int i = 0; i < s.size() - 1; ++i) {
1140 qreal delta = 1/(s[i+1].first - s[i].first);
1141 uint next_color = ARGB_COMBINE_ALPHA(colors[i+1], alpha);
1142 if (colorInterpolation)
1143 next_color = PREMUL(next_color);
1144
1145 while (fpos < s[i+1].first && pos < size) {
1146 int dist = int(256 * ((fpos - s[i].first) * delta));
1147 int idist = 256 - dist;
1148 if (colorInterpolation)
1149 colorTable[pos] = endianColor(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
1150 else
1151 colorTable[pos] = endianColor(PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist)));
1152 ++pos;
1153 fpos += incr;
1154 }
1155 current_color = next_color;
1156 }
1157
1158 Q_ASSERT(s.size() > 0);
1159
1160 uint last_color = endianColor(PREMUL(ARGB_COMBINE_ALPHA(colors[s.size() - 1], alpha)));
1161 for (;pos < size; ++pos)
1162 colorTable[pos] = last_color;
1163
1164 // Make sure the last color stop is represented at the end of the table
1165 colorTable[size-1] = last_color;
1166}
1167
1168#ifndef Q_WS_QWS
1169Q_GLOBAL_STATIC(QGLGradientCache, qt_opengl_gradient_cache)
1170#endif
1171
1172void QOpenGLPaintEnginePrivate::createGradientPaletteTexture(const QGradient& g)
1173{
1174#ifdef QT_OPENGL_ES //###
1175 Q_UNUSED(g);
1176#else
1177 GLuint texId = qt_opengl_gradient_cache()->getBuffer(g, opacity, device->context());
1178 glBindTexture(GL_TEXTURE_1D, texId);
1179 grad_palette = texId;
1180 if (g.spread() == QGradient::RepeatSpread || g.type() == QGradient::ConicalGradient)
1181 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
1182 else if (g.spread() == QGradient::ReflectSpread)
1183 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT_IBM);
1184 else
1185 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1186
1187 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1188 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1189
1190#endif
1191}
1192
1193
1194inline void QOpenGLPaintEnginePrivate::setGradientOps(const QBrush &brush, const QRectF &bounds)
1195{
1196 current_style = brush.style();
1197
1198 if (current_style < Qt::LinearGradientPattern || current_style > Qt::ConicalGradientPattern) {
1199 setGLBrush(brush.color());
1200 qt_glColor4ubv(brush_color);
1201 }
1202
1203 updateGradient(brush, bounds);
1204
1205#ifndef QT_OPENGL_ES //### GLES does not have GL_TEXTURE_GEN_ so we are falling back for gradients
1206 glDisable(GL_TEXTURE_GEN_S);
1207 glDisable(GL_TEXTURE_1D);
1208
1209 if (current_style == Qt::LinearGradientPattern) {
1210 if (high_quality_antialiasing || !has_fast_composition_mode) {
1211 fragment_brush = FRAGMENT_PROGRAM_BRUSH_LINEAR;
1212 } else {
1213 glEnable(GL_TEXTURE_GEN_S);
1214 glEnable(GL_TEXTURE_1D);
1215 }
1216 } else {
1217 if (use_fragment_programs) {
1218 if (current_style == Qt::RadialGradientPattern)
1219 fragment_brush = FRAGMENT_PROGRAM_BRUSH_RADIAL;
1220 else if (current_style == Qt::ConicalGradientPattern)
1221 fragment_brush = FRAGMENT_PROGRAM_BRUSH_CONICAL;
1222 else if (current_style == Qt::SolidPattern)
1223 fragment_brush = FRAGMENT_PROGRAM_BRUSH_SOLID;
1224 else if (current_style == Qt::TexturePattern && !brush.texture().isQBitmap())
1225 fragment_brush = FRAGMENT_PROGRAM_BRUSH_TEXTURE;
1226 else
1227 fragment_brush = FRAGMENT_PROGRAM_BRUSH_PATTERN;
1228 }
1229 }
1230#endif
1231}
1232
1233QOpenGLPaintEngine::QOpenGLPaintEngine()
1234 : QPaintEngineEx(*(new QOpenGLPaintEnginePrivate))
1235{
1236}
1237
1238QOpenGLPaintEngine::~QOpenGLPaintEngine()
1239{
1240}
1241
1242bool QOpenGLPaintEngine::begin(QPaintDevice *pdev)
1243{
1244 Q_D(QOpenGLPaintEngine);
1245
1246 if (pdev->devType() == QInternal::OpenGL)
1247 d->device = static_cast<QGLPaintDevice*>(pdev);
1248 else
1249 d->device = QGLPaintDevice::getDevice(pdev);
1250
1251 if (!d->device)
1252 return false;
1253
1254 d->offscreen.setDevice(pdev);
1255 d->has_fast_pen = false;
1256 d->inverseScale = 1;
1257 d->opacity = 1;
1258 d->device->beginPaint();
1259 d->matrix = QTransform();
1260 d->has_antialiasing = false;
1261 d->high_quality_antialiasing = false;
1262
1263 QSize sz(d->device->size());
1264 d->dirty_stencil = QRect(0, 0, sz.width(), sz.height());
1265
1266 d->use_emulation = false;
1267
1268 for (int i = 0; i < 4; ++i)
1269 for (int j = 0; j < 4; ++j)
1270 d->mv_matrix[i][j] = (i == j ? qreal(1) : qreal(0));
1271
1272 bool has_frag_program = (QGLExtensions::glExtensions() & QGLExtensions::FragmentProgram)
1273 && (pdev->devType() != QInternal::Pixmap);
1274
1275 QGLContext *ctx = const_cast<QGLContext *>(d->device->context());
1276 if (!ctx) {
1277 qWarning() << "QOpenGLPaintEngine: paint device doesn't have a valid GL context.";
1278 return false;
1279 }
1280
1281 if (has_frag_program)
1282 has_frag_program = qt_resolve_frag_program_extensions(ctx) && qt_resolve_version_1_3_functions(ctx);
1283
1284 d->use_stencil_method = d->device->format().stencil()
1285 && (QGLExtensions::glExtensions() & QGLExtensions::StencilWrap);
1286 if (d->device->format().directRendering()
1287 && (d->use_stencil_method && QGLExtensions::glExtensions() & QGLExtensions::StencilTwoSide))
1288 d->has_stencil_face_ext = qt_resolve_stencil_face_extension(ctx);
1289
1290#ifdef Q_WS_X11
1291 static bool nvidia_workaround_needs_init = true;
1292 if (nvidia_workaround_needs_init) {
1293 // nvidia 9x.xx unix drivers contain a bug which requires us to
1294 // call glFinish before releasing an fbo to avoid painting
1295 // artifacts
1296 const QByteArray versionString(reinterpret_cast<const char*>(glGetString(GL_VERSION)));
1297 const int pos = versionString.indexOf("NVIDIA");
1298 if (pos >= 0) {
1299 const float nvidiaDriverVersion = versionString.mid(pos + strlen("NVIDIA")).toFloat();
1300 qt_nvidiaFboNeedsFinish = nvidiaDriverVersion >= 90.0 && nvidiaDriverVersion < 100.0;
1301 }
1302 nvidia_workaround_needs_init = false;
1303 }
1304#endif
1305
1306#ifndef QT_OPENGL_ES
1307 if (!ctx->d_ptr->internal_context) {
1308 glGetDoublev(GL_PROJECTION_MATRIX, &d->projection_matrix[0][0]);
1309 glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
1310 glPushAttrib(GL_ALL_ATTRIB_BITS);
1311
1312 glDisableClientState(GL_EDGE_FLAG_ARRAY);
1313 glDisableClientState(GL_INDEX_ARRAY);
1314 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1315 glDisable(GL_TEXTURE_1D);
1316 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1317 glPixelTransferi(GL_MAP_COLOR, false);
1318 glPixelTransferi(GL_MAP_STENCIL, false);
1319 glDisable(GL_TEXTURE_GEN_S);
1320
1321 glPixelStorei(GL_PACK_SWAP_BYTES, false);
1322 glPixelStorei(GL_PACK_LSB_FIRST, false);
1323 glPixelStorei(GL_PACK_ROW_LENGTH, 0);
1324 glPixelStorei(GL_PACK_SKIP_ROWS, 0);
1325 glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
1326 glPixelStorei(GL_PACK_ALIGNMENT, 4);
1327
1328 glPixelStorei(GL_UNPACK_SWAP_BYTES, false);
1329 glPixelStorei(GL_UNPACK_LSB_FIRST, false);
1330 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1331 glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
1332 glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
1333 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
1334
1335 if (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_2) {
1336 glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0);
1337 glPixelStorei(GL_PACK_SKIP_IMAGES, 0);
1338 glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
1339 glPixelStorei(GL_UNPACK_SKIP_IMAGES, 0);
1340 }
1341 }
1342#endif
1343
1344 if (!ctx->d_ptr->internal_context) {
1345 glMatrixMode(GL_MODELVIEW);
1346 glPushMatrix();
1347 glMatrixMode(GL_TEXTURE);
1348 glPushMatrix();
1349 glLoadIdentity();
1350 glDisableClientState(GL_COLOR_ARRAY);
1351 glDisableClientState(GL_NORMAL_ARRAY);
1352 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1353 glDisableClientState(GL_VERTEX_ARRAY);
1354
1355 if (QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers)
1356 glDisable(GL_MULTISAMPLE);
1357 glDisable(GL_TEXTURE_2D);
1358 if (QGLExtensions::glExtensions() & QGLExtensions::TextureRectangle)
1359 glDisable(GL_TEXTURE_RECTANGLE_NV);
1360 glDisable(GL_STENCIL_TEST);
1361 glDisable(GL_CULL_FACE);
1362 glDisable(GL_LIGHTING);
1363 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1364 }
1365
1366 d->offscreen.begin();
1367
1368 glViewport(0, 0, sz.width(), sz.height()); // XXX (Embedded): We need a solution for GLWidgets that draw in a part or a bigger surface...
1369 glMatrixMode(GL_PROJECTION);
1370 glLoadIdentity();
1371#ifdef QT_OPENGL_ES
1372 glOrthof(0, sz.width(), sz.height(), 0, -999999, 999999);
1373#else
1374 glOrtho(0, sz.width(), sz.height(), 0, -999999, 999999);
1375#endif
1376 glMatrixMode(GL_MODELVIEW);
1377 glLoadIdentity();
1378 glEnable(GL_BLEND);
1379 d->composition_mode = QPainter::CompositionMode_SourceOver;
1380
1381#ifdef QT_OPENGL_ES
1382 d->max_texture_size = ctx->d_func()->maxTextureSize();
1383#else
1384 bool shared_ctx = QGLContext::areSharing(d->device->context(), d->shader_ctx);
1385
1386 if (shared_ctx) {
1387 d->max_texture_size = d->shader_ctx->d_func()->maxTextureSize();
1388 } else {
1389 d->max_texture_size = ctx->d_func()->maxTextureSize();
1390
1391 if (d->shader_ctx) {
1392 d->shader_ctx->makeCurrent();
1393 glBindTexture(GL_TEXTURE_1D, 0);
1394 glDeleteTextures(1, &d->grad_palette);
1395
1396 if (has_frag_program && d->use_fragment_programs)
1397 glDeleteTextures(1, &d->drawable_texture);
1398 ctx->makeCurrent();
1399 }
1400 d->shader_ctx = d->device->context();
1401 glGenTextures(1, &d->grad_palette);
1402
1403 qt_mask_texture_cache()->clearCache();
1404 d->use_fragment_programs = has_frag_program;
1405 }
1406
1407 if (d->use_fragment_programs && (!shared_ctx || sz.width() > d->drawable_texture_size.width()
1408 || sz.height() > d->drawable_texture_size.height()))
1409 {
1410 // delete old texture if size has increased, otherwise it was deleted earlier
1411 if (shared_ctx)
1412 glDeleteTextures(1, &d->drawable_texture);
1413
1414 d->dirty_drawable_texture = true;
1415 d->drawable_texture_size = QSize(qt_next_power_of_two(sz.width()),
1416 qt_next_power_of_two(sz.height()));
1417 }
1418#endif
1419
1420 updateClipRegion(QRegion(), Qt::NoClip);
1421 penChanged();
1422 brushChanged();
1423 opacityChanged();
1424 compositionModeChanged();
1425 renderHintsChanged();
1426 transformChanged();
1427 return true;
1428}
1429
1430bool QOpenGLPaintEngine::end()
1431{
1432 Q_D(QOpenGLPaintEngine);
1433 d->flushDrawQueue();
1434 d->offscreen.end();
1435 QGLContext *ctx = const_cast<QGLContext *>(d->device->context());
1436 if (!ctx->d_ptr->internal_context) {
1437 glMatrixMode(GL_TEXTURE);
1438 glPopMatrix();
1439 glMatrixMode(GL_MODELVIEW);
1440 glPopMatrix();
1441 }
1442#ifndef QT_OPENGL_ES
1443 if (ctx->d_ptr->internal_context) {
1444 glDisable(GL_SCISSOR_TEST);
1445 } else {
1446 glMatrixMode(GL_PROJECTION);
1447 glLoadMatrixd(&d->projection_matrix[0][0]);
1448 glPopAttrib();
1449 glPopClientAttrib();
1450 }
1451#endif
1452 d->device->endPaint();
1453 qt_mask_texture_cache()->maintainCache();
1454
1455#if defined(Q_WS_X11)
1456 // clear out the references we hold for textures bound with the
1457 // texture_from_pixmap extension
1458 ctx->d_func()->boundPixmaps.clear();
1459#endif
1460 return true;
1461}
1462
1463void QOpenGLPaintEngine::updateState(const QPaintEngineState &state)
1464{
1465 Q_D(QOpenGLPaintEngine);
1466 QPaintEngine::DirtyFlags flags = state.state();
1467
1468 bool update_fast_pen = false;
1469
1470 if (flags & DirtyOpacity) {
1471 update_fast_pen = true;
1472 d->opacity = state.opacity();
1473 if (d->opacity > 1.0f)
1474 d->opacity = 1.0f;
1475 if (d->opacity < 0.f)
1476 d->opacity = 0.f;
1477 // force update
1478 flags |= DirtyPen;
1479 flags |= DirtyBrush;
1480 }
1481
1482 if (flags & DirtyTransform) {
1483 update_fast_pen = true;
1484 updateMatrix(state.transform());
1485 // brush setup depends on transform state
1486 if (state.brush().style() != Qt::NoBrush)
1487 flags |= DirtyBrush;
1488 }
1489
1490 if (flags & DirtyPen) {
1491 update_fast_pen = true;
1492 updatePen(state.pen());
1493 }
1494
1495 if (flags & (DirtyBrush | DirtyBrushOrigin)) {
1496 updateBrush(state.brush(), state.brushOrigin());
1497 }
1498
1499 if (flags & DirtyFont) {
1500 updateFont(state.font());
1501 }
1502
1503 if (state.state() & DirtyClipEnabled) {
1504 if (state.isClipEnabled())
1505 updateClipRegion(painter()->clipRegion(), Qt::ReplaceClip);
1506 else
1507 updateClipRegion(QRegion(), Qt::NoClip);
1508 }
1509
1510 if (flags & DirtyClipPath) {
1511 updateClipRegion(QRegion(state.clipPath().toFillPolygon().toPolygon(),
1512 state.clipPath().fillRule()),
1513 state.clipOperation());
1514 }
1515
1516 if (flags & DirtyClipRegion) {
1517 updateClipRegion(state.clipRegion(), state.clipOperation());
1518 }
1519
1520 if (flags & DirtyHints) {
1521 updateRenderHints(state.renderHints());
1522 }
1523
1524 if (flags & DirtyCompositionMode) {
1525 updateCompositionMode(state.compositionMode());
1526 }
1527
1528 if (update_fast_pen) {
1529 Q_D(QOpenGLPaintEngine);
1530 qreal pen_width = d->cpen.widthF();
1531 d->has_fast_pen =
1532 ((pen_width == 0 || (pen_width <= 1 && d->txop <= QTransform::TxTranslate))
1533 || d->cpen.isCosmetic())
1534 && d->cpen.style() == Qt::SolidLine
1535 && d->cpen.isSolid();
1536 }
1537}
1538
1539
1540void QOpenGLPaintEnginePrivate::setInvMatrixData(const QTransform &inv_matrix)
1541{
1542 inv_matrix_data[0][0] = inv_matrix.m11();
1543 inv_matrix_data[1][0] = inv_matrix.m21();
1544 inv_matrix_data[2][0] = inv_matrix.m31();
1545
1546 inv_matrix_data[0][1] = inv_matrix.m12();
1547 inv_matrix_data[1][1] = inv_matrix.m22();
1548 inv_matrix_data[2][1] = inv_matrix.m32();
1549
1550 inv_matrix_data[0][2] = inv_matrix.m13();
1551 inv_matrix_data[1][2] = inv_matrix.m23();
1552 inv_matrix_data[2][2] = inv_matrix.m33();
1553}
1554
1555
1556void QOpenGLPaintEnginePrivate::updateGradient(const QBrush &brush, const QRectF &)
1557{
1558#ifdef QT_OPENGL_ES
1559 Q_UNUSED(brush);
1560#else
1561 bool has_mirrored_repeat = QGLExtensions::glExtensions() & QGLExtensions::MirroredRepeat;
1562 Qt::BrushStyle style = brush.style();
1563
1564 QTransform m = brush.transform();
1565
1566 if (has_mirrored_repeat && style == Qt::LinearGradientPattern) {
1567 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
1568 QTransform m = brush.transform();
1569 QPointF realStart = g->start();
1570 QPointF realFinal = g->finalStop();
1571 QPointF start = m.map(realStart);
1572 QPointF stop;
1573
1574 if (qFuzzyCompare(m.m11(), m.m22()) && m.m12() == 0.0 && m.m21() == 0.0) {
1575 // It is a simple uniform scale and/or translation
1576 stop = m.map(realFinal);
1577 } else {
1578 // It is not enough to just transform the endpoints.
1579 // We have to make sure the _pattern_ is transformed correctly.
1580
1581 qreal odx = realFinal.x() - realStart.x();
1582 qreal ody = realFinal.y() - realStart.y();
1583
1584 // nx, ny and dx, dy are normal and gradient direction after transform:
1585 qreal nx = m.m11()*ody - m.m21()*odx;
1586 qreal ny = m.m12()*ody - m.m22()*odx;
1587
1588 qreal dx = m.m11()*odx + m.m21()*ody;
1589 qreal dy = m.m12()*odx + m.m22()*ody;
1590
1591 qreal lx = 1 / (dx - dy*nx/ny);
1592 qreal ly = 1 / (dy - dx*ny/nx);
1593 qreal l = 1 / qSqrt(lx*lx+ly*ly);
1594
1595 stop = start + QPointF(-ny, nx) * l/qSqrt(nx*nx+ny*ny);
1596 }
1597
1598 float tr[4], f;
1599 tr[0] = stop.x() - start.x();
1600 tr[1] = stop.y() - start.y();
1601 f = 1.0 / (tr[0]*tr[0] + tr[1]*tr[1]);
1602 tr[0] *= f;
1603 tr[1] *= f;
1604 tr[2] = 0;
1605 tr[3] = -(start.x()*tr[0] + start.y()*tr[1]);
1606 brush_color[0] = brush_color[1] = brush_color[2] = brush_color[3] = 255;
1607 qt_glColor4ubv(brush_color);
1608 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
1609 glTexGenfv(GL_S, GL_OBJECT_PLANE, tr);
1610 }
1611
1612 if (use_fragment_programs) {
1613 if (style == Qt::RadialGradientPattern) {
1614 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
1615 QPointF realCenter = g->center();
1616 QPointF realFocal = g->focalPoint();
1617 qreal realRadius = g->radius();
1618 QTransform translate(1, 0, 0, 1, -realFocal.x(), -realFocal.y());
1619 QTransform gl_to_qt(1, 0, 0, -1, 0, pdev->height());
1620 QTransform m = QTransform(matrix).translate(brush_origin.x(), brush_origin.y());
1621 QTransform inv_matrix = gl_to_qt * (brush.transform() * m).inverted() * translate;
1622
1623 setInvMatrixData(inv_matrix);
1624
1625 fmp_data[0] = realCenter.x() - realFocal.x();
1626 fmp_data[1] = realCenter.y() - realFocal.y();
1627
1628 fmp2_m_radius2_data[0] = -fmp_data[0] * fmp_data[0] - fmp_data[1] * fmp_data[1] + realRadius * realRadius;
1629 } else if (style == Qt::ConicalGradientPattern) {
1630 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
1631 QPointF realCenter = g->center();
1632 QTransform translate(1, 0, 0, 1, -realCenter.x(), -realCenter.y());
1633 QTransform gl_to_qt(1, 0, 0, -1, 0, pdev->height());
1634 QTransform m = QTransform(matrix).translate(brush_origin.x(), brush_origin.y());
1635 QTransform inv_matrix = gl_to_qt * (brush.transform() * m).inverted() * translate;
1636
1637 setInvMatrixData(inv_matrix);
1638
1639 angle_data[0] = -(g->angle() * 2 * Q_PI) / 360.0;
1640 } else if (style == Qt::LinearGradientPattern) {
1641 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
1642
1643 QPointF realStart = g->start();
1644 QPointF realFinal = g->finalStop();
1645 QTransform translate(1, 0, 0, 1, -realStart.x(), -realStart.y());
1646 QTransform gl_to_qt(1, 0, 0, -1, 0, pdev->height());
1647 QTransform m = QTransform(matrix).translate(brush_origin.x(), brush_origin.y());
1648 QTransform inv_matrix = gl_to_qt * (brush.transform() * m).inverted() * translate;
1649
1650 setInvMatrixData(inv_matrix);
1651
1652 QPointF l = realFinal - realStart;
1653
1654 linear_data[0] = l.x();
1655 linear_data[1] = l.y();
1656
1657 linear_data[2] = 1.0f / (l.x() * l.x() + l.y() * l.y());
1658 } else if (style != Qt::SolidPattern) {
1659 QTransform gl_to_qt(1, 0, 0, -1, 0, pdev->height());
1660 QTransform m = QTransform(matrix).translate(brush_origin.x(), brush_origin.y());
1661 QTransform inv_matrix = gl_to_qt * (brush.transform() * m).inverted();
1662
1663 setInvMatrixData(inv_matrix);
1664 }
1665 }
1666
1667 if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern) {
1668 createGradientPaletteTexture(*brush.gradient());
1669 }
1670#endif
1671}
1672
1673
1674class QOpenGLTessellator : public QTessellator
1675{
1676public:
1677 QOpenGLTessellator() {}
1678 ~QOpenGLTessellator() { }
1679 QGLTrapezoid toGLTrapezoid(const Trapezoid &trap);
1680};
1681
1682QGLTrapezoid QOpenGLTessellator::toGLTrapezoid(const Trapezoid &trap)
1683{
1684 QGLTrapezoid t;
1685
1686 t.top = Q27Dot5ToDouble(trap.top);
1687 t.bottom = Q27Dot5ToDouble(trap.bottom);
1688
1689 Q27Dot5 y = trap.topLeft->y - trap.bottomLeft->y;
1690
1691 qreal topLeftY = Q27Dot5ToDouble(trap.topLeft->y);
1692
1693 qreal tx = Q27Dot5ToDouble(trap.topLeft->x);
1694 qreal m = (-tx + Q27Dot5ToDouble(trap.bottomLeft->x)) / Q27Dot5ToDouble(y);
1695 t.topLeftX = tx + m * (topLeftY - t.top);
1696 t.bottomLeftX = tx + m * (topLeftY - t.bottom);
1697
1698 y = trap.topRight->y - trap.bottomRight->y;
1699
1700 qreal topRightY = Q27Dot5ToDouble(trap.topRight->y);
1701
1702 tx = Q27Dot5ToDouble(trap.topRight->x);
1703 m = (-tx + Q27Dot5ToDouble(trap.bottomRight->x)) / Q27Dot5ToDouble(y);
1704
1705 t.topRightX = tx + m * (topRightY - Q27Dot5ToDouble(trap.top));
1706 t.bottomRightX = tx + m * (topRightY - Q27Dot5ToDouble(trap.bottom));
1707
1708 return t;
1709}
1710
1711class QOpenGLImmediateModeTessellator : public QOpenGLTessellator
1712{
1713public:
1714 void addTrap(const Trapezoid &trap);
1715 void tessellate(const QPointF *points, int nPoints, bool winding) {
1716 trapezoids.reserve(trapezoids.size() + nPoints);
1717 setWinding(winding);
1718 QTessellator::tessellate(points, nPoints);
1719 }
1720
1721 QVector<QGLTrapezoid> trapezoids;
1722};
1723
1724void QOpenGLImmediateModeTessellator::addTrap(const Trapezoid &trap)
1725{
1726 trapezoids.append(toGLTrapezoid(trap));
1727}
1728
1729#ifndef QT_OPENGL_ES
1730static void drawTrapezoid(const QGLTrapezoid &trap, const qreal offscreenHeight, QGLContext *ctx)
1731{
1732 qreal minX = qMin(trap.topLeftX, trap.bottomLeftX);
1733 qreal maxX = qMax(trap.topRightX, trap.bottomRightX);
1734
1735 if (qFuzzyCompare(trap.top, trap.bottom) || qFuzzyCompare(minX, maxX) ||
1736 (qFuzzyCompare(trap.topLeftX, trap.topRightX) && qFuzzyCompare(trap.bottomLeftX, trap.bottomRightX)))
1737 return;
1738
1739 const qreal xpadding = 1.0;
1740 const qreal ypadding = 1.0;
1741
1742 qreal topDist = offscreenHeight - trap.top;
1743 qreal bottomDist = offscreenHeight - trap.bottom;
1744
1745 qreal reciprocal = bottomDist / (bottomDist - topDist);
1746
1747 qreal leftB = trap.bottomLeftX + (trap.topLeftX - trap.bottomLeftX) * reciprocal;
1748 qreal rightB = trap.bottomRightX + (trap.topRightX - trap.bottomRightX) * reciprocal;
1749
1750 const bool topZero = qFuzzyIsNull(topDist);
1751
1752 reciprocal = topZero ? 1.0 / bottomDist : 1.0 / topDist;
1753
1754 qreal leftA = topZero ? (trap.bottomLeftX - leftB) * reciprocal : (trap.topLeftX - leftB) * reciprocal;
1755 qreal rightA = topZero ? (trap.bottomRightX - rightB) * reciprocal : (trap.topRightX - rightB) * reciprocal;
1756
1757 qreal invLeftA = qFuzzyIsNull(leftA) ? 0.0 : 1.0 / leftA;
1758 qreal invRightA = qFuzzyIsNull(rightA) ? 0.0 : 1.0 / rightA;
1759
1760 // fragment program needs the negative of invRightA as it mirrors the line
1761 glTexCoord4f(topDist, bottomDist, invLeftA, -invRightA);
1762 glMultiTexCoord4f(GL_TEXTURE1, leftA, leftB, rightA, rightB);
1763
1764 qreal topY = trap.top - ypadding;
1765 qreal bottomY = trap.bottom + ypadding;
1766
1767 qreal bounds_bottomLeftX = leftA * (offscreenHeight - bottomY) + leftB;
1768 qreal bounds_bottomRightX = rightA * (offscreenHeight - bottomY) + rightB;
1769 qreal bounds_topLeftX = leftA * (offscreenHeight - topY) + leftB;
1770 qreal bounds_topRightX = rightA * (offscreenHeight - topY) + rightB;
1771
1772 QPointF leftNormal(1, -leftA);
1773 leftNormal /= qSqrt(leftNormal.x() * leftNormal.x() + leftNormal.y() * leftNormal.y());
1774 QPointF rightNormal(1, -rightA);
1775 rightNormal /= qSqrt(rightNormal.x() * rightNormal.x() + rightNormal.y() * rightNormal.y());
1776
1777 qreal left_padding = xpadding / qAbs(leftNormal.x());
1778 qreal right_padding = xpadding / qAbs(rightNormal.x());
1779
1780 glVertex2d(bounds_topLeftX - left_padding, topY);
1781 glVertex2d(bounds_topRightX + right_padding, topY);
1782 glVertex2d(bounds_bottomRightX + right_padding, bottomY);
1783 glVertex2d(bounds_bottomLeftX - left_padding, bottomY);
1784
1785 glTexCoord4f(0.0f, 0.0f, 0.0f, 1.0f);
1786}
1787#endif // !Q_WS_QWS
1788
1789class QOpenGLTrapezoidToArrayTessellator : public QOpenGLTessellator
1790{
1791public:
1792 QOpenGLTrapezoidToArrayTessellator() : vertices(0), allocated(0), size(0) {}
1793 ~QOpenGLTrapezoidToArrayTessellator() { free(vertices); }
1794 GLfloat *vertices;
1795 int allocated;
1796 int size;
1797 QRectF bounds;
1798 void addTrap(const Trapezoid &trap);
1799 void tessellate(const QPointF *points, int nPoints, bool winding) {
1800 size = 0;
1801 setWinding(winding);
1802 bounds = QTessellator::tessellate(points, nPoints);
1803 }
1804};
1805
1806void QOpenGLTrapezoidToArrayTessellator::addTrap(const Trapezoid &trap)
1807{
1808 // On OpenGL ES we convert the trap to 2 triangles
1809#ifndef QT_OPENGL_ES
1810 if (size > allocated - 8) {
1811#else
1812 if (size > allocated - 12) {
1813#endif
1814 allocated = qMax(2*allocated, 512);
1815 vertices = (GLfloat *)realloc(vertices, allocated * sizeof(GLfloat));
1816 }
1817
1818 QGLTrapezoid t = toGLTrapezoid(trap);
1819
1820#ifndef QT_OPENGL_ES
1821 vertices[size++] = t.topLeftX;
1822 vertices[size++] = t.top;
1823 vertices[size++] = t.topRightX;
1824 vertices[size++] = t.top;
1825 vertices[size++] = t.bottomRightX;
1826 vertices[size++] = t.bottom;
1827 vertices[size++] = t.bottomLeftX;
1828 vertices[size++] = t.bottom;
1829#else
1830 // First triangle
1831 vertices[size++] = t.topLeftX;
1832 vertices[size++] = t.top;
1833 vertices[size++] = t.topRightX;
1834 vertices[size++] = t.top;
1835 vertices[size++] = t.bottomRightX;
1836 vertices[size++] = t.bottom;
1837
1838 // Second triangle
1839 vertices[size++] = t.bottomLeftX;
1840 vertices[size++] = t.bottom;
1841 vertices[size++] = t.topLeftX;
1842 vertices[size++] = t.top;
1843 vertices[size++] = t.bottomRightX;
1844 vertices[size++] = t.bottom;
1845#endif
1846}
1847
1848
1849void QOpenGLPaintEnginePrivate::fillPolygon_dev(const QPointF *polygonPoints, int pointCount,
1850 Qt::FillRule fill)
1851{
1852 QOpenGLTrapezoidToArrayTessellator tessellator;
1853 tessellator.tessellate(polygonPoints, pointCount, fill == Qt::WindingFill);
1854
1855 DEBUG_ONCE qDebug() << "QOpenGLPaintEnginePrivate: Drawing polygon with" << pointCount << "points using fillPolygon_dev";
1856
1857 setGradientOps(cbrush, tessellator.bounds);
1858
1859 bool fast_style = current_style == Qt::LinearGradientPattern
1860 || current_style == Qt::SolidPattern;
1861
1862#ifndef QT_OPENGL_ES
1863 GLenum geometry_mode = GL_QUADS;
1864#else
1865 GLenum geometry_mode = GL_TRIANGLES;
1866#endif
1867
1868 if (use_fragment_programs && !(fast_style && has_fast_composition_mode)) {
1869 composite(geometry_mode, tessellator.vertices, tessellator.size / 2);
1870 } else {
1871 glVertexPointer(2, GL_FLOAT, 0, tessellator.vertices);
1872 glEnableClientState(GL_VERTEX_ARRAY);
1873 glDrawArrays(geometry_mode, 0, tessellator.size/2);
1874 glDisableClientState(GL_VERTEX_ARRAY);
1875 }
1876}
1877
1878
1879inline void QOpenGLPaintEnginePrivate::lineToStencil(qreal x, qreal y)
1880{
1881 tess_points.add(QPointF(x, y));
1882
1883 if (x > max_x)
1884 max_x = x;
1885 else if (x < min_x)
1886 min_x = x;
1887 if (y > max_y)
1888 max_y = y;
1889 else if (y < min_y)
1890 min_y = y;
1891}
1892
1893inline void QOpenGLPaintEnginePrivate::curveToStencil(const QPointF &cp1, const QPointF &cp2, const QPointF &ep)
1894{
1895 qreal inverseScaleHalf = inverseScale / 2;
1896
1897 QBezier beziers[32];
1898 beziers[0] = QBezier::fromPoints(tess_points.last(), cp1, cp2, ep);
1899 QBezier *b = beziers;
1900 while (b >= beziers) {
1901 // check if we can pop the top bezier curve from the stack
1902 qreal l = qAbs(b->x4 - b->x1) + qAbs(b->y4 - b->y1);
1903 qreal d;
1904 if (l > inverseScale) {
1905 d = qAbs( (b->x4 - b->x1)*(b->y1 - b->y2) - (b->y4 - b->y1)*(b->x1 - b->x2) )
1906 + qAbs( (b->x4 - b->x1)*(b->y1 - b->y3) - (b->y4 - b->y1)*(b->x1 - b->x3) );
1907 d /= l;
1908 } else {
1909 d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) +
1910 qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3);
1911 }
1912 if (d < inverseScaleHalf || b == beziers + 31) {
1913 // good enough, we pop it off and add the endpoint
1914 lineToStencil(b->x4, b->y4);
1915 --b;
1916 } else {
1917 // split, second half of the polygon goes lower into the stack
1918 b->split(b+1, b);
1919 ++b;
1920 }
1921 }
1922}
1923
1924
1925void QOpenGLPaintEnginePrivate::pathToVertexArrays(const QPainterPath &path)
1926{
1927 const QPainterPath::Element &first = path.elementAt(0);
1928 min_x = max_x = first.x;
1929 min_y = max_y = first.y;
1930
1931 tess_points.reset();
1932 tess_points_stops.clear();
1933 lineToStencil(first.x, first.y);
1934
1935 for (int i=1; i<path.elementCount(); ++i) {
1936 const QPainterPath::Element &e = path.elementAt(i);
1937 switch (e.type) {
1938 case QPainterPath::MoveToElement:
1939 tess_points_stops.append(tess_points.size());
1940 lineToStencil(e.x, e.y);
1941 break;
1942 case QPainterPath::LineToElement:
1943 lineToStencil(e.x, e.y);
1944 break;
1945 case QPainterPath::CurveToElement:
1946 curveToStencil(e, path.elementAt(i+1), path.elementAt(i+2));
1947 i+=2;
1948 break;
1949 default:
1950 break;
1951 }
1952 }
1953 lineToStencil(first.x, first.y);
1954 tess_points_stops.append(tess_points.size());
1955}
1956
1957
1958void QOpenGLPaintEnginePrivate::drawVertexArrays()
1959{
1960 if (tess_points_stops.count() == 0)
1961 return;
1962 glEnableClientState(GL_VERTEX_ARRAY);
1963 glVertexPointer(2, GL_DOUBLE, 0, tess_points.data());
1964 int previous_stop = 0;
1965 foreach(int stop, tess_points_stops) {
1966 glDrawArrays(GL_TRIANGLE_FAN, previous_stop, stop-previous_stop);
1967 previous_stop = stop;
1968 }
1969 glDisableClientState(GL_VERTEX_ARRAY);
1970}
1971
1972void QOpenGLPaintEnginePrivate::fillVertexArray(Qt::FillRule fillRule)
1973{
1974 Q_Q(QOpenGLPaintEngine);
1975
1976 QRect rect = dirty_stencil.boundingRect();
1977
1978 if (use_system_clip)
1979 rect = q->systemClip().intersected(dirty_stencil).boundingRect();
1980
1981 glStencilMask(~0);
1982
1983 if (!rect.isEmpty()) {
1984 disableClipping();
1985
1986 glEnable(GL_SCISSOR_TEST);
1987
1988 const int left = rect.left();
1989 const int width = rect.width();
1990 const int bottom = device->size().height() - (rect.bottom() + 1);
1991 const int height = rect.height();
1992
1993 glScissor(left, bottom, width, height);
1994
1995 glClearStencil(0);
1996 glClear(GL_STENCIL_BUFFER_BIT);
1997 dirty_stencil -= rect;
1998
1999 glDisable(GL_SCISSOR_TEST);
2000
2001 enableClipping();
2002 }
2003
2004 // Enable stencil.
2005 glEnable(GL_STENCIL_TEST);
2006
2007 // Disable color writes.
2008 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
2009
2010 GLuint stencilMask = 0;
2011
2012 if (fillRule == Qt::OddEvenFill) {
2013 stencilMask = 1;
2014
2015 // Enable stencil writes.
2016 glStencilMask(stencilMask);
2017
2018 // Set stencil xor mode.
2019 glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT);
2020
2021 // Disable stencil func.
2022 glStencilFunc(GL_ALWAYS, 0, ~0);
2023
2024 drawVertexArrays();
2025 } else if (fillRule == Qt::WindingFill) {
2026 stencilMask = ~0;
2027
2028 if (has_stencil_face_ext) {
2029 QGL_FUNC_CONTEXT;
2030 glEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
2031
2032 glActiveStencilFaceEXT(GL_BACK);
2033 glStencilOp(GL_KEEP, GL_KEEP, GL_DECR_WRAP_EXT);
2034 glStencilFunc(GL_ALWAYS, 0, ~0);
2035
2036 glActiveStencilFaceEXT(GL_FRONT);
2037 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP_EXT);
2038 glStencilFunc(GL_ALWAYS, 0, ~0);
2039
2040 drawVertexArrays();
2041
2042 glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
2043 } else {
2044 glStencilFunc(GL_ALWAYS, 0, ~0);
2045 glEnable(GL_CULL_FACE);
2046
2047 glCullFace(GL_BACK);
2048 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP_EXT);
2049 drawVertexArrays();
2050
2051 glCullFace(GL_FRONT);
2052 glStencilOp(GL_KEEP, GL_KEEP, GL_DECR_WRAP_EXT);
2053 drawVertexArrays();
2054
2055 glDisable(GL_CULL_FACE);
2056 }
2057 }
2058
2059 // Enable color writes.
2060 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
2061 glStencilMask(stencilMask);
2062
2063 setGradientOps(cbrush, QRectF(QPointF(min_x, min_y), QSizeF(max_x - min_x, max_y - min_y)));
2064
2065 bool fast_fill = has_fast_composition_mode && (current_style == Qt::LinearGradientPattern || current_style == Qt::SolidPattern);
2066
2067 if (use_fragment_programs && !fast_fill) {
2068 DEBUG_ONCE qDebug() << "QOpenGLPaintEnginePrivate: Drawing polygon using stencil method (fragment programs)";
2069 QRectF rect(QPointF(min_x, min_y), QSizeF(max_x - min_x, max_y - min_y));
2070
2071 // Enable stencil func.
2072 glStencilFunc(GL_NOTEQUAL, 0, stencilMask);
2073 glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
2074 composite(rect);
2075 } else {
2076 DEBUG_ONCE qDebug() << "QOpenGLPaintEnginePrivate: Drawing polygon using stencil method (no fragment programs)";
2077
2078 // Enable stencil func.
2079 glStencilFunc(GL_NOTEQUAL, 0, stencilMask);
2080 glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
2081#ifndef QT_OPENGL_ES
2082 glBegin(GL_QUADS);
2083 glVertex2f(min_x, min_y);
2084 glVertex2f(max_x, min_y);
2085 glVertex2f(max_x, max_y);
2086 glVertex2f(min_x, max_y);
2087 glEnd();
2088#endif
2089 }
2090
2091 // Disable stencil writes.
2092 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
2093 glStencilMask(0);
2094 glDisable(GL_STENCIL_TEST);
2095}
2096
2097void QOpenGLPaintEnginePrivate::fillPath(const QPainterPath &path)
2098{
2099 if (path.isEmpty())
2100 return;
2101
2102 if (use_stencil_method && !high_quality_antialiasing) {
2103 pathToVertexArrays(path);
2104 fillVertexArray(path.fillRule());
2105 return;
2106 }
2107
2108 glMatrixMode(GL_MODELVIEW);
2109 glLoadIdentity();
2110
2111 if (high_quality_antialiasing)
2112 drawOffscreenPath(path);
2113 else {
2114 QPolygonF poly = path.toFillPolygon(matrix);
2115 fillPolygon_dev(poly.data(), poly.count(),
2116 path.fillRule());
2117 }
2118
2119 updateGLMatrix();
2120}
2121
2122
2123static inline bool needsEmulation(Qt::BrushStyle style)
2124{
2125 return !(style == Qt::SolidPattern
2126 || (style == Qt::LinearGradientPattern
2127 && (QGLExtensions::glExtensions() & QGLExtensions::MirroredRepeat)));
2128}
2129
2130void QOpenGLPaintEnginePrivate::updateUseEmulation()
2131{
2132 use_emulation = !use_fragment_programs
2133 && ((has_pen && needsEmulation(pen_brush_style))
2134 || (has_brush && needsEmulation(brush_style)));
2135}
2136
2137void QOpenGLPaintEngine::updatePen(const QPen &pen)
2138{
2139 Q_D(QOpenGLPaintEngine);
2140 Qt::PenStyle pen_style = pen.style();
2141 d->pen_brush_style = pen.brush().style();
2142 d->cpen = pen;
2143 d->has_pen = (pen_style != Qt::NoPen) && (d->pen_brush_style != Qt::NoBrush);
2144 d->updateUseEmulation();
2145
2146 if (pen.isCosmetic()) {
2147 GLfloat width = pen.widthF() == 0.0f ? 1.0f : pen.widthF();
2148 glLineWidth(width);
2149 glPointSize(width);
2150 }
2151
2152 if (d->pen_brush_style >= Qt::LinearGradientPattern
2153 && d->pen_brush_style <= Qt::ConicalGradientPattern)
2154 {
2155 d->setGLPen(Qt::white);
2156 } else {
2157 d->setGLPen(pen.color());
2158 }
2159
2160 d->updateFastPen();
2161}
2162
2163void QOpenGLPaintEngine::updateBrush(const QBrush &brush, const QPointF &origin)
2164{
2165 Q_D(QOpenGLPaintEngine);
2166 d->cbrush = brush;
2167 d->brush_style = brush.style();
2168 d->brush_origin = origin;
2169 d->has_brush = (d->brush_style != Qt::NoBrush);
2170 d->updateUseEmulation();
2171}
2172
2173void QOpenGLPaintEngine::updateFont(const QFont &)
2174{
2175}
2176
2177void QOpenGLPaintEngine::updateMatrix(const QTransform &mtx)
2178{
2179 Q_D(QOpenGLPaintEngine);
2180
2181 d->matrix = mtx;
2182
2183 d->mv_matrix[0][0] = mtx.m11();
2184 d->mv_matrix[0][1] = mtx.m12();
2185 d->mv_matrix[0][2] = 0;
2186 d->mv_matrix[0][3] = mtx.m13();
2187
2188 d->mv_matrix[1][0] = mtx.m21();
2189 d->mv_matrix[1][1] = mtx.m22();
2190 d->mv_matrix[1][2] = 0;
2191 d->mv_matrix[1][3] = mtx.m23();
2192
2193 d->mv_matrix[2][0] = 0;
2194 d->mv_matrix[2][1] = 0;
2195 d->mv_matrix[2][2] = 1;
2196 d->mv_matrix[2][3] = 0;
2197
2198 d->mv_matrix[3][0] = mtx.dx();
2199 d->mv_matrix[3][1] = mtx.dy();
2200 d->mv_matrix[3][2] = 0;
2201 d->mv_matrix[3][3] = mtx.m33();
2202
2203 d->txop = mtx.type();
2204
2205 // 1/10000 == 0.0001, so we have good enough res to cover curves
2206 // that span the entire widget...
2207 d->inverseScale = qMax(1 / qMax( qMax(qAbs(mtx.m11()), qAbs(mtx.m22())),
2208 qMax(qAbs(mtx.m12()), qAbs(mtx.m21())) ),
2209 qreal(0.0001));
2210
2211 d->updateGLMatrix();
2212 d->updateFastPen();
2213}
2214
2215void QOpenGLPaintEnginePrivate::updateGLMatrix() const
2216{
2217 glMatrixMode(GL_MODELVIEW);
2218#ifndef QT_OPENGL_ES
2219 glLoadMatrixd(&mv_matrix[0][0]);
2220#else
2221 glLoadMatrixf(&mv_matrix[0][0]);
2222#endif
2223}
2224
2225void QOpenGLPaintEnginePrivate::disableClipping()
2226{
2227 glDisable(GL_DEPTH_TEST);
2228 glDisable(GL_SCISSOR_TEST);
2229}
2230
2231void QOpenGLPaintEnginePrivate::enableClipping()
2232{
2233 Q_Q(QOpenGLPaintEngine);
2234 if (!q->state()->hasClipping)
2235 return;
2236
2237 if (q->state()->fastClip.isEmpty())
2238 glEnable(GL_DEPTH_TEST);
2239 else
2240 updateDepthClip(); // this will enable the scissor test
2241}
2242
2243void QOpenGLPaintEnginePrivate::updateDepthClip()
2244{
2245 Q_Q(QOpenGLPaintEngine);
2246
2247 ++q->state()->depthClipId;
2248
2249 glDisable(GL_DEPTH_TEST);
2250 glDisable(GL_SCISSOR_TEST);
2251
2252 if (!q->state()->hasClipping)
2253 return;
2254
2255 QRect fastClip;
2256 if (q->state()->clipEnabled) {
2257 fastClip = q->state()->fastClip;
2258 } else if (use_system_clip && q->systemClip().rects().count() == 1) {
2259 fastClip = q->systemClip().rects().at(0);
2260 }
2261
2262 if (!fastClip.isEmpty()) {
2263 glEnable(GL_SCISSOR_TEST);
2264
2265 const int left = fastClip.left();
2266 const int width = fastClip.width();
2267 const int bottom = device->size().height() - (fastClip.bottom() + 1);
2268 const int height = fastClip.height();
2269
2270 glScissor(left, bottom, width, height);
2271 return;
2272 }
2273
2274#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_2)
2275 glClearDepthf(0.0f);
2276#else
2277 glClearDepth(0.0f);
2278#endif
2279
2280 glEnable(GL_DEPTH_TEST);
2281 glDepthMask(GL_TRUE);
2282 glClear(GL_DEPTH_BUFFER_BIT);
2283
2284 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
2285 glDepthFunc(GL_ALWAYS);
2286
2287 const QVector<QRect> rects = q->state()->clipEnabled ? q->state()->clipRegion.rects() : q->systemClip().rects();
2288
2289 // rectangle count * 2 (triangles) * vertex count * component count (Z omitted)
2290 QDataBuffer<GLfloat> clipVertex(rects.size()*2*3*2);
2291 for (int i = 0; i < rects.size(); ++i) {
2292 GLfloat x = GLfloat(rects.at(i).left());
2293 GLfloat w = GLfloat(rects.at(i).width());
2294 GLfloat h = GLfloat(rects.at(i).height());
2295 GLfloat y = GLfloat(rects.at(i).top());
2296
2297 // First triangle
2298 clipVertex.add(x);
2299 clipVertex.add(y);
2300
2301 clipVertex.add(x);
2302 clipVertex.add(y + h);
2303
2304 clipVertex.add(x + w);
2305 clipVertex.add(y);
2306
2307 // Second triangle
2308 clipVertex.add(x);
2309 clipVertex.add(y + h);
2310
2311 clipVertex.add(x + w);
2312 clipVertex.add(y + h);
2313
2314 clipVertex.add (x + w);
2315 clipVertex.add(y);
2316 }
2317
2318 if (rects.size()) {
2319 glMatrixMode(GL_MODELVIEW);
2320 glLoadIdentity();
2321
2322 glEnableClientState(GL_VERTEX_ARRAY);
2323 glVertexPointer(2, GL_FLOAT, 0, clipVertex.data());
2324
2325 glDrawArrays(GL_TRIANGLES, 0, rects.size()*2*3);
2326 glDisableClientState(GL_VERTEX_ARRAY);
2327 updateGLMatrix();
2328 }
2329
2330 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
2331 glDepthMask(GL_FALSE);
2332 glDepthFunc(GL_LEQUAL);
2333}
2334
2335void QOpenGLPaintEnginePrivate::systemStateChanged()
2336{
2337 Q_Q(QOpenGLPaintEngine);
2338 if (q->painter()->hasClipping())
2339 q->updateClipRegion(q->painter()->clipRegion(), Qt::ReplaceClip);
2340 else
2341 q->updateClipRegion(QRegion(), Qt::NoClip);
2342}
2343
2344void QOpenGLPaintEngine::updateClipRegion(const QRegion &clipRegion, Qt::ClipOperation op)
2345{
2346 Q_D(QOpenGLPaintEngine);
2347
2348 // clipping is only supported when a stencil or depth buffer is
2349 // available
2350 if (!d->device->format().depth())
2351 return;
2352
2353 d->use_system_clip = false;
2354 QRegion sysClip = systemClip();
2355 if (!sysClip.isEmpty()) {
2356 if (d->pdev->devType() != QInternal::Widget) {
2357 d->use_system_clip = true;
2358 } else {
2359#ifndef Q_WS_QWS
2360 // Only use the system clip if we're currently rendering a widget with a GL painter.
2361 if (d->currentClipWidget) {
2362 QWidgetPrivate *widgetPrivate = qt_widget_private(d->currentClipWidget->window());
2363 d->use_system_clip = widgetPrivate->extra && widgetPrivate->extra->inRenderWithPainter;
2364 }
2365#endif
2366 }
2367 }
2368
2369 d->flushDrawQueue();
2370
2371 if (op == Qt::NoClip && !d->use_system_clip) {
2372 state()->hasClipping = false;
2373 state()->clipRegion = QRegion();
2374 d->updateDepthClip();
2375 return;
2376 }
2377
2378 bool isScreenClip = false;
2379 if (!d->use_system_clip) {
2380 QVector<QRect> untransformedRects = clipRegion.rects();
2381
2382 if (untransformedRects.size() == 1) {
2383 QPainterPath path;
2384 path.addRect(untransformedRects[0]);
2385 path = d->matrix.map(path);
2386
2387 if (path.contains(QRectF(QPointF(), d->device->size())))
2388 isScreenClip = true;
2389 }
2390 }
2391
2392 QRegion region = isScreenClip ? QRegion() : clipRegion * d->matrix;
2393 switch (op) {
2394 case Qt::NoClip:
2395 if (!d->use_system_clip)
2396 break;
2397 state()->clipRegion = sysClip;
2398 break;
2399 case Qt::IntersectClip:
2400 if (isScreenClip)
2401 return;
2402 if (state()->hasClipping) {
2403 state()->clipRegion &= region;
2404 break;
2405 }
2406 // fall through
2407 case Qt::ReplaceClip:
2408 if (d->use_system_clip)
2409 state()->clipRegion = region & sysClip;
2410 else
2411 state()->clipRegion = region;
2412 break;
2413 case Qt::UniteClip:
2414 state()->clipRegion |= region;
2415 if (d->use_system_clip)
2416 state()->clipRegion &= sysClip;
2417 break;
2418 default:
2419 break;
2420 }
2421
2422 if (isScreenClip) {
2423 state()->hasClipping = false;
2424 state()->clipRegion = QRegion();
2425 } else {
2426 state()->hasClipping = op != Qt::NoClip || d->use_system_clip;
2427 }
2428
2429 if (state()->hasClipping && state()->clipRegion.rects().size() == 1)
2430 state()->fastClip = state()->clipRegion.rects().at(0);
2431 else
2432 state()->fastClip = QRect();
2433
2434 d->updateDepthClip();
2435}
2436
2437void QOpenGLPaintEngine::updateRenderHints(QPainter::RenderHints hints)
2438{
2439 Q_D(QOpenGLPaintEngine);
2440
2441 d->flushDrawQueue();
2442 d->use_smooth_pixmap_transform = bool(hints & QPainter::SmoothPixmapTransform);
2443 if ((hints & QPainter::Antialiasing) || (hints & QPainter::HighQualityAntialiasing)) {
2444 if (d->use_fragment_programs && QGLOffscreen::isSupported()
2445 && (hints & QPainter::HighQualityAntialiasing)) {
2446 d->high_quality_antialiasing = true;
2447 } else {
2448 d->high_quality_antialiasing = false;
2449 if (QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers)
2450 glEnable(GL_MULTISAMPLE);
2451 }
2452 } else {
2453 d->high_quality_antialiasing = false;
2454 if (QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers)
2455 glDisable(GL_MULTISAMPLE);
2456 }
2457
2458 if (d->high_quality_antialiasing) {
2459 d->offscreen.initialize();
2460
2461 if (!d->offscreen.isValid()) {
2462 DEBUG_ONCE_STR("Unable to initialize offscreen, disabling high quality antialiasing");
2463 d->high_quality_antialiasing = false;
2464 if (QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers)
2465 glEnable(GL_MULTISAMPLE);
2466 }
2467 }
2468
2469 d->has_antialiasing = d->high_quality_antialiasing
2470 || ((hints & QPainter::Antialiasing)
2471 && (QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers));
2472}
2473
2474
2475void QOpenGLPaintEnginePrivate::setPorterDuffData(float a, float b, float x, float y, float z)
2476{
2477 porterduff_ab_data[0] = a;
2478 porterduff_ab_data[1] = b;
2479
2480 porterduff_xyz_data[0] = x;
2481 porterduff_xyz_data[1] = y;
2482 porterduff_xyz_data[2] = z;
2483}
2484
2485
2486void QOpenGLPaintEngine::updateCompositionMode(QPainter::CompositionMode composition_mode)
2487{
2488 Q_D(QOpenGLPaintEngine);
2489
2490 if (!d->use_fragment_programs && composition_mode > QPainter::CompositionMode_Plus)
2491 composition_mode = QPainter::CompositionMode_SourceOver;
2492
2493 d->composition_mode = composition_mode;
2494
2495 d->has_fast_composition_mode = (!d->high_quality_antialiasing && composition_mode <= QPainter::CompositionMode_Plus)
2496 || composition_mode == QPainter::CompositionMode_SourceOver
2497 || composition_mode == QPainter::CompositionMode_Destination
2498 || composition_mode == QPainter::CompositionMode_DestinationOver
2499 || composition_mode == QPainter::CompositionMode_DestinationOut
2500 || composition_mode == QPainter::CompositionMode_SourceAtop
2501 || composition_mode == QPainter::CompositionMode_Xor
2502 || composition_mode == QPainter::CompositionMode_Plus;
2503
2504 if (d->has_fast_composition_mode)
2505 d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODE_BLEND_MODE_MASK : COMPOSITION_MODE_BLEND_MODE_NOMASK;
2506 else if (composition_mode <= QPainter::CompositionMode_Plus)
2507 d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_SIMPLE_PORTER_DUFF : COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK;
2508 else
2509 switch (composition_mode) {
2510 case QPainter::CompositionMode_Multiply:
2511 d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_MULTIPLY : COMPOSITION_MODES_MULTIPLY_NOMASK;
2512 break;
2513 case QPainter::CompositionMode_Screen:
2514 d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_SCREEN : COMPOSITION_MODES_SCREEN_NOMASK;
2515 break;
2516 case QPainter::CompositionMode_Overlay:
2517 d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_OVERLAY : COMPOSITION_MODES_OVERLAY_NOMASK;
2518 break;
2519 case QPainter::CompositionMode_Darken:
2520 d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_DARKEN : COMPOSITION_MODES_DARKEN_NOMASK;
2521 break;
2522 case QPainter::CompositionMode_Lighten:
2523 d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_LIGHTEN : COMPOSITION_MODES_LIGHTEN_NOMASK;
2524 break;
2525 case QPainter::CompositionMode_ColorDodge:
2526 d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_COLORDODGE : COMPOSITION_MODES_COLORDODGE_NOMASK;
2527 break;
2528 case QPainter::CompositionMode_ColorBurn:
2529 d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_COLORBURN : COMPOSITION_MODES_COLORBURN_NOMASK;
2530 break;
2531 case QPainter::CompositionMode_HardLight:
2532 d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_HARDLIGHT : COMPOSITION_MODES_HARDLIGHT_NOMASK;
2533 break;
2534 case QPainter::CompositionMode_SoftLight:
2535 d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_SOFTLIGHT : COMPOSITION_MODES_SOFTLIGHT_NOMASK;
2536 break;
2537 case QPainter::CompositionMode_Difference:
2538 d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_DIFFERENCE : COMPOSITION_MODES_DIFFERENCE_NOMASK;
2539 break;
2540 case QPainter::CompositionMode_Exclusion:
2541 d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_EXCLUSION : COMPOSITION_MODES_EXCLUSION_NOMASK;
2542 break;
2543 default:
2544 Q_ASSERT(false);
2545 }
2546
2547 switch(composition_mode) {
2548 case QPainter::CompositionMode_DestinationOver:
2549 glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);
2550 d->setPorterDuffData(0, 1, 1, 1, 1);
2551 break;
2552 case QPainter::CompositionMode_Clear:
2553 glBlendFunc(GL_ZERO, GL_ZERO);
2554 d->setPorterDuffData(0, 0, 0, 0, 0);
2555 break;
2556 case QPainter::CompositionMode_Source:
2557 glBlendFunc(GL_ONE, GL_ZERO);
2558 d->setPorterDuffData(1, 0, 1, 1, 0);
2559 break;
2560 case QPainter::CompositionMode_Destination:
2561 glBlendFunc(GL_ZERO, GL_ONE);
2562 d->setPorterDuffData(0, 1, 1, 0, 1);
2563 break;
2564 case QPainter::CompositionMode_SourceIn:
2565 glBlendFunc(GL_DST_ALPHA, GL_ZERO);
2566 d->setPorterDuffData(1, 0, 1, 0, 0);
2567 break;
2568 case QPainter::CompositionMode_DestinationIn:
2569 glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
2570 d->setPorterDuffData(0, 1, 1, 0, 0);
2571 break;
2572 case QPainter::CompositionMode_SourceOut:
2573 glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ZERO);
2574 d->setPorterDuffData(0, 0, 0, 1, 0);
2575 break;
2576 case QPainter::CompositionMode_DestinationOut:
2577 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
2578 d->setPorterDuffData(0, 0, 0, 0, 1);
2579 break;
2580 case QPainter::CompositionMode_SourceAtop:
2581 glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2582 d->setPorterDuffData(1, 0, 1, 0, 1);
2583 break;
2584 case QPainter::CompositionMode_DestinationAtop:
2585 glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA);
2586 d->setPorterDuffData(0, 1, 1, 1, 0);
2587 break;
2588 case QPainter::CompositionMode_Xor:
2589 glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2590 d->setPorterDuffData(0, 0, 0, 1, 1);
2591 break;
2592 case QPainter::CompositionMode_SourceOver:
2593 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
2594 d->setPorterDuffData(1, 0, 1, 1, 1);
2595 break;
2596 case QPainter::CompositionMode_Plus:
2597 glBlendFunc(GL_ONE, GL_ONE);
2598 d->setPorterDuffData(1, 1, 1, 1, 1);
2599 break;
2600 default:
2601 break;
2602 }
2603}
2604
2605class QGLMaskGenerator
2606{
2607public:
2608 QGLMaskGenerator(const QPainterPath &path, const QTransform &matrix, qreal stroke_width = -1)
2609 : p(path),
2610 m(matrix),
2611 w(stroke_width)
2612 {
2613 }
2614
2615 virtual QRect screenRect() = 0;
2616 virtual void drawMask(const QRect &rect) = 0;
2617
2618 QPainterPath path() const { return p; }
2619 QTransform matrix() const { return m; }
2620 qreal strokeWidth() const { return w; }
2621
2622 virtual ~QGLMaskGenerator() {}
2623
2624private:
2625 QPainterPath p;
2626 QTransform m;
2627 qreal w;
2628};
2629
2630void QGLMaskTextureCache::setOffscreenSize(const QSize &sz)
2631{
2632 Q_ASSERT(sz.width() == sz.height());
2633
2634 if (offscreenSize != sz) {
2635 offscreenSize = sz;
2636 clearCache();
2637 }
2638}
2639
2640void QGLMaskTextureCache::clearCache()
2641{
2642 cache.clear();
2643
2644 int quad_tree_size = 1;
2645
2646 for (int i = block_size; i < offscreenSize.width(); i *= 2)
2647 quad_tree_size += quad_tree_size * 4;
2648
2649 for (int i = 0; i < 4; ++i) {
2650 occupied_quadtree[i].resize(quad_tree_size);
2651
2652 occupied_quadtree[i][0].key = 0;
2653 occupied_quadtree[i][0].largest_available_block = offscreenSize.width();
2654 occupied_quadtree[i][0].largest_used_block = 0;
2655
2656 DEBUG_ONCE qDebug() << "QGLMaskTextureCache:: created quad tree of size" << quad_tree_size;
2657 }
2658}
2659
2660void QGLMaskTextureCache::setDrawableSize(const QSize &sz)
2661{
2662 drawableSize = sz;
2663}
2664
2665void QGLMaskTextureCache::maintainCache()
2666{
2667 QGLTextureCacheHash::iterator it = cache.begin();
2668 QGLTextureCacheHash::iterator end = cache.end();
2669
2670 while (it != end) {
2671 CacheInfo &cache_info = it.value();
2672 ++cache_info.age;
2673
2674 if (cache_info.age > 1) {
2675 quadtreeInsert(cache_info.loc.channel, 0, cache_info.loc.rect);
2676 it = cache.erase(it);
2677 } else {
2678 ++it;
2679 }
2680 }
2681}
2682
2683//#define DISABLE_MASK_CACHE
2684
2685QGLMaskTextureCache::CacheLocation QGLMaskTextureCache::getMask(QGLMaskGenerator &maskGenerator, QOpenGLPaintEnginePrivate *e)
2686{
2687#ifndef DISABLE_MASK_CACHE
2688 engine = e;
2689
2690 quint64 key = hash(maskGenerator.path(), maskGenerator.matrix(), maskGenerator.strokeWidth());
2691
2692 if (key == 0)
2693 key = 1;
2694
2695 CacheInfo info(maskGenerator.path(), maskGenerator.matrix(), maskGenerator.strokeWidth());
2696
2697 QGLTextureCacheHash::iterator it = cache.find(key);
2698
2699 while (it != cache.end() && it.key() == key) {
2700 CacheInfo &cache_info = it.value();
2701 if (info.stroke_width == cache_info.stroke_width && info.matrix == cache_info.matrix && info.path == cache_info.path) {
2702 DEBUG_ONCE_STR("QGLMaskTextureCache::getMask(): Using cached mask");
2703
2704 cache_info.age = 0;
2705 return cache_info.loc;
2706 }
2707 ++it;
2708 }
2709
2710 // mask was not found, create new mask
2711
2712 DEBUG_ONCE_STR("QGLMaskTextureCache::getMask(): Creating new mask...");
2713
2714 createMask(key, info, maskGenerator);
2715
2716 cache.insert(key, info);
2717
2718 return info.loc;
2719#else
2720 CacheInfo info(maskGenerator.path(), maskGenerator.matrix());
2721 createMask(0, info, maskGenerator);
2722 return info.loc;
2723#endif
2724}
2725
2726#ifndef FloatToQuint64
2727#define FloatToQuint64(i) (quint64)((i) * 32)
2728#endif
2729
2730quint64 QGLMaskTextureCache::hash(const QPainterPath &p, const QTransform &m, qreal w)
2731{
2732 Q_ASSERT(sizeof(quint64) == 8);
2733
2734 quint64 h = 0;
2735
2736 for (int i = 0; i < p.elementCount(); ++i) {
2737 h += FloatToQuint64(p.elementAt(i).x) << 32;
2738 h += FloatToQuint64(p.elementAt(i).y);
2739 h += p.elementAt(i).type;
2740 }
2741
2742 h += FloatToQuint64(m.m11());
2743#ifndef Q_OS_WINCE // ###
2744 //Compiler crashes for arm on WinCE
2745 h += FloatToQuint64(m.m12()) << 4;
2746 h += FloatToQuint64(m.m13()) << 8;
2747 h += FloatToQuint64(m.m21()) << 12;
2748 h += FloatToQuint64(m.m22()) << 16;
2749 h += FloatToQuint64(m.m23()) << 20;
2750 h += FloatToQuint64(m.m31()) << 24;
2751 h += FloatToQuint64(m.m32()) << 28;
2752#endif
2753 h += FloatToQuint64(m.m33()) << 32;
2754
2755 h += FloatToQuint64(w);
2756
2757 return h;
2758}
2759
2760void QGLMaskTextureCache::createMask(quint64 key, CacheInfo &info, QGLMaskGenerator &maskGenerator)
2761{
2762 info.loc.screen_rect = maskGenerator.screenRect();
2763
2764 if (info.loc.screen_rect.isEmpty()) {
2765 info.loc.channel = 0;
2766 info.loc.rect = QRect();
2767 return;
2768 }
2769
2770 quadtreeAllocate(key, info.loc.screen_rect.size(), &info.loc.rect, &info.loc.channel);
2771
2772 int ch = info.loc.channel;
2773 glColorMask(ch == 0, ch == 1, ch == 2, ch == 3);
2774
2775 maskGenerator.drawMask(info.loc.rect);
2776
2777 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
2778}
2779
2780int QGLMaskTextureCache::quadtreeBlocksize(int node)
2781{
2782 DEBUG_ONCE qDebug() << "Offscreen size:" << offscreenSize.width();
2783
2784 int blocksize = offscreenSize.width();
2785
2786 while (node) {
2787 node = (node - 1) / 4;
2788 blocksize /= 2;
2789 }
2790
2791 return blocksize;
2792}
2793
2794QPoint QGLMaskTextureCache::quadtreeLocation(int node)
2795{
2796 QPoint location;
2797 int blocksize = quadtreeBlocksize(node);
2798
2799 while (node) {
2800 --node;
2801
2802 if (node & 1)
2803 location.setX(location.x() + blocksize);
2804
2805 if (node & 2)
2806 location.setY(location.y() + blocksize);
2807
2808 node /= 4;
2809 blocksize *= 2;
2810 }
2811
2812 return location;
2813}
2814
2815void QGLMaskTextureCache::quadtreeUpdate(int channel, int node, int current_block_size)
2816{
2817 while (node) {
2818 node = (node - 1) / 4;
2819
2820 int first_child = node * 4 + 1;
2821
2822 int largest_available = 0;
2823 int largest_used = 0;
2824
2825 bool all_empty = true;
2826
2827 for (int i = 0; i < 4; ++i) {
2828 largest_available = qMax(largest_available, occupied_quadtree[channel][first_child + i].largest_available_block);
2829 largest_used = qMax(largest_used, occupied_quadtree[channel][first_child + i].largest_used_block);
2830
2831 if (occupied_quadtree[channel][first_child + i].largest_available_block < current_block_size)
2832 all_empty = false;
2833 }
2834
2835 current_block_size *= 2;
2836
2837 if (all_empty) {
2838 occupied_quadtree[channel][node].largest_available_block = current_block_size;
2839 occupied_quadtree[channel][node].largest_used_block = 0;
2840 } else {
2841 occupied_quadtree[channel][node].largest_available_block = largest_available;
2842 occupied_quadtree[channel][node].largest_used_block = largest_used;
2843 }
2844 }
2845}
2846
2847void QGLMaskTextureCache::quadtreeInsert(int channel, quint64 key, const QRect &rect, int node)
2848{
2849 int current_block_size = quadtreeBlocksize(node);
2850 QPoint location = quadtreeLocation(node);
2851 QRect relative = rect.translated(-location);
2852
2853 if (relative.left() >= current_block_size || relative.top() >= current_block_size
2854 || relative.right() < 0 || relative.bottom() < 0)
2855 return;
2856
2857 if (current_block_size == block_size // no more refining possible
2858 || (relative.top() < block_size && relative.bottom() >= (current_block_size - block_size)
2859 && relative.left() < block_size && relative.right() >= (current_block_size - block_size)))
2860 {
2861 if (key != 0) {
2862 occupied_quadtree[channel][node].largest_available_block = 0;
2863 occupied_quadtree[channel][node].largest_used_block = rect.width() * rect.height();
2864 } else {
2865 occupied_quadtree[channel][node].largest_available_block = current_block_size;
2866 occupied_quadtree[channel][node].largest_used_block = 0;
2867 }
2868
2869 occupied_quadtree[channel][node].key = key;
2870
2871 quadtreeUpdate(channel, node, current_block_size);
2872 } else {
2873 if (key && occupied_quadtree[channel][node].largest_available_block == current_block_size) {
2874 // refining the quad tree, initialize child nodes
2875 int half_block_size = current_block_size / 2;
2876
2877 int temp = node * 4 + 1;
2878 for (int sibling = 0; sibling < 4; ++sibling) {
2879 occupied_quadtree[channel][temp + sibling].largest_available_block = half_block_size;
2880 occupied_quadtree[channel][temp + sibling].largest_used_block = 0;
2881 occupied_quadtree[channel][temp + sibling].key = 0;
2882 }