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

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

trunk: Merged in qt 4.6.2 sources.

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