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

Last change on this file since 780 was 769, checked in by Dmitry A. Kuminov, 15 years ago

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

File size: 177.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 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
64#include "private/qtessellator_p.h"
65
66#include "util/fragmentprograms_p.h"
67
68#ifdef Q_WS_QWS
69#include "private/qglwindowsurface_qws_p.h"
70#include "qwsmanager_qws.h"
71#include "private/qwsmanager_p.h"
72#endif
73
74#ifdef QT_OPENGL_ES_1_CL
75#include "qgl_cl_p.h"
76#endif
77
78#define QGL_FUNC_CONTEXT QGLContext *ctx = const_cast<QGLContext *>(device->context());
79
80#include <stdlib.h>
81#include "qpaintengine_opengl_p.h"
82
83QT_BEGIN_NAMESPACE
84
85extern QImage qt_imageForBrush(int brushStyle, bool invert); //in qbrush.cpp
86#ifdef QT_MAC_USE_COCOA
87extern void *qt_current_nsopengl_context(); // qgl_mac.mm
88#endif
89
90#define QREAL_MAX 9e100
91#define QREAL_MIN -9e100
92
93extern int qt_next_power_of_two(int v);
94
95#define DISABLE_DEBUG_ONCE
96
97//#define DEBUG_DISPLAY_MASK_TEXTURE
98
99#ifdef DISABLE_DEBUG_ONCE
100#define DEBUG_OVERRIDE(state) ;
101#define DEBUG_ONCE_STR(str) ;
102#define DEBUG_ONCE if (0)
103#else
104static int DEBUG_OVERRIDE_FLAG = 0;
105static bool DEBUG_TEMP_FLAG;
106#define DEBUG_OVERRIDE(state) { state ? ++DEBUG_OVERRIDE_FLAG : --DEBUG_OVERRIDE_FLAG; }
107#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)
108#define DEBUG_ONCE_STR(str) DEBUG_ONCE qDebug() << (str);
109#endif
110
111#ifdef Q_WS_X11
112static bool qt_nvidiaFboNeedsFinish = false;
113#endif
114
115static inline void qt_glColor4ubv(unsigned char *col)
116{
117 glColor4f(col[0]/255.0f, col[1]/255.0f, col[2]/255.0f, col[3]/255.0f);
118}
119
120struct QT_PointF {
121 qreal x;
122 qreal y;
123};
124
125struct QGLTrapezoid
126{
127 QGLTrapezoid()
128 {}
129
130 QGLTrapezoid(qreal top_, qreal bottom_, qreal topLeftX_, qreal topRightX_, qreal bottomLeftX_, qreal bottomRightX_)
131 : top(top_),
132 bottom(bottom_),
133 topLeftX(topLeftX_),
134 topRightX(topRightX_),
135 bottomLeftX(bottomLeftX_),
136 bottomRightX(bottomRightX_)
137 {}
138
139 const QGLTrapezoid translated(const QPointF &delta) const;
140
141 qreal top;
142 qreal bottom;
143 qreal topLeftX;
144 qreal topRightX;
145 qreal bottomLeftX;
146 qreal bottomRightX;
147};
148
149const QGLTrapezoid QGLTrapezoid::translated(const QPointF &delta) const
150{
151 QGLTrapezoid trap(*this);
152 trap.top += delta.y();
153 trap.bottom += delta.y();
154 trap.topLeftX += delta.x();
155 trap.topRightX += delta.x();
156 trap.bottomLeftX += delta.x();
157 trap.bottomRightX += delta.x();
158 return trap;
159}
160
161
162class QOpenGLImmediateModeTessellator;
163class QGLMaskGenerator;
164class QGLOffscreen;
165
166class QGLMaskTextureCache
167{
168public:
169 void setOffscreenSize(const QSize &offscreenSize);
170 void setDrawableSize(const QSize &drawableSize);
171
172 struct CacheLocation {
173 QRect rect;
174 int channel;
175
176 QRect screen_rect;
177 };
178
179 struct CacheInfo {
180 inline CacheInfo(const QPainterPath &p, const QTransform &m, qreal w = -1) :
181 path(p), matrix(m), stroke_width(w), age(0) {}
182
183 QPainterPath path;
184 QTransform matrix;
185 qreal stroke_width;
186
187 CacheLocation loc;
188
189 int age;
190 };
191
192 struct QuadTreeNode {
193 quint64 key;
194
195 int largest_available_block;
196 int largest_used_block;
197 };
198
199 CacheLocation getMask(QGLMaskGenerator &maskGenerator, QOpenGLPaintEnginePrivate *engine);
200
201 typedef QMultiHash<quint64, CacheInfo> QGLTextureCacheHash;
202
203 enum {block_size = 64};
204
205 // throw out keys that are too old
206 void maintainCache();
207 void clearCache();
208
209private:
210 quint64 hash(const QPainterPath &p, const QTransform &m, qreal w);
211
212 void createMask(quint64 key, CacheInfo &info, QGLMaskGenerator &maskGenerator);
213
214 QSize offscreenSize;
215 QSize drawableSize;
216
217 QGLTextureCacheHash cache;
218
219 QVector<QuadTreeNode> occupied_quadtree[4];
220
221 void quadtreeUpdate(int channel, int node, int current_block_size);
222 void quadtreeAllocate(quint64 key, const QSize &size, QRect *rect, int *channel);
223
224 bool quadtreeFindAvailableLocation(const QSize &size, QRect *rect, int *channel);
225 void quadtreeFindExistingLocation(const QSize &size, QRect *rect, int *channel);
226
227 void quadtreeInsert(int channel, quint64 key, const QRect &rect, int node = 0);
228 void quadtreeClear(int channel, const QRect &rect, int node = 0);
229
230 int quadtreeBlocksize(int node);
231 QPoint quadtreeLocation(int node);
232
233 QOpenGLPaintEnginePrivate *engine;
234};
235
236Q_GLOBAL_STATIC(QGLMaskTextureCache, qt_mask_texture_cache)
237
238class QGLOffscreen : public QObject
239{
240 Q_OBJECT
241public:
242 QGLOffscreen()
243 : QObject(),
244 offscreen(0),
245 ctx(0),
246 mask_dim(0),
247 activated(false),
248 bound(false)
249 {
250 connect(QGLSignalProxy::instance(),
251 SIGNAL(aboutToDestroyContext(const QGLContext*)),
252 SLOT(cleanupGLContextRefs(const QGLContext*)));
253 }
254
255 inline void setDevice(QPaintDevice *pdev);
256
257 void begin();
258 void end();
259
260 inline void bind();
261 inline void release();
262
263 inline bool isBound() const;
264
265 inline QSize drawableSize() const;
266 inline QSize offscreenSize() const;
267
268 inline GLuint offscreenTexture() const;
269
270 QGLContext *context() const;
271
272 static bool isSupported();
273
274 inline void initialize();
275
276 inline bool isValid() const;
277
278public Q_SLOTS:
279 void cleanupGLContextRefs(const QGLContext *context) {
280 if (context == ctx) {
281 delete offscreen;
282 ctx = 0;
283 offscreen = 0;
284 mask_dim = 0;
285 }
286 }
287
288private:
289 QGLPaintDevice* device;
290
291 QGLFramebufferObject *offscreen;
292 QGLContext *ctx;
293
294 // dimensions of mask texture (square)
295 int mask_dim;
296 QSize last_failed_size;
297
298 bool drawable_fbo;
299
300 bool activated;
301 bool initialized;
302
303 bool bound;
304};
305
306inline void QGLOffscreen::setDevice(QPaintDevice *pdev)
307{
308 if (pdev->devType() == QInternal::OpenGL)
309 device = static_cast<QGLPaintDevice*>(pdev);
310 else
311 device = QGLPaintDevice::getDevice(pdev);
312
313 if (!device)
314 return;
315
316 drawable_fbo = (pdev->devType() == QInternal::FramebufferObject);
317}
318
319void QGLOffscreen::begin()
320{
321#ifndef QT_OPENGL_ES
322 initialized = false;
323
324 if (activated)
325 initialize();
326#endif
327}
328
329void QGLOffscreen::initialize()
330{
331#ifndef QT_OPENGL_ES
332 if (initialized)
333 return;
334
335 activated = true;
336 initialized = true;
337
338 int dim = qMax(2048, static_cast<int>(qt_next_power_of_two(qMax(device->size().width(), device->size().height()))));
339
340 bool shared_context = QGLContext::areSharing(device->context(), ctx);
341 bool would_fail = last_failed_size.isValid() &&
342 (device->size().width() >= last_failed_size.width() ||
343 device->size().height() >= last_failed_size.height());
344 bool needs_refresh = dim > mask_dim || !shared_context;
345
346 if (needs_refresh && !would_fail) {
347 DEBUG_ONCE qDebug() << "QGLOffscreen::initialize(): creating offscreen of size" << dim;
348 delete offscreen;
349 offscreen = new QGLFramebufferObject(dim, dim, GLenum(GL_TEXTURE_2D));
350 mask_dim = dim;
351
352 if (!offscreen->isValid()) {
353 qWarning("QGLOffscreen: Invalid offscreen fbo (size %dx%d)", mask_dim, mask_dim);
354 delete offscreen;
355 offscreen = 0;
356 mask_dim = 0;
357 last_failed_size = device->size();
358 }
359 }
360
361 qt_mask_texture_cache()->setOffscreenSize(offscreenSize());
362 qt_mask_texture_cache()->setDrawableSize(device->size());
363 ctx = device->context();
364#endif
365}
366
367inline bool QGLOffscreen::isValid() const
368{
369 return offscreen;
370}
371
372void QGLOffscreen::end()
373{
374 if (bound)
375 release();
376#ifdef DEBUG_DISPLAY_MASK_TEXTURE
377 glReadBuffer(GL_BACK);
378 glDrawBuffer(GL_BACK);
379 glMatrixMode(GL_MODELVIEW);
380 glLoadIdentity();
381 glColor4f(1, 1, 1, 1);
382 glDisable(GL_DEPTH_TEST);
383 glBlendFunc(GL_ONE, GL_ZERO);
384 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
385 glEnable(GL_TEXTURE_2D);
386 glBindTexture(GL_TEXTURE_2D, offscreen->texture());
387
388 glBegin(GL_QUADS);
389 glTexCoord2f(0.0, 1.0); glVertex2f(0.0, 0.0);
390 glTexCoord2f(1.0, 1.0); glVertex2f(drawable.size().width(), 0.0);
391 glTexCoord2f(1.0, 0.0); glVertex2f(drawable.size().width(), drawable.size().height());
392 glTexCoord2f(0.0, 0.0); glVertex2f(0.0, drawable.size().height());
393 glEnd();
394
395 glBindTexture(GL_TEXTURE_2D, 0);
396 glDisable(GL_TEXTURE_2D);
397#endif
398}
399
400inline void QGLOffscreen::bind()
401{
402#ifndef QT_OPENGL_ES
403 Q_ASSERT(initialized);
404
405 if (!offscreen || bound)
406 return;
407
408 DEBUG_ONCE qDebug() << "QGLOffscreen: binding offscreen";
409 offscreen->bind();
410
411 bound = true;
412
413 glViewport(0, 0, offscreenSize().width(), offscreenSize().height());
414
415 glMatrixMode(GL_PROJECTION);
416 glLoadIdentity();
417 glOrtho(0, offscreenSize().width(), offscreenSize().height(), 0, -999999, 999999);
418 glMatrixMode(GL_MODELVIEW);
419#endif
420}
421
422inline void QGLOffscreen::release()
423{
424#ifndef QT_OPENGL_ES
425 if (!offscreen || !bound)
426 return;
427
428#ifdef Q_WS_X11
429 // workaround for bug in nvidia driver versions 9x.xx
430 if (qt_nvidiaFboNeedsFinish)
431 glFinish();
432#endif
433
434 DEBUG_ONCE_STR("QGLOffscreen: releasing offscreen");
435
436 if (drawable_fbo)
437 device->ensureActiveTarget(); //###
438 else
439 offscreen->release();
440
441 QSize sz(device->size());
442 glViewport(0, 0, sz.width(), sz.height());
443
444 glMatrixMode(GL_PROJECTION);
445 glLoadIdentity();
446#ifndef QT_OPENGL_ES
447 glOrtho(0, sz.width(), sz.height(), 0, -999999, 999999);
448#else
449 glOrthof(0, sz.width(), sz.height(), 0, -999999, 999999);
450#endif
451 glMatrixMode(GL_MODELVIEW);
452
453 bound = false;
454#endif
455}
456
457inline bool QGLOffscreen::isBound() const
458{
459 return bound;
460}
461
462inline QSize QGLOffscreen::drawableSize() const
463{
464 return device->size();
465}
466
467inline QSize QGLOffscreen::offscreenSize() const
468{
469 return QSize(mask_dim, mask_dim);
470}
471
472inline GLuint QGLOffscreen::offscreenTexture() const
473{
474 return offscreen ? offscreen->texture() : 0;
475}
476
477inline QGLContext *QGLOffscreen::context() const
478{
479 return ctx;
480}
481
482bool QGLOffscreen::isSupported()
483{
484 return (QGLExtensions::glExtensions() & QGLExtensions::FramebufferObject); // for fbo
485}
486
487struct QDrawQueueItem
488{
489 QDrawQueueItem(qreal _opacity,
490 QBrush _brush,
491 const QPointF &_brush_origion,
492 QPainter::CompositionMode _composition_mode,
493 const QTransform &_matrix,
494 QGLMaskTextureCache::CacheLocation _location)
495 : opacity(_opacity),
496 brush(_brush),
497 brush_origin(_brush_origion),
498 composition_mode(_composition_mode),
499 matrix(_matrix),
500 location(_location) {}
501 qreal opacity;
502 QBrush brush;
503 QPointF brush_origin;
504 QPainter::CompositionMode composition_mode;
505
506 QTransform matrix;
507 QGLMaskTextureCache::CacheLocation location;
508};
509
510////////// GL program cache: start
511
512typedef struct {
513 int brush; // brush index or mask index
514 int mode; // composition mode index
515 bool mask;
516 GLuint program;
517} GLProgram;
518
519typedef QMultiHash<const QGLContext *, GLProgram> QGLProgramHash;
520
521class QGLProgramCache : public QObject
522{
523 Q_OBJECT
524public:
525 QGLProgramCache() {
526 // we have to know when a context is deleted so we can free
527 // any program handles it holds
528 connect(QGLSignalProxy::instance(), SIGNAL(aboutToDestroyContext(const QGLContext*)),
529 SLOT(cleanupPrograms(const QGLContext*)));
530
531 }
532 ~QGLProgramCache() {
533 // at this point the cache should contain 0 elements
534 // Q_ASSERT(program.size() == 0);
535 }
536
537 GLuint getProgram(const QGLContext *ctx, int brush, int mode, bool mask_mode)
538 {
539 // 1. see if we have an entry for the ctx context
540 QList<GLProgram> progs = programs.values(ctx);
541 for (int i=0; i<progs.size(); ++i) {
542 const GLProgram &prg = progs.at(i);
543 if (mask_mode) {
544 if (prg.mask && prg.brush == brush)
545 return prg.program;
546 } else {
547 if (!prg.mask && prg.brush == brush && prg.mode == mode)
548 return prg.program;
549 }
550 }
551
552 // 2. try to find a match in a shared context, and update the
553 // hash with the entry found
554 QList<const QGLContext *> contexts = programs.uniqueKeys();
555 for (int i=0; i<contexts.size(); ++i) {
556 const QGLContext *cx = contexts.at(i);
557 if (cx != ctx && QGLContext::areSharing(cx, ctx)) {
558 QList<GLProgram> progs = programs.values(cx);
559 for (int k=0; k<progs.size(); ++k) {
560 const GLProgram &prg = progs.at(k);
561 if (mask_mode) {
562 if (prg.mask && prg.brush == brush) {
563 programs.insert(ctx, prg);
564 return prg.program;
565 }
566 } else {
567 if (!prg.mask && prg.brush == brush && prg.mode == mode) {
568 programs.insert(ctx, prg);
569 return prg.program;
570 }
571 }
572 }
573 }
574 }
575
576 // 3. compile a new program and place it into the cache
577 // NB! assumes ctx is the current GL context
578 GLProgram prg;
579 prg.brush = brush;
580 prg.mode = mode;
581 prg.mask = mask_mode;
582 glGenProgramsARB(1, &prg.program);
583 glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, prg.program);
584 const char *src = mask_mode
585 ? mask_fragment_program_sources[brush]
586 : painter_fragment_program_sources[brush][mode];
587 // necessary for .NET 2002, apparently
588 const GLbyte *gl_src = reinterpret_cast<const GLbyte *>(src);
589
590 while (glGetError() != GL_NO_ERROR) {} // reset error state
591 glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB,
592 int(strlen(src)), gl_src);
593 if (glGetError() != GL_NO_ERROR) {
594// qDebug() << "QGLProgramCache: Unable to compile fragment program.";
595 glDeleteProgramsARB(1, &prg.program);
596 return 0;
597 }
598
599// qDebug() << "QGLProgramCache: Creating GL program:" << prg.program << hex << ctx;
600 programs.insert(ctx, prg);
601 return prg.program;
602 }
603
604public Q_SLOTS:
605 void cleanupPrograms(const QGLContext *context)
606 {
607 QGLProgramHash::iterator it = programs.begin();
608 while (it != programs.end()) {
609 if (it.key() == context) {
610 if (!context->isSharing()) {
611 // the ctx variable below is needed for the glDeleteProgramARB call
612 // since it is resolved from our extension system
613 // NB! assumes context is the current GL context
614 const QGLContext *ctx = context;
615 // qDebug() << "QGLProgramHash: Deleting GL program:" << it.value().program << hex << it.key();
616 glDeleteProgramsARB(1, &it.value().program);
617 }
618 it = programs.erase(it);
619 } else {
620 ++it;
621 }
622 }
623 }
624
625private:
626 QGLProgramHash programs;
627};
628
629Q_GLOBAL_STATIC(QGLProgramCache, qt_gl_program_cache)
630
631////////// GL program cache: end
632
633class QOpenGLPaintEnginePrivate;
634class QGLPrivateCleanup : public QObject
635{
636 Q_OBJECT
637public:
638 QGLPrivateCleanup(QOpenGLPaintEnginePrivate *priv)
639 : p(priv)
640 {
641 connect(QGLSignalProxy::instance(),
642 SIGNAL(aboutToDestroyContext(const QGLContext*)),
643 SLOT(cleanupGLContextRefs(const QGLContext*)));
644 }
645
646public Q_SLOTS:
647 void cleanupGLContextRefs(const QGLContext *context);
648
649private:
650 QOpenGLPaintEnginePrivate *p;
651};
652
653class QOpenGLPaintEnginePrivate : public QPaintEngineExPrivate
654{
655 Q_DECLARE_PUBLIC(QOpenGLPaintEngine)
656public:
657 QOpenGLPaintEnginePrivate()
658 : opacity(1)
659 , composition_mode(QPainter::CompositionMode_SourceOver)
660 , has_fast_pen(false)
661 , use_stencil_method(false)
662 , dirty_drawable_texture(false)
663 , has_stencil_face_ext(false)
664 , use_fragment_programs(false)
665 , high_quality_antialiasing(false)
666 , use_smooth_pixmap_transform(false)
667 , use_emulation(false)
668 , txop(QTransform::TxNone)
669 , inverseScale(1)
670 , moveToCount(0)
671 , last_created_state(0)
672 , shader_ctx(0)
673 , grad_palette(0)
674 , drawable_texture(0)
675 , ref_cleaner(this)
676 {}
677
678 inline void setGLPen(const QColor &c) {
679 uint alpha = qRound(c.alpha() * opacity);
680 pen_color[0] = qt_div_255(c.red() * alpha);
681 pen_color[1] = qt_div_255(c.green() * alpha);
682 pen_color[2] = qt_div_255(c.blue() * alpha);
683 pen_color[3] = alpha;
684 }
685
686 inline void setGLBrush(const QColor &c) {
687 uint alpha = qRound(c.alpha() * opacity);
688 brush_color[0] = qt_div_255(c.red() * alpha);
689 brush_color[1] = qt_div_255(c.green() * alpha);
690 brush_color[2] = qt_div_255(c.blue() * alpha);
691 brush_color[3] = alpha;
692 }
693
694 inline void setGradientOps(const QBrush &brush, const QRectF &bounds);
695 void createGradientPaletteTexture(const QGradient& g);
696
697 void updateGradient(const QBrush &brush, const QRectF &bounds);
698
699 inline void lineToStencil(qreal x, qreal y);
700 inline void curveToStencil(const QPointF &cp1, const QPointF &cp2, const QPointF &ep);
701 void pathToVertexArrays(const QPainterPath &path);
702 void fillVertexArray(Qt::FillRule fillRule);
703 void drawVertexArrays();
704 void fillPath(const QPainterPath &path);
705 void fillPolygon_dev(const QPointF *polygonPoints, int pointCount,
706 Qt::FillRule fill);
707
708 void drawFastRect(const QRectF &rect);
709 void strokePath(const QPainterPath &path, bool use_cache);
710 void strokePathFastPen(const QPainterPath &path, bool needsResolving);
711 void strokeLines(const QPainterPath &path);
712
713 void updateDepthClip();
714 void systemStateChanged();
715
716 void cleanupGLContextRefs(const QGLContext *context) {
717 if (context == shader_ctx)
718 shader_ctx = 0;
719 }
720
721 inline void updateFastPen() {
722 qreal pen_width = cpen.widthF();
723 has_fast_pen =
724 ((pen_width == 0 || (pen_width <= 1 && matrix.type() <= QTransform::TxTranslate))
725 || cpen.isCosmetic())
726 && cpen.style() == Qt::SolidLine
727 && cpen.isSolid();
728
729 }
730
731 void disableClipping();
732 void enableClipping();
733 void ensureDrawableTexture();
734
735 QPen cpen;
736 QBrush cbrush;
737 Qt::BrushStyle brush_style;
738 QPointF brush_origin;
739 Qt::BrushStyle pen_brush_style;
740 qreal opacity;
741 QPainter::CompositionMode composition_mode;
742
743 Qt::BrushStyle current_style;
744
745 uint has_pen : 1;
746 uint has_brush : 1;
747 uint has_fast_pen : 1;
748 uint use_stencil_method : 1;
749 uint dirty_drawable_texture : 1;
750 uint has_stencil_face_ext : 1;
751 uint use_fragment_programs : 1;
752 uint high_quality_antialiasing : 1;
753 uint has_antialiasing : 1;
754 uint has_fast_composition_mode : 1;
755 uint use_smooth_pixmap_transform : 1;
756 uint use_system_clip : 1;
757 uint use_emulation : 1;
758
759 QRegion dirty_stencil;
760
761 void updateUseEmulation();
762
763 QTransform matrix;
764 GLubyte pen_color[4];
765 GLubyte brush_color[4];
766 QTransform::TransformationType txop;
767 QGLPaintDevice* device;
768 QGLOffscreen offscreen;
769
770 qreal inverseScale;
771
772 int moveToCount;
773 QPointF path_start;
774
775 bool isFastRect(const QRectF &r);
776
777 void drawImageAsPath(const QRectF &r, const QImage &img, const QRectF &sr);
778 void drawTiledImageAsPath(const QRectF &r, const QImage &img, qreal sx, qreal sy, const QPointF &offset);
779
780 void drawOffscreenPath(const QPainterPath &path);
781
782 void composite(const QRectF &rect, const QPoint &maskOffset = QPoint());
783 void composite(GLuint primitive, const q_vertexType *vertexArray, int vertexCount, const QPoint &maskOffset = QPoint());
784
785 bool createFragmentPrograms();
786 void deleteFragmentPrograms();
787 void updateFragmentProgramData(int locations[]);
788
789 void cacheItemErased(int channel, const QRect &rect);
790
791 void addItem(const QGLMaskTextureCache::CacheLocation &location);
792 void drawItem(const QDrawQueueItem &item);
793 void flushDrawQueue();
794
795 void copyDrawable(const QRectF &rect);
796
797 void updateGLMatrix() const;
798
799 mutable QPainterState *last_created_state;
800
801 QGLContext *shader_ctx;
802 GLuint grad_palette;
803
804 GLuint painter_fragment_programs[num_fragment_brushes][num_fragment_composition_modes];
805 GLuint mask_fragment_programs[num_fragment_masks];
806
807 float inv_matrix_data[3][4];
808 float fmp_data[4];
809 float fmp2_m_radius2_data[4];
810 float angle_data[4];
811 float linear_data[4];
812
813 float porterduff_ab_data[4];
814 float porterduff_xyz_data[4];
815
816 float mask_offset_data[4];
817 float mask_channel_data[4];
818
819 FragmentBrushType fragment_brush;
820 FragmentCompositionModeType fragment_composition_mode;
821
822 void setPorterDuffData(float a, float b, float x, float y, float z);
823 void setInvMatrixData(const QTransform &inv_matrix);
824
825 qreal max_x;
826 qreal max_y;
827 qreal min_x;
828 qreal min_y;
829
830 QDataBuffer<QPointF> tess_points;
831 QVector<int> tess_points_stops;
832
833 GLdouble projection_matrix[4][4];
834
835#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_2)
836 GLfloat mv_matrix[4][4];
837#else
838 GLdouble mv_matrix[4][4];
839#endif
840
841 QList<QDrawQueueItem> drawQueue;
842
843 GLuint drawable_texture;
844 QSize drawable_texture_size;
845
846 int max_texture_size;
847
848 QGLPrivateCleanup ref_cleaner;
849 friend class QGLMaskTextureCache;
850};
851
852class QOpenGLCoordinateOffset
853{
854public:
855 QOpenGLCoordinateOffset(QOpenGLPaintEnginePrivate *d);
856 ~QOpenGLCoordinateOffset();
857
858 static void enableOffset(QOpenGLPaintEnginePrivate *d);
859 static void disableOffset(QOpenGLPaintEnginePrivate *d);
860
861private:
862 QOpenGLPaintEnginePrivate *d;
863};
864
865QOpenGLCoordinateOffset::QOpenGLCoordinateOffset(QOpenGLPaintEnginePrivate *d_)
866 : d(d_)
867{
868 enableOffset(d);
869}
870
871void QOpenGLCoordinateOffset::enableOffset(QOpenGLPaintEnginePrivate *d)
872{
873 if (!d->has_antialiasing) {
874 glMatrixMode(GL_MODELVIEW);
875 glPushMatrix();
876 d->mv_matrix[3][0] += 0.5;
877 d->mv_matrix[3][1] += 0.5;
878 d->updateGLMatrix();
879 }
880}
881
882QOpenGLCoordinateOffset::~QOpenGLCoordinateOffset()
883{
884 disableOffset(d);
885}
886
887void QOpenGLCoordinateOffset::disableOffset(QOpenGLPaintEnginePrivate *d)
888{
889 if (!d->has_antialiasing) {
890 glMatrixMode(GL_MODELVIEW);
891 glPopMatrix();
892 d->mv_matrix[3][0] -= 0.5;
893 d->mv_matrix[3][1] -= 0.5;
894 }
895}
896
897void QGLPrivateCleanup::cleanupGLContextRefs(const QGLContext *context)
898{
899 p->cleanupGLContextRefs(context);
900}
901
902
903static inline void updateTextureFilter(GLenum target, GLenum wrapMode, bool smoothPixmapTransform)
904{
905 if (smoothPixmapTransform) {
906 glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
907 glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
908 } else {
909 glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
910 glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
911 }
912 glTexParameterf(target, GL_TEXTURE_WRAP_S, wrapMode);
913 glTexParameterf(target, GL_TEXTURE_WRAP_T, wrapMode);
914}
915
916static inline QPainterPath strokeForPath(const QPainterPath &path, const QPen &cpen) {
917 QPainterPathStroker stroker;
918 if (cpen.style() == Qt::CustomDashLine)
919 stroker.setDashPattern(cpen.dashPattern());
920 else
921 stroker.setDashPattern(cpen.style());
922
923 stroker.setCapStyle(cpen.capStyle());
924 stroker.setJoinStyle(cpen.joinStyle());
925 stroker.setMiterLimit(cpen.miterLimit());
926
927 qreal width = cpen.widthF();
928 if (width == 0)
929 stroker.setWidth(1);
930 else
931 stroker.setWidth(width);
932
933 QPainterPath stroke = stroker.createStroke(path);
934 stroke.setFillRule(Qt::WindingFill);
935 return stroke;
936}
937
938class QGLStrokeCache
939{
940 struct CacheInfo
941 {
942 inline CacheInfo(QPainterPath p, QPainterPath sp, QPen stroke_pen) :
943 path(p), stroked_path(sp), pen(stroke_pen) {}
944 QPainterPath path;
945 QPainterPath stroked_path;
946 QPen pen;
947 };
948
949 typedef QMultiHash<quint64, CacheInfo> QGLStrokeTableHash;
950
951public:
952 inline QPainterPath getStrokedPath(const QPainterPath &path, const QPen &pen) {
953 quint64 hash_val = 0;
954
955 for (int i = 0; i < path.elementCount() && i <= 2; i++) {
956 hash_val += quint64(path.elementAt(i).x);
957 hash_val += quint64(path.elementAt(i).y);
958 }
959
960 QGLStrokeTableHash::const_iterator it = cache.constFind(hash_val);
961
962 if (it == cache.constEnd())
963 return addCacheElement(hash_val, path, pen);
964 else {
965 do {
966 const CacheInfo &cache_info = it.value();
967 if (cache_info.path == path && cache_info.pen == pen)
968 return cache_info.stroked_path;
969 ++it;
970 } while (it != cache.constEnd() && it.key() == hash_val);
971 // an exact match for this path was not found, create new cache element
972 return addCacheElement(hash_val, path, pen);
973 }
974 }
975
976protected:
977 inline int maxCacheSize() const { return 500; }
978 QPainterPath addCacheElement(quint64 hash_val, QPainterPath path, const QPen &pen) {
979 if (cache.size() == maxCacheSize()) {
980 int elem_to_remove = qrand() % maxCacheSize();
981 cache.remove(cache.keys()[elem_to_remove]); // may remove more than 1, but OK
982 }
983 QPainterPath stroke = strokeForPath(path, pen);
984 CacheInfo cache_entry(path, stroke, pen);
985 return cache.insert(hash_val, cache_entry).value().stroked_path;
986 }
987
988 QGLStrokeTableHash cache;
989};
990
991Q_GLOBAL_STATIC(QGLStrokeCache, qt_opengl_stroke_cache)
992
993class QGLGradientCache : public QObject
994{
995 Q_OBJECT
996 struct CacheInfo
997 {
998 inline CacheInfo(QGradientStops s, qreal op, QGradient::InterpolationMode mode) :
999 stops(s), opacity(op), interpolationMode(mode) {}
1000
1001 GLuint texId;
1002 QGradientStops stops;
1003 qreal opacity;
1004 QGradient::InterpolationMode interpolationMode;
1005 };
1006
1007 typedef QMultiHash<quint64, CacheInfo> QGLGradientColorTableHash;
1008
1009public:
1010 QGLGradientCache() : QObject(), buffer_ctx(0)
1011 {
1012 connect(QGLSignalProxy::instance(),
1013 SIGNAL(aboutToDestroyContext(const QGLContext*)),
1014 SLOT(cleanupGLContextRefs(const QGLContext*)));
1015 }
1016
1017 inline GLuint getBuffer(const QGradient &gradient, qreal opacity, QGLContext *ctx) {
1018 if (buffer_ctx && !QGLContext::areSharing(buffer_ctx, ctx))
1019 cleanCache();
1020
1021 buffer_ctx = ctx;
1022
1023 quint64 hash_val = 0;
1024
1025 QGradientStops stops = gradient.stops();
1026 for (int i = 0; i < stops.size() && i <= 2; i++)
1027 hash_val += stops[i].second.rgba();
1028
1029 QGLGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
1030
1031 if (it == cache.constEnd())
1032 return addCacheElement(hash_val, gradient, opacity);
1033 else {
1034 do {
1035 const CacheInfo &cache_info = it.value();
1036 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode()) {
1037 return cache_info.texId;
1038 }
1039 ++it;
1040 } while (it != cache.constEnd() && it.key() == hash_val);
1041 // an exact match for these stops and opacity was not found, create new cache
1042 return addCacheElement(hash_val, gradient, opacity);
1043 }
1044 }
1045
1046 inline int paletteSize() const { return 1024; }
1047
1048protected:
1049 inline int maxCacheSize() const { return 60; }
1050 inline void generateGradientColorTable(const QGradient& g,
1051 uint *colorTable,
1052 int size, qreal opacity) const;
1053 GLuint addCacheElement(quint64 hash_val, const QGradient &gradient, qreal opacity) {
1054 if (cache.size() == maxCacheSize()) {
1055 int elem_to_remove = qrand() % maxCacheSize();
1056 quint64 key = cache.keys()[elem_to_remove];
1057
1058 // need to call glDeleteTextures on each removed cache entry:
1059 QGLGradientColorTableHash::const_iterator it = cache.constFind(key);
1060 do {
1061 glDeleteTextures(1, &it.value().texId);
1062 } while (++it != cache.constEnd() && it.key() == key);
1063 cache.remove(key); // may remove more than 1, but OK
1064 }
1065 CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
1066 uint buffer[1024];
1067 generateGradientColorTable(gradient, buffer, paletteSize(), opacity);
1068 glGenTextures(1, &cache_entry.texId);
1069#ifndef QT_OPENGL_ES
1070 glBindTexture(GL_TEXTURE_1D, cache_entry.texId);
1071 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, paletteSize(),
1072 0, GL_BGRA, GL_UNSIGNED_BYTE, buffer);
1073#else
1074 // create 2D one-line texture instead. This requires an impl of manual GL_TEXGEN for all primitives
1075#endif
1076 return cache.insert(hash_val, cache_entry).value().texId;
1077 }
1078
1079 void cleanCache() {
1080 QGLShareContextScope scope(buffer_ctx);
1081 QGLGradientColorTableHash::const_iterator it = cache.constBegin();
1082 for (; it != cache.constEnd(); ++it) {
1083 const CacheInfo &cache_info = it.value();
1084 glDeleteTextures(1, &cache_info.texId);
1085 }
1086 cache.clear();
1087 }
1088
1089 QGLGradientColorTableHash cache;
1090
1091 QGLContext *buffer_ctx;
1092
1093public Q_SLOTS:
1094 void cleanupGLContextRefs(const QGLContext *context) {
1095 if (context == buffer_ctx) {
1096 cleanCache();
1097 buffer_ctx = 0;
1098 }
1099 }
1100};
1101
1102static inline uint endianColor(uint c)
1103{
1104#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
1105 return c;
1106#else
1107 return ( (c << 24) & 0xff000000)
1108 | ((c >> 24) & 0x000000ff)
1109 | ((c << 8) & 0x00ff0000)
1110 | ((c >> 8) & 0x0000ff00);
1111#endif // Q_BYTE_ORDER
1112}
1113
1114void QGLGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, qreal opacity) const
1115{
1116 int pos = 0;
1117 QGradientStops s = gradient.stops();
1118 QVector<uint> colors(s.size());
1119
1120 for (int i = 0; i < s.size(); ++i)
1121 colors[i] = s[i].second.rgba();
1122
1123 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
1124
1125 uint alpha = qRound(opacity * 256);
1126 uint current_color = ARGB_COMBINE_ALPHA(colors[0], alpha);
1127 qreal incr = 1.0 / qreal(size);
1128 qreal fpos = 1.5 * incr;
1129 colorTable[pos++] = endianColor(PREMUL(current_color));
1130
1131 while (fpos <= s.first().first) {
1132 colorTable[pos] = colorTable[pos - 1];
1133 pos++;
1134 fpos += incr;
1135 }
1136
1137 if (colorInterpolation)
1138 current_color = PREMUL(current_color);
1139
1140 for (int i = 0; i < s.size() - 1; ++i) {
1141 qreal delta = 1/(s[i+1].first - s[i].first);
1142 uint next_color = ARGB_COMBINE_ALPHA(colors[i+1], alpha);
1143 if (colorInterpolation)
1144 next_color = PREMUL(next_color);
1145
1146 while (fpos < s[i+1].first && pos < size) {
1147 int dist = int(256 * ((fpos - s[i].first) * delta));
1148 int idist = 256 - dist;
1149 if (colorInterpolation)
1150 colorTable[pos] = endianColor(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
1151 else
1152 colorTable[pos] = endianColor(PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist)));
1153 ++pos;
1154 fpos += incr;
1155 }
1156 current_color = next_color;
1157 }
1158
1159 Q_ASSERT(s.size() > 0);
1160
1161 uint last_color = endianColor(PREMUL(ARGB_COMBINE_ALPHA(colors[s.size() - 1], alpha)));
1162 for (;pos < size; ++pos)
1163 colorTable[pos] = last_color;
1164
1165 // Make sure the last color stop is represented at the end of the table
1166 colorTable[size-1] = last_color;
1167}
1168
1169#ifndef Q_WS_QWS
1170Q_GLOBAL_STATIC(QGLGradientCache, qt_opengl_gradient_cache)
1171#endif
1172
1173void QOpenGLPaintEnginePrivate::createGradientPaletteTexture(const QGradient& g)
1174{
1175#ifdef QT_OPENGL_ES //###
1176 Q_UNUSED(g);
1177#else
1178 GLuint texId = qt_opengl_gradient_cache()->getBuffer(g, opacity, device->context());
1179 glBindTexture(GL_TEXTURE_1D, texId);
1180 grad_palette = texId;
1181 if (g.spread() == QGradient::RepeatSpread || g.type() == QGradient::ConicalGradient)
1182 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
1183 else if (g.spread() == QGradient::ReflectSpread)
1184 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT_IBM);
1185 else
1186 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1187
1188 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1189 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1190
1191#endif
1192}
1193
1194
1195inline void QOpenGLPaintEnginePrivate::setGradientOps(const QBrush &brush, const QRectF &bounds)
1196{
1197 current_style = brush.style();
1198
1199 if (current_style < Qt::LinearGradientPattern || current_style > Qt::ConicalGradientPattern) {
1200 setGLBrush(brush.color());
1201 qt_glColor4ubv(brush_color);
1202 }
1203
1204 updateGradient(brush, bounds);
1205
1206#ifndef QT_OPENGL_ES //### GLES does not have GL_TEXTURE_GEN_ so we are falling back for gradients
1207 glDisable(GL_TEXTURE_GEN_S);
1208 glDisable(GL_TEXTURE_1D);
1209
1210 if (current_style == Qt::LinearGradientPattern) {
1211 if (high_quality_antialiasing || !has_fast_composition_mode) {
1212 fragment_brush = FRAGMENT_PROGRAM_BRUSH_LINEAR;
1213 } else {
1214 glEnable(GL_TEXTURE_GEN_S);
1215 glEnable(GL_TEXTURE_1D);
1216 }
1217 } else {
1218 if (use_fragment_programs) {
1219 if (current_style == Qt::RadialGradientPattern)
1220 fragment_brush = FRAGMENT_PROGRAM_BRUSH_RADIAL;
1221 else if (current_style == Qt::ConicalGradientPattern)
1222 fragment_brush = FRAGMENT_PROGRAM_BRUSH_CONICAL;
1223 else if (current_style == Qt::SolidPattern)
1224 fragment_brush = FRAGMENT_PROGRAM_BRUSH_SOLID;
1225 else if (current_style == Qt::TexturePattern && !brush.texture().isQBitmap())
1226 fragment_brush = FRAGMENT_PROGRAM_BRUSH_TEXTURE;
1227 else
1228 fragment_brush = FRAGMENT_PROGRAM_BRUSH_PATTERN;
1229 }
1230 }
1231#endif
1232}
1233
1234QOpenGLPaintEngine::QOpenGLPaintEngine()
1235 : QPaintEngineEx(*(new QOpenGLPaintEnginePrivate))
1236{
1237}
1238
1239QOpenGLPaintEngine::~QOpenGLPaintEngine()
1240{
1241}
1242
1243bool QOpenGLPaintEngine::begin(QPaintDevice *pdev)
1244{
1245 Q_D(QOpenGLPaintEngine);
1246
1247 if (pdev->devType() == QInternal::OpenGL)
1248 d->device = static_cast<QGLPaintDevice*>(pdev);
1249 else
1250 d->device = QGLPaintDevice::getDevice(pdev);
1251
1252 if (!d->device)
1253 return false;
1254
1255 d->offscreen.setDevice(pdev);
1256 d->has_fast_pen = false;
1257 d->inverseScale = 1;
1258 d->opacity = 1;
1259 d->device->beginPaint();
1260 d->matrix = QTransform();
1261 d->has_antialiasing = false;
1262 d->high_quality_antialiasing = false;
1263
1264 QSize sz(d->device->size());
1265 d->dirty_stencil = QRect(0, 0, sz.width(), sz.height());
1266
1267 d->use_emulation = false;
1268
1269 for (int i = 0; i < 4; ++i)
1270 for (int j = 0; j < 4; ++j)
1271 d->mv_matrix[i][j] = (i == j ? qreal(1) : qreal(0));
1272
1273 bool has_frag_program = (QGLExtensions::glExtensions() & QGLExtensions::FragmentProgram)
1274 && (pdev->devType() != QInternal::Pixmap);
1275
1276 QGLContext *ctx = const_cast<QGLContext *>(d->device->context());
1277 if (!ctx) {
1278 qWarning() << "QOpenGLPaintEngine: paint device doesn't have a valid GL context.";
1279 return false;
1280 }
1281
1282 if (has_frag_program)
1283 has_frag_program = qt_resolve_frag_program_extensions(ctx) && qt_resolve_version_1_3_functions(ctx);
1284
1285 d->use_stencil_method = d->device->format().stencil()
1286 && (QGLExtensions::glExtensions() & QGLExtensions::StencilWrap);
1287 if (d->device->format().directRendering()
1288 && (d->use_stencil_method && QGLExtensions::glExtensions() & QGLExtensions::StencilTwoSide))
1289 d->has_stencil_face_ext = qt_resolve_stencil_face_extension(ctx);
1290
1291#ifdef Q_WS_X11
1292 static bool nvidia_workaround_needs_init = true;
1293 if (nvidia_workaround_needs_init) {
1294 // nvidia 9x.xx unix drivers contain a bug which requires us to
1295 // call glFinish before releasing an fbo to avoid painting
1296 // artifacts
1297 const QByteArray versionString(reinterpret_cast<const char*>(glGetString(GL_VERSION)));
1298 const int pos = versionString.indexOf("NVIDIA");
1299 if (pos >= 0) {
1300 const float nvidiaDriverVersion = versionString.mid(pos + strlen("NVIDIA")).toFloat();
1301 qt_nvidiaFboNeedsFinish = nvidiaDriverVersion >= 90.0 && nvidiaDriverVersion < 100.0;
1302 }
1303 nvidia_workaround_needs_init = false;
1304 }
1305#endif
1306
1307#ifndef QT_OPENGL_ES
1308 if (!ctx->d_ptr->internal_context) {
1309 glGetDoublev(GL_PROJECTION_MATRIX, &d->projection_matrix[0][0]);
1310 glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
1311 glPushAttrib(GL_ALL_ATTRIB_BITS);
1312
1313 glDisableClientState(GL_EDGE_FLAG_ARRAY);
1314 glDisableClientState(GL_INDEX_ARRAY);
1315 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1316 glDisable(GL_TEXTURE_1D);
1317 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1318 glPixelTransferi(GL_MAP_COLOR, false);
1319 glPixelTransferi(GL_MAP_STENCIL, false);
1320 glDisable(GL_TEXTURE_GEN_S);
1321
1322 glPixelStorei(GL_PACK_SWAP_BYTES, false);
1323 glPixelStorei(GL_PACK_LSB_FIRST, false);
1324 glPixelStorei(GL_PACK_ROW_LENGTH, 0);
1325 glPixelStorei(GL_PACK_SKIP_ROWS, 0);
1326 glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
1327 glPixelStorei(GL_PACK_ALIGNMENT, 4);
1328
1329 glPixelStorei(GL_UNPACK_SWAP_BYTES, false);
1330 glPixelStorei(GL_UNPACK_LSB_FIRST, false);
1331 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1332 glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
1333 glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
1334 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
1335
1336 if (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_2) {
1337 glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0);
1338 glPixelStorei(GL_PACK_SKIP_IMAGES, 0);
1339 glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
1340 glPixelStorei(GL_UNPACK_SKIP_IMAGES, 0);
1341 }
1342 }
1343#endif
1344
1345 if (!ctx->d_ptr->internal_context) {
1346 glMatrixMode(GL_MODELVIEW);
1347 glPushMatrix();
1348 glMatrixMode(GL_TEXTURE);
1349 glPushMatrix();
1350 glLoadIdentity();
1351 glDisableClientState(GL_COLOR_ARRAY);
1352 glDisableClientState(GL_NORMAL_ARRAY);
1353 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1354 glDisableClientState(GL_VERTEX_ARRAY);
1355
1356 if (QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers)
1357 glDisable(GL_MULTISAMPLE);
1358 glDisable(GL_TEXTURE_2D);
1359 if (QGLExtensions::glExtensions() & QGLExtensions::TextureRectangle)
1360 glDisable(GL_TEXTURE_RECTANGLE_NV);
1361 glDisable(GL_STENCIL_TEST);
1362 glDisable(GL_CULL_FACE);
1363 glDisable(GL_LIGHTING);
1364 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1365 }
1366
1367 d->offscreen.begin();
1368
1369 glViewport(0, 0, sz.width(), sz.height()); // XXX (Embedded): We need a solution for GLWidgets that draw in a part or a bigger surface...
1370 glMatrixMode(GL_PROJECTION);
1371 glLoadIdentity();
1372#ifdef QT_OPENGL_ES
1373 glOrthof(0, sz.width(), sz.height(), 0, -999999, 999999);
1374#else
1375 glOrtho(0, sz.width(), sz.height(), 0, -999999, 999999);
1376#endif
1377 glMatrixMode(GL_MODELVIEW);
1378 glLoadIdentity();
1379 glEnable(GL_BLEND);
1380 d->composition_mode = QPainter::CompositionMode_SourceOver;
1381
1382#ifdef QT_OPENGL_ES
1383 d->max_texture_size = ctx->d_func()->maxTextureSize();
1384#else
1385 bool shared_ctx = QGLContext::areSharing(d->device->context(), d->shader_ctx);
1386
1387 if (shared_ctx) {
1388 d->max_texture_size = d->shader_ctx->d_func()->maxTextureSize();
1389 } else {
1390 d->max_texture_size = ctx->d_func()->maxTextureSize();
1391
1392 if (d->shader_ctx) {
1393 d->shader_ctx->makeCurrent();
1394 glBindTexture(GL_TEXTURE_1D, 0);
1395 glDeleteTextures(1, &d->grad_palette);
1396
1397 if (has_frag_program && d->use_fragment_programs)
1398 glDeleteTextures(1, &d->drawable_texture);
1399 ctx->makeCurrent();
1400 }
1401 d->shader_ctx = d->device->context();
1402 glGenTextures(1, &d->grad_palette);
1403
1404 qt_mask_texture_cache()->clearCache();
1405 d->use_fragment_programs = has_frag_program;
1406 }
1407
1408 if (d->use_fragment_programs && (!shared_ctx || sz.width() > d->drawable_texture_size.width()
1409 || sz.height() > d->drawable_texture_size.height()))
1410 {
1411 // delete old texture if size has increased, otherwise it was deleted earlier
1412 if (shared_ctx)
1413 glDeleteTextures(1, &d->drawable_texture);
1414
1415 d->dirty_drawable_texture = true;
1416 d->drawable_texture_size = QSize(qt_next_power_of_two(sz.width()),
1417 qt_next_power_of_two(sz.height()));
1418 }
1419#endif
1420
1421 updateClipRegion(QRegion(), Qt::NoClip);
1422 penChanged();
1423 brushChanged();
1424 opacityChanged();
1425 compositionModeChanged();
1426 renderHintsChanged();
1427 transformChanged();
1428 return true;
1429}
1430
1431bool QOpenGLPaintEngine::end()
1432{
1433 Q_D(QOpenGLPaintEngine);
1434 d->flushDrawQueue();
1435 d->offscreen.end();
1436 QGLContext *ctx = const_cast<QGLContext *>(d->device->context());
1437 if (!ctx->d_ptr->internal_context) {
1438 glMatrixMode(GL_TEXTURE);
1439 glPopMatrix();
1440 glMatrixMode(GL_MODELVIEW);
1441 glPopMatrix();
1442 }
1443#ifndef QT_OPENGL_ES
1444 if (ctx->d_ptr->internal_context) {
1445 glDisable(GL_SCISSOR_TEST);
1446 } else {
1447 glMatrixMode(GL_PROJECTION);
1448 glLoadMatrixd(&d->projection_matrix[0][0]);
1449 glPopAttrib();
1450 glPopClientAttrib();
1451 }
1452#endif
1453 d->device->endPaint();
1454 qt_mask_texture_cache()->maintainCache();
1455
1456#if defined(Q_WS_X11)
1457 // clear out the references we hold for textures bound with the
1458 // texture_from_pixmap extension
1459 ctx->d_func()->boundPixmaps.clear();
1460#endif
1461 return true;
1462}
1463
1464void QOpenGLPaintEngine::updateState(const QPaintEngineState &state)
1465{
1466 Q_D(QOpenGLPaintEngine);
1467 QPaintEngine::DirtyFlags flags = state.state();
1468
1469 bool update_fast_pen = false;
1470
1471 if (flags & DirtyOpacity) {
1472 update_fast_pen = true;
1473 d->opacity = state.opacity();
1474 if (d->opacity > 1.0f)
1475 d->opacity = 1.0f;
1476 if (d->opacity < 0.f)
1477 d->opacity = 0.f;
1478 // force update
1479 flags |= DirtyPen;
1480 flags |= DirtyBrush;
1481 }
1482
1483 if (flags & DirtyTransform) {
1484 update_fast_pen = true;
1485 updateMatrix(state.transform());
1486 // brush setup depends on transform state
1487 if (state.brush().style() != Qt::NoBrush)
1488 flags |= DirtyBrush;
1489 }
1490
1491 if (flags & DirtyPen) {
1492 update_fast_pen = true;
1493 updatePen(state.pen());
1494 }
1495
1496 if (flags & (DirtyBrush | DirtyBrushOrigin)) {
1497 updateBrush(state.brush(), state.brushOrigin());
1498 }
1499
1500 if (flags & DirtyFont) {
1501 updateFont(state.font());
1502 }
1503
1504 if (state.state() & DirtyClipEnabled) {
1505 if (state.isClipEnabled())
1506 updateClipRegion(painter()->clipRegion(), Qt::ReplaceClip);
1507 else
1508 updateClipRegion(QRegion(), Qt::NoClip);
1509 }
1510
1511 if (flags & DirtyClipPath) {
1512 updateClipRegion(QRegion(state.clipPath().toFillPolygon().toPolygon(),
1513 state.clipPath().fillRule()),
1514 state.clipOperation());
1515 }
1516
1517 if (flags & DirtyClipRegion) {
1518 updateClipRegion(state.clipRegion(), state.clipOperation());
1519 }
1520
1521 if (flags & DirtyHints) {
1522 updateRenderHints(state.renderHints());
1523 }
1524
1525 if (flags & DirtyCompositionMode) {
1526 updateCompositionMode(state.compositionMode());
1527 }
1528
1529 if (update_fast_pen) {
1530 Q_D(QOpenGLPaintEngine);
1531 qreal pen_width = d->cpen.widthF();
1532 d->has_fast_pen =
1533 ((pen_width == 0 || (pen_width <= 1 && d->txop <= QTransform::TxTranslate))
1534 || d->cpen.isCosmetic())
1535 && d->cpen.style() == Qt::SolidLine
1536 && d->cpen.isSolid();
1537 }
1538}
1539
1540
1541void QOpenGLPaintEnginePrivate::setInvMatrixData(const QTransform &inv_matrix)
1542{
1543 inv_matrix_data[0][0] = inv_matrix.m11();
1544 inv_matrix_data[1][0] = inv_matrix.m21();
1545 inv_matrix_data[2][0] = inv_matrix.m31();
1546
1547 inv_matrix_data[0][1] = inv_matrix.m12();
1548 inv_matrix_data[1][1] = inv_matrix.m22();
1549 inv_matrix_data[2][1] = inv_matrix.m32();
1550
1551 inv_matrix_data[0][2] = inv_matrix.m13();
1552 inv_matrix_data[1][2] = inv_matrix.m23();
1553 inv_matrix_data[2][2] = inv_matrix.m33();
1554}
1555
1556
1557void QOpenGLPaintEnginePrivate::updateGradient(const QBrush &brush, const QRectF &)
1558{
1559#ifdef QT_OPENGL_ES
1560 Q_UNUSED(brush);
1561#else
1562 bool has_mirrored_repeat = QGLExtensions::glExtensions() & QGLExtensions::MirroredRepeat;
1563 Qt::BrushStyle style = brush.style();
1564
1565 QTransform m = brush.transform();
1566
1567 if (has_mirrored_repeat && style == Qt::LinearGradientPattern) {
1568 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
1569 QTransform m = brush.transform();
1570 QPointF realStart = g->start();
1571 QPointF realFinal = g->finalStop();
1572 QPointF start = m.map(realStart);
1573 QPointF stop;
1574
1575 if (qFuzzyCompare(m.m11(), m.m22()) && m.m12() == 0.0 && m.m21() == 0.0) {
1576 // It is a simple uniform scale and/or translation
1577 stop = m.map(realFinal);
1578 } else {
1579 // It is not enough to just transform the endpoints.
1580 // We have to make sure the _pattern_ is transformed correctly.
1581
1582 qreal odx = realFinal.x() - realStart.x();
1583 qreal ody = realFinal.y() - realStart.y();
1584
1585 // nx, ny and dx, dy are normal and gradient direction after transform:
1586 qreal nx = m.m11()*ody - m.m21()*odx;
1587 qreal ny = m.m12()*ody - m.m22()*odx;
1588
1589 qreal dx = m.m11()*odx + m.m21()*ody;
1590 qreal dy = m.m12()*odx + m.m22()*ody;
1591
1592 qreal lx = 1 / (dx - dy*nx/ny);
1593 qreal ly = 1 / (dy - dx*ny/nx);
1594 qreal l = 1 / qSqrt(lx*lx+ly*ly);
1595
1596 stop = start + QPointF(-ny, nx) * l/qSqrt(nx*nx+ny*ny);
1597 }
1598
1599 float tr[4], f;
1600 tr[0] = stop.x() - start.x();
1601 tr[1] = stop.y() - start.y();
1602 f = 1.0 / (tr[0]*tr[0] + tr[1]*tr[1]);
1603 tr[0] *= f;
1604 tr[1] *= f;
1605 tr[2] = 0;
1606 tr[3] = -(start.x()*tr[0] + start.y()*tr[1]);
1607 brush_color[0] = brush_color[1] = brush_color[2] = brush_color[3] = 255;
1608 qt_glColor4ubv(brush_color);
1609 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
1610 glTexGenfv(GL_S, GL_OBJECT_PLANE, tr);
1611 }
1612
1613 if (use_fragment_programs) {
1614 if (style == Qt::RadialGradientPattern) {
1615 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
1616 QPointF realCenter = g->center();
1617 QPointF realFocal = g->focalPoint();
1618 qreal realRadius = g->radius();
1619 QTransform translate(1, 0, 0, 1, -realFocal.x(), -realFocal.y());
1620 QTransform gl_to_qt(1, 0, 0, -1, 0, pdev->height());
1621 QTransform m = QTransform(matrix).translate(brush_origin.x(), brush_origin.y());
1622 QTransform inv_matrix = gl_to_qt * (brush.transform() * m).inverted() * translate;
1623
1624 setInvMatrixData(inv_matrix);
1625
1626 fmp_data[0] = realCenter.x() - realFocal.x();
1627 fmp_data[1] = realCenter.y() - realFocal.y();
1628
1629 fmp2_m_radius2_data[0] = -fmp_data[0] * fmp_data[0] - fmp_data[1] * fmp_data[1] + realRadius * realRadius;
1630 } else if (style == Qt::ConicalGradientPattern) {
1631 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
1632 QPointF realCenter = g->center();
1633 QTransform translate(1, 0, 0, 1, -realCenter.x(), -realCenter.y());
1634 QTransform gl_to_qt(1, 0, 0, -1, 0, pdev->height());
1635 QTransform m = QTransform(matrix).translate(brush_origin.x(), brush_origin.y());
1636 QTransform inv_matrix = gl_to_qt * (brush.transform() * m).inverted() * translate;
1637
1638 setInvMatrixData(inv_matrix);
1639
1640 angle_data[0] = -(g->angle() * 2 * Q_PI) / 360.0;
1641 } else if (style == Qt::LinearGradientPattern) {
1642 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
1643
1644 QPointF realStart = g->start();
1645 QPointF realFinal = g->finalStop();
1646 QTransform translate(1, 0, 0, 1, -realStart.x(), -realStart.y());
1647 QTransform gl_to_qt(1, 0, 0, -1, 0, pdev->height());
1648 QTransform m = QTransform(matrix).translate(brush_origin.x(), brush_origin.y());
1649 QTransform inv_matrix = gl_to_qt * (brush.transform() * m).inverted() * translate;
1650
1651 setInvMatrixData(inv_matrix);
1652
1653 QPointF l = realFinal - realStart;
1654
1655 linear_data[0] = l.x();
1656 linear_data[1] = l.y();
1657
1658 linear_data[2] = 1.0f / (l.x() * l.x() + l.y() * l.y());
1659 } else if (style != Qt::SolidPattern) {
1660 QTransform gl_to_qt(1, 0, 0, -1, 0, pdev->height());
1661 QTransform m = QTransform(matrix).translate(brush_origin.x(), brush_origin.y());
1662 QTransform inv_matrix = gl_to_qt * (brush.transform() * m).inverted();
1663
1664 setInvMatrixData(inv_matrix);
1665 }
1666 }
1667
1668 if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern) {
1669 createGradientPaletteTexture(*brush.gradient());
1670 }
1671#endif
1672}
1673
1674
1675class QOpenGLTessellator : public QTessellator
1676{
1677public:
1678 QOpenGLTessellator() {}
1679 ~QOpenGLTessellator() { }
1680 QGLTrapezoid toGLTrapezoid(const Trapezoid &trap);
1681};
1682
1683QGLTrapezoid QOpenGLTessellator::toGLTrapezoid(const Trapezoid &trap)
1684{
1685 QGLTrapezoid t;
1686
1687 t.top = Q27Dot5ToDouble(trap.top);
1688 t.bottom = Q27Dot5ToDouble(trap.bottom);
1689
1690 Q27Dot5 y = trap.topLeft->y - trap.bottomLeft->y;
1691
1692 qreal topLeftY = Q27Dot5ToDouble(trap.topLeft->y);
1693
1694 qreal tx = Q27Dot5ToDouble(trap.topLeft->x);
1695 qreal m = (-tx + Q27Dot5ToDouble(trap.bottomLeft->x)) / Q27Dot5ToDouble(y);
1696 t.topLeftX = tx + m * (topLeftY - t.top);
1697 t.bottomLeftX = tx + m * (topLeftY - t.bottom);
1698
1699 y = trap.topRight->y - trap.bottomRight->y;
1700
1701 qreal topRightY = Q27Dot5ToDouble(trap.topRight->y);
1702
1703 tx = Q27Dot5ToDouble(trap.topRight->x);
1704 m = (-tx + Q27Dot5ToDouble(trap.bottomRight->x)) / Q27Dot5ToDouble(y);
1705
1706 t.topRightX = tx + m * (topRightY - Q27Dot5ToDouble(trap.top));
1707 t.bottomRightX = tx + m * (topRightY - Q27Dot5ToDouble(trap.bottom));
1708
1709 return t;
1710}
1711
1712class QOpenGLImmediateModeTessellator : public QOpenGLTessellator
1713{
1714public:
1715 void addTrap(const Trapezoid &trap);
1716 void tessellate(const QPointF *points, int nPoints, bool winding) {
1717 trapezoids.reserve(trapezoids.size() + nPoints);
1718 setWinding(winding);
1719 QTessellator::tessellate(points, nPoints);
1720 }
1721
1722 QVector<QGLTrapezoid> trapezoids;
1723};
1724
1725void QOpenGLImmediateModeTessellator::addTrap(const Trapezoid &trap)
1726{
1727 trapezoids.append(toGLTrapezoid(trap));
1728}
1729
1730#ifndef QT_OPENGL_ES
1731static void drawTrapezoid(const QGLTrapezoid &trap, const qreal offscreenHeight, QGLContext *ctx)
1732{
1733 qreal minX = qMin(trap.topLeftX, trap.bottomLeftX);
1734 qreal maxX = qMax(trap.topRightX, trap.bottomRightX);
1735
1736 if (qFuzzyCompare(trap.top, trap.bottom) || qFuzzyCompare(minX, maxX) ||
1737 (qFuzzyCompare(trap.topLeftX, trap.topRightX) && qFuzzyCompare(trap.bottomLeftX, trap.bottomRightX)))
1738 return;
1739
1740 const qreal xpadding = 1.0;
1741 const qreal ypadding = 1.0;
1742
1743 qreal topDist = offscreenHeight - trap.top;
1744 qreal bottomDist = offscreenHeight - trap.bottom;
1745
1746 qreal reciprocal = bottomDist / (bottomDist - topDist);
1747
1748 qreal leftB = trap.bottomLeftX + (trap.topLeftX - trap.bottomLeftX) * reciprocal;
1749 qreal rightB = trap.bottomRightX + (trap.topRightX - trap.bottomRightX) * reciprocal;
1750
1751 const bool topZero = qFuzzyIsNull(topDist);
1752
1753 reciprocal = topZero ? 1.0 / bottomDist : 1.0 / topDist;
1754
1755 qreal leftA = topZero ? (trap.bottomLeftX - leftB) * reciprocal : (trap.topLeftX - leftB) * reciprocal;
1756 qreal rightA = topZero ? (trap.bottomRightX - rightB) * reciprocal : (trap.topRightX - rightB) * reciprocal;
1757
1758 qreal invLeftA = qFuzzyIsNull(leftA) ? 0.0 : 1.0 / leftA;
1759 qreal invRightA = qFuzzyIsNull(rightA) ? 0.0 : 1.0 / rightA;
1760
1761 // fragment program needs the negative of invRightA as it mirrors the line
1762 glTexCoord4f(topDist, bottomDist, invLeftA, -invRightA);
1763 glMultiTexCoord4f(GL_TEXTURE1, leftA, leftB, rightA, rightB);
1764
1765 qreal topY = trap.top - ypadding;
1766 qreal bottomY = trap.bottom + ypadding;
1767
1768 qreal bounds_bottomLeftX = leftA * (offscreenHeight - bottomY) + leftB;
1769 qreal bounds_bottomRightX = rightA * (offscreenHeight - bottomY) + rightB;
1770 qreal bounds_topLeftX = leftA * (offscreenHeight - topY) + leftB;
1771 qreal bounds_topRightX = rightA * (offscreenHeight - topY) + rightB;
1772
1773 QPointF leftNormal(1, -leftA);
1774 leftNormal /= qSqrt(leftNormal.x() * leftNormal.x() + leftNormal.y() * leftNormal.y());
1775 QPointF rightNormal(1, -rightA);
1776 rightNormal /= qSqrt(rightNormal.x() * rightNormal.x() + rightNormal.y() * rightNormal.y());
1777
1778 qreal left_padding = xpadding / qAbs(leftNormal.x());
1779 qreal right_padding = xpadding / qAbs(rightNormal.x());
1780
1781 glVertex2d(bounds_topLeftX - left_padding, topY);
1782 glVertex2d(bounds_topRightX + right_padding, topY);
1783 glVertex2d(bounds_bottomRightX + right_padding, bottomY);
1784 glVertex2d(bounds_bottomLeftX - left_padding, bottomY);
1785
1786 glTexCoord4f(0.0f, 0.0f, 0.0f, 1.0f);
1787}
1788#endif // !Q_WS_QWS
1789
1790class QOpenGLTrapezoidToArrayTessellator : public QOpenGLTessellator
1791{
1792public:
1793 QOpenGLTrapezoidToArrayTessellator() : vertices(0), allocated(0), size(0) {}
1794 ~QOpenGLTrapezoidToArrayTessellator() { free(vertices); }
1795 q_vertexType *vertices;
1796 int allocated;
1797 int size;
1798 QRectF bounds;
1799 void addTrap(const Trapezoid &trap);
1800 void tessellate(const QPointF *points, int nPoints, bool winding) {
1801 size = 0;
1802 setWinding(winding);
1803 bounds = QTessellator::tessellate(points, nPoints);
1804 }
1805};
1806
1807void QOpenGLTrapezoidToArrayTessellator::addTrap(const Trapezoid &trap)
1808{
1809 // On OpenGL ES we convert the trap to 2 triangles
1810#ifndef QT_OPENGL_ES
1811 if (size > allocated - 8) {
1812#else
1813 if (size > allocated - 12) {
1814#endif
1815 allocated = qMax(2*allocated, 512);
1816 vertices = (q_vertexType *)realloc(vertices, allocated * sizeof(q_vertexType));
1817 }
1818
1819 QGLTrapezoid t = toGLTrapezoid(trap);
1820
1821#ifndef QT_OPENGL_ES
1822 vertices[size++] = f2vt(t.topLeftX);
1823 vertices[size++] = f2vt(t.top);
1824 vertices[size++] = f2vt(t.topRightX);
1825 vertices[size++] = f2vt(t.top);
1826 vertices[size++] = f2vt(t.bottomRightX);
1827 vertices[size++] = f2vt(t.bottom);
1828 vertices[size++] = f2vt(t.bottomLeftX);
1829 vertices[size++] = f2vt(t.bottom);
1830#else
1831 // First triangle
1832 vertices[size++] = f2vt(t.topLeftX);
1833 vertices[size++] = f2vt(t.top);
1834 vertices[size++] = f2vt(t.topRightX);
1835 vertices[size++] = f2vt(t.top);
1836 vertices[size++] = f2vt(t.bottomRightX);
1837 vertices[size++] = f2vt(t.bottom);
1838
1839 // Second triangle
1840 vertices[size++] = f2vt(t.bottomLeftX);
1841 vertices[size++] = f2vt(t.bottom);
1842 vertices[size++] = f2vt(t.topLeftX);
1843 vertices[size++] = f2vt(t.top);
1844 vertices[size++] = f2vt(t.bottomRightX);
1845 vertices[size++] = f2vt(t.bottom);
1846#endif
1847}
1848
1849
1850void QOpenGLPaintEnginePrivate::fillPolygon_dev(const QPointF *polygonPoints, int pointCount,
1851 Qt::FillRule fill)
1852{
1853 QOpenGLTrapezoidToArrayTessellator tessellator;
1854 tessellator.tessellate(polygonPoints, pointCount, fill == Qt::WindingFill);
1855
1856 DEBUG_ONCE qDebug() << "QOpenGLPaintEnginePrivate: Drawing polygon with" << pointCount << "points using fillPolygon_dev";
1857
1858 setGradientOps(cbrush, tessellator.bounds);
1859
1860 bool fast_style = current_style == Qt::LinearGradientPattern
1861 || current_style == Qt::SolidPattern;
1862
1863#ifndef QT_OPENGL_ES
1864 GLenum geometry_mode = GL_QUADS;
1865#else
1866 GLenum geometry_mode = GL_TRIANGLES;
1867#endif
1868
1869 if (use_fragment_programs && !(fast_style && has_fast_composition_mode)) {
1870 composite(geometry_mode, tessellator.vertices, tessellator.size / 2);
1871 } else {
1872 glVertexPointer(2, q_vertexTypeEnum, 0, tessellator.vertices);
1873 glEnableClientState(GL_VERTEX_ARRAY);
1874 glDrawArrays(geometry_mode, 0, tessellator.size/2);
1875 glDisableClientState(GL_VERTEX_ARRAY);
1876 }
1877}
1878
1879
1880inline void QOpenGLPaintEnginePrivate::lineToStencil(qreal x, qreal y)
1881{
1882 tess_points.add(QPointF(x, y));
1883
1884 if (x > max_x)
1885 max_x = x;
1886 else if (x < min_x)
1887 min_x = x;
1888 if (y > max_y)
1889 max_y = y;
1890 else if (y < min_y)
1891 min_y = y;
1892}
1893
1894inline void QOpenGLPaintEnginePrivate::curveToStencil(const QPointF &cp1, const QPointF &cp2, const QPointF &ep)
1895{
1896 qreal inverseScaleHalf = inverseScale / 2;
1897
1898 QBezier beziers[32];
1899 beziers[0] = QBezier::fromPoints(tess_points.last(), cp1, cp2, ep);
1900 QBezier *b = beziers;
1901 while (b >= beziers) {
1902 // check if we can pop the top bezier curve from the stack
1903 qreal l = qAbs(b->x4 - b->x1) + qAbs(b->y4 - b->y1);
1904 qreal d;
1905 if (l > inverseScale) {
1906 d = qAbs( (b->x4 - b->x1)*(b->y1 - b->y2) - (b->y4 - b->y1)*(b->x1 - b->x2) )
1907 + qAbs( (b->x4 - b->x1)*(b->y1 - b->y3) - (b->y4 - b->y1)*(b->x1 - b->x3) );
1908 d /= l;
1909 } else {
1910 d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) +
1911 qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3);
1912 }
1913 if (d < inverseScaleHalf || b == beziers + 31) {
1914 // good enough, we pop it off and add the endpoint
1915 lineToStencil(b->x4, b->y4);
1916 --b;
1917 } else {
1918 // split, second half of the polygon goes lower into the stack
1919 b->split(b+1, b);
1920 ++b;
1921 }
1922 }
1923}
1924
1925
1926void QOpenGLPaintEnginePrivate::pathToVertexArrays(const QPainterPath &path)
1927{
1928 const QPainterPath::Element &first = path.elementAt(0);
1929 min_x = max_x = first.x;
1930 min_y = max_y = first.y;
1931
1932 tess_points.reset();
1933 tess_points_stops.clear();
1934 lineToStencil(first.x, first.y);
1935
1936 for (int i=1; i<path.elementCount(); ++i) {
1937 const QPainterPath::Element &e = path.elementAt(i);
1938 switch (e.type) {
1939 case QPainterPath::MoveToElement:
1940 tess_points_stops.append(tess_points.size());
1941 lineToStencil(e.x, e.y);
1942 break;
1943 case QPainterPath::LineToElement:
1944 lineToStencil(e.x, e.y);
1945 break;
1946 case QPainterPath::CurveToElement:
1947 curveToStencil(e, path.elementAt(i+1), path.elementAt(i+2));
1948 i+=2;
1949 break;
1950 default:
1951 break;
1952 }
1953 }
1954 lineToStencil(first.x, first.y);
1955 tess_points_stops.append(tess_points.size());
1956}
1957
1958
1959void QOpenGLPaintEnginePrivate::drawVertexArrays()
1960{
1961 glEnableClientState(GL_VERTEX_ARRAY);
1962 glVertexPointer(2, GL_DOUBLE, 0, tess_points.data());
1963 int previous_stop = 0;
1964 foreach(int stop, tess_points_stops) {
1965 glDrawArrays(GL_TRIANGLE_FAN, previous_stop, stop-previous_stop);
1966 previous_stop = stop;
1967 }
1968 glDisableClientState(GL_VERTEX_ARRAY);
1969}
1970
1971void QOpenGLPaintEnginePrivate::fillVertexArray(Qt::FillRule fillRule)
1972{
1973 Q_Q(QOpenGLPaintEngine);
1974
1975 QRect rect = dirty_stencil.boundingRect();
1976
1977 if (use_system_clip)
1978 rect = q->systemClip().intersected(dirty_stencil).boundingRect();
1979
1980 glStencilMask(~0);
1981
1982 if (!rect.isEmpty()) {
1983 disableClipping();
1984
1985 glEnable(GL_SCISSOR_TEST);
1986
1987 const int left = rect.left();
1988 const int width = rect.width();
1989 const int bottom = device->size().height() - (rect.bottom() + 1);
1990 const int height = rect.height();
1991
1992 glScissor(left, bottom, width, height);
1993
1994 glClearStencil(0);
1995 glClear(GL_STENCIL_BUFFER_BIT);
1996 dirty_stencil -= rect;
1997
1998 glDisable(GL_SCISSOR_TEST);
1999
2000 enableClipping();
2001 }
2002
2003 // Enable stencil.
2004 glEnable(GL_STENCIL_TEST);
2005
2006 // Disable color writes.
2007 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
2008
2009 GLuint stencilMask = 0;
2010
2011 if (fillRule == Qt::OddEvenFill) {
2012 stencilMask = 1;
2013
2014 // Enable stencil writes.
2015 glStencilMask(stencilMask);
2016
2017 // Set stencil xor mode.
2018 glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT);
2019
2020 // Disable stencil func.
2021 glStencilFunc(GL_ALWAYS, 0, ~0);
2022
2023 drawVertexArrays();
2024 } else if (fillRule == Qt::WindingFill) {
2025 stencilMask = ~0;
2026
2027 if (has_stencil_face_ext) {
2028 QGL_FUNC_CONTEXT;
2029 glEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
2030
2031 glActiveStencilFaceEXT(GL_BACK);
2032 glStencilOp(GL_KEEP, GL_KEEP, GL_DECR_WRAP_EXT);
2033 glStencilFunc(GL_ALWAYS, 0, ~0);
2034
2035 glActiveStencilFaceEXT(GL_FRONT);
2036 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP_EXT);
2037 glStencilFunc(GL_ALWAYS, 0, ~0);
2038
2039 drawVertexArrays();
2040
2041 glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
2042 } else {
2043 glStencilFunc(GL_ALWAYS, 0, ~0);
2044 glEnable(GL_CULL_FACE);
2045
2046 glCullFace(GL_BACK);
2047 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP_EXT);
2048 drawVertexArrays();
2049
2050 glCullFace(GL_FRONT);
2051 glStencilOp(GL_KEEP, GL_KEEP, GL_DECR_WRAP_EXT);
2052 drawVertexArrays();
2053
2054 glDisable(GL_CULL_FACE);
2055 }
2056 }
2057
2058 // Enable color writes.
2059 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
2060 glStencilMask(stencilMask);
2061
2062 setGradientOps(cbrush, QRectF(QPointF(min_x, min_y), QSizeF(max_x - min_x, max_y - min_y)));
2063
2064 bool fast_fill = has_fast_composition_mode && (current_style == Qt::LinearGradientPattern || current_style == Qt::SolidPattern);
2065
2066 if (use_fragment_programs && !fast_fill) {
2067 DEBUG_ONCE qDebug() << "QOpenGLPaintEnginePrivate: Drawing polygon using stencil method (fragment programs)";
2068 QRectF rect(QPointF(min_x, min_y), QSizeF(max_x - min_x, max_y - min_y));
2069
2070 // Enable stencil func.
2071 glStencilFunc(GL_NOTEQUAL, 0, stencilMask);
2072 glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
2073 composite(rect);
2074 } else {
2075 DEBUG_ONCE qDebug() << "QOpenGLPaintEnginePrivate: Drawing polygon using stencil method (no fragment programs)";
2076
2077 // Enable stencil func.
2078 glStencilFunc(GL_NOTEQUAL, 0, stencilMask);
2079 glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
2080#ifndef QT_OPENGL_ES
2081 glBegin(GL_QUADS);
2082 glVertex2f(min_x, min_y);
2083 glVertex2f(max_x, min_y);
2084 glVertex2f(max_x, max_y);
2085 glVertex2f(min_x, max_y);
2086 glEnd();
2087#endif
2088 }
2089
2090 // Disable stencil writes.
2091 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
2092 glStencilMask(0);
2093 glDisable(GL_STENCIL_TEST);
2094}
2095
2096void QOpenGLPaintEnginePrivate::fillPath(const QPainterPath &path)
2097{
2098 if (path.isEmpty())
2099 return;
2100
2101 if (use_stencil_method && !high_quality_antialiasing) {
2102 pathToVertexArrays(path);
2103 fillVertexArray(path.fillRule());
2104 return;
2105 }
2106
2107 glMatrixMode(GL_MODELVIEW);
2108 glLoadIdentity();
2109
2110 if (high_quality_antialiasing)
2111 drawOffscreenPath(path);
2112 else {
2113 QPolygonF poly = path.toFillPolygon(matrix);
2114 fillPolygon_dev(poly.data(), poly.count(),
2115 path.fillRule());
2116 }
2117
2118 updateGLMatrix();
2119}
2120
2121
2122static inline bool needsEmulation(Qt::BrushStyle style)
2123{
2124 return !(style == Qt::SolidPattern
2125 || (style == Qt::LinearGradientPattern
2126 && (QGLExtensions::glExtensions() & QGLExtensions::MirroredRepeat)));
2127}
2128
2129void QOpenGLPaintEnginePrivate::updateUseEmulation()
2130{
2131 use_emulation = !use_fragment_programs
2132 && ((has_pen && needsEmulation(pen_brush_style))
2133 || (has_brush && needsEmulation(brush_style)));
2134}
2135
2136void QOpenGLPaintEngine::updatePen(const QPen &pen)
2137{
2138 Q_D(QOpenGLPaintEngine);
2139 Qt::PenStyle pen_style = pen.style();
2140 d->pen_brush_style = pen.brush().style();
2141 d->cpen = pen;
2142 d->has_pen = (pen_style != Qt::NoPen) && (d->pen_brush_style != Qt::NoBrush);
2143 d->updateUseEmulation();
2144
2145 if (pen.isCosmetic()) {
2146 GLfloat width = pen.widthF() == 0.0f ? 1.0f : pen.widthF();
2147 glLineWidth(width);
2148 glPointSize(width);
2149 }
2150
2151 if (d->pen_brush_style >= Qt::LinearGradientPattern
2152 && d->pen_brush_style <= Qt::ConicalGradientPattern)
2153 {
2154 d->setGLPen(Qt::white);
2155 } else {
2156 d->setGLPen(pen.color());
2157 }
2158
2159 d->updateFastPen();
2160}
2161
2162void QOpenGLPaintEngine::updateBrush(const QBrush &brush, const QPointF &origin)
2163{
2164 Q_D(QOpenGLPaintEngine);
2165 d->cbrush = brush;
2166 d->brush_style = brush.style();
2167 d->brush_origin = origin;
2168 d->has_brush = (d->brush_style != Qt::NoBrush);
2169 d->updateUseEmulation();
2170}
2171
2172void QOpenGLPaintEngine::updateFont(const QFont &)
2173{
2174}
2175
2176void QOpenGLPaintEngine::updateMatrix(const QTransform &mtx)
2177{
2178 Q_D(QOpenGLPaintEngine);
2179
2180 d->matrix = mtx;
2181
2182 d->mv_matrix[0][0] = mtx.m11();
2183 d->mv_matrix[0][1] = mtx.m12();
2184 d->mv_matrix[0][2] = 0;
2185 d->mv_matrix[0][3] = mtx.m13();
2186
2187 d->mv_matrix[1][0] = mtx.m21();
2188 d->mv_matrix[1][1] = mtx.m22();
2189 d->mv_matrix[1][2] = 0;
2190 d->mv_matrix[1][3] = mtx.m23();
2191
2192 d->mv_matrix[2][0] = 0;
2193 d->mv_matrix[2][1] = 0;
2194 d->mv_matrix[2][2] = 1;
2195 d->mv_matrix[2][3] = 0;
2196
2197 d->mv_matrix[3][0] = mtx.dx();
2198 d->mv_matrix[3][1] = mtx.dy();
2199 d->mv_matrix[3][2] = 0;
2200 d->mv_matrix[3][3] = mtx.m33();
2201
2202 d->txop = mtx.type();
2203
2204 // 1/10000 == 0.0001, so we have good enough res to cover curves
2205 // that span the entire widget...
2206 d->inverseScale = qMax(1 / qMax( qMax(qAbs(mtx.m11()), qAbs(mtx.m22())),
2207 qMax(qAbs(mtx.m12()), qAbs(mtx.m21())) ),
2208 qreal(0.0001));
2209
2210 d->updateGLMatrix();
2211 d->updateFastPen();
2212}
2213
2214void QOpenGLPaintEnginePrivate::updateGLMatrix() const
2215{
2216 glMatrixMode(GL_MODELVIEW);
2217#ifndef QT_OPENGL_ES
2218 glLoadMatrixd(&mv_matrix[0][0]);
2219#else
2220 glLoadMatrixf(&mv_matrix[0][0]);
2221#endif
2222}
2223
2224void QOpenGLPaintEnginePrivate::disableClipping()
2225{
2226 glDisable(GL_DEPTH_TEST);
2227 glDisable(GL_SCISSOR_TEST);
2228}
2229
2230void QOpenGLPaintEnginePrivate::enableClipping()
2231{
2232 Q_Q(QOpenGLPaintEngine);
2233 if (!q->state()->hasClipping)
2234 return;
2235
2236 if (q->state()->fastClip.isEmpty())
2237 glEnable(GL_DEPTH_TEST);
2238 else
2239 updateDepthClip(); // this will enable the scissor test
2240}
2241
2242void QOpenGLPaintEnginePrivate::updateDepthClip()
2243{
2244 Q_Q(QOpenGLPaintEngine);
2245
2246 ++q->state()->depthClipId;
2247
2248 glDisable(GL_DEPTH_TEST);
2249 glDisable(GL_SCISSOR_TEST);
2250
2251 if (!q->state()->hasClipping)
2252 return;
2253
2254 QRect fastClip;
2255 if (q->state()->clipEnabled) {
2256 fastClip = q->state()->fastClip;
2257 } else if (use_system_clip && q->systemClip().rects().count() == 1) {
2258 fastClip = q->systemClip().rects().at(0);
2259 }
2260
2261 if (!fastClip.isEmpty()) {
2262 glEnable(GL_SCISSOR_TEST);
2263
2264 const int left = fastClip.left();
2265 const int width = fastClip.width();
2266 const int bottom = device->size().height() - (fastClip.bottom() + 1);
2267 const int height = fastClip.height();
2268
2269 glScissor(left, bottom, width, height);
2270 return;
2271 }
2272
2273#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_2) || defined(QT_OPENGL_ES_1_CL)
2274 glClearDepthf(0.0f);
2275#else
2276 glClearDepth(0.0f);
2277#endif
2278
2279 glEnable(GL_DEPTH_TEST);
2280 glDepthMask(GL_TRUE);
2281 glClear(GL_DEPTH_BUFFER_BIT);
2282
2283 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
2284 glDepthFunc(GL_ALWAYS);
2285
2286 const QVector<QRect> rects = q->state()->clipEnabled ? q->state()->clipRegion.rects() : q->systemClip().rects();
2287
2288 // rectangle count * 2 (triangles) * vertex count * component count (Z omitted)
2289 QDataBuffer<q_vertexType> clipVertex(rects.size()*2*3*2);
2290 for (int i = 0; i < rects.size(); ++i) {
2291 q_vertexType x = i2vt(rects.at(i).left());
2292 q_vertexType w = i2vt(rects.at(i).width());
2293 q_vertexType h = i2vt(rects.at(i).height());
2294 q_vertexType y = i2vt(rects.at(i).top());
2295
2296 // First triangle
2297 clipVertex.add(x);
2298 clipVertex.add(y);
2299
2300 clipVertex.add(x);
2301 clipVertex.add(y + h);
2302
2303 clipVertex.add(x + w);
2304 clipVertex.add(y);
2305
2306 // Second triangle
2307 clipVertex.add(x);
2308 clipVertex.add(y + h);
2309
2310 clipVertex.add(x + w);
2311 clipVertex.add(y + h);
2312
2313 clipVertex.add (x + w);
2314 clipVertex.add(y);
2315 }
2316
2317 if (rects.size()) {
2318 glMatrixMode(GL_MODELVIEW);
2319 glLoadIdentity();
2320
2321 glEnableClientState(GL_VERTEX_ARRAY);
2322 glVertexPointer(2, q_vertexTypeEnum, 0, clipVertex.data());
2323
2324 glDrawArrays(GL_TRIANGLES, 0, rects.size()*2*3);
2325 glDisableClientState(GL_VERTEX_ARRAY);
2326 updateGLMatrix();
2327 }
2328
2329 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
2330 glDepthMask(GL_FALSE);
2331 glDepthFunc(GL_LEQUAL);
2332}
2333
2334void QOpenGLPaintEnginePrivate::systemStateChanged()
2335{
2336 Q_Q(QOpenGLPaintEngine);
2337 if (q->painter()->hasClipping())
2338 q->updateClipRegion(q->painter()->clipRegion(), Qt::ReplaceClip);
2339 else
2340 q->updateClipRegion(QRegion(), Qt::NoClip);
2341}
2342
2343void QOpenGLPaintEngine::updateClipRegion(const QRegion &clipRegion, Qt::ClipOperation op)
2344{
2345 Q_D(QOpenGLPaintEngine);
2346
2347 // clipping is only supported when a stencil or depth buffer is
2348 // available
2349 if (!d->device->format().depth())
2350 return;
2351
2352 d->use_system_clip = false;
2353 QRegion sysClip = systemClip();
2354 if (!sysClip.isEmpty()) {
2355 if (d->pdev->devType() != QInternal::Widget) {
2356 d->use_system_clip = true;
2357 } else {
2358#ifndef Q_WS_QWS
2359 // Only use the system clip if we're currently rendering a widget with a GL painter.
2360 if (d->currentClipWidget) {
2361 QWidgetPrivate *widgetPrivate = qt_widget_private(d->currentClipWidget->window());
2362 d->use_system_clip = widgetPrivate->extra && widgetPrivate->extra->inRenderWithPainter;
2363 }
2364#endif
2365 }
2366 }
2367
2368 d->flushDrawQueue();
2369
2370 if (op == Qt::NoClip && !d->use_system_clip) {
2371 state()->hasClipping = false;
2372 state()->clipRegion = QRegion();
2373 d->updateDepthClip();
2374 return;
2375 }
2376
2377 bool isScreenClip = false;
2378 if (!d->use_system_clip) {
2379 QVector<QRect> untransformedRects = clipRegion.rects();
2380
2381 if (untransformedRects.size() == 1) {
2382 QPainterPath path;
2383 path.addRect(untransformedRects[0]);
2384 path = d->matrix.map(path);
2385
2386 if (path.contains(QRectF(QPointF(), d->device->size())))
2387 isScreenClip = true;
2388 }
2389 }
2390
2391 QRegion region = isScreenClip ? QRegion() : clipRegion * d->matrix;
2392 switch (op) {
2393 case Qt::NoClip:
2394 if (!d->use_system_clip)
2395 break;
2396 state()->clipRegion = sysClip;
2397 break;
2398 case Qt::IntersectClip:
2399 if (isScreenClip)
2400 return;
2401 if (state()->hasClipping) {
2402 state()->clipRegion &= region;
2403 break;
2404 }
2405 // fall through
2406 case Qt::ReplaceClip:
2407 if (d->use_system_clip)
2408 state()->clipRegion = region & sysClip;
2409 else
2410 state()->clipRegion = region;
2411 break;
2412 case Qt::UniteClip:
2413 state()->clipRegion |= region;
2414 if (d->use_system_clip)
2415 state()->clipRegion &= sysClip;
2416 break;
2417 default:
2418 break;
2419 }
2420
2421 if (isScreenClip) {
2422 state()->hasClipping = false;
2423 state()->clipRegion = QRegion();
2424 } else {
2425 state()->hasClipping = op != Qt::NoClip || d->use_system_clip;
2426 }
2427
2428 if (state()->hasClipping && state()->clipRegion.rects().size() == 1)
2429 state()->fastClip = state()->clipRegion.rects().at(0);
2430 else
2431 state()->fastClip = QRect();
2432
2433 d->updateDepthClip();
2434}
2435
2436void QOpenGLPaintEngine::updateRenderHints(QPainter::RenderHints hints)
2437{
2438 Q_D(QOpenGLPaintEngine);
2439
2440 d->flushDrawQueue();
2441 d->use_smooth_pixmap_transform = bool(hints & QPainter::SmoothPixmapTransform);
2442 if ((hints & QPainter::Antialiasing) || (hints & QPainter::HighQualityAntialiasing)) {
2443 if (d->use_fragment_programs && QGLOffscreen::isSupported()
2444 && (hints & QPainter::HighQualityAntialiasing)) {
2445 d->high_quality_antialiasing = true;
2446 } else {
2447 d->high_quality_antialiasing = false;
2448 if (QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers)
2449 glEnable(GL_MULTISAMPLE);
2450 }
2451 } else {
2452 d->high_quality_antialiasing = false;
2453 if (QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers)
2454 glDisable(GL_MULTISAMPLE);
2455 }
2456
2457 if (d->high_quality_antialiasing) {
2458 d->offscreen.initialize();
2459
2460 if (!d->offscreen.isValid()) {
2461 DEBUG_ONCE_STR("Unable to initialize offscreen, disabling high quality antialiasing");
2462 d->high_quality_antialiasing = false;
2463 if (QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers)
2464 glEnable(GL_MULTISAMPLE);
2465 }
2466 }
2467
2468 d->has_antialiasing = d->high_quality_antialiasing
2469 || ((hints & QPainter::Antialiasing)
2470 && (QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers));
2471}
2472
2473
2474void QOpenGLPaintEnginePrivate::setPorterDuffData(float a, float b, float x, float y, float z)
2475{
2476 porterduff_ab_data[0] = a;
2477 porterduff_ab_data[1] = b;
2478
2479 porterduff_xyz_data[0] = x;
2480 porterduff_xyz_data[1] = y;
2481 porterduff_xyz_data[2] = z;
2482}
2483
2484
2485void QOpenGLPaintEngine::updateCompositionMode(QPainter::CompositionMode composition_mode)
2486{
2487 Q_D(QOpenGLPaintEngine);
2488
2489 if (!d->use_fragment_programs && composition_mode > QPainter::CompositionMode_Plus)
2490 composition_mode = QPainter::CompositionMode_SourceOver;
2491
2492 d->composition_mode = composition_mode;
2493
2494 d->has_fast_composition_mode = (!d->high_quality_antialiasing && composition_mode <= QPainter::CompositionMode_Plus)
2495 || composition_mode == QPainter::CompositionMode_SourceOver
2496 || composition_mode == QPainter::CompositionMode_Destination
2497 || composition_mode == QPainter::CompositionMode_DestinationOver
2498 || composition_mode == QPainter::CompositionMode_DestinationOut
2499 || composition_mode == QPainter::CompositionMode_SourceAtop
2500 || composition_mode == QPainter::CompositionMode_Xor
2501 || composition_mode == QPainter::CompositionMode_Plus;
2502
2503 if (d->has_fast_composition_mode)
2504 d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODE_BLEND_MODE_MASK : COMPOSITION_MODE_BLEND_MODE_NOMASK;
2505 else if (composition_mode <= QPainter::CompositionMode_Plus)
2506 d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_SIMPLE_PORTER_DUFF : COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK;
2507 else
2508 switch (composition_mode) {
2509 case QPainter::CompositionMode_Multiply:
2510 d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_MULTIPLY : COMPOSITION_MODES_MULTIPLY_NOMASK;
2511 break;
2512 case QPainter::CompositionMode_Screen:
2513 d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_SCREEN : COMPOSITION_MODES_SCREEN_NOMASK;
2514 break;
2515 case QPainter::CompositionMode_Overlay:
2516 d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_OVERLAY : COMPOSITION_MODES_OVERLAY_NOMASK;
2517 break;
2518 case QPainter::CompositionMode_Darken:
2519 d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_DARKEN : COMPOSITION_MODES_DARKEN_NOMASK;
2520 break;
2521 case QPainter::CompositionMode_Lighten:
2522 d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_LIGHTEN : COMPOSITION_MODES_LIGHTEN_NOMASK;
2523 break;
2524 case QPainter::CompositionMode_ColorDodge:
2525 d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_COLORDODGE : COMPOSITION_MODES_COLORDODGE_NOMASK;
2526 break;
2527 case QPainter::CompositionMode_ColorBurn:
2528 d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_COLORBURN : COMPOSITION_MODES_COLORBURN_NOMASK;
2529 break;
2530 case QPainter::CompositionMode_HardLight:
2531 d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_HARDLIGHT : COMPOSITION_MODES_HARDLIGHT_NOMASK;
2532 break;
2533 case QPainter::CompositionMode_SoftLight:
2534 d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_SOFTLIGHT : COMPOSITION_MODES_SOFTLIGHT_NOMASK;
2535 break;
2536 case QPainter::CompositionMode_Difference:
2537 d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_DIFFERENCE : COMPOSITION_MODES_DIFFERENCE_NOMASK;
2538 break;
2539 case QPainter::CompositionMode_Exclusion:
2540 d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_EXCLUSION : COMPOSITION_MODES_EXCLUSION_NOMASK;
2541 break;
2542 default:
2543 Q_ASSERT(false);
2544 }
2545
2546 switch(composition_mode) {
2547 case QPainter::CompositionMode_DestinationOver:
2548 glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);
2549 d->setPorterDuffData(0, 1, 1, 1, 1);
2550 break;
2551 case QPainter::CompositionMode_Clear:
2552 glBlendFunc(GL_ZERO, GL_ZERO);
2553 d->setPorterDuffData(0, 0, 0, 0, 0);
2554 break;
2555 case QPainter::CompositionMode_Source:
2556 glBlendFunc(GL_ONE, GL_ZERO);
2557 d->setPorterDuffData(1, 0, 1, 1, 0);
2558 break;
2559 case QPainter::CompositionMode_Destination:
2560 glBlendFunc(GL_ZERO, GL_ONE);
2561 d->setPorterDuffData(0, 1, 1, 0, 1);
2562 break;
2563 case QPainter::CompositionMode_SourceIn:
2564 glBlendFunc(GL_DST_ALPHA, GL_ZERO);
2565 d->setPorterDuffData(1, 0, 1, 0, 0);
2566 break;
2567 case QPainter::CompositionMode_DestinationIn:
2568 glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
2569 d->setPorterDuffData(0, 1, 1, 0, 0);
2570 break;
2571 case QPainter::CompositionMode_SourceOut:
2572 glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ZERO);
2573 d->setPorterDuffData(0, 0, 0, 1, 0);
2574 break;
2575 case QPainter::CompositionMode_DestinationOut:
2576 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
2577 d->setPorterDuffData(0, 0, 0, 0, 1);
2578 break;
2579 case QPainter::CompositionMode_SourceAtop:
2580 glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2581 d->setPorterDuffData(1, 0, 1, 0, 1);
2582 break;
2583 case QPainter::CompositionMode_DestinationAtop:
2584 glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA);
2585 d->setPorterDuffData(0, 1, 1, 1, 0);
2586 break;
2587 case QPainter::CompositionMode_Xor:
2588 glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2589 d->setPorterDuffData(0, 0, 0, 1, 1);
2590 break;
2591 case QPainter::CompositionMode_SourceOver:
2592 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
2593 d->setPorterDuffData(1, 0, 1, 1, 1);
2594 break;
2595 case QPainter::CompositionMode_Plus:
2596 glBlendFunc(GL_ONE, GL_ONE);
2597 d->setPorterDuffData(1, 1, 1, 1, 1);
2598 break;
2599 default:
2600 break;
2601 }
2602}
2603
2604class QGLMaskGenerator
2605{
2606public:
2607 QGLMaskGenerator(const QPainterPath &path, const QTransform &matrix, qreal stroke_width = -1)
2608 : p(path),
2609 m(matrix),
2610 w(stroke_width)
2611 {
2612 }
2613
2614 virtual QRect screenRect() = 0;
2615 virtual void drawMask(const QRect &rect) = 0;
2616
2617 QPainterPath path() const { return p; }
2618 QTransform matrix() const { return m; }
2619 qreal strokeWidth() const { return w; }
2620
2621 virtual ~QGLMaskGenerator() {}
2622
2623private:
2624 QPainterPath p;
2625 QTransform m;
2626 qreal w;
2627};
2628
2629void QGLMaskTextureCache::setOffscreenSize(const QSize &sz)
2630{
2631 Q_ASSERT(sz.width() == sz.height());
2632
2633 if (offscreenSize != sz) {
2634 offscreenSize = sz;
2635 clearCache();
2636 }
2637}
2638
2639void QGLMaskTextureCache::clearCache()
2640{
2641 cache.clear();
2642
2643 int quad_tree_size = 1;
2644
2645 for (int i = block_size; i < offscreenSize.width(); i *= 2)
2646 quad_tree_size += quad_tree_size * 4;
2647
2648 for (int i = 0; i < 4; ++i) {
2649 occupied_quadtree[i].resize(quad_tree_size);
2650
2651 occupied_quadtree[i][0].key = 0;
2652 occupied_quadtree[i][0].largest_available_block = offscreenSize.width();
2653 occupied_quadtree[i][0].largest_used_block = 0;
2654
2655 DEBUG_ONCE qDebug() << "QGLMaskTextureCache:: created quad tree of size" << quad_tree_size;
2656 }
2657}
2658
2659void QGLMaskTextureCache::setDrawableSize(const QSize &sz)
2660{
2661 drawableSize = sz;
2662}
2663
2664void QGLMaskTextureCache::maintainCache()
2665{
2666 QGLTextureCacheHash::iterator it = cache.begin();
2667 QGLTextureCacheHash::iterator end = cache.end();
2668
2669 while (it != end) {
2670 CacheInfo &cache_info = it.value();
2671 ++cache_info.age;
2672
2673 if (cache_info.age > 1) {
2674 quadtreeInsert(cache_info.loc.channel, 0, cache_info.loc.rect);
2675 it = cache.erase(it);
2676 } else {
2677 ++it;
2678 }
2679 }
2680}
2681
2682//#define DISABLE_MASK_CACHE
2683
2684QGLMaskTextureCache::CacheLocation QGLMaskTextureCache::getMask(QGLMaskGenerator &maskGenerator, QOpenGLPaintEnginePrivate *e)
2685{
2686#ifndef DISABLE_MASK_CACHE
2687 engine = e;
2688
2689 quint64 key = hash(maskGenerator.path(), maskGenerator.matrix(), maskGenerator.strokeWidth());
2690
2691 if (key == 0)
2692 key = 1;
2693
2694 CacheInfo info(maskGenerator.path(), maskGenerator.matrix(), maskGenerator.strokeWidth());
2695
2696 QGLTextureCacheHash::iterator it = cache.find(key);
2697
2698 while (it != cache.end() && it.key() == key) {
2699 CacheInfo &cache_info = it.value();
2700 if (info.stroke_width == cache_info.stroke_width && info.matrix == cache_info.matrix && info.path == cache_info.path) {
2701 DEBUG_ONCE_STR("QGLMaskTextureCache::getMask(): Using cached mask");
2702
2703 cache_info.age = 0;
2704 return cache_info.loc;
2705 }
2706 ++it;
2707 }
2708
2709 // mask was not found, create new mask
2710
2711 DEBUG_ONCE_STR("QGLMaskTextureCache::getMask(): Creating new mask...");
2712
2713 createMask(key, info, maskGenerator);
2714
2715 cache.insert(key, info);
2716
2717 return info.loc;
2718#else
2719 CacheInfo info(maskGenerator.path(), maskGenerator.matrix());
2720 createMask(0, info, maskGenerator);
2721 return info.loc;
2722#endif
2723}
2724
2725#ifndef FloatToQuint64
2726#define FloatToQuint64(i) (quint64)((i) * 32)
2727#endif
2728
2729quint64 QGLMaskTextureCache::hash(const QPainterPath &p, const QTransform &m, qreal w)
2730{
2731 Q_ASSERT(sizeof(quint64) == 8);
2732
2733 quint64 h = 0;
2734
2735 for (int i = 0; i < p.elementCount(); ++i) {
2736 h += FloatToQuint64(p.elementAt(i).x) << 32;
2737 h += FloatToQuint64(p.elementAt(i).y);
2738 h += p.elementAt(i).type;
2739 }
2740
2741 h += FloatToQuint64(m.m11());
2742#ifndef Q_OS_WINCE // ###
2743 //Compiler crashes for arm on WinCE
2744 h += FloatToQuint64(m.m12()) << 4;
2745 h += FloatToQuint64(m.m13()) << 8;
2746 h += FloatToQuint64(m.m21()) << 12;
2747 h += FloatToQuint64(m.m22()) << 16;
2748 h += FloatToQuint64(m.m23()) << 20;
2749 h += FloatToQuint64(m.m31()) << 24;
2750 h += FloatToQuint64(m.m32()) << 28;
2751#endif
2752 h += FloatToQuint64(m.m33()) << 32;
2753
2754 h += FloatToQuint64(w);
2755
2756 return h;
2757}
2758
2759void QGLMaskTextureCache::createMask(quint64 key, CacheInfo &info, QGLMaskGenerator &maskGenerator)
2760{
2761 info.loc.screen_rect = maskGenerator.screenRect();
2762
2763 if (info.loc.screen_rect.isEmpty()) {
2764 info.loc.channel = 0;
2765 info.loc.rect = QRect();
2766 return;
2767 }
2768
2769 quadtreeAllocate(key, info.loc.screen_rect.size(), &info.loc.rect, &info.loc.channel);
2770
2771 int ch = info.loc.channel;
2772 glColorMask(ch == 0, ch == 1, ch == 2, ch == 3);
2773
2774 maskGenerator.drawMask(info.loc.rect);
2775
2776 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
2777}
2778
2779int QGLMaskTextureCache::quadtreeBlocksize(int node)
2780{
2781 DEBUG_ONCE qDebug() << "Offscreen size:" << offscreenSize.width();
2782
2783 int blocksize = offscreenSize.width();
2784
2785 while (node) {
2786 node = (node - 1) / 4;
2787 blocksize /= 2;
2788 }
2789
2790 return blocksize;
2791}
2792
2793QPoint QGLMaskTextureCache::quadtreeLocation(int node)
2794{
2795 QPoint location;
2796 int blocksize = quadtreeBlocksize(node);
2797
2798 while (node) {
2799 --node;
2800
2801 if (node & 1)
2802 location.setX(location.x() + blocksize);
2803
2804 if (node & 2)
2805 location.setY(location.y() + blocksize);
2806
2807 node /= 4;
2808 blocksize *= 2;
2809 }
2810
2811 return location;
2812}
2813
2814void QGLMaskTextureCache::quadtreeUpdate(int channel, int node, int current_block_size)
2815{
2816 while (node) {
2817 node = (node - 1) / 4;
2818
2819 int first_child = node * 4 + 1;
2820
2821 int largest_available = 0;
2822 int largest_used = 0;
2823
2824 bool all_empty = true;
2825
2826 for (int i = 0; i < 4; ++i) {
2827 largest_available = qMax(largest_available, occupied_quadtree[channel][first_child + i].largest_available_block);
2828 largest_used = qMax(largest_used, occupied_quadtree[channel][first_child + i].largest_used_block);
2829
2830 if (occupied_quadtree[channel][first_child + i].largest_available_block < current_block_size)
2831 all_empty = false;
2832 }
2833
2834 current_block_size *= 2;
2835
2836 if (all_empty) {
2837 occupied_quadtree[channel][node].largest_available_block = current_block_size;
2838 occupied_quadtree[channel][node].largest_used_block = 0;
2839 } else {
2840 occupied_quadtree[channel][node].largest_available_block = largest_available;
2841 occupied_quadtree[channel][node].largest_used_block = largest_used;
2842 }
2843 }
2844}
2845
2846void QGLMaskTextureCache::quadtreeInsert(int channel, quint64 key, const QRect &rect, int node)
2847{
2848 int current_block_size = quadtreeBlocksize(node);
2849 QPoint location = quadtreeLocation(node);
2850 QRect relative = rect.translated(-location);
2851
2852 if (relative.left() >= current_block_size || relative.top() >= current_block_size
2853 || relative.right() < 0 || relative.bottom() < 0)
2854 return;
2855
2856 if (current_block_size == block_size // no more refining possible
2857 || (relative.top() < block_size && relative.bottom() >= (current_block_size - block_size)
2858 && relative.left() < block_size && relative.right() >= (current_block_size - block_size)))
2859 {
2860 if (key != 0) {
2861 occupied_quadtree[channel][node].largest_available_block = 0;
2862 occupied_quadtree[channel][node].largest_used_block = rect.width() * rect.height();
2863 } else {
2864 occupied_quadtree[channel][node].largest_available_block = current_block_size;
2865 occupied_quadtree[channel][node].largest_used_block = 0;
2866 }
2867
2868 occupied_quadtree[channel][node].key = key;
2869
2870 quadtreeUpdate(channel, node, current_block_size);
2871 } else {
2872 if (key && occupied_quadtree[channel][node].largest_available_block == current_block_size) {
2873 // refining the quad tree, initialize child nodes
2874 int half_block_size = current_block_size / 2;
2875
2876 int temp = node * 4 + 1;
2877 for (int sibling = 0; sibling < 4; ++sibling) {
2878 occupied_quadtree[channel][temp + sibling].largest_available_block = half_block_size;
2879 occupied_quadtree[channel][temp + sibling].largest_used_block = 0;
2880 occupied_quadtree[channel][temp + sibling].key = 0;
2881 }
2882 }
2883
2884 node = node * 4 + 1;
2885
2886 for (int sibling = 0; sibling < 4; ++sibling)
2887 quadtreeInsert(channel, key, rect, node + sibling);
2888 }
2889}
2890
2891void QGLMaskTextureCache::quadtreeClear(int channel, const QRect &rect, int node)
2892{
2893 const quint64 &key = occupied_quadtree[channel][node].key;
2894
2895 int current_block_size = quadtreeBlocksize(node);
2896 QPoint location = quadtreeLocation(node);
2897
2898 QRect relative = rect.translated(-location);
2899
2900 if (relative.left() >= current_block_size || relative.top() >= current_block_size
2901 || relative.right() < 0 || relative.bottom() < 0)
2902 return;
2903
2904 if (key != 0) {
2905 QGLTextureCacheHash::iterator it = cache.find(key);
2906
2907 Q_ASSERT(it != cache.end());
2908
2909 while (it != cache.end() && it.key() == key) {
2910 const CacheInfo &cache_info = it.value();
2911
2912 if (cache_info.loc.channel == channel
2913 && cache_info.loc.rect.left() <= location.x()
2914 && cache_info.loc.rect.top() <= location.y()
2915 && cache_info.loc.rect.right() >= location.x()
2916 && cache_info.loc.rect.bottom() >= location.y())
2917 {
2918 quadtreeInsert(channel, 0, cache_info.loc.rect);
2919 engine->cacheItemErased(channel, cache_info.loc.rect);
2920 cache.erase(it);
2921 goto found;
2922 } else {
2923 ++it;
2924 }
2925 }
2926
2927 // if we don't find the key there's an error in the quadtree
2928 Q_ASSERT(false);
2929found:
2930 Q_ASSERT(occupied_quadtree[channel][node].key == 0);
2931 } else if (occupied_quadtree[channel][node].largest_available_block < current_block_size) {
2932 Q_ASSERT(current_block_size >= block_size);
2933
2934 node = node * 4 + 1;
2935
2936 for (int sibling = 0; sibling < 4; ++sibling)
2937 quadtreeClear(channel, rect, node + sibling);
2938 }
2939}
2940
2941bool QGLMaskTextureCache::quadtreeFindAvailableLocation(const QSize &size, QRect *rect, int *channel)
2942{
2943 int needed_block_size = qMax(1, qMax(size.width(), size.height()));
2944
2945 for (int i = 0; i < 4; ++i) {
2946 int current_block_size = offscreenSize.width();
2947
2948 if (occupied_quadtree[i][0].largest_available_block >= needed_block_size) {
2949 int node = 0;
2950
2951 while (current_block_size != occupied_quadtree[i][node].largest_available_block) {
2952 Q_ASSERT(current_block_size > block_size);
2953 Q_ASSERT(current_block_size > occupied_quadtree[i][node].largest_available_block);
2954
2955 node = node * 4 + 1;
2956 current_block_size /= 2;
2957
2958 int sibling = 0;
2959
2960 while (occupied_quadtree[i][node + sibling].largest_available_block < needed_block_size)
2961 ++sibling;
2962
2963 Q_ASSERT(sibling < 4);
2964 node += sibling;
2965 }
2966
2967 *channel = i;
2968 *rect = QRect(quadtreeLocation(node), size);
2969
2970 return true;
2971 }
2972 }
2973
2974 return false;
2975}
2976
2977void QGLMaskTextureCache::quadtreeFindExistingLocation(const QSize &size, QRect *rect, int *channel)
2978{
2979 // try to pick small masks to throw out, as large masks are more expensive to recompute
2980 *channel = qrand() % 4;
2981 for (int i = 0; i < 4; ++i)
2982 if (occupied_quadtree[i][0].largest_used_block < occupied_quadtree[*channel][0].largest_used_block)
2983 *channel = i;
2984
2985 int needed_block_size = qt_next_power_of_two(qMax(1, qMax(size.width(), size.height())));
2986
2987 int node = 0;
2988 int current_block_size = offscreenSize.width();
2989
2990 while (current_block_size > block_size
2991 && current_block_size >= needed_block_size * 2
2992 && occupied_quadtree[*channel][node].key == 0)
2993 {
2994 node = node * 4 + 1;
2995
2996 int sibling = 0;
2997
2998 for (int i = 1; i < 4; ++i) {
2999 if (occupied_quadtree[*channel][node + i].largest_used_block
3000 <= occupied_quadtree[*channel][node + sibling].largest_used_block)
3001 {
3002 sibling = i;
3003 }
3004 }
3005
3006 node += sibling;
3007 current_block_size /= 2;
3008 }
3009
3010 *rect = QRect(quadtreeLocation(node), size);
3011}
3012
3013void QGLMaskTextureCache::quadtreeAllocate(quint64 key, const QSize &size, QRect *rect, int *channel)
3014{
3015#ifndef DISABLE_MASK_CACHE
3016 if (!quadtreeFindAvailableLocation(size, rect, channel)) {
3017 quadtreeFindExistingLocation(size, rect, channel);
3018 quadtreeClear(*channel, *rect);
3019 }
3020
3021 quadtreeInsert(*channel, key, *rect);
3022#else
3023 *channel = 0;
3024 *rect = QRect(QPoint(), size);
3025#endif
3026}
3027
3028class QGLTrapezoidMaskGenerator : public QGLMaskGenerator
3029{
3030public:
3031 QGLTrapezoidMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offscreen, GLuint maskFragmentProgram, qreal strokeWidth = -1.0);
3032
3033 QRect screenRect();
3034 void drawMask(const QRect &rect);
3035
3036private:
3037 QRect screen_rect;
3038 bool has_screen_rect;
3039
3040 QGLOffscreen *offscreen;
3041
3042 GLuint maskFragmentProgram;
3043
3044 virtual QVector<QGLTrapezoid> generateTrapezoids() = 0;
3045 virtual QRect computeScreenRect() = 0;
3046};
3047
3048class QGLPathMaskGenerator : public QGLTrapezoidMaskGenerator
3049{
3050public:
3051 QGLPathMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offscreen, GLuint maskFragmentProgram);
3052
3053private:
3054 QVector<QGLTrapezoid> generateTrapezoids();
3055 QRect computeScreenRect();
3056
3057 QPolygonF poly;
3058};
3059
3060class QGLLineMaskGenerator : public QGLTrapezoidMaskGenerator
3061{
3062public:
3063 QGLLineMaskGenerator(const QPainterPath &path, const QTransform &matrix, qreal width, QGLOffscreen &offscreen, GLuint maskFragmentProgram);
3064
3065private:
3066 QVector<QGLTrapezoid> generateTrapezoids();
3067 QRect computeScreenRect();
3068
3069 QPainterPath transformedPath;
3070};
3071
3072class QGLRectMaskGenerator : public QGLTrapezoidMaskGenerator
3073{
3074public:
3075 QGLRectMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offscreen, GLuint maskFragmentProgram);
3076
3077private:
3078 QVector<QGLTrapezoid> generateTrapezoids();
3079 QRect computeScreenRect();
3080
3081 QPainterPath transformedPath;
3082};
3083
3084class QGLEllipseMaskGenerator : public QGLMaskGenerator
3085{
3086public:
3087 QGLEllipseMaskGenerator(const QRectF &rect, const QTransform &matrix, QGLOffscreen &offscreen, GLuint maskFragmentProgram, int *maskVariableLocations);
3088
3089 QRect screenRect();
3090 void drawMask(const QRect &rect);
3091
3092private:
3093 QRect screen_rect;
3094
3095 QRectF ellipseRect;
3096
3097 QGLOffscreen *offscreen;
3098
3099 GLuint maskFragmentProgram;
3100
3101 int *maskVariableLocations;
3102
3103 float vertexArray[4 * 2];
3104};
3105
3106QGLTrapezoidMaskGenerator::QGLTrapezoidMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offs, GLuint program, qreal stroke_width)
3107 : QGLMaskGenerator(path, matrix, stroke_width)
3108 , has_screen_rect(false)
3109 , offscreen(&offs)
3110 , maskFragmentProgram(program)
3111{
3112}
3113
3114extern void qt_add_rect_to_array(const QRectF &r, q_vertexType *array);
3115extern void qt_add_texcoords_to_array(qreal x1, qreal y1, qreal x2, qreal y2, q_vertexType *array);
3116
3117void QGLTrapezoidMaskGenerator::drawMask(const QRect &rect)
3118{
3119#ifdef QT_OPENGL_ES
3120 Q_UNUSED(rect);
3121#else
3122 glMatrixMode(GL_MODELVIEW);
3123 glPushMatrix();
3124 glLoadIdentity();
3125
3126 QGLContext *ctx = offscreen->context();
3127 offscreen->bind();
3128
3129 glDisable(GL_TEXTURE_GEN_S);
3130 glDisable(GL_TEXTURE_1D);
3131
3132 GLfloat vertexArray[4 * 2];
3133 qt_add_rect_to_array(rect, vertexArray);
3134
3135 bool needs_scissor = rect != screen_rect;
3136
3137 if (needs_scissor) {
3138 glEnable(GL_SCISSOR_TEST);
3139 glScissor(rect.left(), offscreen->offscreenSize().height() - rect.bottom() - 1, rect.width(), rect.height());
3140 }
3141
3142 QVector<QGLTrapezoid> trapezoids = generateTrapezoids();
3143
3144 // clear mask
3145 glBlendFunc(GL_ZERO, GL_ZERO); // clear
3146 glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
3147 glEnableClientState(GL_VERTEX_ARRAY);
3148 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
3149 glDisableClientState(GL_VERTEX_ARRAY);
3150
3151 glBlendFunc(GL_ONE, GL_ONE); // add mask
3152 glEnable(GL_FRAGMENT_PROGRAM_ARB);
3153 glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, maskFragmentProgram);
3154
3155 QPoint delta = rect.topLeft() - screen_rect.topLeft();
3156 glBegin(GL_QUADS);
3157 for (int i = 0; i < trapezoids.size(); ++i)
3158 drawTrapezoid(trapezoids[i].translated(delta), offscreen->offscreenSize().height(), ctx);
3159 glEnd();
3160
3161 if (needs_scissor)
3162 glDisable(GL_SCISSOR_TEST);
3163
3164 glDisable(GL_FRAGMENT_PROGRAM_ARB);
3165
3166 glMatrixMode(GL_MODELVIEW);
3167 glPopMatrix();
3168#endif
3169}
3170
3171QRect QGLTrapezoidMaskGenerator::screenRect()
3172{
3173 if (!has_screen_rect) {
3174 screen_rect = computeScreenRect();
3175 has_screen_rect = true;
3176 }
3177
3178 screen_rect = screen_rect.intersected(QRect(QPoint(), offscreen->drawableSize()));
3179
3180 return screen_rect;
3181}
3182
3183QGLPathMaskGenerator::QGLPathMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offs, GLuint program)
3184 : QGLTrapezoidMaskGenerator(path, matrix, offs, program)
3185{
3186}
3187
3188QRect QGLPathMaskGenerator::computeScreenRect()
3189{
3190 poly = path().toFillPolygon(matrix());
3191 return poly.boundingRect().toAlignedRect();
3192}
3193
3194QVector<QGLTrapezoid> QGLPathMaskGenerator::generateTrapezoids()
3195{
3196 QOpenGLImmediateModeTessellator tessellator;
3197 tessellator.tessellate(poly.data(), poly.count(), path().fillRule() == Qt::WindingFill);
3198 return tessellator.trapezoids;
3199}
3200
3201QGLRectMaskGenerator::QGLRectMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offs, GLuint program)
3202 : QGLTrapezoidMaskGenerator(path, matrix, offs, program)
3203{
3204}
3205
3206QGLLineMaskGenerator::QGLLineMaskGenerator(const QPainterPath &path, const QTransform &matrix, qreal width, QGLOffscreen &offs, GLuint program)
3207 : QGLTrapezoidMaskGenerator(path, matrix, offs, program, width)
3208{
3209}
3210
3211QRect QGLRectMaskGenerator::computeScreenRect()
3212{
3213 transformedPath = matrix().map(path());
3214
3215 return transformedPath.controlPointRect().adjusted(-1, -1, 1, 1).toAlignedRect();
3216}
3217
3218QRect QGLLineMaskGenerator::computeScreenRect()
3219{
3220 transformedPath = matrix().map(path());
3221
3222 return transformedPath.controlPointRect().adjusted(-1, -1, 1, 1).toAlignedRect();
3223}
3224
3225QVector<QGLTrapezoid> QGLLineMaskGenerator::generateTrapezoids()
3226{
3227 QOpenGLImmediateModeTessellator tessellator;
3228 QPointF last;
3229 for (int i = 0; i < transformedPath.elementCount(); ++i) {
3230 QPainterPath::Element element = transformedPath.elementAt(i);
3231
3232 Q_ASSERT(!element.isCurveTo());
3233
3234 if (element.isLineTo())
3235 tessellator.tessellateRect(last, element, strokeWidth());
3236
3237 last = element;
3238 }
3239
3240 return tessellator.trapezoids;
3241}
3242
3243QVector<QGLTrapezoid> QGLRectMaskGenerator::generateTrapezoids()
3244{
3245 Q_ASSERT(transformedPath.elementCount() == 5);
3246
3247 QOpenGLImmediateModeTessellator tessellator;
3248 if (matrix().type() <= QTransform::TxScale) {
3249 QPointF a = transformedPath.elementAt(0);
3250 QPointF b = transformedPath.elementAt(1);
3251 QPointF c = transformedPath.elementAt(2);
3252 QPointF d = transformedPath.elementAt(3);
3253
3254 QPointF first = (a + d) * 0.5;
3255 QPointF last = (b + c) * 0.5;
3256
3257 QPointF delta = a - d;
3258
3259 // manhattan distance (no rotation)
3260 qreal width = qAbs(delta.x()) + qAbs(delta.y());
3261
3262 Q_ASSERT(qFuzzyIsNull(delta.x()) || qFuzzyIsNull(delta.y()));
3263
3264 tessellator.tessellateRect(first, last, width);
3265 } else {
3266 QPointF points[5];
3267
3268 for (int i = 0; i < 5; ++i)
3269 points[i] = transformedPath.elementAt(i);
3270
3271 tessellator.tessellateConvex(points, 5);
3272 }
3273 return tessellator.trapezoids;
3274}
3275
3276static QPainterPath ellipseRectToPath(const QRectF &rect)
3277{
3278 QPainterPath path;
3279 path.addEllipse(rect);
3280 return path;
3281}
3282
3283QGLEllipseMaskGenerator::QGLEllipseMaskGenerator(const QRectF &rect, const QTransform &matrix, QGLOffscreen &offs, GLuint program, int *locations)
3284 : QGLMaskGenerator(ellipseRectToPath(rect), matrix),
3285 ellipseRect(rect),
3286 offscreen(&offs),
3287 maskFragmentProgram(program),
3288 maskVariableLocations(locations)
3289{
3290}
3291
3292QRect QGLEllipseMaskGenerator::screenRect()
3293{
3294 QPointF center = ellipseRect.center();
3295
3296 QPointF points[] = {
3297 QPointF(ellipseRect.left(), center.y()),
3298 QPointF(ellipseRect.right(), center.y()),
3299 QPointF(center.x(), ellipseRect.top()),
3300 QPointF(center.x(), ellipseRect.bottom())
3301 };
3302
3303 qreal min_screen_delta_len = QREAL_MAX;
3304
3305 for (int i = 0; i < 4; ++i) {
3306 QPointF delta = points[i] - center;
3307
3308 // normalize
3309 delta /= qSqrt(delta.x() * delta.x() + delta.y() * delta.y());
3310
3311 QPointF screen_delta(matrix().m11() * delta.x() + matrix().m21() * delta.y(),
3312 matrix().m12() * delta.x() + matrix().m22() * delta.y());
3313
3314 min_screen_delta_len = qMin(min_screen_delta_len,
3315 qreal(qSqrt(screen_delta.x() * screen_delta.x() + screen_delta.y() * screen_delta.y())));
3316 }
3317
3318 const qreal padding = 2.0f;
3319
3320 qreal grow = padding / min_screen_delta_len;
3321
3322 QRectF boundingRect = ellipseRect.adjusted(-grow, -grow, grow, grow);
3323
3324 boundingRect = matrix().mapRect(boundingRect);
3325
3326 QPointF p(0.5, 0.5);
3327
3328 screen_rect = QRect((boundingRect.topLeft() - p).toPoint(),
3329 (boundingRect.bottomRight() + p).toPoint());
3330
3331 return screen_rect;
3332}
3333
3334void QGLEllipseMaskGenerator::drawMask(const QRect &rect)
3335{
3336#ifdef QT_OPENGL_ES
3337 Q_UNUSED(rect);
3338#else
3339 QGLContext *ctx = offscreen->context();
3340 offscreen->bind();
3341
3342 glDisable(GL_TEXTURE_GEN_S);
3343 glDisable(GL_TEXTURE_1D);
3344
3345 // fragment program needs the inverse radii of the ellipse
3346 glTexCoord2f(1.0f / (ellipseRect.width() * 0.5f),
3347 1.0f / (ellipseRect.height() * 0.5f));
3348
3349 QTransform translate(1, 0, 0, 1, -ellipseRect.center().x(), -ellipseRect.center().y());
3350 QTransform gl_to_qt(1, 0, 0, -1, 0, offscreen->drawableSize().height());
3351 QTransform inv_matrix = gl_to_qt * matrix().inverted() * translate;
3352
3353 float m[3][4] = { { inv_matrix.m11(), inv_matrix.m12(), inv_matrix.m13() },
3354 { inv_matrix.m21(), inv_matrix.m22(), inv_matrix.m23() },
3355 { inv_matrix.m31(), inv_matrix.m32(), inv_matrix.m33() } };
3356
3357 QPoint offs(screen_rect.left() - rect.left(), (offscreen->drawableSize().height() - screen_rect.top())
3358 - (offscreen->offscreenSize().height() - rect.top()));
3359
3360 // last component needs to be 1.0f to avoid Nvidia bug on linux
3361 float ellipse_offset[4] = { offs.x(), offs.y(), 0.0f, 1.0f };
3362
3363 GLfloat vertexArray[4 * 2];
3364 qt_add_rect_to_array(rect, vertexArray);
3365
3366 glBlendFunc(GL_ONE, GL_ZERO); // set mask
3367 glEnable(GL_FRAGMENT_PROGRAM_ARB);
3368 glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, maskFragmentProgram);
3369
3370 glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, maskVariableLocations[VAR_INV_MATRIX_M0], m[0]);
3371 glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, maskVariableLocations[VAR_INV_MATRIX_M1], m[1]);
3372 glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, maskVariableLocations[VAR_INV_MATRIX_M2], m[2]);
3373
3374 glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, maskVariableLocations[VAR_ELLIPSE_OFFSET], ellipse_offset);
3375
3376 glEnableClientState(GL_VERTEX_ARRAY);
3377 glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
3378 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
3379 glDisableClientState(GL_VERTEX_ARRAY);
3380 glDisable(GL_FRAGMENT_PROGRAM_ARB);
3381#endif
3382}
3383
3384void QOpenGLPaintEnginePrivate::drawOffscreenPath(const QPainterPath &path)
3385{
3386#ifdef Q_WS_QWS
3387 Q_UNUSED(path);
3388#else
3389 DEBUG_ONCE_STR("QOpenGLPaintEnginePrivate::drawOffscreenPath()");
3390
3391 disableClipping();
3392
3393 GLuint program = qt_gl_program_cache()->getProgram(device->context(),
3394 FRAGMENT_PROGRAM_MASK_TRAPEZOID_AA, 0, true);
3395 QGLPathMaskGenerator maskGenerator(path, matrix, offscreen, program);
3396 addItem(qt_mask_texture_cache()->getMask(maskGenerator, this));
3397
3398 enableClipping();
3399#endif
3400}
3401
3402void QOpenGLPaintEnginePrivate::drawFastRect(const QRectF &r)
3403{
3404 Q_Q(QOpenGLPaintEngine);
3405 DEBUG_ONCE_STR("QOpenGLPaintEngine::drawRects(): drawing fast rect");
3406
3407 q_vertexType vertexArray[10];
3408 qt_add_rect_to_array(r, vertexArray);
3409
3410 if (has_pen)
3411 QOpenGLCoordinateOffset::enableOffset(this);
3412
3413 if (has_brush) {
3414 flushDrawQueue();
3415
3416 bool temp = high_quality_antialiasing;
3417 high_quality_antialiasing = false;
3418
3419 q->updateCompositionMode(composition_mode);
3420
3421 setGradientOps(cbrush, r);
3422
3423 bool fast_style = current_style == Qt::LinearGradientPattern
3424 || current_style == Qt::SolidPattern;
3425
3426 if (fast_style && has_fast_composition_mode) {
3427 glEnableClientState(GL_VERTEX_ARRAY);
3428 glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
3429 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
3430 glDisableClientState(GL_VERTEX_ARRAY);
3431 } else {
3432 composite(r);
3433 }
3434
3435 high_quality_antialiasing = temp;
3436
3437 q->updateCompositionMode(composition_mode);
3438 }
3439
3440 if (has_pen) {
3441 if (has_fast_pen && !high_quality_antialiasing) {
3442 setGradientOps(cpen.brush(), r);
3443
3444 vertexArray[8] = vertexArray[0];
3445 vertexArray[9] = vertexArray[1];
3446
3447 glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
3448 glEnableClientState(GL_VERTEX_ARRAY);
3449 glDrawArrays(GL_LINE_STRIP, 0, 5);
3450 glDisableClientState(GL_VERTEX_ARRAY);
3451 } else {
3452 QPainterPath path;
3453 path.setFillRule(Qt::WindingFill);
3454
3455 qreal left = r.left();
3456 qreal right = r.right();
3457 qreal top = r.top();
3458 qreal bottom = r.bottom();
3459
3460 path.moveTo(left, top);
3461 path.lineTo(right, top);
3462 path.lineTo(right, bottom);
3463 path.lineTo(left, bottom);
3464 path.lineTo(left, top);
3465
3466 strokePath(path, false);
3467 }
3468
3469 QOpenGLCoordinateOffset::disableOffset(this);
3470 }
3471}
3472
3473bool QOpenGLPaintEnginePrivate::isFastRect(const QRectF &rect)
3474{
3475 if (matrix.type() < QTransform::TxRotate) {
3476 QRectF r = matrix.mapRect(rect);
3477 return r.topLeft().toPoint() == r.topLeft()
3478 && r.bottomRight().toPoint() == r.bottomRight();
3479 }
3480
3481 return false;
3482}
3483
3484void QOpenGLPaintEngine::drawRects(const QRect *rects, int rectCount)
3485{
3486 struct RectF {
3487 qreal x;
3488 qreal y;
3489 qreal w;
3490 qreal h;
3491 };
3492 Q_ASSERT(sizeof(RectF) == sizeof(QRectF));
3493 RectF fr[256];
3494 while (rectCount) {
3495 int i = 0;
3496 while (i < rectCount && i < 256) {
3497 fr[i].x = rects[i].x();
3498 fr[i].y = rects[i].y();
3499 fr[i].w = rects[i].width();
3500 fr[i].h = rects[i].height();
3501 ++i;
3502 }
3503 drawRects((QRectF *)(void *)fr, i);
3504 rects += i;
3505 rectCount -= i;
3506 }
3507}
3508
3509void QOpenGLPaintEngine::drawRects(const QRectF *rects, int rectCount)
3510{
3511 Q_D(QOpenGLPaintEngine);
3512
3513 if (d->use_emulation) {
3514 QPaintEngineEx::drawRects(rects, rectCount);
3515 return;
3516 }
3517
3518 for (int i=0; i<rectCount; ++i) {
3519 const QRectF &r = rects[i];
3520
3521 // optimization for rects which can be drawn aliased
3522 if (!d->high_quality_antialiasing || d->isFastRect(r)) {
3523 d->drawFastRect(r);
3524 } else {
3525 QPainterPath path;
3526 path.addRect(r);
3527
3528 if (d->has_brush) {
3529 d->disableClipping();
3530 GLuint program = qt_gl_program_cache()->getProgram(d->device->context(),
3531 FRAGMENT_PROGRAM_MASK_TRAPEZOID_AA, 0, true);
3532
3533 if (d->matrix.type() >= QTransform::TxProject) {
3534 QGLPathMaskGenerator maskGenerator(path, d->matrix, d->offscreen, program);
3535 d->addItem(qt_mask_texture_cache()->getMask(maskGenerator, d));
3536 } else {
3537 QGLRectMaskGenerator maskGenerator(path, d->matrix, d->offscreen, program);
3538 d->addItem(qt_mask_texture_cache()->getMask(maskGenerator, d));
3539 }
3540
3541 d->enableClipping();
3542 }
3543
3544 if (d->has_pen) {
3545 if (d->has_fast_pen)
3546 d->strokeLines(path);
3547 else
3548 d->strokePath(path, false);
3549 }
3550 }
3551 }
3552}
3553
3554static void addQuadAsTriangle(q_vertexType *quad, q_vertexType *triangle)
3555{
3556 triangle[0] = quad[0];
3557 triangle[1] = quad[1];
3558
3559 triangle[2] = quad[2];
3560 triangle[3] = quad[3];
3561
3562 triangle[4] = quad[4];
3563 triangle[5] = quad[5];
3564
3565 triangle[6] = quad[4];
3566 triangle[7] = quad[5];
3567
3568 triangle[8] = quad[6];
3569 triangle[9] = quad[7];
3570
3571 triangle[10] = quad[0];
3572 triangle[11] = quad[1];
3573}
3574
3575void QOpenGLPaintEngine::drawPoints(const QPoint *points, int pointCount)
3576{
3577 Q_ASSERT(sizeof(QT_PointF) == sizeof(QPointF));
3578 QT_PointF fp[256];
3579 while (pointCount) {
3580 int i = 0;
3581 while (i < pointCount && i < 256) {
3582 fp[i].x = points[i].x();
3583 fp[i].y = points[i].y();
3584 ++i;
3585 }
3586 drawPoints((QPointF *)(void *)fp, i);
3587 points += i;
3588 pointCount -= i;
3589 }
3590}
3591
3592void QOpenGLPaintEngine::drawPoints(const QPointF *points, int pointCount)
3593{
3594 Q_D(QOpenGLPaintEngine);
3595
3596 if (d->use_emulation) {
3597 QPaintEngineEx::drawPoints(points, pointCount);
3598 return;
3599 }
3600
3601 d->setGradientOps(d->cpen.brush(), QRectF());
3602
3603 if (!d->cpen.isCosmetic() || d->high_quality_antialiasing) {
3604 Qt::PenCapStyle capStyle = d->cpen.capStyle();
3605 if (capStyle == Qt::FlatCap)
3606 d->cpen.setCapStyle(Qt::SquareCap);
3607 QPaintEngine::drawPoints(points, pointCount);
3608 d->cpen.setCapStyle(capStyle);
3609 return;
3610 }
3611
3612 d->flushDrawQueue();
3613
3614 if (d->has_fast_pen) {
3615 QVarLengthArray<q_vertexType> vertexArray(6 * pointCount);
3616
3617 glMatrixMode(GL_MODELVIEW);
3618 glPushMatrix();
3619 glLoadIdentity();
3620
3621 int j = 0;
3622 for (int i = 0; i < pointCount; ++i) {
3623 QPointF mapped = d->matrix.map(points[i]);
3624
3625 qreal xf = qRound(mapped.x());
3626 qreal yf = qRound(mapped.y());
3627
3628 q_vertexType x = f2vt(xf);
3629 q_vertexType y = f2vt(yf);
3630
3631 vertexArray[j++] = x;
3632 vertexArray[j++] = y - f2vt(0.5);
3633
3634 vertexArray[j++] = x + f2vt(1.5);
3635 vertexArray[j++] = y + f2vt(1.0);
3636
3637 vertexArray[j++] = x;
3638 vertexArray[j++] = y + f2vt(1.0);
3639 }
3640
3641 glEnableClientState(GL_VERTEX_ARRAY);
3642
3643 glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray.constData());
3644 glDrawArrays(GL_TRIANGLES, 0, pointCount*3);
3645
3646 glDisableClientState(GL_VERTEX_ARRAY);
3647
3648 glPopMatrix();
3649 return;
3650 }
3651
3652 const qreal *vertexArray = reinterpret_cast<const qreal*>(&points[0]);
3653
3654 if (sizeof(qreal) == sizeof(double)) {
3655 Q_ASSERT(sizeof(QPointF) == 16);
3656 glVertexPointer(2, GL_DOUBLE, 0, vertexArray);
3657 }
3658 else {
3659 Q_ASSERT(sizeof(QPointF) == 8);
3660 glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
3661 }
3662
3663 glEnableClientState(GL_VERTEX_ARRAY);
3664 glDrawArrays(GL_POINTS, 0, pointCount);
3665 glDisableClientState(GL_VERTEX_ARRAY);
3666}
3667
3668void QOpenGLPaintEngine::drawLines(const QLine *lines, int lineCount)
3669{
3670 struct PointF {
3671 qreal x;
3672 qreal y;
3673 };
3674 struct LineF {
3675 PointF p1;
3676 PointF p2;
3677 };
3678 Q_ASSERT(sizeof(PointF) == sizeof(QPointF));
3679 Q_ASSERT(sizeof(LineF) == sizeof(QLineF));
3680 LineF fl[256];
3681 while (lineCount) {
3682 int i = 0;
3683 while (i < lineCount && i < 256) {
3684 fl[i].p1.x = lines[i].x1();
3685 fl[i].p1.y = lines[i].y1();
3686 fl[i].p2.x = lines[i].x2();
3687 fl[i].p2.y = lines[i].y2();
3688 ++i;
3689 }
3690 drawLines((QLineF *)(void *)fl, i);
3691 lines += i;
3692 lineCount -= i;
3693 }
3694}
3695
3696void QOpenGLPaintEngine::drawLines(const QLineF *lines, int lineCount)
3697{
3698 Q_D(QOpenGLPaintEngine);
3699
3700 if (d->use_emulation) {
3701 QPaintEngineEx::drawLines(lines, lineCount);
3702 return;
3703 }
3704
3705 if (d->has_pen) {
3706 QOpenGLCoordinateOffset offset(d);
3707 if (d->has_fast_pen && !d->high_quality_antialiasing) {
3708 //### gradient resolving on lines isn't correct
3709 d->setGradientOps(d->cpen.brush(), QRectF());
3710
3711 bool useRects = false;
3712 // scale or 90 degree rotation?
3713 if (d->matrix.type() <= QTransform::TxTranslate
3714 || (!d->cpen.isCosmetic()
3715 && (d->matrix.type() <= QTransform::TxScale
3716 || (d->matrix.type() == QTransform::TxRotate
3717 && d->matrix.m11() == 0 && d->matrix.m22() == 0)))) {
3718 useRects = true;
3719 for (int i = 0; i < lineCount; ++i) {
3720 if (lines[i].p1().x() != lines[i].p2().x()
3721 && lines[i].p1().y() != lines[i].p2().y()) {
3722 useRects = false;
3723 break;
3724 }
3725 }
3726 }
3727
3728 q_vertexType endCap = f2vt(d->cpen.capStyle() == Qt::FlatCap ? 0 : 0.5);
3729 if (useRects) {
3730 QVarLengthArray<q_vertexType> vertexArray(12 * lineCount);
3731
3732 q_vertexType quad[8];
3733 for (int i = 0; i < lineCount; ++i) {
3734 q_vertexType x1 = f2vt(lines[i].x1());
3735 q_vertexType x2 = f2vt(lines[i].x2());
3736 q_vertexType y1 = f2vt(lines[i].y1());
3737 q_vertexType y2 = f2vt(lines[i].y2());
3738
3739 if (x1 == x2) {
3740 if (y1 > y2)
3741 qSwap(y1, y2);
3742
3743 quad[0] = x1 - f2vt(0.5);
3744 quad[1] = y1 - endCap;
3745
3746 quad[2] = x1 + f2vt(0.5);
3747 quad[3] = y1 - endCap;
3748
3749 quad[4] = x1 + f2vt(0.5);
3750 quad[5] = y2 + endCap;
3751
3752 quad[6] = x1 - f2vt(0.5);
3753 quad[7] = y2 + endCap;
3754 } else {
3755 if (x1 > x2)
3756 qSwap(x1, x2);
3757
3758 quad[0] = x1 - endCap;
3759 quad[1] = y1 + f2vt(0.5);
3760
3761 quad[2] = x1 - endCap;
3762 quad[3] = y1 - f2vt(0.5);
3763
3764 quad[4] = x2 + endCap;
3765 quad[5] = y1 - f2vt(0.5);
3766
3767 quad[6] = x2 + endCap;
3768 quad[7] = y1 + f2vt(0.5);
3769 }
3770
3771 addQuadAsTriangle(quad, &vertexArray[12*i]);
3772 }
3773
3774 glEnableClientState(GL_VERTEX_ARRAY);
3775
3776 glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray.constData());
3777 glDrawArrays(GL_TRIANGLES, 0, lineCount*6);
3778
3779 glDisableClientState(GL_VERTEX_ARRAY);
3780 } else {
3781 QVarLengthArray<q_vertexType> vertexArray(4 * lineCount);
3782 for (int i = 0; i < lineCount; ++i) {
3783 const QPointF a = lines[i].p1();
3784 vertexArray[4*i] = f2vt(lines[i].x1());
3785 vertexArray[4*i+1] = f2vt(lines[i].y1());
3786 vertexArray[4*i+2] = f2vt(lines[i].x2());
3787 vertexArray[4*i+3] = f2vt(lines[i].y2());
3788 }
3789
3790 glEnableClientState(GL_VERTEX_ARRAY);
3791
3792 glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray.constData());
3793 glDrawArrays(GL_LINES, 0, lineCount*2);
3794
3795 glVertexPointer(2, q_vertexTypeEnum, 4*sizeof(q_vertexType), vertexArray.constData() + 2);
3796 glDrawArrays(GL_POINTS, 0, lineCount);
3797
3798 glDisableClientState(GL_VERTEX_ARRAY);
3799 }
3800 } else {
3801 QPainterPath path;
3802 path.setFillRule(Qt::WindingFill);
3803 for (int i=0; i<lineCount; ++i) {
3804 const QLineF &l = lines[i];
3805
3806 if (l.p1() == l.p2()) {
3807 if (d->cpen.capStyle() != Qt::FlatCap) {
3808 QPointF p = l.p1();
3809 drawPoints(&p, 1);
3810 }
3811 continue;
3812 }
3813
3814 path.moveTo(l.x1(), l.y1());
3815 path.lineTo(l.x2(), l.y2());
3816 }
3817
3818 if (d->has_fast_pen && d->high_quality_antialiasing)
3819 d->strokeLines(path);
3820 else
3821 d->strokePath(path, false);
3822 }
3823 }
3824}
3825
3826void QOpenGLPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
3827{
3828 Q_ASSERT(sizeof(QT_PointF) == sizeof(QPointF));
3829 QVarLengthArray<QT_PointF> p(pointCount);
3830 for (int i=0; i<pointCount; ++i) {
3831 p[i].x = points[i].x();
3832 p[i].y = points[i].y();
3833 }
3834 drawPolygon((QPointF *)p.data(), pointCount, mode);
3835}
3836
3837void QOpenGLPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
3838{
3839 Q_D(QOpenGLPaintEngine);
3840 if(pointCount < 2)
3841 return;
3842
3843 if (d->use_emulation) {
3844 QPaintEngineEx::drawPolygon(points, pointCount, mode);
3845 return;
3846 }
3847
3848 QRectF bounds;
3849 if ((mode == ConvexMode && !d->high_quality_antialiasing && state()->brushNeedsResolving()) ||
3850 ((d->has_fast_pen && !d->high_quality_antialiasing) && state()->penNeedsResolving())) {
3851 qreal minx = points[0].x(), miny = points[0].y(),
3852 maxx = points[0].x(), maxy = points[0].y();
3853 for (int i = 1; i < pointCount; ++i) {
3854 const QPointF &pt = points[i];
3855 if (minx > pt.x())
3856 minx = pt.x();
3857 if (miny > pt.y())
3858 miny = pt.y();
3859 if (maxx < pt.x())
3860 maxx = pt.x();
3861 if (maxy < pt.y())
3862 maxy = pt.y();
3863 }
3864 bounds = QRectF(minx, maxx, maxx-minx, maxy-miny);
3865 }
3866
3867 QOpenGLCoordinateOffset offset(d);
3868
3869 if (d->has_brush && mode != PolylineMode) {
3870 if (mode == ConvexMode && !d->high_quality_antialiasing) {
3871 //### resolving on polygon from points isn't correct
3872 d->setGradientOps(d->cbrush, bounds);
3873
3874 const qreal *vertexArray = reinterpret_cast<const qreal*>(&points[0]);
3875
3876 if (sizeof(qreal) == sizeof(double)) {
3877 Q_ASSERT(sizeof(QPointF) == 16);
3878 glVertexPointer(2, GL_DOUBLE, 0, vertexArray);
3879 }
3880 else {
3881 Q_ASSERT(sizeof(QPointF) == 8);
3882 glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
3883 }
3884
3885 glEnableClientState(GL_VERTEX_ARRAY);
3886 glDrawArrays(GL_TRIANGLE_FAN, 0, pointCount);
3887 glDisableClientState(GL_VERTEX_ARRAY);
3888 } else {
3889 QPainterPath path;
3890 path.setFillRule(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
3891 path.moveTo(points[0]);
3892 for (int i=1; i<pointCount; ++i)
3893 path.lineTo(points[i]);
3894 d->fillPath(path);
3895 }
3896 }
3897
3898 if (d->has_pen) {
3899 if (d->has_fast_pen && !d->high_quality_antialiasing) {
3900 d->setGradientOps(d->cpen.brush(), bounds);
3901 QVarLengthArray<q_vertexType> vertexArray(pointCount*2 + 2);
3902 glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray.constData());
3903 int i;
3904 for (i=0; i<pointCount; ++i) {
3905 vertexArray[i*2] = f2vt(points[i].x());
3906 vertexArray[i*2+1] = f2vt(points[i].y());
3907 }
3908
3909 glEnableClientState(GL_VERTEX_ARRAY);
3910 if (mode != PolylineMode) {
3911 vertexArray[i*2] = vertexArray[0];
3912 vertexArray[i*2+1] = vertexArray[1];
3913 glDrawArrays(GL_LINE_STRIP, 0, pointCount+1);
3914 } else {
3915 glDrawArrays(GL_LINE_STRIP, 0, pointCount);
3916 glDrawArrays(GL_POINTS, pointCount-1, 1);
3917 }
3918 glDisableClientState(GL_VERTEX_ARRAY);
3919 } else {
3920 QPainterPath path(points[0]);
3921 for (int i = 1; i < pointCount; ++i)
3922 path.lineTo(points[i]);
3923 if (mode != PolylineMode)
3924 path.lineTo(points[0]);
3925
3926 if (d->has_fast_pen)
3927 d->strokeLines(path);
3928 else
3929 d->strokePath(path, true);
3930 }
3931 }
3932}
3933
3934void QOpenGLPaintEnginePrivate::strokeLines(const QPainterPath &path)
3935{
3936 DEBUG_ONCE_STR("QOpenGLPaintEnginePrivate::strokeLines()");
3937
3938 qreal penWidth = cpen.widthF();
3939
3940 GLuint program = qt_gl_program_cache()->getProgram(device->context(),
3941 FRAGMENT_PROGRAM_MASK_TRAPEZOID_AA, 0, true);
3942 QGLLineMaskGenerator maskGenerator(path, matrix, penWidth == 0 ? 1.0 : penWidth,
3943 offscreen, program);
3944
3945 disableClipping();
3946
3947 QBrush temp = cbrush;
3948 QPointF origin = brush_origin;
3949
3950 cbrush = cpen.brush();
3951 brush_origin = QPointF();
3952
3953 addItem(qt_mask_texture_cache()->getMask(maskGenerator, this));
3954
3955 cbrush = temp;
3956 brush_origin = origin;
3957
3958 enableClipping();
3959}
3960
3961extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
3962
3963void QOpenGLPaintEnginePrivate::strokePath(const QPainterPath &path, bool use_cache)
3964{
3965 QBrush old_brush = cbrush;
3966 cbrush = cpen.brush();
3967
3968 qreal txscale = 1;
3969 if (cpen.isCosmetic() || (qt_scaleForTransform(matrix, &txscale) && txscale != 1)) {
3970 QTransform temp = matrix;
3971 matrix = QTransform();
3972 glPushMatrix();
3973
3974 if (has_antialiasing) {
3975 glLoadIdentity();
3976 } else {
3977 float offs_matrix[] =
3978 { 1, 0, 0, 0,
3979 0, 1, 0, 0,
3980 0, 0, 1, 0,
3981 0.5, 0.5, 0, 1 };
3982 glLoadMatrixf(offs_matrix);
3983 }
3984
3985 QPen pen = cpen;
3986 if (txscale != 1)
3987 pen.setWidthF(pen.widthF() * txscale);
3988 if (use_cache)
3989 fillPath(qt_opengl_stroke_cache()->getStrokedPath(temp.map(path), pen));
3990 else
3991 fillPath(strokeForPath(temp.map(path), pen));
3992
3993 glPopMatrix();
3994 matrix = temp;
3995 } else if (use_cache) {
3996 fillPath(qt_opengl_stroke_cache()->getStrokedPath(path, cpen));
3997 } else {
3998 fillPath(strokeForPath(path, cpen));
3999 }
4000
4001 cbrush = old_brush;
4002}
4003
4004void QOpenGLPaintEnginePrivate::strokePathFastPen(const QPainterPath &path, bool needsResolving)
4005{
4006#ifndef QT_OPENGL_ES
4007 QRectF bounds;
4008 if (needsResolving)
4009 bounds = path.controlPointRect();
4010 setGradientOps(cpen.brush(), bounds);
4011
4012 QBezier beziers[32];
4013 for (int i=0; i<path.elementCount(); ++i) {
4014 const QPainterPath::Element &e = path.elementAt(i);
4015 switch (e.type) {
4016 case QPainterPath::MoveToElement:
4017 if (i != 0)
4018 glEnd(); // GL_LINE_STRIP
4019 glBegin(GL_LINE_STRIP);
4020 glVertex2d(e.x, e.y);
4021
4022 break;
4023 case QPainterPath::LineToElement:
4024 glVertex2d(e.x, e.y);
4025 break;
4026
4027 case QPainterPath::CurveToElement:
4028 {
4029 QPointF sp = path.elementAt(i-1);
4030 QPointF cp2 = path.elementAt(i+1);
4031 QPointF ep = path.elementAt(i+2);
4032 i+=2;
4033
4034 qreal inverseScaleHalf = inverseScale / 2;
4035 beziers[0] = QBezier::fromPoints(sp, e, cp2, ep);
4036 QBezier *b = beziers;
4037 while (b >= beziers) {
4038 // check if we can pop the top bezier curve from the stack
4039 qreal l = qAbs(b->x4 - b->x1) + qAbs(b->y4 - b->y1);
4040 qreal d;
4041 if (l > inverseScale) {
4042 d = qAbs( (b->x4 - b->x1)*(b->y1 - b->y2)
4043 - (b->y4 - b->y1)*(b->x1 - b->x2) )
4044 + qAbs( (b->x4 - b->x1)*(b->y1 - b->y3)
4045 - (b->y4 - b->y1)*(b->x1 - b->x3) );
4046 d /= l;
4047 } else {
4048 d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) +
4049 qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3);
4050 }
4051 if (d < inverseScaleHalf || b == beziers + 31) {
4052 // good enough, we pop it off and add the endpoint
4053 glVertex2d(b->x4, b->y4);
4054 --b;
4055 } else {
4056 // split, second half of the polygon goes lower into the stack
4057 b->split(b+1, b);
4058 ++b;
4059 }
4060 }
4061 } // case CurveToElement
4062 default:
4063 break;
4064 } // end of switch
4065 }
4066 glEnd(); // GL_LINE_STRIP
4067#else
4068 // have to use vertex arrays on embedded
4069 QRectF bounds;
4070 if (needsResolving)
4071 bounds = path.controlPointRect();
4072 setGradientOps(cpen.brush(), bounds);
4073
4074 glEnableClientState(GL_VERTEX_ARRAY);
4075 tess_points.reset();
4076 QBezier beziers[32];
4077 for (int i=0; i<path.elementCount(); ++i) {
4078 const QPainterPath::Element &e = path.elementAt(i);
4079 switch (e.type) {
4080 case QPainterPath::MoveToElement:
4081 if (i != 0) {
4082 glVertexPointer(2, q_vertexTypeEnum, 0, tess_points.data());
4083 glDrawArrays(GL_LINE_STRIP, 0, tess_points.size());
4084 tess_points.reset();
4085 }
4086 tess_points.add(QPointF(e.x, e.y));
4087
4088 break;
4089 case QPainterPath::LineToElement:
4090 tess_points.add(QPointF(e.x, e.y));
4091 break;
4092
4093 case QPainterPath::CurveToElement:
4094 {
4095 QPointF sp = path.elementAt(i-1);
4096 QPointF cp2 = path.elementAt(i+1);
4097 QPointF ep = path.elementAt(i+2);
4098 i+=2;
4099
4100 qreal inverseScaleHalf = inverseScale / 2;
4101 beziers[0] = QBezier::fromPoints(sp, e, cp2, ep);
4102 QBezier *b = beziers;
4103 while (b >= beziers) {
4104 // check if we can pop the top bezier curve from the stack
4105 qreal l = qAbs(b->x4 - b->x1) + qAbs(b->y4 - b->y1);
4106 qreal d;
4107 if (l > inverseScale) {
4108 d = qAbs( (b->x4 - b->x1)*(b->y1 - b->y2)
4109 - (b->y4 - b->y1)*(b->x1 - b->x2) )
4110 + qAbs( (b->x4 - b->x1)*(b->y1 - b->y3)
4111 - (b->y4 - b->y1)*(b->x1 - b->x3) );
4112 d /= l;
4113 } else {
4114 d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) +
4115 qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3);
4116 }
4117 if (d < inverseScaleHalf || b == beziers + 31) {
4118 // good enough, we pop it off and add the endpoint
4119 tess_points.add(QPointF(b->x4, b->y4));
4120 --b;
4121 } else {
4122 // split, second half of the polygon goes lower into the stack
4123 b->split(b+1, b);
4124 ++b;
4125 }
4126 }
4127 } // case CurveToElement
4128 default:
4129 break;
4130 } // end of switch
4131 }
4132 glVertexPointer(2, q_vertexTypeEnum, 0, tess_points.data());
4133 glDrawArrays(GL_LINE_STRIP, 0, tess_points.size());
4134 glDisableClientState(GL_VERTEX_ARRAY);
4135#endif
4136}
4137
4138static bool pathClosed(const QPainterPath &path)
4139{
4140 QPointF lastMoveTo = path.elementAt(0);
4141 QPointF lastPoint = lastMoveTo;
4142
4143 for (int i = 1; i < path.elementCount(); ++i) {
4144 const QPainterPath::Element &e = path.elementAt(i);
4145 switch (e.type) {
4146 case QPainterPath::MoveToElement:
4147 if (lastMoveTo != lastPoint)
4148 return false;
4149 lastMoveTo = lastPoint = e;
4150 break;
4151 case QPainterPath::LineToElement:
4152 lastPoint = e;
4153 break;
4154 case QPainterPath::CurveToElement:
4155 lastPoint = path.elementAt(i + 2);
4156 i+=2;
4157 break;
4158 default:
4159 break;
4160 }
4161 }
4162
4163 return lastMoveTo == lastPoint;
4164}
4165
4166void QOpenGLPaintEngine::drawPath(const QPainterPath &path)
4167{
4168 Q_D(QOpenGLPaintEngine);
4169
4170 if (path.isEmpty())
4171 return;
4172
4173 if (d->use_emulation) {
4174 QPaintEngineEx::drawPath(path);
4175 return;
4176 }
4177
4178 QOpenGLCoordinateOffset offset(d);
4179
4180 if (d->has_brush) {
4181 bool path_closed = pathClosed(path);
4182
4183 bool has_thick_pen =
4184 path_closed
4185 && d->has_pen
4186 && d->cpen.style() == Qt::SolidLine
4187 && d->cpen.isSolid()
4188 && d->cpen.color().alpha() == 255
4189 && d->txop < QTransform::TxProject
4190 && d->cpen.widthF() >= 2 / qSqrt(qMin(d->matrix.m11() * d->matrix.m11()
4191 + d->matrix.m21() * d->matrix.m21(),
4192 d->matrix.m12() * d->matrix.m12()
4193 + d->matrix.m22() * d->matrix.m22()));
4194
4195 if (has_thick_pen) {
4196 DEBUG_ONCE qDebug() << "QOpenGLPaintEngine::drawPath(): Using thick pen optimization, style:" << d->cbrush.style();
4197
4198 d->flushDrawQueue();
4199
4200 bool temp = d->high_quality_antialiasing;
4201 d->high_quality_antialiasing = false;
4202
4203 updateCompositionMode(d->composition_mode);
4204
4205 d->fillPath(path);
4206
4207 d->high_quality_antialiasing = temp;
4208 updateCompositionMode(d->composition_mode);
4209 } else {
4210 d->fillPath(path);
4211 }
4212 }
4213
4214 if (d->has_pen) {
4215 if (d->has_fast_pen && !d->high_quality_antialiasing)
4216 d->strokePathFastPen(path, state()->penNeedsResolving());
4217 else
4218 d->strokePath(path, true);
4219 }
4220}
4221
4222void QOpenGLPaintEnginePrivate::drawImageAsPath(const QRectF &r, const QImage &img, const QRectF &sr)
4223{
4224 QBrush old_brush = cbrush;
4225 QPointF old_brush_origin = brush_origin;
4226
4227 qreal scaleX = r.width() / sr.width();
4228 qreal scaleY = r.height() / sr.height();
4229
4230 QTransform brush_matrix = QTransform::fromTranslate(r.left(), r.top());
4231 brush_matrix.scale(scaleX, scaleY);
4232 brush_matrix.translate(-sr.left(), -sr.top());
4233
4234 cbrush = QBrush(img);
4235 cbrush.setTransform(brush_matrix);
4236 brush_origin = QPointF();
4237
4238 QPainterPath p;
4239 p.addRect(r);
4240 fillPath(p);
4241
4242 cbrush = old_brush;
4243 brush_origin = old_brush_origin;
4244}
4245
4246void QOpenGLPaintEnginePrivate::drawTiledImageAsPath(const QRectF &r, const QImage &img, qreal sx, qreal sy,
4247 const QPointF &offset)
4248{
4249 QBrush old_brush = cbrush;
4250 QPointF old_brush_origin = brush_origin;
4251
4252 QTransform brush_matrix = QTransform::fromTranslate(r.left(), r.top());
4253 brush_matrix.scale(sx, sy);
4254 brush_matrix.translate(-offset.x(), -offset.y());
4255
4256 cbrush = QBrush(img);
4257 cbrush.setTransform(brush_matrix);
4258 brush_origin = QPointF();
4259
4260 QPainterPath p;
4261 p.addRect(r);
4262 fillPath(p);
4263
4264 cbrush = old_brush;
4265 brush_origin = old_brush_origin;
4266}
4267
4268static const QRectF scaleRect(const QRectF &r, qreal sx, qreal sy)
4269{
4270 return QRectF(r.x() * sx, r.y() * sy, r.width() * sx, r.height() * sy);
4271}
4272
4273template <typename T>
4274static const T qSubImage(const T &image, const QRectF &src, QRectF *srcNew)
4275{
4276 const int sx1 = qMax(0, qFloor(src.left()));
4277 const int sy1 = qMax(0, qFloor(src.top()));
4278 const int sx2 = qMin(image.width(), qCeil(src.right()));
4279 const int sy2 = qMin(image.height(), qCeil(src.bottom()));
4280
4281 const T sub = image.copy(sx1, sy1, sx2 - sx1, sy2 - sy1);
4282
4283 if (srcNew)
4284 *srcNew = src.translated(-sx1, -sy1);
4285
4286 return sub;
4287}
4288
4289void QOpenGLPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
4290{
4291 Q_D(QOpenGLPaintEngine);
4292 if (pm.depth() == 1) {
4293 QPixmap tpx(pm.size());
4294 tpx.fill(Qt::transparent);
4295 QPainter p(&tpx);
4296 p.setPen(d->cpen);
4297 p.drawPixmap(0, 0, pm);
4298 p.end();
4299 drawPixmap(r, tpx, sr);
4300 return;
4301 }
4302
4303 const int sz = d->max_texture_size;
4304 if (pm.width() > sz || pm.height() > sz) {
4305 QRectF subsr;
4306 const QPixmap sub = qSubImage(pm, sr, &subsr);
4307
4308 if (sub.width() <= sz && sub.height() <= sz) {
4309 drawPixmap(r, sub, subsr);
4310 } else {
4311 const QPixmap scaled = sub.scaled(sz, sz, Qt::KeepAspectRatio);
4312 const qreal sx = scaled.width() / qreal(sub.width());
4313 const qreal sy = scaled.height() / qreal(sub.height());
4314
4315 drawPixmap(r, scaled, scaleRect(subsr, sx, sy));
4316 }
4317 return;
4318 }
4319
4320
4321 if (d->composition_mode > QPainter::CompositionMode_Plus || (d->high_quality_antialiasing && !d->isFastRect(r)))
4322 d->drawImageAsPath(r, pm.toImage(), sr);
4323 else {
4324 GLenum target = qt_gl_preferredTextureTarget();
4325 d->flushDrawQueue();
4326 QGLTexture *tex =
4327 d->device->context()->d_func()->bindTexture(pm, target, GL_RGBA,
4328 QGLContext::InternalBindOption);
4329 drawTextureRect(pm.width(), pm.height(), r, sr, target, tex);
4330 }
4331}
4332
4333void QOpenGLPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &offset)
4334{
4335 Q_D(QOpenGLPaintEngine);
4336 if (pm.depth() == 1) {
4337 QPixmap tpx(pm.size());
4338 tpx.fill(Qt::transparent);
4339 QPainter p(&tpx);
4340 p.setPen(d->cpen);
4341 p.drawPixmap(0, 0, pm);
4342 p.end();
4343 drawTiledPixmap(r, tpx, offset);
4344 return;
4345 }
4346
4347 QImage scaled;
4348 const int sz = d->max_texture_size;
4349 if (pm.width() > sz || pm.height() > sz) {
4350 int rw = qCeil(r.width());
4351 int rh = qCeil(r.height());
4352 if (rw < pm.width() && rh < pm.height()) {
4353 drawTiledPixmap(r, pm.copy(0, 0, rw, rh), offset);
4354 return;
4355 }
4356
4357 scaled = pm.toImage().scaled(sz, sz, Qt::KeepAspectRatio);
4358 }
4359
4360 if (d->composition_mode > QPainter::CompositionMode_Plus || (d->high_quality_antialiasing && !d->isFastRect(r))) {
4361 if (scaled.isNull())
4362 d->drawTiledImageAsPath(r, pm.toImage(), 1, 1, offset);
4363 else {
4364 const qreal sx = pm.width() / qreal(scaled.width());
4365 const qreal sy = pm.height() / qreal(scaled.height());
4366 d->drawTiledImageAsPath(r, scaled, sx, sy, offset);
4367 }
4368 } else {
4369 d->flushDrawQueue();
4370
4371 QGLTexture *tex;
4372 if (scaled.isNull())
4373 tex = d->device->context()->d_func()->bindTexture(pm, GL_TEXTURE_2D, GL_RGBA,
4374 QGLContext::InternalBindOption);
4375 else
4376 tex = d->device->context()->d_func()->bindTexture(scaled, GL_TEXTURE_2D, GL_RGBA,
4377 QGLContext::InternalBindOption);
4378 updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, d->use_smooth_pixmap_transform);
4379
4380#ifndef QT_OPENGL_ES
4381 glPushAttrib(GL_CURRENT_BIT);
4382 glDisable(GL_TEXTURE_GEN_S);
4383#endif
4384 glColor4f(d->opacity, d->opacity, d->opacity, d->opacity);
4385 glEnable(GL_TEXTURE_2D);
4386
4387 GLdouble tc_w = r.width()/pm.width();
4388 GLdouble tc_h = r.height()/pm.height();
4389
4390 // Rotate the texture so that it is aligned correctly and the
4391 // wrapping is done correctly
4392 if (tex->options & QGLContext::InvertedYBindOption) {
4393 glMatrixMode(GL_TEXTURE);
4394 glPushMatrix();
4395 glRotatef(180.0, 0.0, 1.0, 0.0);
4396 glRotatef(180.0, 0.0, 0.0, 1.0);
4397 }
4398
4399 q_vertexType vertexArray[4*2];
4400 q_vertexType texCoordArray[4*2];
4401
4402 double offset_x = offset.x() / pm.width();
4403 double offset_y = offset.y() / pm.height();
4404
4405 qt_add_rect_to_array(r, vertexArray);
4406 qt_add_texcoords_to_array(offset_x, offset_y,
4407 tc_w + offset_x, tc_h + offset_y, texCoordArray);
4408
4409 glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
4410 glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray);
4411
4412 glEnableClientState(GL_VERTEX_ARRAY);
4413 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
4414 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
4415 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
4416 glDisableClientState(GL_VERTEX_ARRAY);
4417 if (tex->options & QGLContext::InvertedYBindOption)
4418 glPopMatrix();
4419
4420 glDisable(GL_TEXTURE_2D);
4421#ifndef QT_OPENGL_ES
4422 glPopAttrib();
4423#endif
4424 }
4425}
4426
4427void QOpenGLPaintEngine::drawImage(const QRectF &r, const QImage &image, const QRectF &sr,
4428 Qt::ImageConversionFlags)
4429{
4430 Q_D(QOpenGLPaintEngine);
4431
4432 const int sz = d->max_texture_size;
4433 if (image.width() > sz || image.height() > sz) {
4434 QRectF subsr;
4435 const QImage sub = qSubImage(image, sr, &subsr);
4436
4437 if (sub.width() <= sz && sub.height() <= sz) {
4438 drawImage(r, sub, subsr, 0);
4439 } else {
4440 const QImage scaled = sub.scaled(sz, sz, Qt::KeepAspectRatio);
4441 const qreal sx = scaled.width() / qreal(sub.width());
4442 const qreal sy = scaled.height() / qreal(sub.height());
4443
4444 drawImage(r, scaled, scaleRect(subsr, sx, sy), 0);
4445 }
4446 return;
4447 }
4448
4449 if (d->composition_mode > QPainter::CompositionMode_Plus || (d->high_quality_antialiasing && !d->isFastRect(r)))
4450 d->drawImageAsPath(r, image, sr);
4451 else {
4452 GLenum target = qt_gl_preferredTextureTarget();
4453 d->flushDrawQueue();
4454 QGLTexture *tex =
4455 d->device->context()->d_func()->bindTexture(image, target, GL_RGBA,
4456 QGLContext::InternalBindOption);
4457 drawTextureRect(image.width(), image.height(), r, sr, target, tex);
4458 }
4459}
4460
4461void QOpenGLPaintEngine::drawTextureRect(int tx_width, int tx_height, const QRectF &r,
4462 const QRectF &sr, GLenum target, QGLTexture *tex)
4463{
4464 Q_D(QOpenGLPaintEngine);
4465#ifndef QT_OPENGL_ES
4466 glPushAttrib(GL_CURRENT_BIT);
4467 glDisable(GL_TEXTURE_GEN_S);
4468#endif
4469 glColor4f(d->opacity, d->opacity, d->opacity, d->opacity);
4470 glEnable(target);
4471 updateTextureFilter(target, GL_CLAMP_TO_EDGE, d->use_smooth_pixmap_transform);
4472
4473 qreal x1, x2, y1, y2;
4474 if (target == GL_TEXTURE_2D) {
4475 x1 = sr.x() / tx_width;
4476 x2 = x1 + sr.width() / tx_width;
4477 if (tex->options & QGLContext::InvertedYBindOption) {
4478 y1 = 1 - (sr.bottom() / tx_height);
4479 y2 = 1 - (sr.y() / tx_height);
4480 } else {
4481 y1 = sr.bottom() / tx_height;
4482 y2 = sr.y() / tx_height;
4483 }
4484 } else {
4485 x1 = sr.x();
4486 x2 = sr.right();
4487 y1 = sr.bottom();
4488 y2 = sr.y();
4489 }
4490
4491 q_vertexType vertexArray[4*2];
4492 q_vertexType texCoordArray[4*2];
4493
4494 qt_add_rect_to_array(r, vertexArray);
4495 qt_add_texcoords_to_array(x1, y2, x2, y1, texCoordArray);
4496
4497 glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
4498 glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray);
4499
4500 glEnableClientState(GL_VERTEX_ARRAY);
4501 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
4502 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
4503 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
4504 glDisableClientState(GL_VERTEX_ARRAY);
4505
4506 glDisable(target);
4507#ifndef QT_OPENGL_ES
4508 glPopAttrib();
4509#endif
4510}
4511
4512#ifdef Q_WS_WIN
4513HDC
4514#else
4515Qt::HANDLE
4516#endif
4517QOpenGLPaintEngine::handle() const
4518{
4519 return 0;
4520}
4521
4522static const int x_margin = 1;
4523static const int y_margin = 0;
4524
4525struct QGLGlyphCoord {
4526 // stores the offset and size of a glyph texture
4527 qreal x;
4528 qreal y;
4529 qreal width;
4530 qreal height;
4531 qreal log_width;
4532 qreal log_height;
4533 QFixed x_offset;
4534 QFixed y_offset;
4535};
4536
4537struct QGLFontTexture {
4538 int x_offset; // glyph offset within the
4539 int y_offset;
4540 GLuint texture;
4541 int width;
4542 int height;
4543};
4544
4545typedef QHash<glyph_t, QGLGlyphCoord*> QGLGlyphHash;
4546typedef QHash<QFontEngine*, QGLGlyphHash*> QGLFontGlyphHash;
4547typedef QHash<quint64, QGLFontTexture*> QGLFontTexHash;
4548typedef QHash<const QGLContext*, QGLFontGlyphHash*> QGLContextHash;
4549
4550static inline void qt_delete_glyph_hash(QGLGlyphHash *hash)
4551{
4552 qDeleteAll(*hash);
4553 delete hash;
4554}
4555
4556class QGLGlyphCache : public QObject
4557{
4558 Q_OBJECT
4559public:
4560 QGLGlyphCache() : QObject(0) { current_cache = 0; }
4561 ~QGLGlyphCache();
4562 QGLGlyphCoord *lookup(QFontEngine *, glyph_t);
4563 void cacheGlyphs(QGLContext *, const QTextItemInt &, const QVarLengthArray<glyph_t> &);
4564 void cleanCache();
4565 void allocTexture(int width, int height, GLuint texture);
4566
4567public slots:
4568 void cleanupContext(const QGLContext *);
4569 void fontEngineDestroyed(QObject *);
4570 void widgetDestroyed(QObject *);
4571
4572protected:
4573 QGLGlyphHash *current_cache;
4574 QGLFontTexHash qt_font_textures;
4575 QGLContextHash qt_context_cache;
4576};
4577
4578QGLGlyphCache::~QGLGlyphCache()
4579{
4580// qDebug() << "cleaning out the QGLGlyphCache";
4581 cleanCache();
4582}
4583
4584void QGLGlyphCache::fontEngineDestroyed(QObject *o)
4585{
4586// qDebug() << "fontEngineDestroyed()";
4587 QFontEngine *fe = static_cast<QFontEngine *>(o); // safe, since only the type is used
4588 QList<const QGLContext *> keys = qt_context_cache.keys();
4589 const QGLContext *ctx = 0;
4590
4591 for (int i=0; i < keys.size(); ++i) {
4592 QGLFontGlyphHash *font_cache = qt_context_cache.value(keys.at(i));
4593 if (font_cache->find(fe) != font_cache->end()) {
4594 ctx = keys.at(i);
4595 QGLGlyphHash *cache = font_cache->take(fe);
4596 qt_delete_glyph_hash(cache);
4597 break;
4598 }
4599 }
4600
4601 quint64 font_key = (reinterpret_cast<quint64>(ctx) << 32) | reinterpret_cast<quint64>(fe);
4602 QGLFontTexture *tex = qt_font_textures.take(font_key);
4603 if (tex) {
4604#ifdef Q_WS_MAC
4605 if (
4606# ifndef QT_MAC_USE_COCOA
4607 aglGetCurrentContext() != 0
4608# else
4609 qt_current_nsopengl_context() != 0
4610# endif
4611 )
4612#endif
4613 glDeleteTextures(1, &tex->texture);
4614 delete tex;
4615 }
4616}
4617
4618void QGLGlyphCache::widgetDestroyed(QObject *)
4619{
4620// qDebug() << "widget destroyed";
4621 cleanCache(); // ###
4622}
4623
4624void QGLGlyphCache::cleanupContext(const QGLContext *ctx)
4625{
4626// qDebug() << "==> cleaning for: " << hex << ctx;
4627 QGLFontGlyphHash *font_cache = qt_context_cache.take(ctx);
4628
4629 if (font_cache) {
4630 QList<QFontEngine *> keys = font_cache->keys();
4631 for (int i=0; i < keys.size(); ++i) {
4632 QFontEngine *fe = keys.at(i);
4633 qt_delete_glyph_hash(font_cache->take(fe));
4634 quint64 font_key = (reinterpret_cast<quint64>(ctx) << 32) | reinterpret_cast<quint64>(fe);
4635 QGLFontTexture *font_tex = qt_font_textures.take(font_key);
4636 if (font_tex) {
4637#ifdef Q_WS_MAC
4638 if (
4639# ifndef QT_MAC_USE_COCOA
4640 aglGetCurrentContext() == 0
4641# else
4642 qt_current_nsopengl_context() != 0
4643# endif
4644 )
4645#endif
4646 glDeleteTextures(1, &font_tex->texture);
4647 delete font_tex;
4648 }
4649 }
4650 delete font_cache;
4651 }
4652// qDebug() << "<=== done cleaning, num tex:" << qt_font_textures.size() << "num ctx:" << qt_context_cache.size();
4653}
4654
4655void QGLGlyphCache::cleanCache()
4656{
4657 QGLFontTexHash::const_iterator it = qt_font_textures.constBegin();
4658 if (QGLContext::currentContext()) {
4659 while (it != qt_font_textures.constEnd()) {
4660#if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA)
4661 if (qt_current_nsopengl_context() == 0)
4662 break;
4663#endif
4664 glDeleteTextures(1, &it.value()->texture);
4665 ++it;
4666 }
4667 }
4668 qDeleteAll(qt_font_textures);
4669 qt_font_textures.clear();
4670
4671 QList<const QGLContext *> keys = qt_context_cache.keys();
4672 for (int i=0; i < keys.size(); ++i) {
4673 QGLFontGlyphHash *font_cache = qt_context_cache.value(keys.at(i));
4674 QGLFontGlyphHash::Iterator it = font_cache->begin();
4675 for (; it != font_cache->end(); ++it)
4676 qt_delete_glyph_hash(it.value());
4677 font_cache->clear();
4678 }
4679 qDeleteAll(qt_context_cache);
4680 qt_context_cache.clear();
4681}
4682
4683void QGLGlyphCache::allocTexture(int width, int height, GLuint texture)
4684{
4685 uchar *tex_data = (uchar *) malloc(width*height*2);
4686 memset(tex_data, 0, width*height*2);
4687 glBindTexture(GL_TEXTURE_2D, texture);
4688#ifndef QT_OPENGL_ES
4689 glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE8_ALPHA8,
4690 width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, tex_data);
4691#else
4692 glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA,
4693 width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, tex_data);
4694#endif
4695 free(tex_data);
4696}
4697
4698#if 0
4699// useful for debugging the glyph cache
4700static QImage getCurrentTexture(const QColor &color, QGLFontTexture *font_tex)
4701{
4702 ushort *old_tex_data = (ushort *) malloc(font_tex->width*font_tex->height*2);
4703 glGetTexImage(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, old_tex_data);
4704 QImage im(font_tex->width, font_tex->height, QImage::Format_ARGB32);
4705 for (int y=0; y<font_tex->height; ++y) {
4706 for (int x=0; x<font_tex->width; ++x) {
4707 im.setPixel(x, y, ((*(old_tex_data+x+y*font_tex->width)) << 24) | (0x00ffffff & color.rgb()));
4708 }
4709 }
4710 delete old_tex_data;
4711 return im;
4712}
4713#endif
4714
4715void QGLGlyphCache::cacheGlyphs(QGLContext *context, const QTextItemInt &ti,
4716 const QVarLengthArray<glyph_t> &glyphs)
4717{
4718 QGLContextHash::const_iterator dev_it = qt_context_cache.constFind(context);
4719 QGLFontGlyphHash *font_cache = 0;
4720 const QGLContext *context_key = 0;
4721
4722 if (dev_it == qt_context_cache.constEnd()) {
4723 // check for shared contexts
4724 QList<const QGLContext *> contexts = qt_context_cache.keys();
4725 for (int i=0; i<contexts.size(); ++i) {
4726 const QGLContext *ctx = contexts.at(i);
4727 if (ctx != context && QGLContext::areSharing(context, ctx)) {
4728 context_key = ctx;
4729 dev_it = qt_context_cache.constFind(context_key);
4730 break;
4731 }
4732 }
4733 }
4734
4735 if (dev_it == qt_context_cache.constEnd()) {
4736 // no shared contexts either - create a new entry
4737 font_cache = new QGLFontGlyphHash;
4738// qDebug() << "new context" << context << font_cache;
4739 qt_context_cache.insert(context, font_cache);
4740 if (context->isValid() && context->device()->devType() == QInternal::Widget) {
4741 QWidget *widget = static_cast<QWidget *>(context->device());
4742 connect(widget, SIGNAL(destroyed(QObject*)), SLOT(widgetDestroyed(QObject*)));
4743 connect(QGLSignalProxy::instance(),
4744 SIGNAL(aboutToDestroyContext(const QGLContext*)),
4745 SLOT(cleanupContext(const QGLContext*)));
4746 }
4747 } else {
4748 font_cache = dev_it.value();
4749 }
4750 Q_ASSERT(font_cache != 0);
4751
4752 QGLFontGlyphHash::const_iterator cache_it = font_cache->constFind(ti.fontEngine);
4753 QGLGlyphHash *cache = 0;
4754 if (cache_it == font_cache->constEnd()) {
4755 cache = new QGLGlyphHash;
4756 font_cache->insert(ti.fontEngine, cache);
4757 connect(ti.fontEngine, SIGNAL(destroyed(QObject*)), SLOT(fontEngineDestroyed(QObject*)));
4758 } else {
4759 cache = cache_it.value();
4760 }
4761 current_cache = cache;
4762
4763 quint64 font_key = (reinterpret_cast<quint64>(context_key ? context_key : context) << 32)
4764 | reinterpret_cast<quint64>(ti.fontEngine);
4765 QGLFontTexHash::const_iterator it = qt_font_textures.constFind(font_key);
4766 QGLFontTexture *font_tex;
4767 if (it == qt_font_textures.constEnd()) {
4768 GLuint font_texture;
4769 glGenTextures(1, &font_texture);
4770 GLint tex_height = qt_next_power_of_two(qRound(ti.ascent.toReal() + ti.descent.toReal())+2);
4771 GLint tex_width = qt_next_power_of_two(tex_height*30); // ###
4772 GLint max_tex_size;
4773 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size);
4774 Q_ASSERT(max_tex_size > 0);
4775 if (tex_width > max_tex_size)
4776 tex_width = max_tex_size;
4777 allocTexture(tex_width, tex_height, font_texture);
4778 font_tex = new QGLFontTexture;
4779 font_tex->texture = font_texture;
4780 font_tex->x_offset = x_margin;
4781 font_tex->y_offset = y_margin;
4782 font_tex->width = tex_width;
4783 font_tex->height = tex_height;
4784// qDebug() << "new font tex - width:" << tex_width << "height:"<< tex_height
4785// << hex << "tex id:" << font_tex->texture << "key:" << font_key << "num cached:" << qt_font_textures.size();
4786 qt_font_textures.insert(font_key, font_tex);
4787 } else {
4788 font_tex = it.value();
4789 glBindTexture(GL_TEXTURE_2D, font_tex->texture);
4790 }
4791
4792 for (int i=0; i< glyphs.size(); ++i) {
4793 QGLGlyphHash::const_iterator it = cache->constFind(glyphs[i]);
4794 if (it == cache->constEnd()) {
4795 // render new glyph and put it in the cache
4796 glyph_metrics_t metrics = ti.fontEngine->boundingBox(glyphs[i]);
4797 int glyph_width = qRound(metrics.width.toReal())+2;
4798 int glyph_height = qRound(ti.ascent.toReal() + ti.descent.toReal())+2;
4799
4800 if (font_tex->x_offset + glyph_width + x_margin > font_tex->width) {
4801 int strip_height = qt_next_power_of_two(qRound(ti.ascent.toReal() + ti.descent.toReal())+2);
4802 font_tex->x_offset = x_margin;
4803 font_tex->y_offset += strip_height;
4804 if (font_tex->y_offset >= font_tex->height) {
4805 // get hold of the old font texture
4806 uchar *old_tex_data = (uchar *) malloc(font_tex->width*font_tex->height*2);
4807 int old_tex_height = font_tex->height;
4808#ifndef QT_OPENGL_ES
4809 glGetTexImage(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, old_tex_data);
4810#endif
4811
4812 // realloc a larger texture
4813 glDeleteTextures(1, &font_tex->texture);
4814 glGenTextures(1, &font_tex->texture);
4815 font_tex->height = qt_next_power_of_two(font_tex->height + strip_height);
4816 allocTexture(font_tex->width, font_tex->height, font_tex->texture);
4817
4818 // write back the old texture data
4819 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, font_tex->width, old_tex_height,
4820 GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, old_tex_data);
4821 free(old_tex_data);
4822
4823 // update the texture coords and the y offset for the existing glyphs in
4824 // the cache, because of the texture size change
4825 QGLGlyphHash::iterator it = cache->begin();
4826 while (it != cache->end()) {
4827 it.value()->height = (it.value()->height * old_tex_height) / font_tex->height;
4828 it.value()->y = (it.value()->y * old_tex_height) / font_tex->height;
4829 ++it;
4830 }
4831 }
4832 }
4833
4834 QImage glyph_im(ti.fontEngine->alphaMapForGlyph(glyphs[i]));
4835 glyph_im = glyph_im.convertToFormat(QImage::Format_Indexed8);
4836 glyph_width = glyph_im.width();
4837 Q_ASSERT(glyph_width >= 0);
4838 // pad the glyph width to an even number
4839 if (glyph_width%2 != 0)
4840 ++glyph_width;
4841
4842 QGLGlyphCoord *qgl_glyph = new QGLGlyphCoord;
4843 qgl_glyph->x = qreal(font_tex->x_offset) / font_tex->width;
4844 qgl_glyph->y = qreal(font_tex->y_offset) / font_tex->height;
4845 qgl_glyph->width = qreal(glyph_width) / font_tex->width;
4846 qgl_glyph->height = qreal(glyph_height) / font_tex->height;
4847 qgl_glyph->log_width = qreal(glyph_width);
4848 qgl_glyph->log_height = qgl_glyph->height * font_tex->height;
4849#ifdef Q_WS_MAC
4850 qgl_glyph->x_offset = -metrics.x + 1;
4851 qgl_glyph->y_offset = metrics.y - 2;
4852#else
4853 qgl_glyph->x_offset = -metrics.x;
4854 qgl_glyph->y_offset = metrics.y;
4855#endif
4856
4857 if (!glyph_im.isNull()) {
4858
4859 int idx = 0;
4860 uchar *tex_data = (uchar *) malloc(glyph_width*glyph_im.height()*2);
4861 memset(tex_data, 0, glyph_width*glyph_im.height()*2);
4862
4863 for (int y=0; y<glyph_im.height(); ++y) {
4864 uchar *s = (uchar *) glyph_im.scanLine(y);
4865 for (int x=0; x<glyph_im.width(); ++x) {
4866 uchar alpha = qAlpha(glyph_im.color(*s));
4867 tex_data[idx] = alpha;
4868 tex_data[idx+1] = alpha;
4869 ++s;
4870 idx += 2;
4871 }
4872 if (glyph_im.width()%2 != 0)
4873 idx += 2;
4874 }
4875 glTexSubImage2D(GL_TEXTURE_2D, 0, font_tex->x_offset, font_tex->y_offset,
4876 glyph_width, glyph_im.height(),
4877 GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, tex_data);
4878 free(tex_data);
4879 }
4880 if (font_tex->x_offset + glyph_width + x_margin > font_tex->width) {
4881 font_tex->x_offset = x_margin;
4882 font_tex->y_offset += glyph_height + y_margin;
4883 } else {
4884 font_tex->x_offset += glyph_width + x_margin;
4885 }
4886
4887 cache->insert(glyphs[i], qgl_glyph);
4888 }
4889 }
4890}
4891
4892QGLGlyphCoord *QGLGlyphCache::lookup(QFontEngine *, glyph_t g)
4893{
4894 Q_ASSERT(current_cache != 0);
4895 // ### careful here
4896 QGLGlyphHash::const_iterator it = current_cache->constFind(g);
4897 if (it == current_cache->constEnd())
4898 return 0;
4899 else
4900 return it.value();
4901}
4902
4903Q_GLOBAL_STATIC(QGLGlyphCache, qt_glyph_cache)
4904
4905//
4906// assumption: the context that this is called for has to be the
4907// current context
4908//
4909void qgl_cleanup_glyph_cache(QGLContext *ctx)
4910{
4911 qt_glyph_cache()->cleanupContext(ctx);
4912}
4913
4914void QOpenGLPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
4915{
4916 Q_D(QOpenGLPaintEngine);
4917
4918 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
4919
4920 // fall back to drawing a polygon if the scale factor is large, or
4921 // we use a gradient pen
4922 if ((d->matrix.det() > 1) || (d->pen_brush_style >= Qt::LinearGradientPattern
4923 && d->pen_brush_style <= Qt::ConicalGradientPattern)) {
4924 QPaintEngine::drawTextItem(p, textItem);
4925 return;
4926 }
4927
4928 d->flushDrawQueue();
4929
4930 // add the glyphs used to the glyph texture cache
4931 QVarLengthArray<QFixedPoint> positions;
4932 QVarLengthArray<glyph_t> glyphs;
4933 QTransform matrix = QTransform::fromTranslate(qRound(p.x()), qRound(p.y()));
4934 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
4935
4936 // make sure the glyphs we want to draw are in the cache
4937 qt_glyph_cache()->cacheGlyphs(d->device->context(), ti, glyphs);
4938
4939 d->setGradientOps(Qt::SolidPattern, QRectF()); // turns off gradient ops
4940 qt_glColor4ubv(d->pen_color);
4941 glEnable(GL_TEXTURE_2D);
4942
4943#ifdef Q_WS_QWS
4944 // XXX: it is necessary to disable alpha writes on GLES/embedded because we don't want
4945 // text rendering to update the alpha in the window surface.
4946 // XXX: This may not be needed as this behavior does seem to be caused by driver bug
4947 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
4948#endif
4949
4950 // do the actual drawing
4951 q_vertexType vertexArray[4*2];
4952 q_vertexType texCoordArray[4*2];
4953
4954 glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
4955 glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray);
4956
4957 glEnableClientState(GL_VERTEX_ARRAY);
4958 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
4959 bool antialias = !(ti.fontEngine->fontDef.styleStrategy & QFont::NoAntialias)
4960 && (d->matrix.type() > QTransform::TxTranslate);
4961 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, antialias ? GL_LINEAR : GL_NEAREST);
4962 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, antialias ? GL_LINEAR : GL_NEAREST);
4963
4964 for (int i=0; i< glyphs.size(); ++i) {
4965 QGLGlyphCoord *g = qt_glyph_cache()->lookup(ti.fontEngine, glyphs[i]);
4966
4967 // we don't cache glyphs with no width/height
4968 if (!g)
4969 continue;
4970
4971 qreal x1, x2, y1, y2;
4972 x1 = g->x;
4973 y1 = g->y;
4974 x2 = x1 + g->width;
4975 y2 = y1 + g->height;
4976
4977 QPointF logical_pos((positions[i].x - g->x_offset).toReal(),
4978 (positions[i].y + g->y_offset).toReal());
4979
4980 qt_add_rect_to_array(QRectF(logical_pos, QSizeF(g->log_width, g->log_height)), vertexArray);
4981 qt_add_texcoords_to_array(x1, y1, x2, y2, texCoordArray);
4982
4983 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
4984 }
4985
4986 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
4987 glDisableClientState(GL_VERTEX_ARRAY);
4988
4989 glDisable(GL_TEXTURE_2D);
4990
4991#ifdef Q_WS_QWS
4992 // XXX: This may not be needed as this behavior does seem to be caused by driver bug
4993 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
4994#endif
4995}
4996
4997
4998void QOpenGLPaintEngine::drawEllipse(const QRectF &rect)
4999{
5000#ifndef Q_WS_QWS
5001 Q_D(QOpenGLPaintEngine);
5002
5003 if (d->use_emulation) {
5004 QPaintEngineEx::drawEllipse(rect);
5005 return;
5006 }
5007
5008 if (d->high_quality_antialiasing) {
5009 if (d->has_brush) {
5010 d->disableClipping();
5011
5012 glMatrixMode(GL_MODELVIEW);
5013 glPushMatrix();
5014 glLoadIdentity();
5015
5016 GLuint program = qt_gl_program_cache()->getProgram(d->device->context(),
5017 FRAGMENT_PROGRAM_MASK_ELLIPSE_AA, 0, true);
5018 QGLEllipseMaskGenerator maskGenerator(rect,
5019 d->matrix,
5020 d->offscreen,
5021 program,
5022 mask_variable_locations[FRAGMENT_PROGRAM_MASK_ELLIPSE_AA]);
5023
5024 d->addItem(qt_mask_texture_cache()->getMask(maskGenerator, d));
5025
5026 d->enableClipping();
5027
5028 glMatrixMode(GL_MODELVIEW);
5029 glPopMatrix();
5030 }
5031
5032 if (d->has_pen) {
5033 QPainterPath path;
5034 path.addEllipse(rect);
5035
5036 d->strokePath(path, false);
5037 }
5038 } else {
5039 DEBUG_ONCE_STR("QOpenGLPaintEngine::drawEllipse(): falling back to drawPath()");
5040
5041 QPainterPath path;
5042 path.addEllipse(rect);
5043 drawPath(path);
5044 }
5045#else
5046 QPaintEngineEx::drawEllipse(rect);
5047#endif
5048}
5049
5050
5051void QOpenGLPaintEnginePrivate::updateFragmentProgramData(int locations[])
5052{
5053#ifdef Q_WS_QWS
5054 Q_UNUSED(locations);
5055#else
5056 QGL_FUNC_CONTEXT;
5057
5058 QSize sz = offscreen.offscreenSize();
5059
5060 float inv_mask_size_data[4] = { 1.0f / sz.width(), 1.0f / sz.height(), 0.0f, 0.0f };
5061
5062 sz = drawable_texture_size;
5063
5064 float inv_dst_size_data[4] = { 1.0f / sz.width(), 1.0f / sz.height(), 0.0f, 0.0f };
5065
5066 // default inv size 0.125f == 1.0f / 8.0f for pattern brushes
5067 float inv_brush_texture_size_data[4] = { 0.125f, 0.125f };
5068
5069 // texture patterns have their own size
5070 if (current_style == Qt::TexturePattern) {
5071 QSize sz = cbrush.texture().size();
5072
5073 inv_brush_texture_size_data[0] = 1.0f / sz.width();
5074 inv_brush_texture_size_data[1] = 1.0f / sz.height();
5075 }
5076
5077 for (unsigned int i = 0; i < num_fragment_variables; ++i) {
5078 int location = locations[i];
5079
5080 if (location < 0)
5081 continue;
5082
5083 switch (i) {
5084 case VAR_ANGLE:
5085 glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, angle_data);
5086 break;
5087 case VAR_LINEAR:
5088 glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, linear_data);
5089 break;
5090 case VAR_FMP:
5091 glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, fmp_data);
5092 break;
5093 case VAR_FMP2_M_RADIUS2:
5094 glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, fmp2_m_radius2_data);
5095 break;
5096 case VAR_INV_MASK_SIZE:
5097 glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_mask_size_data);
5098 break;
5099 case VAR_INV_DST_SIZE:
5100 glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_dst_size_data);
5101 break;
5102 case VAR_INV_MATRIX_M0:
5103 glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_matrix_data[0]);
5104 break;
5105 case VAR_INV_MATRIX_M1:
5106 glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_matrix_data[1]);
5107 break;
5108 case VAR_INV_MATRIX_M2:
5109 glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_matrix_data[2]);
5110 break;
5111 case VAR_PORTERDUFF_AB:
5112 glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, porterduff_ab_data);
5113 break;
5114 case VAR_PORTERDUFF_XYZ:
5115 glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, porterduff_xyz_data);
5116 break;
5117 case VAR_INV_BRUSH_TEXTURE_SIZE:
5118 glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_brush_texture_size_data);
5119 break;
5120 case VAR_MASK_OFFSET:
5121 glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, mask_offset_data);
5122 break;
5123 case VAR_MASK_CHANNEL:
5124 glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, mask_channel_data);
5125 break;
5126 case VAR_DST_TEXTURE:
5127 case VAR_MASK_TEXTURE:
5128 case VAR_PALETTE:
5129 case VAR_BRUSH_TEXTURE:
5130 // texture variables, not handled here
5131 break;
5132 default:
5133 qDebug() << "QOpenGLPaintEnginePrivate: Unhandled fragment variable:" << i;
5134 }
5135 }
5136#endif
5137}
5138
5139
5140void QOpenGLPaintEnginePrivate::copyDrawable(const QRectF &rect)
5141{
5142#ifdef Q_WS_QWS
5143 Q_UNUSED(rect);
5144#else
5145 ensureDrawableTexture();
5146
5147 DEBUG_ONCE qDebug() << "Refreshing drawable_texture for rectangle" << rect;
5148 QRectF screen_rect = rect.adjusted(-1, -1, 1, 1);
5149
5150 int left = qMax(0, static_cast<int>(screen_rect.left()));
5151 int width = qMin(device->size().width() - left, static_cast<int>(screen_rect.width()) + 1);
5152
5153 int bottom = qMax(0, static_cast<int>(device->size().height() - screen_rect.bottom()));
5154 int height = qMin(device->size().height() - bottom, static_cast<int>(screen_rect.height()) + 1);
5155
5156 glBindTexture(GL_TEXTURE_2D, drawable_texture);
5157 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, left, bottom, left, bottom, width, height);
5158#endif
5159}
5160
5161
5162void QOpenGLPaintEnginePrivate::composite(const QRectF &rect, const QPoint &maskOffset)
5163{
5164#ifdef Q_WS_QWS
5165 Q_UNUSED(rect);
5166 Q_UNUSED(maskOffset);
5167#else
5168 q_vertexType vertexArray[8];
5169 qt_add_rect_to_array(rect, vertexArray);
5170
5171 composite(GL_TRIANGLE_FAN, vertexArray, 4, maskOffset);
5172#endif
5173}
5174
5175
5176void QOpenGLPaintEnginePrivate::composite(GLuint primitive, const q_vertexType *vertexArray, int vertexCount, const QPoint &maskOffset)
5177{
5178#ifdef QT_OPENGL_ES
5179 Q_UNUSED(primitive);
5180 Q_UNUSED(vertexArray);
5181 Q_UNUSED(vertexCount);
5182 Q_UNUSED(maskOffset);
5183#else
5184 Q_Q(QOpenGLPaintEngine);
5185 QGL_FUNC_CONTEXT;
5186
5187 if (current_style == Qt::NoBrush)
5188 return;
5189
5190 DEBUG_ONCE qDebug() << "QOpenGLPaintEnginePrivate: Using compositing program: fragment_brush ="
5191 << fragment_brush << ", fragment_composition_mode =" << fragment_composition_mode;
5192
5193 if (has_fast_composition_mode)
5194 q->updateCompositionMode(composition_mode);
5195 else {
5196 qreal minX = 1e9, minY = 1e9, maxX = -1e9, maxY = -1e9;
5197
5198 for (int i = 0; i < vertexCount; ++i) {
5199 qreal x = vt2f(vertexArray[2 * i]);
5200 qreal y = vt2f(vertexArray[2 * i + 1]);
5201
5202 qreal tx, ty;
5203 matrix.map(x, y, &tx, &ty);
5204
5205 minX = qMin(minX, tx);
5206 minY = qMin(minY, ty);
5207 maxX = qMax(maxX, tx);
5208 maxY = qMax(maxY, ty);
5209 }
5210
5211 QRectF r(minX, minY, maxX - minX, maxY - minY);
5212 copyDrawable(r);
5213
5214 glBlendFunc(GL_ONE, GL_ZERO);
5215 }
5216
5217 int *locations = painter_variable_locations[fragment_brush][fragment_composition_mode];
5218
5219 int texture_locations[] = { locations[VAR_DST_TEXTURE],
5220 locations[VAR_MASK_TEXTURE],
5221 locations[VAR_PALETTE] };
5222
5223 int brush_texture_location = locations[VAR_BRUSH_TEXTURE];
5224
5225 GLuint texture_targets[] = { GL_TEXTURE_2D,
5226 GL_TEXTURE_2D,
5227 GL_TEXTURE_1D };
5228
5229 GLuint textures[] = { drawable_texture,
5230 offscreen.offscreenTexture(),
5231 grad_palette };
5232
5233 const int num_textures = sizeof(textures) / sizeof(*textures);
5234
5235 Q_ASSERT(num_textures == sizeof(texture_locations) / sizeof(*texture_locations));
5236 Q_ASSERT(num_textures == sizeof(texture_targets) / sizeof(*texture_targets));
5237
5238 for (int i = 0; i < num_textures; ++i)
5239 if (texture_locations[i] >= 0) {
5240 glActiveTexture(GL_TEXTURE0 + texture_locations[i]);
5241 glBindTexture(texture_targets[i], textures[i]);
5242 }
5243
5244 if (brush_texture_location >= 0) {
5245 glActiveTexture(GL_TEXTURE0 + brush_texture_location);
5246
5247 if (current_style == Qt::TexturePattern)
5248 device->context()->d_func()->bindTexture(cbrush.textureImage(), GL_TEXTURE_2D, GL_RGBA,
5249 QGLContext::InternalBindOption);
5250 else
5251 device->context()->d_func()->bindTexture(qt_imageForBrush(current_style, false),
5252 GL_TEXTURE_2D, GL_RGBA,
5253 QGLContext::InternalBindOption);
5254
5255 updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, use_smooth_pixmap_transform);
5256 }
5257
5258 glEnableClientState(GL_VERTEX_ARRAY);
5259 glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
5260 glEnable(GL_FRAGMENT_PROGRAM_ARB);
5261 GLuint program = qt_gl_program_cache()->getProgram(device->context(),
5262 fragment_brush,
5263 fragment_composition_mode, false);
5264 glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, program);
5265
5266 mask_offset_data[0] = maskOffset.x();
5267 mask_offset_data[1] = -maskOffset.y();
5268
5269 updateFragmentProgramData(locations);
5270
5271 glDrawArrays(primitive, 0, vertexCount);
5272
5273 glDisable(GL_FRAGMENT_PROGRAM_ARB);
5274 glDisableClientState(GL_VERTEX_ARRAY);
5275
5276 for (int i = 0; i < num_textures; ++i)
5277 if (texture_locations[i] >= 0) {
5278 glActiveTexture(GL_TEXTURE0 + texture_locations[i]);
5279 glBindTexture(texture_targets[i], 0);
5280 }
5281
5282 if (brush_texture_location >= 0) {
5283 glActiveTexture(GL_TEXTURE0 + brush_texture_location);
5284 glBindTexture(GL_TEXTURE_2D, 0);
5285 }
5286
5287 glActiveTexture(GL_TEXTURE0);
5288
5289 if (!has_fast_composition_mode)
5290 q->updateCompositionMode(composition_mode);
5291#endif
5292}
5293
5294void QOpenGLPaintEnginePrivate::cacheItemErased(int channel, const QRect &rect)
5295{
5296 bool isInDrawQueue = false;
5297
5298 foreach (const QDrawQueueItem &item, drawQueue) {
5299 if (item.location.channel == channel && item.location.rect == rect) {
5300 isInDrawQueue = true;
5301 break;
5302 }
5303 }
5304
5305 if (isInDrawQueue)
5306 flushDrawQueue();
5307}
5308
5309void QOpenGLPaintEnginePrivate::addItem(const QGLMaskTextureCache::CacheLocation &location)
5310{
5311 drawQueue << QDrawQueueItem(opacity, cbrush, brush_origin, composition_mode, matrix, location);
5312}
5313
5314void QOpenGLPaintEnginePrivate::drawItem(const QDrawQueueItem &item)
5315{
5316 Q_Q(QOpenGLPaintEngine);
5317
5318 opacity = item.opacity;
5319 brush_origin = item.brush_origin;
5320 q->updateCompositionMode(item.composition_mode);
5321 matrix = item.matrix;
5322 cbrush = item.brush;
5323 brush_style = item.brush.style();
5324
5325 mask_channel_data[0] = item.location.channel == 0;
5326 mask_channel_data[1] = item.location.channel == 1;
5327 mask_channel_data[2] = item.location.channel == 2;
5328 mask_channel_data[3] = item.location.channel == 3;
5329
5330 setGradientOps(item.brush, item.location.screen_rect);
5331
5332 composite(item.location.screen_rect, item.location.rect.topLeft() - item.location.screen_rect.topLeft()
5333 - QPoint(0, offscreen.offscreenSize().height() - device->size().height()));
5334}
5335
5336void QOpenGLPaintEnginePrivate::flushDrawQueue()
5337{
5338#ifndef QT_OPENGL_ES
5339 Q_Q(QOpenGLPaintEngine);
5340
5341 offscreen.release();
5342
5343 if (!drawQueue.isEmpty()) {
5344 DEBUG_ONCE qDebug() << "QOpenGLPaintEngine::flushDrawQueue():" << drawQueue.size() << "items";
5345
5346 glPushMatrix();
5347 glLoadIdentity();
5348 qreal old_opacity = opacity;
5349 QPointF old_brush_origin = brush_origin;
5350 QPainter::CompositionMode old_composition_mode = composition_mode;
5351 QTransform old_matrix = matrix;
5352 QBrush old_brush = cbrush;
5353
5354 bool hqaa_old = high_quality_antialiasing;
5355
5356 high_quality_antialiasing = true;
5357
5358 foreach (const QDrawQueueItem &item, drawQueue)
5359 drawItem(item);
5360
5361 opacity = old_opacity;
5362 brush_origin = old_brush_origin;
5363 q->updateCompositionMode(old_composition_mode);
5364 matrix = old_matrix;
5365 cbrush = old_brush;
5366 brush_style = old_brush.style();
5367
5368 high_quality_antialiasing = hqaa_old;
5369
5370 setGLBrush(old_brush.color());
5371 qt_glColor4ubv(brush_color);
5372
5373 drawQueue.clear();
5374
5375 glPopMatrix();
5376 }
5377#endif
5378}
5379
5380void QOpenGLPaintEngine::clipEnabledChanged()
5381{
5382 Q_D(QOpenGLPaintEngine);
5383
5384 d->updateDepthClip();
5385}
5386
5387void QOpenGLPaintEngine::penChanged()
5388{
5389 updatePen(state()->pen);
5390}
5391
5392void QOpenGLPaintEngine::brushChanged()
5393{
5394 updateBrush(state()->brush, state()->brushOrigin);
5395}
5396
5397void QOpenGLPaintEngine::brushOriginChanged()
5398{
5399 updateBrush(state()->brush, state()->brushOrigin);
5400}
5401
5402void QOpenGLPaintEngine::opacityChanged()
5403{
5404 Q_D(QOpenGLPaintEngine);
5405 QPainterState *s = state();
5406 d->opacity = s->opacity;
5407 updateBrush(s->brush, s->brushOrigin);
5408 updatePen(s->pen);
5409}
5410
5411void QOpenGLPaintEngine::compositionModeChanged()
5412{
5413 updateCompositionMode(state()->composition_mode);
5414}
5415
5416void QOpenGLPaintEngine::renderHintsChanged()
5417{
5418 updateRenderHints(state()->renderHints);
5419}
5420
5421void QOpenGLPaintEngine::transformChanged()
5422{
5423 updateMatrix(state()->matrix);
5424}
5425
5426static QPainterPath painterPathFromVectorPath(const QVectorPath &path)
5427{
5428 const qreal *points = path.points();
5429 const QPainterPath::ElementType *types = path.elements();
5430
5431 QPainterPath p;
5432 if (types) {
5433 int id = 0;
5434 for (int i=0; i<path.elementCount(); ++i) {
5435 switch(types[i]) {
5436 case QPainterPath::MoveToElement:
5437 p.moveTo(QPointF(points[id], points[id+1]));
5438 id+=2;
5439 break;
5440 case QPainterPath::LineToElement:
5441 p.lineTo(QPointF(points[id], points[id+1]));
5442 id+=2;
5443 break;
5444 case QPainterPath::CurveToElement: {
5445 QPointF p1(points[id], points[id+1]);
5446 QPointF p2(points[id+2], points[id+3]);
5447 QPointF p3(points[id+4], points[id+5]);
5448 p.cubicTo(p1, p2, p3);
5449 id+=6;
5450 break;
5451 }
5452 case QPainterPath::CurveToDataElement:
5453 ;
5454 break;
5455 }
5456 }
5457 } else {
5458 p.moveTo(QPointF(points[0], points[1]));
5459 int id = 2;
5460 for (int i=1; i<path.elementCount(); ++i) {
5461 p.lineTo(QPointF(points[id], points[id+1]));
5462 id+=2;
5463 }
5464 }
5465 if (path.hints() & QVectorPath::WindingFill)
5466 p.setFillRule(Qt::WindingFill);
5467
5468 return p;
5469}
5470
5471void QOpenGLPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
5472{
5473 Q_D(QOpenGLPaintEngine);
5474
5475 if (brush.style() == Qt::NoBrush)
5476 return;
5477
5478 if (!d->use_fragment_programs && needsEmulation(brush.style())) {
5479 QPainter *p = painter();
5480 QBrush oldBrush = p->brush();
5481 p->setBrush(brush);
5482 qt_draw_helper(p->d_ptr.data(), painterPathFromVectorPath(path), QPainterPrivate::FillDraw);
5483 p->setBrush(oldBrush);
5484 return;
5485 }
5486
5487 QBrush old_brush = state()->brush;
5488 updateBrush(brush, state()->brushOrigin);
5489
5490 const qreal *points = path.points();
5491 const QPainterPath::ElementType *types = path.elements();
5492 if (!types && path.shape() == QVectorPath::RectangleHint) {
5493 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
5494 QPen old_pen = state()->pen;
5495 updatePen(Qt::NoPen);
5496 drawRects(&r, 1);
5497 updatePen(old_pen);
5498 } else {
5499 d->fillPath(painterPathFromVectorPath(path));
5500 }
5501
5502 updateBrush(old_brush, state()->brushOrigin);
5503}
5504
5505template <typename T> static inline bool isRect(const T *pts, int elementCount) {
5506 return (elementCount == 5 // 5-point polygon, check for closed rect
5507 && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point
5508 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
5509 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
5510 ) ||
5511 (elementCount == 4 // 4-point polygon, check for unclosed rect
5512 && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
5513 && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
5514 );
5515}
5516
5517void QOpenGLPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
5518{
5519 const qreal *points = path.points();
5520 const QPainterPath::ElementType *types = path.elements();
5521 if (!types && path.shape() == QVectorPath::RectangleHint) {
5522 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
5523 updateClipRegion(QRegion(r.toRect()), op);
5524 return;
5525 }
5526
5527 QPainterPath p;
5528 if (types) {
5529 int id = 0;
5530 for (int i=0; i<path.elementCount(); ++i) {
5531 switch(types[i]) {
5532 case QPainterPath::MoveToElement:
5533 p.moveTo(QPointF(points[id], points[id+1]));
5534 id+=2;
5535 break;
5536 case QPainterPath::LineToElement:
5537 p.lineTo(QPointF(points[id], points[id+1]));
5538 id+=2;
5539 break;
5540 case QPainterPath::CurveToElement: {
5541 QPointF p1(points[id], points[id+1]);
5542 QPointF p2(points[id+2], points[id+3]);
5543 QPointF p3(points[id+4], points[id+5]);
5544 p.cubicTo(p1, p2, p3);
5545 id+=6;
5546 break;
5547 }
5548 case QPainterPath::CurveToDataElement:
5549 ;
5550 break;
5551 }
5552 }
5553 } else if (!path.isEmpty()) {
5554 p.moveTo(QPointF(points[0], points[1]));
5555 int id = 2;
5556 for (int i=1; i<path.elementCount(); ++i) {
5557 p.lineTo(QPointF(points[id], points[id+1]));
5558 id+=2;
5559 }
5560 }
5561 if (path.hints() & QVectorPath::WindingFill)
5562 p.setFillRule(Qt::WindingFill);
5563
5564 updateClipRegion(QRegion(p.toFillPolygon().toPolygon(), p.fillRule()), op);
5565 return;
5566}
5567
5568void QOpenGLPaintEngine::setState(QPainterState *s)
5569{
5570 Q_D(QOpenGLPaintEngine);
5571 QOpenGLPaintEngineState *new_state = static_cast<QOpenGLPaintEngineState *>(s);
5572 QOpenGLPaintEngineState *old_state = state();
5573
5574 QPaintEngineEx::setState(s);
5575
5576 // are we in a save() ?
5577 if (s == d->last_created_state) {
5578 d->last_created_state = 0;
5579 return;
5580 }
5581
5582 if (isActive()) {
5583 if (old_state->depthClipId != new_state->depthClipId)
5584 d->updateDepthClip();
5585 penChanged();
5586 brushChanged();
5587 opacityChanged();
5588 compositionModeChanged();
5589 renderHintsChanged();
5590 transformChanged();
5591 }
5592}
5593
5594QPainterState *QOpenGLPaintEngine::createState(QPainterState *orig) const
5595{
5596 const Q_D(QOpenGLPaintEngine);
5597
5598 QOpenGLPaintEngineState *s;
5599 if (!orig)
5600 s = new QOpenGLPaintEngineState();
5601 else
5602 s = new QOpenGLPaintEngineState(*static_cast<QOpenGLPaintEngineState *>(orig));
5603
5604 d->last_created_state = s;
5605 return s;
5606}
5607
5608//
5609// QOpenGLPaintEngineState
5610//
5611
5612QOpenGLPaintEngineState::QOpenGLPaintEngineState(QOpenGLPaintEngineState &other)
5613 : QPainterState(other)
5614{
5615 clipRegion = other.clipRegion;
5616 hasClipping = other.hasClipping;
5617 fastClip = other.fastClip;
5618 depthClipId = other.depthClipId;
5619}
5620
5621QOpenGLPaintEngineState::QOpenGLPaintEngineState()
5622{
5623 hasClipping = false;
5624 depthClipId = 0;
5625}
5626
5627QOpenGLPaintEngineState::~QOpenGLPaintEngineState()
5628{
5629}
5630
5631void QOpenGLPaintEnginePrivate::ensureDrawableTexture()
5632{
5633 if (!dirty_drawable_texture)
5634 return;
5635
5636 dirty_drawable_texture = false;
5637
5638#ifndef QT_OPENGL_ES
5639 glGenTextures(1, &drawable_texture);
5640 glBindTexture(GL_TEXTURE_2D, drawable_texture);
5641
5642 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,
5643 drawable_texture_size.width(),
5644 drawable_texture_size.height(), 0,
5645 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
5646
5647 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5648 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5649 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5650 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5651#endif
5652}
5653
5654QT_END_NAMESPACE
5655
5656#include "qpaintengine_opengl.moc"
Note: See TracBrowser for help on using the repository browser.