1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qopengltextureblitter.h"
41
42#include <QtGui/QOpenGLBuffer>
43#include <QtGui/QOpenGLShaderProgram>
44#include <QtGui/QOpenGLVertexArrayObject>
45#include <QtGui/QOpenGLContext>
46#include <QtGui/QOpenGLFunctions>
47#include <QtGui/QOpenGLExtraFunctions>
48
49#ifndef GL_TEXTURE_EXTERNAL_OES
50#define GL_TEXTURE_EXTERNAL_OES 0x8D65
51#endif
52#ifndef GL_TEXTURE_RECTANGLE
53#define GL_TEXTURE_RECTANGLE 0x84F5
54#endif
55#ifndef GL_TEXTURE_WIDTH
56#define GL_TEXTURE_WIDTH 0x1000
57#endif
58#ifndef GL_TEXTURE_HEIGHT
59#define GL_TEXTURE_HEIGHT 0x1001
60#endif
61
62QT_BEGIN_NAMESPACE
63
64/*!
65 \class QOpenGLTextureBlitter
66 \brief The QOpenGLTextureBlitter class provides a convenient way to draw textured quads via OpenGL.
67 \since 5.8
68 \ingroup painting-3D
69 \inmodule QtGui
70
71 Drawing textured quads, in order to get the contents of a texture
72 onto the screen, is a common operation when developing 2D user
73 interfaces. QOpenGLTextureBlitter provides a convenience class to
74 avoid repeating vertex data, shader sources, buffer and program
75 management and matrix calculations.
76
77 For example, a QOpenGLWidget subclass can do the following to draw
78 the contents rendered into a framebuffer at the pixel position \c{(x, y)}:
79
80 \code
81 void OpenGLWidget::initializeGL()
82 {
83 m_blitter.create();
84 m_fbo = new QOpenGLFramebufferObject(size);
85 }
86
87 void OpenGLWidget::paintGL()
88 {
89 m_fbo->bind();
90 // update offscreen content
91 m_fbo->release();
92
93 m_blitter.bind();
94 const QRect targetRect(QPoint(x, y), m_fbo->size());
95 const QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(targetRect, QRect(QPoint(0, 0), m_fbo->size()));
96 m_blitter.blit(m_fbo->texture(), target, QOpenGLTextureBlitter::OriginBottomLeft);
97 m_blitter.release();
98 }
99 \endcode
100
101 The blitter implements GLSL shaders both for GLSL 1.00 (suitable
102 for OpenGL (ES) 2.x and compatibility profiles of newer OpenGL
103 versions) and version 150 (suitable for core profile contexts with
104 OpenGL 3.2 and newer).
105 */
106
107static const char vertex_shader150[] =
108 "#version 150 core\n"
109 "in vec3 vertexCoord;"
110 "in vec2 textureCoord;"
111 "out vec2 uv;"
112 "uniform mat4 vertexTransform;"
113 "uniform mat3 textureTransform;"
114 "void main() {"
115 " uv = (textureTransform * vec3(textureCoord,1.0)).xy;"
116 " gl_Position = vertexTransform * vec4(vertexCoord,1.0);"
117 "}";
118
119static const char fragment_shader150[] =
120 "#version 150 core\n"
121 "in vec2 uv;"
122 "out vec4 fragcolor;"
123 "uniform sampler2D textureSampler;"
124 "uniform bool swizzle;"
125 "uniform float opacity;"
126 "void main() {"
127 " vec4 tmpFragColor = texture(textureSampler, uv);"
128 " tmpFragColor.a *= opacity;"
129 " fragcolor = swizzle ? tmpFragColor.bgra : tmpFragColor;"
130 "}";
131
132static const char vertex_shader[] =
133 "attribute highp vec3 vertexCoord;"
134 "attribute highp vec2 textureCoord;"
135 "varying highp vec2 uv;"
136 "uniform highp mat4 vertexTransform;"
137 "uniform highp mat3 textureTransform;"
138 "void main() {"
139 " uv = (textureTransform * vec3(textureCoord,1.0)).xy;"
140 " gl_Position = vertexTransform * vec4(vertexCoord,1.0);"
141 "}";
142
143static const char fragment_shader[] =
144 "varying highp vec2 uv;"
145 "uniform sampler2D textureSampler;"
146 "uniform bool swizzle;"
147 "uniform highp float opacity;"
148 "void main() {"
149 " highp vec4 tmpFragColor = texture2D(textureSampler,uv);"
150 " tmpFragColor.a *= opacity;"
151 " gl_FragColor = swizzle ? tmpFragColor.bgra : tmpFragColor;"
152 "}";
153
154static const char fragment_shader_external_oes[] =
155 "#extension GL_OES_EGL_image_external : require\n"
156 "varying highp vec2 uv;"
157 "uniform samplerExternalOES textureSampler;\n"
158 "uniform bool swizzle;"
159 "uniform highp float opacity;"
160 "void main() {"
161 " highp vec4 tmpFragColor = texture2D(textureSampler, uv);"
162 " tmpFragColor.a *= opacity;"
163 " gl_FragColor = swizzle ? tmpFragColor.bgra : tmpFragColor;"
164 "}";
165
166static const char fragment_shader_rectangle[] =
167 "varying highp vec2 uv;"
168 "uniform sampler2DRect textureSampler;"
169 "uniform bool swizzle;"
170 "uniform highp float opacity;"
171 "void main() {"
172 " highp vec4 tmpFragColor = texture2DRect(textureSampler,uv);"
173 " tmpFragColor.a *= opacity;"
174 " gl_FragColor = swizzle ? tmpFragColor.bgra : tmpFragColor;"
175 "}";
176
177static const char fragment_shader150_rectangle[] =
178 "#version 150 core\n"
179 "in vec2 uv;"
180 "out vec4 fragcolor;"
181 "uniform sampler2DRect textureSampler;"
182 "uniform bool swizzle;"
183 "uniform float opacity;"
184 "void main() {"
185 " vec4 tmpFragColor = texture(textureSampler, uv);"
186 " tmpFragColor.a *= opacity;"
187 " fragcolor = swizzle ? tmpFragColor.bgra : tmpFragColor;"
188 "}";
189
190static const GLfloat vertex_buffer_data[] = {
191 -1,-1, 0,
192 -1, 1, 0,
193 1,-1, 0,
194 -1, 1, 0,
195 1,-1, 0,
196 1, 1, 0
197};
198
199static const GLfloat texture_buffer_data[] = {
200 0, 0,
201 0, 1,
202 1, 0,
203 0, 1,
204 1, 0,
205 1, 1
206};
207
208class TextureBinder
209{
210public:
211 TextureBinder(GLenum target, GLuint textureId) : m_target(target)
212 {
213 QOpenGLContext::currentContext()->functions()->glBindTexture(target: m_target, texture: textureId);
214 }
215 ~TextureBinder()
216 {
217 QOpenGLContext::currentContext()->functions()->glBindTexture(target: m_target, texture: 0);
218 }
219
220private:
221 GLenum m_target;
222};
223
224class QOpenGLTextureBlitterPrivate
225{
226public:
227 enum TextureMatrixUniform {
228 User,
229 Identity,
230 IdentityFlipped
231 };
232
233 enum ProgramIndex {
234 TEXTURE_2D,
235 TEXTURE_EXTERNAL_OES,
236 TEXTURE_RECTANGLE
237 };
238
239 QOpenGLTextureBlitterPrivate() :
240 swizzle(false),
241 opacity(1.0f),
242 vao(new QOpenGLVertexArrayObject),
243 currentTarget(TEXTURE_2D)
244 { }
245
246 bool buildProgram(ProgramIndex idx, const char *vs, const char *fs);
247
248 void blit(GLuint texture, const QMatrix4x4 &targetTransform, const QMatrix3x3 &sourceTransform);
249 void blit(GLuint texture, const QMatrix4x4 &targetTransform, QOpenGLTextureBlitter::Origin origin);
250
251 QMatrix3x3 toTextureCoordinates(const QMatrix3x3 &sourceTransform) const;
252
253 void prepareProgram(const QMatrix4x4 &vertexTransform);
254
255 bool supportsRectangleTarget() const;
256
257 QOpenGLBuffer vertexBuffer;
258 QOpenGLBuffer textureBuffer;
259 struct Program {
260 Program() :
261 vertexCoordAttribPos(0),
262 vertexTransformUniformPos(0),
263 textureCoordAttribPos(0),
264 textureTransformUniformPos(0),
265 swizzleUniformPos(0),
266 opacityUniformPos(0),
267 swizzle(false),
268 opacity(0.0f),
269 textureMatrixUniformState(User)
270 { }
271 QScopedPointer<QOpenGLShaderProgram> glProgram;
272 GLuint vertexCoordAttribPos;
273 GLuint vertexTransformUniformPos;
274 GLuint textureCoordAttribPos;
275 GLuint textureTransformUniformPos;
276 GLuint swizzleUniformPos;
277 GLuint opacityUniformPos;
278 bool swizzle;
279 float opacity;
280 TextureMatrixUniform textureMatrixUniformState;
281 } programs[3];
282 bool swizzle;
283 float opacity;
284 QScopedPointer<QOpenGLVertexArrayObject> vao;
285 GLenum currentTarget;
286};
287
288static inline QOpenGLTextureBlitterPrivate::ProgramIndex targetToProgramIndex(GLenum target)
289{
290 switch (target) {
291 case GL_TEXTURE_2D:
292 return QOpenGLTextureBlitterPrivate::TEXTURE_2D;
293 case GL_TEXTURE_EXTERNAL_OES:
294 return QOpenGLTextureBlitterPrivate::TEXTURE_EXTERNAL_OES;
295 case GL_TEXTURE_RECTANGLE:
296 return QOpenGLTextureBlitterPrivate::TEXTURE_RECTANGLE;
297 default:
298 qWarning(msg: "Unsupported texture target 0x%x", target);
299 return QOpenGLTextureBlitterPrivate::TEXTURE_2D;
300 }
301}
302
303void QOpenGLTextureBlitterPrivate::prepareProgram(const QMatrix4x4 &vertexTransform)
304{
305 Program *program = &programs[targetToProgramIndex(target: currentTarget)];
306
307 vertexBuffer.bind();
308 program->glProgram->setAttributeBuffer(location: program->vertexCoordAttribPos, GL_FLOAT, offset: 0, tupleSize: 3, stride: 0);
309 program->glProgram->enableAttributeArray(location: program->vertexCoordAttribPos);
310 vertexBuffer.release();
311
312 program->glProgram->setUniformValue(location: program->vertexTransformUniformPos, value: vertexTransform);
313
314 textureBuffer.bind();
315 program->glProgram->setAttributeBuffer(location: program->textureCoordAttribPos, GL_FLOAT, offset: 0, tupleSize: 2, stride: 0);
316 program->glProgram->enableAttributeArray(location: program->textureCoordAttribPos);
317 textureBuffer.release();
318
319 if (swizzle != program->swizzle) {
320 program->glProgram->setUniformValue(location: program->swizzleUniformPos, value: swizzle);
321 program->swizzle = swizzle;
322 }
323
324 if (opacity != program->opacity) {
325 program->glProgram->setUniformValue(location: program->opacityUniformPos, value: opacity);
326 program->opacity = opacity;
327 }
328}
329
330QMatrix3x3 QOpenGLTextureBlitterPrivate::toTextureCoordinates(const QMatrix3x3 &sourceTransform) const
331{
332 if (currentTarget == GL_TEXTURE_RECTANGLE) {
333 // Non-normalized coordinates
334 QMatrix4x4 textureTransform(sourceTransform);
335 if (auto *glFunctions = QOpenGLContext::currentContext()->extraFunctions()) {
336 int width, height;
337 glFunctions->glGetTexLevelParameteriv(target: currentTarget, level: 0, GL_TEXTURE_WIDTH, params: &width);
338 glFunctions->glGetTexLevelParameteriv(target: currentTarget, level: 0, GL_TEXTURE_HEIGHT, params: &height);
339 textureTransform.scale(x: width, y: height);
340 }
341 return textureTransform.toGenericMatrix<3, 3>();
342 }
343
344 return sourceTransform; // Normalized coordinates
345}
346
347void QOpenGLTextureBlitterPrivate::blit(GLuint texture,
348 const QMatrix4x4 &targetTransform,
349 const QMatrix3x3 &sourceTransform)
350{
351 TextureBinder binder(currentTarget, texture);
352 prepareProgram(vertexTransform: targetTransform);
353
354 Program *program = &programs[targetToProgramIndex(target: currentTarget)];
355
356 const QMatrix3x3 textureTransform = toTextureCoordinates(sourceTransform);
357 program->glProgram->setUniformValue(location: program->textureTransformUniformPos, value: textureTransform);
358 program->textureMatrixUniformState = User;
359
360 QOpenGLContext::currentContext()->functions()->glDrawArrays(GL_TRIANGLES, first: 0, count: 6);
361}
362
363void QOpenGLTextureBlitterPrivate::blit(GLuint texture,
364 const QMatrix4x4 &targetTransform,
365 QOpenGLTextureBlitter::Origin origin)
366{
367 TextureBinder binder(currentTarget, texture);
368 prepareProgram(vertexTransform: targetTransform);
369
370 Program *program = &programs[targetToProgramIndex(target: currentTarget)];
371
372 if (origin == QOpenGLTextureBlitter::OriginTopLeft) {
373 if (program->textureMatrixUniformState != IdentityFlipped) {
374 QMatrix3x3 sourceTransform;
375 sourceTransform(1,1) = -1;
376 sourceTransform(1,2) = 1;
377 const QMatrix3x3 textureTransform = toTextureCoordinates(sourceTransform);
378 program->glProgram->setUniformValue(location: program->textureTransformUniformPos, value: textureTransform);
379 program->textureMatrixUniformState = IdentityFlipped;
380 }
381 } else if (program->textureMatrixUniformState != Identity) {
382 const QMatrix3x3 textureTransform = toTextureCoordinates(sourceTransform: QMatrix3x3());
383 program->glProgram->setUniformValue(location: program->textureTransformUniformPos, value: textureTransform);
384 program->textureMatrixUniformState = Identity;
385 }
386
387 QOpenGLContext::currentContext()->functions()->glDrawArrays(GL_TRIANGLES, first: 0, count: 6);
388}
389
390bool QOpenGLTextureBlitterPrivate::buildProgram(ProgramIndex idx, const char *vs, const char *fs)
391{
392 Program *p = &programs[idx];
393
394 p->glProgram.reset(other: new QOpenGLShaderProgram);
395
396 p->glProgram->addCacheableShaderFromSourceCode(type: QOpenGLShader::Vertex, source: vs);
397 p->glProgram->addCacheableShaderFromSourceCode(type: QOpenGLShader::Fragment, source: fs);
398 p->glProgram->link();
399 if (!p->glProgram->isLinked()) {
400 qWarning() << "Could not link shader program:\n" << p->glProgram->log();
401 return false;
402 }
403
404 p->glProgram->bind();
405
406 p->vertexCoordAttribPos = p->glProgram->attributeLocation(name: "vertexCoord");
407 p->vertexTransformUniformPos = p->glProgram->uniformLocation(name: "vertexTransform");
408 p->textureCoordAttribPos = p->glProgram->attributeLocation(name: "textureCoord");
409 p->textureTransformUniformPos = p->glProgram->uniformLocation(name: "textureTransform");
410 p->swizzleUniformPos = p->glProgram->uniformLocation(name: "swizzle");
411 p->opacityUniformPos = p->glProgram->uniformLocation(name: "opacity");
412
413 p->glProgram->setUniformValue(location: p->swizzleUniformPos, value: false);
414
415 // minmize state left set after a create()
416 p->glProgram->release();
417
418 return true;
419}
420
421/*!
422 Constructs a new QOpenGLTextureBlitter instance.
423
424 \note no graphics resources are initialized in the
425 constructor. This makes it safe to place plain
426 QOpenGLTextureBlitter members into classes because the actual
427 initialization that depends on the OpenGL context happens only in
428 create().
429 */
430QOpenGLTextureBlitter::QOpenGLTextureBlitter()
431 : d_ptr(new QOpenGLTextureBlitterPrivate)
432{
433}
434
435/*!
436 Destructs the instance.
437
438 \note When the OpenGL context - or a context sharing resources
439 with it - that was current when calling create() is not current,
440 graphics resources will not be released. Therefore, it is
441 recommended to call destroy() manually instead of relying on the
442 destructor to perform OpenGL resource cleanup.
443 */
444QOpenGLTextureBlitter::~QOpenGLTextureBlitter()
445{
446 destroy();
447}
448
449/*!
450 Initializes the graphics resources used by the blitter.
451
452 \return \c true if successful, \c false if there was a
453 failure. Failures can occur when there is no OpenGL context
454 current on the current thread, or when shader compilation fails
455 for some reason.
456
457 \sa isCreated(), destroy()
458 */
459bool QOpenGLTextureBlitter::create()
460{
461 QOpenGLContext *currentContext = QOpenGLContext::currentContext();
462 if (!currentContext)
463 return false;
464
465 Q_D(QOpenGLTextureBlitter);
466
467 if (d->programs[QOpenGLTextureBlitterPrivate::TEXTURE_2D].glProgram)
468 return true;
469
470 QSurfaceFormat format = currentContext->format();
471 if (format.profile() == QSurfaceFormat::CoreProfile && format.version() >= qMakePair(x: 3,y: 2)) {
472 if (!d->buildProgram(idx: QOpenGLTextureBlitterPrivate::TEXTURE_2D, vs: vertex_shader150, fs: fragment_shader150))
473 return false;
474 if (d->supportsRectangleTarget())
475 if (!d->buildProgram(idx: QOpenGLTextureBlitterPrivate::TEXTURE_RECTANGLE, vs: vertex_shader150, fs: fragment_shader150_rectangle))
476 return false;
477 } else {
478 if (!d->buildProgram(idx: QOpenGLTextureBlitterPrivate::TEXTURE_2D, vs: vertex_shader, fs: fragment_shader))
479 return false;
480 if (supportsExternalOESTarget())
481 if (!d->buildProgram(idx: QOpenGLTextureBlitterPrivate::TEXTURE_EXTERNAL_OES, vs: vertex_shader, fs: fragment_shader_external_oes))
482 return false;
483 if (d->supportsRectangleTarget())
484 if (!d->buildProgram(idx: QOpenGLTextureBlitterPrivate::TEXTURE_RECTANGLE, vs: vertex_shader, fs: fragment_shader_rectangle))
485 return false;
486 }
487
488 // Create and bind the VAO, if supported.
489 QOpenGLVertexArrayObject::Binder vaoBinder(d->vao.data());
490
491 d->vertexBuffer.create();
492 d->vertexBuffer.bind();
493 d->vertexBuffer.allocate(data: vertex_buffer_data, count: sizeof(vertex_buffer_data));
494 d->vertexBuffer.release();
495
496 d->textureBuffer.create();
497 d->textureBuffer.bind();
498 d->textureBuffer.allocate(data: texture_buffer_data, count: sizeof(texture_buffer_data));
499 d->textureBuffer.release();
500
501 return true;
502}
503
504/*!
505 \return \c true if create() was called and succeeded. \c false otherwise.
506
507 \sa create(), destroy()
508 */
509bool QOpenGLTextureBlitter::isCreated() const
510{
511 Q_D(const QOpenGLTextureBlitter);
512 return d->programs[QOpenGLTextureBlitterPrivate::TEXTURE_2D].glProgram;
513}
514
515/*!
516 Frees all graphics resources held by the blitter. Assumes that
517 the OpenGL context, or another context sharing resources with it,
518 that was current on the thread when invoking create() is current.
519
520 The function has no effect when the blitter is not in created state.
521
522 \sa create()
523 */
524void QOpenGLTextureBlitter::destroy()
525{
526 if (!isCreated())
527 return;
528 Q_D(QOpenGLTextureBlitter);
529 d->programs[QOpenGLTextureBlitterPrivate::TEXTURE_2D].glProgram.reset();
530 d->programs[QOpenGLTextureBlitterPrivate::TEXTURE_EXTERNAL_OES].glProgram.reset();
531 d->programs[QOpenGLTextureBlitterPrivate::TEXTURE_RECTANGLE].glProgram.reset();
532 d->vertexBuffer.destroy();
533 d->textureBuffer.destroy();
534 d->vao.reset();
535}
536
537/*!
538 \return \c true when bind() accepts \c GL_TEXTURE_EXTERNAL_OES as
539 its target argument.
540
541 \sa bind(), blit()
542 */
543bool QOpenGLTextureBlitter::supportsExternalOESTarget() const
544{
545 QOpenGLContext *ctx = QOpenGLContext::currentContext();
546 return ctx && ctx->isOpenGLES() && ctx->hasExtension(extension: "GL_OES_EGL_image_external");
547}
548
549/*!
550 \return \c true when bind() accepts \c GL_TEXTURE_RECTANGLE as
551 its target argument.
552
553 \sa bind(), blit()
554 */
555bool QOpenGLTextureBlitterPrivate::supportsRectangleTarget() const
556{
557 QOpenGLContext *ctx = QOpenGLContext::currentContext();
558 if (!ctx || ctx->isOpenGLES())
559 return false;
560
561 if (ctx->hasExtension(extension: "GL_ARB_texture_rectangle"))
562 return true;
563
564 if (ctx->hasExtension(extension: "GL_EXT_texture_rectangle"))
565 return true;
566
567 QSurfaceFormat f = ctx->format();
568 const auto version = qMakePair(x: f.majorVersion(), y: f.minorVersion());
569 if (version >= qMakePair(x: 3, y: 1))
570 return true;
571
572 return false;
573}
574
575/*!
576 Binds the graphics resources used by the blitter. This must be
577 called before calling blit(). Code modifying the OpenGL state
578 should be avoided between the call to bind() and blit() because
579 otherwise conflicts may arise.
580
581 \a target is the texture target for the source texture and must be
582 either \c GL_TEXTURE_2D, \c GL_TEXTURE_RECTANGLE, or \c GL_OES_EGL_image_external.
583
584 \sa release(), blit()
585 */
586void QOpenGLTextureBlitter::bind(GLenum target)
587{
588 Q_D(QOpenGLTextureBlitter);
589
590 if (d->vao->isCreated())
591 d->vao->bind();
592
593 d->currentTarget = target;
594 QOpenGLTextureBlitterPrivate::Program *p = &d->programs[targetToProgramIndex(target)];
595 p->glProgram->bind();
596
597 d->vertexBuffer.bind();
598 p->glProgram->setAttributeBuffer(location: p->vertexCoordAttribPos, GL_FLOAT, offset: 0, tupleSize: 3, stride: 0);
599 p->glProgram->enableAttributeArray(location: p->vertexCoordAttribPos);
600 d->vertexBuffer.release();
601
602 d->textureBuffer.bind();
603 p->glProgram->setAttributeBuffer(location: p->textureCoordAttribPos, GL_FLOAT, offset: 0, tupleSize: 2, stride: 0);
604 p->glProgram->enableAttributeArray(location: p->textureCoordAttribPos);
605 d->textureBuffer.release();
606}
607
608/*!
609 Unbinds the graphics resources used by the blitter.
610
611 \sa bind()
612 */
613void QOpenGLTextureBlitter::release()
614{
615 Q_D(QOpenGLTextureBlitter);
616 d->programs[targetToProgramIndex(target: d->currentTarget)].glProgram->release();
617 if (d->vao->isCreated())
618 d->vao->release();
619}
620
621/*!
622 Sets whether swizzling is enabled for the red and blue color channels to
623 \a swizzle. An BGRA to RGBA conversion (occurring in the shader on
624 the GPU, instead of a slow CPU-side transformation) can be useful
625 when the source texture contains data from a QImage with a format
626 like QImage::Format_ARGB32 which maps to BGRA on little endian
627 systems.
628
629 By default the red-blue swizzle is disabled since this is what a
630 texture attached to an framebuffer object or a texture based on a
631 byte ordered QImage format (like QImage::Format_RGBA8888) needs.
632 */
633void QOpenGLTextureBlitter::setRedBlueSwizzle(bool swizzle)
634{
635 Q_D(QOpenGLTextureBlitter);
636 d->swizzle = swizzle;
637}
638
639/*!
640 Changes the opacity to \a opacity. The default opacity is 1.0.
641
642 \note the blitter does not alter the blend state. It is up to the
643 caller of blit() to ensure the correct blend settings are active.
644
645 */
646void QOpenGLTextureBlitter::setOpacity(float opacity)
647{
648 Q_D(QOpenGLTextureBlitter);
649 d->opacity = opacity;
650}
651
652/*!
653 \enum QOpenGLTextureBlitter::Origin
654
655 \value OriginBottomLeft Indicates that the data in the texture
656 follows the OpenGL convention of coordinate systems, meaning Y is
657 running from bottom to top.
658
659 \value OriginTopLeft Indicates that the data in the texture has Y
660 running from top to bottom, which is typical with regular,
661 unflipped image data.
662
663 \sa blit()
664 */
665
666/*!
667 Performs the blit with the source texture \a texture.
668
669 \a targetTransform specifies the transformation applied. This is
670 usually generated by the targetTransform() helper function.
671
672 \a sourceOrigin specifies if the image data needs flipping. When
673 \a texture corresponds to a texture attached to an FBO pass
674 OriginBottomLeft. On the other hand, when \a texture is based on
675 unflipped image data, pass OriginTopLeft. This is more efficient
676 than using QImage::mirrored().
677
678 \sa targetTransform(), Origin, bind()
679 */
680void QOpenGLTextureBlitter::blit(GLuint texture,
681 const QMatrix4x4 &targetTransform,
682 Origin sourceOrigin)
683{
684 Q_D(QOpenGLTextureBlitter);
685 d->blit(texture, targetTransform, origin: sourceOrigin);
686}
687
688/*!
689 Performs the blit with the source texture \a texture.
690
691 \a targetTransform specifies the transformation applied. This is
692 usually generated by the targetTransform() helper function.
693
694 \a sourceTransform specifies the transformation applied to the
695 source. This allows using only a sub-rect of the source
696 texture. This is usually generated by the sourceTransform() helper
697 function.
698
699 \sa sourceTransform(), targetTransform(), Origin, bind()
700 */
701void QOpenGLTextureBlitter::blit(GLuint texture,
702 const QMatrix4x4 &targetTransform,
703 const QMatrix3x3 &sourceTransform)
704{
705 Q_D(QOpenGLTextureBlitter);
706 d->blit(texture, targetTransform, sourceTransform);
707}
708
709/*!
710 Calculates a target transform suitable for blit().
711
712 \a target is the target rectangle in pixels. \a viewport describes
713 the source dimensions and will in most cases be set to (0, 0,
714 image width, image height).
715
716 For unscaled output the size of \a target and \a viewport should
717 match.
718
719 \sa blit()
720 */
721QMatrix4x4 QOpenGLTextureBlitter::targetTransform(const QRectF &target,
722 const QRect &viewport)
723{
724 qreal x_scale = target.width() / viewport.width();
725 qreal y_scale = target.height() / viewport.height();
726
727 const QPointF relative_to_viewport = target.topLeft() - viewport.topLeft();
728 qreal x_translate = x_scale - 1 + ((relative_to_viewport.x() / viewport.width()) * 2);
729 qreal y_translate = -y_scale + 1 - ((relative_to_viewport.y() / viewport.height()) * 2);
730
731 QMatrix4x4 matrix;
732 matrix(0,3) = x_translate;
733 matrix(1,3) = y_translate;
734
735 matrix(0,0) = x_scale;
736 matrix(1,1) = y_scale;
737
738 return matrix;
739}
740
741/*!
742 Calculates a 3x3 matrix suitable as the input to blit(). This is
743 used when only a part of the texture is to be used in the blit.
744
745 \a subTexture is the desired source rectangle in pixels, \a
746 textureSize is the full width and height of the texture data. \a
747 origin specifies the orientation of the image data when it comes
748 to the Y axis.
749
750 \sa blit(), Origin
751 */
752QMatrix3x3 QOpenGLTextureBlitter::sourceTransform(const QRectF &subTexture,
753 const QSize &textureSize,
754 Origin origin)
755{
756 qreal x_scale = subTexture.width() / textureSize.width();
757 qreal y_scale = subTexture.height() / textureSize.height();
758
759 const QPointF topLeft = subTexture.topLeft();
760 qreal x_translate = topLeft.x() / textureSize.width();
761 qreal y_translate = topLeft.y() / textureSize.height();
762
763 if (origin == OriginTopLeft) {
764 y_scale = -y_scale;
765 y_translate = 1 - y_translate;
766 }
767
768 QMatrix3x3 matrix;
769 matrix(0,2) = x_translate;
770 matrix(1,2) = y_translate;
771
772 matrix(0,0) = x_scale;
773 matrix(1,1) = y_scale;
774
775 return matrix;
776}
777
778QT_END_NAMESPACE
779

source code of qtbase/src/gui/opengl/qopengltextureblitter.cpp