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