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

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

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

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