1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qsgdefaultimagenode_p.h"
44 #include <QtQuick/qsgtextureprovider.h>
46 #include <QtCore/qvarlengtharray.h>
47 #include <QtCore/qmath.h>
48 #include <QtGui/qopenglfunctions.h>
52 QSGDefaultImageNode::QSGDefaultImageNode()
53 : m_sourceRect(0, 0, 1, 1)
54 , m_dirtyGeometry(false)
55 , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4)
57 setMaterial(&m_materialO);
58 setOpaqueMaterial(&m_material);
59 setGeometry(&m_geometry);
61 #ifdef QML_RUNTIME_TESTING
62 description = QLatin1String("image");
66 void QSGDefaultImageNode::setTargetRect(const QRectF &rect)
68 if (rect == m_targetRect)
71 m_dirtyGeometry = true;
74 void QSGDefaultImageNode::setSourceRect(const QRectF &rect)
76 if (rect == m_sourceRect)
79 m_dirtyGeometry = true;
83 void QSGDefaultImageNode::setFiltering(QSGTexture::Filtering filtering)
85 if (m_material.filtering() == filtering)
88 m_material.setFiltering(filtering);
89 m_materialO.setFiltering(filtering);
90 markDirty(DirtyMaterial);
94 void QSGDefaultImageNode::setMipmapFiltering(QSGTexture::Filtering filtering)
96 if (m_material.mipmapFiltering() == filtering)
99 m_material.setMipmapFiltering(filtering);
100 m_materialO.setMipmapFiltering(filtering);
101 markDirty(DirtyMaterial);
104 void QSGDefaultImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode)
106 if (m_material.verticalWrapMode() == wrapMode)
109 m_material.setVerticalWrapMode(wrapMode);
110 m_materialO.setVerticalWrapMode(wrapMode);
111 markDirty(DirtyMaterial);
114 void QSGDefaultImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode)
116 if (m_material.horizontalWrapMode() == wrapMode)
119 m_material.setHorizontalWrapMode(wrapMode);
120 m_materialO.setHorizontalWrapMode(wrapMode);
121 markDirty(DirtyMaterial);
125 void QSGDefaultImageNode::setTexture(QSGTexture *texture)
127 if (texture == m_material.texture())
130 m_material.setTexture(texture);
131 m_materialO.setTexture(texture);
133 // if (!texture.isNull())
134 // m_material.setBlending(texture->hasAlphaChannel());
135 markDirty(DirtyMaterial);
137 // Because the texture can be a different part of the atlas, we need to update it...
138 m_dirtyGeometry = true;
141 void QSGDefaultImageNode::update()
147 void QSGDefaultImageNode::preprocess()
149 bool doDirty = false;
150 QSGDynamicTexture *t = qobject_cast<QSGDynamicTexture *>(m_material.texture());
152 doDirty = t->updateTexture();
155 // ### texture cleanup
156 // bool alpha = m_material.blending();
157 // if (!m_material->texture().isNull() && alpha != m_material.texture()->hasAlphaChannel()) {
158 // m_material.setBlending(!alpha);
163 markDirty(DirtyMaterial);
166 inline static bool isPowerOfTwo(int x)
168 // Assumption: x >= 1
169 return x == (x & -x);
173 struct X { float x, tx; };
174 struct Y { float y, ty; };
177 void QSGDefaultImageNode::updateGeometry()
179 const QSGTexture *t = m_material.texture();
181 m_geometry.allocate(4);
182 m_geometry.setDrawingMode(GL_TRIANGLE_STRIP);
183 QSGGeometry::updateTexturedRectGeometry(&m_geometry, QRectF(), QRectF());
185 QRectF textureRect = t->normalizedTextureSubRect();
187 bool isSubRect = textureRect != QRectF(0, 0, 1, 1);
188 const int ceilRight = qCeil(m_sourceRect.right());
189 const int floorLeft = qFloor(m_sourceRect.left());
190 const int ceilBottom = qCeil(m_sourceRect.bottom());
191 const int floorTop = qFloor(m_sourceRect.top());
192 const int hCells = ceilRight - floorLeft;
193 const int vCells = ceilBottom - floorTop;
194 bool isRepeating = hCells > 1 || vCells > 1;
196 #ifdef QT_OPENGL_ES_2
197 QOpenGLContext *ctx = QOpenGLContext::currentContext();
198 bool npotSupported = ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures);
200 QSize size = t->textureSize();
201 bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height());
203 if (isRepeating && (isSubRect || (isNpot && !npotSupported))) {
205 if (isRepeating && isSubRect) {
207 m_geometry.allocate(hCells * vCells * 4, hCells * vCells * 6);
208 m_geometry.setDrawingMode(GL_TRIANGLES);
209 QVarLengthArray<X, 32> xData(2 * hCells);
210 QVarLengthArray<Y, 32> yData(2 * vCells);
211 X *xs = xData.data();
212 Y *ys = yData.data();
214 xs->x = m_targetRect.left();
215 xs->tx = textureRect.x() + (m_sourceRect.left() - floorLeft) * textureRect.width();
217 ys->y = m_targetRect.top();
218 ys->ty = textureRect.y() + (m_sourceRect.top() - floorTop) * textureRect.height();
222 b = m_targetRect.width() / m_sourceRect.width();
223 a = m_targetRect.x() - m_sourceRect.x() * b;
225 float tex_x1 = textureRect.x();
226 float tex_x2 = textureRect.right();
227 float tex_y1 = textureRect.y();
228 float tex_y2 = textureRect.bottom();
229 for (int i = floorLeft + 1; i <= ceilRight - 1; ++i) {
230 xs[0].x = xs[1].x = a + b * i;
235 b = m_targetRect.height() / m_sourceRect.height();
236 a = m_targetRect.y() - m_sourceRect.y() * b;
237 for (int i = floorTop + 1; i <= ceilBottom - 1; ++i) {
238 ys[0].y = ys[1].y = a + b * i;
244 xs->x = m_targetRect.right();
245 xs->tx = textureRect.x() + (m_sourceRect.right() - ceilRight + 1) * textureRect.width();
247 ys->y = m_targetRect.bottom();
248 ys->ty = textureRect.y() + (m_sourceRect.bottom() - ceilBottom + 1) * textureRect.height();
250 QSGGeometry::TexturedPoint2D *vertices = m_geometry.vertexDataAsTexturedPoint2D();
252 for (int j = 0; j < vCells; ++j, ys += 2) {
254 for (int i = 0; i < hCells; ++i, xs += 2) {
255 vertices[0].x = vertices[2].x = xs[0].x;
256 vertices[0].tx = vertices[2].tx = xs[0].tx;
257 vertices[1].x = vertices[3].x = xs[1].x;
258 vertices[1].tx = vertices[3].tx = xs[1].tx;
260 vertices[0].y = vertices[1].y = ys[0].y;
261 vertices[0].ty = vertices[1].ty = ys[0].ty;
262 vertices[2].y = vertices[3].y = ys[1].y;
263 vertices[2].ty = vertices[3].ty = ys[1].ty;
269 quint16 *indices = m_geometry.indexDataAsUShort();
270 for (int i = 0; i < 4 * vCells * hCells; i += 4) {
279 QRectF sr(textureRect.x() + m_sourceRect.x() * textureRect.width(),
280 textureRect.y() + m_sourceRect.y() * textureRect.height(),
281 m_sourceRect.width() * textureRect.width(),
282 m_sourceRect.height() * textureRect.height());
284 m_geometry.allocate(4);
285 m_geometry.setDrawingMode(GL_TRIANGLE_STRIP);
286 QSGGeometry::updateTexturedRectGeometry(&m_geometry, m_targetRect, sr);
289 markDirty(DirtyGeometry);
290 m_dirtyGeometry = false;