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

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