Initial import from qtquick2.
[profile/ivi/qtdeclarative.git] / src / declarative / items / qsgninepatchnode.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qsgninepatchnode_p.h"
43 #include <private/qsgadaptationlayer_p.h>
44 #include <private/qmath_p.h>
45
46 QSGNinePatchNode::QSGNinePatchNode()
47     : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0)
48     , m_horizontalTileMode(QSGBorderImage::Stretch)
49     , m_verticalTileMode(QSGBorderImage::Stretch)
50     , m_dirtyGeometry(false)
51 {
52     setOpaqueMaterial(&m_material);
53     setMaterial(&m_materialO);
54     setGeometry(&m_geometry);
55     m_geometry.setDrawingMode(GL_TRIANGLES);
56 }
57
58 void QSGNinePatchNode::setInnerRect(const QRectF &rect)
59 {
60     if (m_innerRect == rect)
61         return;
62     m_innerRect = rect;
63     m_dirtyGeometry = true;
64 }
65
66 void QSGNinePatchNode::setRect(const QRectF &rect)
67 {
68     if (m_targetRect == rect)
69         return;
70     m_targetRect = rect;
71     m_dirtyGeometry = true;
72 }
73
74 void QSGNinePatchNode::setHorzontalTileMode(QSGBorderImage::TileMode mode)
75 {
76     if (mode == QSGBorderImage::TileMode(m_horizontalTileMode))
77         return;
78     m_horizontalTileMode = mode;
79     m_dirtyGeometry = true;
80 }
81
82
83 void QSGNinePatchNode::setVerticalTileMode(QSGBorderImage::TileMode mode)
84 {
85     if (mode == QSGBorderImage::TileMode(m_verticalTileMode))
86         return;
87     m_verticalTileMode = mode;
88     m_dirtyGeometry = true;
89 }
90
91
92 void QSGNinePatchNode::setFiltering(QSGTexture::Filtering filtering)
93 {
94     if (m_material.filtering() == filtering)
95         return;
96
97     m_material.setFiltering(filtering);
98     m_materialO.setFiltering(filtering);
99     markDirty(DirtyMaterial);
100 }
101
102 QSGTexture::Filtering QSGNinePatchNode::filtering() const
103 {
104     return m_material.filtering();
105 }
106
107 void QSGNinePatchNode::setTexture(QSGTexture *texture)
108 {
109     if (texture == m_material.texture())
110         return;
111     m_material.setTexture(texture);
112     m_materialO.setTexture(texture);
113     markDirty(DirtyMaterial);
114 }
115
116 QSGTexture *QSGNinePatchNode::texture() const
117 {
118     return m_material.texture();
119 }
120
121 void QSGNinePatchNode::update()
122 {
123     if (!m_dirtyGeometry)
124         return;
125
126     // For stretch this algorithm could be simplified to use less vertices
127     // as more vertices could be reused then, but I doubt its where our main
128     // problem will lie. This way, we at least share the algorithm between all
129
130     Q_ASSERT(m_material.texture());
131
132     float tw = m_material.texture()->textureSize().width();
133     float th = m_material.texture()->textureSize().height();
134
135     float rightBorder = tw - m_innerRect.right();
136     float bottomBorder = th - m_innerRect.bottom();
137
138 //    qDebug() << m_innerRect << m_targetRect << m_horizontalTileMode << m_verticalTileMode;
139
140     int xChunkCount = 0; // Number of chunks
141     float xChunkSize = 0; // Size of chunk in pixels
142     float xTexSize = m_innerRect.width(); // Size of the texture to stretch/tile
143     float xSize = m_targetRect.width() - m_innerRect.left() - rightBorder; // Size of area to fill with chunks
144
145     if (m_horizontalTileMode == QSGBorderImage::Repeat) {
146         xChunkCount = qCeil(xSize / xTexSize);
147         xChunkSize = xTexSize;
148     } else if (m_horizontalTileMode == QSGBorderImage::Round) {
149         xChunkCount = qCeil(xSize / xTexSize);
150         xChunkSize = xSize / xChunkCount;
151     } else {
152         xChunkCount = 1;
153         xChunkSize = xSize;
154     }
155
156     int yChunkCount = 0;
157     float yChunkSize = 0; // Relative to target rect.
158     float yTexSize = m_innerRect.height(); // Size of the texture to stretch/tile
159     float ySize = m_targetRect.height() - m_innerRect.top() - bottomBorder;
160
161     if (m_verticalTileMode == QSGBorderImage::Repeat) {
162         yChunkCount = qCeil(ySize / yTexSize);
163         yChunkSize = yTexSize;
164     } else if (m_verticalTileMode == QSGBorderImage::Round) {
165         yChunkCount = qCeil(ySize / yTexSize);
166         yChunkSize = ySize / yChunkCount;
167     } else {
168         yChunkCount = 1;
169         yChunkSize = ySize;
170     }
171
172     int xTotalChunkCount = xChunkCount + 2;
173     int yTotalChunkCount = yChunkCount + 2;
174
175     int totalChunkCount = xTotalChunkCount * yTotalChunkCount;
176     int vertexCount = totalChunkCount * 4;
177     int indexCount = totalChunkCount * 6;
178
179     if (vertexCount != m_geometry.vertexCount() || indexCount != m_geometry.indexCount())
180         m_geometry.allocate(vertexCount, indexCount);
181
182     QSGGeometry::TexturedPoint2D *v = m_geometry.vertexDataAsTexturedPoint2D();
183
184
185     // Fill in the vertices.. The loop below is pretty much an exact replica
186     // of the one inside fillRow.
187     float yTexChunk1 = 1 - m_innerRect.top() / th;
188     float yTexChunk2 = 1 - m_innerRect.bottom() / th;
189
190     fillRow(v, 0, 1, xChunkCount, xChunkSize);
191     fillRow(v, m_innerRect.y(), yTexChunk1, xChunkCount, xChunkSize);
192
193     for (int yc=0; yc<yChunkCount; ++yc) {
194         float yy = m_innerRect.y() + yChunkSize * yc;
195         fillRow(v, yy, yTexChunk1, xChunkCount, xChunkSize);
196
197         // Special case the last one
198         if (yc == yChunkCount - 1) {
199             float t = m_verticalTileMode == QSGBorderImage::Repeat
200                     ? yTexChunk1 + (yTexChunk2 - yTexChunk1) * (m_targetRect.height() - bottomBorder - yy) / yChunkSize
201                     : yTexChunk2;
202             fillRow(v, m_targetRect.height() - bottomBorder, t, xChunkCount, xChunkSize);
203         } else {
204             fillRow(v, yy + yChunkSize, yTexChunk2, xChunkCount, xChunkSize);
205         }
206     }
207
208     fillRow(v, m_targetRect.height() - bottomBorder, yTexChunk2, xChunkCount, xChunkSize);
209     fillRow(v, m_targetRect.height(), 0, xChunkCount, xChunkSize);
210
211
212 //    v = m_geometry.vertexDataAsTexturedPoint2D();
213 //    for (int i=0; i<m_geometry.vertexCount(); ++i) {
214 //        printf("Vertex: %d:  (%.3f, %.3f) - (%.3f, %.3f)\n",
215 //               i,
216 //               v->x, v->y, v->tx, v->ty);
217 //        ++v;
218 //    }
219
220     quint16 *i = m_geometry.indexDataAsUShort();
221     int row = xTotalChunkCount * 2;
222     for (int r=0; r<yTotalChunkCount; ++r) {
223         int offset = r * row * 2;
224         for (int c=0; c<xTotalChunkCount; ++c) {
225             *i++ = offset + c * 2;
226             *i++ = offset + c * 2 + 1;
227             *i++ = offset + c * 2 + row;
228             *i++ = offset + c * 2 + 1;
229             *i++ = offset + c * 2 + row + 1;
230             *i++ = offset + c * 2 + row;
231         }
232     }
233
234 //    i = m_geometry.indexDataAsUShort();
235 //    for (int idx=0; idx<m_geometry.indexCount(); idx+=6) {
236 //        printf("%2d: ", idx / 6);
237 //        for (int s=0; s<6; ++s)
238 //            printf(" %d", i[idx + s]);
239 //        printf("\n");
240 //    }
241
242     markDirty(QSGNode::DirtyGeometry);
243 }
244
245 void QSGNinePatchNode::fillRow(QSGGeometry::TexturedPoint2D *&v, float y, float ty, int xChunkCount, float xChunkSize)
246 {
247     float tw = m_material.texture()->textureSize().width();
248     float rightBorder = tw - m_innerRect.right();
249     float xTexChunk1 = m_innerRect.left() / tw;
250     float xTexChunk2 = m_innerRect.right() / tw;
251
252     v++->set(0, y, 0, ty);
253     v++->set(m_innerRect.x(), y, xTexChunk1, ty);
254
255     for (int xc=0; xc<xChunkCount; ++xc) {
256         float xx = m_innerRect.x() + xChunkSize * xc;
257         v++->set(xx, y, xTexChunk1, ty);
258
259         // Special case the last one
260         if (xc == xChunkCount - 1) {
261             float t = m_horizontalTileMode == QSGBorderImage::Repeat
262                     ? xTexChunk1 + (xTexChunk2 - xTexChunk1) * (m_targetRect.width() - rightBorder - xx) / xChunkSize
263                     : xTexChunk2;
264             v->set(m_targetRect.width() - rightBorder, y, t, ty);
265         } else {
266             v->set(xx + xChunkSize, y, xTexChunk2, ty);
267         }
268         ++v;
269     }
270
271     v++->set(m_targetRect.width() - rightBorder, y, xTexChunk2, ty);
272     v++->set(m_targetRect.width(), y, 1, ty);
273 }