5d5f1ce919fc555f70ffb2f238df032c8c549a6e
[profile/ivi/qtdeclarative.git] / src / quick / scenegraph / qsgdefaultimagenode.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qsgdefaultimagenode_p.h"
43
44 #include <QtQuick/qsgtextureprovider.h>
45
46 #include <QtCore/qvarlengtharray.h>
47 #include <QtCore/qmath.h>
48 #include <QtGui/qopenglfunctions.h>
49
50 QT_BEGIN_NAMESPACE
51
52 QSGDefaultImageNode::QSGDefaultImageNode()
53     : m_sourceRect(0, 0, 1, 1)
54     , m_dirtyGeometry(false)
55     , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4)
56 {
57     setMaterial(&m_materialO);
58     setOpaqueMaterial(&m_material);
59     setGeometry(&m_geometry);
60
61 #ifdef QML_RUNTIME_TESTING
62     description = QLatin1String("image");
63 #endif
64 }
65
66 void QSGDefaultImageNode::setTargetRect(const QRectF &rect)
67 {
68     if (rect == m_targetRect)
69         return;
70     m_targetRect = rect;
71     m_dirtyGeometry = true;
72 }
73
74 void QSGDefaultImageNode::setSourceRect(const QRectF &rect)
75 {
76     if (rect == m_sourceRect)
77         return;
78     m_sourceRect = rect;
79     m_dirtyGeometry = true;
80 }
81
82
83 void QSGDefaultImageNode::setFiltering(QSGTexture::Filtering filtering)
84 {
85     if (m_material.filtering() == filtering)
86         return;
87
88     m_material.setFiltering(filtering);
89     m_materialO.setFiltering(filtering);
90     markDirty(DirtyMaterial);
91 }
92
93
94 void QSGDefaultImageNode::setMipmapFiltering(QSGTexture::Filtering filtering)
95 {
96     if (m_material.mipmapFiltering() == filtering)
97         return;
98
99     m_material.setMipmapFiltering(filtering);
100     m_materialO.setMipmapFiltering(filtering);
101     markDirty(DirtyMaterial);
102 }
103
104 void QSGDefaultImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode)
105 {
106     if (m_material.verticalWrapMode() == wrapMode)
107         return;
108
109     m_material.setVerticalWrapMode(wrapMode);
110     m_materialO.setVerticalWrapMode(wrapMode);
111     markDirty(DirtyMaterial);
112 }
113
114 void QSGDefaultImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode)
115 {
116     if (m_material.horizontalWrapMode() == wrapMode)
117         return;
118
119     m_material.setHorizontalWrapMode(wrapMode);
120     m_materialO.setHorizontalWrapMode(wrapMode);
121     markDirty(DirtyMaterial);
122 }
123
124
125 void QSGDefaultImageNode::setTexture(QSGTexture *texture)
126 {
127     if (texture == m_material.texture())
128         return;
129
130     m_material.setTexture(texture);
131     m_materialO.setTexture(texture);
132     // Texture cleanup
133 //    if (!texture.isNull())
134 //        m_material.setBlending(texture->hasAlphaChannel());
135     markDirty(DirtyMaterial);
136
137     // Because the texture can be a different part of the atlas, we need to update it...
138     m_dirtyGeometry = true;
139 }
140
141 void QSGDefaultImageNode::update()
142 {
143     if (m_dirtyGeometry)
144         updateGeometry();
145 }
146
147 void QSGDefaultImageNode::preprocess()
148 {
149     bool doDirty = false;
150     QSGDynamicTexture *t = qobject_cast<QSGDynamicTexture *>(m_material.texture());
151     if (t) {
152         doDirty = t->updateTexture();
153         updateGeometry();
154     }
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);
159 //        doDirty = true;
160 //    }
161
162     if (doDirty)
163         markDirty(DirtyMaterial);
164 }
165
166 inline static bool isPowerOfTwo(int x)
167 {
168     // Assumption: x >= 1
169     return x == (x & -x);
170 }
171
172 namespace {
173     struct X { float x, tx; };
174     struct Y { float y, ty; };
175 }
176
177 void QSGDefaultImageNode::updateGeometry()
178 {
179     const QSGTexture *t = m_material.texture();
180     if (!t) {
181         m_geometry.allocate(4);
182         m_geometry.setDrawingMode(GL_TRIANGLE_STRIP);
183         QSGGeometry::updateTexturedRectGeometry(&m_geometry, QRectF(), QRectF());
184     } else {
185         QRectF textureRect = t->normalizedTextureSubRect();
186
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;
195
196 #ifdef QT_OPENGL_ES_2
197         QOpenGLContext *ctx = QOpenGLContext::currentContext();
198         bool npotSupported = ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures);
199
200         QSize size = t->textureSize();
201         bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height());
202
203         if (isRepeating && (isSubRect || (isNpot && !npotSupported))) {
204 #else
205         if (isRepeating && isSubRect) {
206 #endif
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();
213             
214             xs->x = m_targetRect.left();
215             xs->tx = textureRect.x() + (m_sourceRect.left() - floorLeft) * textureRect.width();
216             ++xs;
217             ys->y = m_targetRect.top();
218             ys->ty = textureRect.y() + (m_sourceRect.top() - floorTop) * textureRect.height();
219             ++ys;
220
221             float a, b;
222             b = m_targetRect.width() / m_sourceRect.width();
223             a = m_targetRect.x() - m_sourceRect.x() * b;
224
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;
231                 xs[0].tx = tex_x2;
232                 xs[1].tx = tex_x1;
233                 xs += 2;
234             }
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;
239                 ys[0].ty = tex_y2;
240                 ys[1].ty = tex_y1;
241                 ys += 2;
242             }
243
244             xs->x = m_targetRect.right();
245             xs->tx = textureRect.x() + (m_sourceRect.right() - ceilRight + 1) * textureRect.width();
246
247             ys->y = m_targetRect.bottom();
248             ys->ty = textureRect.y() + (m_sourceRect.bottom() - ceilBottom + 1) * textureRect.height();
249
250             QSGGeometry::TexturedPoint2D *vertices = m_geometry.vertexDataAsTexturedPoint2D();
251             ys = yData.data();
252             for (int j = 0; j < vCells; ++j, ys += 2) {
253                 xs = xData.data();
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;
259
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;
264
265                     vertices += 4;
266                 }
267             }
268
269             quint16 *indices = m_geometry.indexDataAsUShort();
270             for (int i = 0; i < 4 * vCells * hCells; i += 4) {
271                 *indices++ = i;
272                 *indices++ = i + 2;
273                 *indices++ = i + 3;
274                 *indices++ = i + 3;
275                 *indices++ = i + 1;
276                 *indices++ = i;
277             }
278         } else {
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());
283
284             m_geometry.allocate(4);
285             m_geometry.setDrawingMode(GL_TRIANGLE_STRIP);
286             QSGGeometry::updateTexturedRectGeometry(&m_geometry, m_targetRect, sr);
287         }
288     }
289     markDirty(DirtyGeometry);
290     m_dirtyGeometry = false;
291 }
292
293 QT_END_NAMESPACE