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