88f7da57fa4a55e7cded422c3634d3a82920b75b
[profile/ivi/qtdeclarative.git] / src / quick / items / qquickninepatchnode.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 "qquickninepatchnode_p.h"
43 #include <private/qsgadaptationlayer_p.h>
44 #include <private/qmath_p.h>
45
46 QQuickNinePatchNode::QQuickNinePatchNode()
47     : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0)
48     , m_horizontalTileMode(QQuickBorderImage::Stretch)
49     , m_verticalTileMode(QQuickBorderImage::Stretch)
50     , m_dirtyGeometry(false)
51     , m_mirror(false)
52 {
53     setOpaqueMaterial(&m_material);
54     setMaterial(&m_materialO);
55     setGeometry(&m_geometry);
56     m_geometry.setDrawingMode(GL_TRIANGLES);
57 #ifdef QML_RUNTIME_TESTING
58     description = QLatin1String("borderimage");
59 #endif
60 }
61
62 void QQuickNinePatchNode::setInnerRect(const QRectF &rect)
63 {
64     if (m_innerRect == rect)
65         return;
66     m_innerRect = rect;
67     m_dirtyGeometry = true;
68 }
69
70 void QQuickNinePatchNode::setRect(const QRectF &rect)
71 {
72     if (m_targetRect == rect)
73         return;
74     m_targetRect = rect;
75     m_dirtyGeometry = true;
76 }
77
78 void QQuickNinePatchNode::setHorzontalTileMode(QQuickBorderImage::TileMode mode)
79 {
80     if (mode == QQuickBorderImage::TileMode(m_horizontalTileMode))
81         return;
82     m_horizontalTileMode = mode;
83     m_dirtyGeometry = true;
84 }
85
86
87 void QQuickNinePatchNode::setVerticalTileMode(QQuickBorderImage::TileMode mode)
88 {
89     if (mode == QQuickBorderImage::TileMode(m_verticalTileMode))
90         return;
91     m_verticalTileMode = mode;
92     m_dirtyGeometry = true;
93 }
94
95
96 void QQuickNinePatchNode::setFiltering(QSGTexture::Filtering filtering)
97 {
98     if (m_material.filtering() == filtering)
99         return;
100
101     m_material.setFiltering(filtering);
102     m_materialO.setFiltering(filtering);
103     markDirty(DirtyMaterial);
104 }
105
106 QSGTexture::Filtering QQuickNinePatchNode::filtering() const
107 {
108     return m_material.filtering();
109 }
110
111 void QQuickNinePatchNode::setTexture(QSGTexture *texture)
112 {
113     if (texture == m_material.texture())
114         return;
115     m_material.setTexture(texture);
116     m_materialO.setTexture(texture);
117     markDirty(DirtyMaterial);
118 }
119
120 QSGTexture *QQuickNinePatchNode::texture() const
121 {
122     return m_material.texture();
123 }
124
125 void QQuickNinePatchNode::setMirror(bool m)
126 {
127     if (m_mirror == m)
128         return;
129     m_mirror = m;
130     m_dirtyGeometry = true;
131 }
132
133
134 void QQuickNinePatchNode::update()
135 {
136     if (!m_dirtyGeometry)
137         return;
138
139     // For stretch this algorithm could be simplified to use less vertices
140     // as more vertices could be reused then, but I doubt its where our main
141     // problem will lie. This way, we at least share the algorithm between all
142
143     Q_ASSERT(m_material.texture());
144
145     float tw = m_material.texture()->textureSize().width();
146     float th = m_material.texture()->textureSize().height();
147
148     QRectF textureSubRect = m_material.texture()->normalizedTextureSubRect();
149     QSize textureSize = m_material.texture()->textureSize();
150
151     float rightBorder = tw - m_innerRect.right();
152     float bottomBorder = th - m_innerRect.bottom();
153
154 //    qDebug() << m_innerRect << m_targetRect << m_horizontalTileMode << m_verticalTileMode;
155
156     int xChunkCount = 0; // Number of chunks
157     float xChunkSize = 0; // Size of chunk in pixels
158     float xTexSize = m_innerRect.width(); // Size of the texture to stretch/tile
159     float xSize = m_targetRect.width() - m_innerRect.left() - rightBorder; // Size of area to fill with chunks
160
161     if (m_horizontalTileMode == QQuickBorderImage::Repeat) {
162         xChunkCount = qCeil(xSize / xTexSize);
163         xChunkSize = xTexSize;
164     } else if (m_horizontalTileMode == QQuickBorderImage::Round) {
165         xChunkCount = qCeil(xSize / xTexSize);
166         qreal fullWidth = xChunkCount * xTexSize;
167         xChunkSize = xTexSize * xSize / fullWidth;
168     } else {
169         xChunkCount = 1;
170         xChunkSize = xSize;
171     }
172
173     int yChunkCount = 0;
174     float yChunkSize = 0; // Relative to target rect.
175     float yTexSize = m_innerRect.height(); // Size of the texture to stretch/tile
176     float ySize = m_targetRect.height() - m_innerRect.top() - bottomBorder;
177
178     if (m_verticalTileMode == QQuickBorderImage::Repeat) {
179         yChunkCount = qCeil(ySize / yTexSize);
180         yChunkSize = yTexSize;
181     } else if (m_verticalTileMode == QQuickBorderImage::Round) {
182         yChunkCount = qCeil(ySize / yTexSize);
183         qreal fullHeight = yChunkCount * yTexSize;
184         yChunkSize = yTexSize * ySize / fullHeight;
185     } else {
186         yChunkCount = 1;
187         yChunkSize = ySize;
188     }
189
190     int xTotalChunkCount = xChunkCount + 2;
191     int yTotalChunkCount = yChunkCount + 2;
192
193     int totalChunkCount = xTotalChunkCount * yTotalChunkCount;
194     int vertexCount = totalChunkCount * 4;
195     int indexCount = totalChunkCount * 6;
196
197     if (vertexCount != m_geometry.vertexCount() || indexCount != m_geometry.indexCount())
198         m_geometry.allocate(vertexCount, indexCount);
199
200     QSGGeometry::TexturedPoint2D *v = m_geometry.vertexDataAsTexturedPoint2D();
201
202
203     // Fill in the vertices.. The loop below is pretty much an exact replica
204     // of the one inside fillRow.
205     float yTexChunk1 = m_innerRect.top() / th;
206     float yTexChunk2 = m_innerRect.bottom() / th;
207
208     fillRow(v, 0, 0, xChunkCount, xChunkSize, textureSubRect, textureSize);
209     fillRow(v, m_innerRect.y(), yTexChunk1, xChunkCount, xChunkSize, textureSubRect, textureSize);
210
211     for (int yc=0; yc<yChunkCount; ++yc) {
212         float yy = m_innerRect.y() + yChunkSize * yc;
213         fillRow(v, yy, yTexChunk1, xChunkCount, xChunkSize, textureSubRect, textureSize);
214
215         // Special case the last one
216         if (yc == yChunkCount - 1) {
217             float t = m_verticalTileMode == QQuickBorderImage::Repeat
218                     ? yTexChunk1 + (yTexChunk2 - yTexChunk1) * (m_targetRect.height() - bottomBorder - yy) / yChunkSize
219                     : yTexChunk2;
220             fillRow(v, m_targetRect.height() - bottomBorder, t, xChunkCount, xChunkSize, textureSubRect, textureSize);
221         } else {
222             fillRow(v, yy + yChunkSize, yTexChunk2, xChunkCount, xChunkSize, textureSubRect, textureSize);
223         }
224     }
225
226     fillRow(v, m_targetRect.height() - bottomBorder, yTexChunk2, xChunkCount, xChunkSize, textureSubRect, textureSize);
227     fillRow(v, m_targetRect.height(), 1, xChunkCount, xChunkSize, textureSubRect, textureSize);
228
229     if (m_mirror) {
230         v = m_geometry.vertexDataAsTexturedPoint2D();
231         for (int i=0; i<m_geometry.vertexCount(); ++i) {
232             v->x = m_targetRect.width() - v->x;
233             ++v;
234         }
235     }
236
237 //    v = m_geometry.vertexDataAsTexturedPoint2D();
238 //    for (int i=0; i<m_geometry.vertexCount(); ++i) {
239 //        printf("Vertex: %d:  (%.3f, %.3f) - (%.3f, %.3f)\n",
240 //               i,
241 //               v->x, v->y, v->tx, v->ty);
242 //        ++v;
243 //    }
244
245     quint16 *i = m_geometry.indexDataAsUShort();
246     int row = xTotalChunkCount * 2;
247     for (int r=0; r<yTotalChunkCount; ++r) {
248         int offset = r * row * 2;
249         for (int c=0; c<xTotalChunkCount; ++c) {
250             *i++ = offset + c * 2;
251             *i++ = offset + c * 2 + 1;
252             *i++ = offset + c * 2 + row;
253             *i++ = offset + c * 2 + 1;
254             *i++ = offset + c * 2 + row + 1;
255             *i++ = offset + c * 2 + row;
256         }
257     }
258
259 //    i = m_geometry.indexDataAsUShort();
260 //    for (int idx=0; idx<m_geometry.indexCount(); idx+=6) {
261 //        printf("%2d: ", idx / 6);
262 //        for (int s=0; s<6; ++s)
263 //            printf(" %d", i[idx + s]);
264 //        printf("\n");
265 //    }
266
267     markDirty(QSGNode::DirtyGeometry);
268 }
269
270 void QQuickNinePatchNode::fillRow(QSGGeometry::TexturedPoint2D *&v, float y, float ty, int xChunkCount, float xChunkSize,
271                                const QRectF &tsr,   // texture sub rect, for atlasses
272                                const QSize &ts)     // texture size in pixels
273 {
274     ty = tsr.y() + ty * tsr.height();
275
276     float tw = ts.width();
277     float rightBorder = tw - m_innerRect.right();
278     float xTexChunk1 = tsr.left() + tsr.width() * m_innerRect.left() / tw;
279     float xTexChunk2 = tsr.left() + tsr.width() * m_innerRect.right() / tw;
280
281     v++->set(0, y, tsr.left(), ty);
282     v++->set(m_innerRect.x(), y, xTexChunk1, ty);
283
284     for (int xc=0; xc<xChunkCount; ++xc) {
285         float xx = m_innerRect.x() + xChunkSize * xc;
286         v++->set(xx, y, xTexChunk1, ty);
287
288         // Special case the last one
289         if (xc == xChunkCount - 1) {
290             float t = m_horizontalTileMode == QQuickBorderImage::Repeat
291                     ? xTexChunk1 + (xTexChunk2 - xTexChunk1) * (m_targetRect.width() - rightBorder - xx) / xChunkSize
292                     : xTexChunk2;
293             v->set(m_targetRect.width() - rightBorder, y, t, ty);
294         } else {
295             v->set(xx + xChunkSize, y, xTexChunk2, ty);
296         }
297         ++v;
298     }
299
300     v++->set(m_targetRect.width() - rightBorder, y, xTexChunk2, ty);
301     v++->set(m_targetRect.width(), y, tsr.right(), ty);
302 }