1 /****************************************************************************
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
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
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.
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.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include "qsgninepatchnode_p.h"
43 #include <private/qsgadaptationlayer_p.h>
44 #include <private/qmath_p.h>
46 QSGNinePatchNode::QSGNinePatchNode()
47 : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0)
48 , m_horizontalTileMode(QSGBorderImage::Stretch)
49 , m_verticalTileMode(QSGBorderImage::Stretch)
50 , m_dirtyGeometry(false)
52 setOpaqueMaterial(&m_material);
53 setMaterial(&m_materialO);
54 setGeometry(&m_geometry);
55 m_geometry.setDrawingMode(GL_TRIANGLES);
58 void QSGNinePatchNode::setInnerRect(const QRectF &rect)
60 if (m_innerRect == rect)
63 m_dirtyGeometry = true;
66 void QSGNinePatchNode::setRect(const QRectF &rect)
68 if (m_targetRect == rect)
71 m_dirtyGeometry = true;
74 void QSGNinePatchNode::setHorzontalTileMode(QSGBorderImage::TileMode mode)
76 if (mode == QSGBorderImage::TileMode(m_horizontalTileMode))
78 m_horizontalTileMode = mode;
79 m_dirtyGeometry = true;
83 void QSGNinePatchNode::setVerticalTileMode(QSGBorderImage::TileMode mode)
85 if (mode == QSGBorderImage::TileMode(m_verticalTileMode))
87 m_verticalTileMode = mode;
88 m_dirtyGeometry = true;
92 void QSGNinePatchNode::setFiltering(QSGTexture::Filtering filtering)
94 if (m_material.filtering() == filtering)
97 m_material.setFiltering(filtering);
98 m_materialO.setFiltering(filtering);
99 markDirty(DirtyMaterial);
102 QSGTexture::Filtering QSGNinePatchNode::filtering() const
104 return m_material.filtering();
107 void QSGNinePatchNode::setTexture(QSGTexture *texture)
109 if (texture == m_material.texture())
111 m_material.setTexture(texture);
112 m_materialO.setTexture(texture);
113 markDirty(DirtyMaterial);
116 QSGTexture *QSGNinePatchNode::texture() const
118 return m_material.texture();
121 void QSGNinePatchNode::update()
123 if (!m_dirtyGeometry)
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
130 Q_ASSERT(m_material.texture());
132 float tw = m_material.texture()->textureSize().width();
133 float th = m_material.texture()->textureSize().height();
135 float rightBorder = tw - m_innerRect.right();
136 float bottomBorder = th - m_innerRect.bottom();
138 // qDebug() << m_innerRect << m_targetRect << m_horizontalTileMode << m_verticalTileMode;
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
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;
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;
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;
172 int xTotalChunkCount = xChunkCount + 2;
173 int yTotalChunkCount = yChunkCount + 2;
175 int totalChunkCount = xTotalChunkCount * yTotalChunkCount;
176 int vertexCount = totalChunkCount * 4;
177 int indexCount = totalChunkCount * 6;
179 if (vertexCount != m_geometry.vertexCount() || indexCount != m_geometry.indexCount())
180 m_geometry.allocate(vertexCount, indexCount);
182 QSGGeometry::TexturedPoint2D *v = m_geometry.vertexDataAsTexturedPoint2D();
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;
190 fillRow(v, 0, 1, xChunkCount, xChunkSize);
191 fillRow(v, m_innerRect.y(), yTexChunk1, xChunkCount, xChunkSize);
193 for (int yc=0; yc<yChunkCount; ++yc) {
194 float yy = m_innerRect.y() + yChunkSize * yc;
195 fillRow(v, yy, yTexChunk1, xChunkCount, xChunkSize);
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
202 fillRow(v, m_targetRect.height() - bottomBorder, t, xChunkCount, xChunkSize);
204 fillRow(v, yy + yChunkSize, yTexChunk2, xChunkCount, xChunkSize);
208 fillRow(v, m_targetRect.height() - bottomBorder, yTexChunk2, xChunkCount, xChunkSize);
209 fillRow(v, m_targetRect.height(), 0, xChunkCount, xChunkSize);
212 // v = m_geometry.vertexDataAsTexturedPoint2D();
213 // for (int i=0; i<m_geometry.vertexCount(); ++i) {
214 // printf("Vertex: %d: (%.3f, %.3f) - (%.3f, %.3f)\n",
216 // v->x, v->y, v->tx, v->ty);
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;
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]);
242 markDirty(QSGNode::DirtyGeometry);
245 void QSGNinePatchNode::fillRow(QSGGeometry::TexturedPoint2D *&v, float y, float ty, int xChunkCount, float xChunkSize)
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;
252 v++->set(0, y, 0, ty);
253 v++->set(m_innerRect.x(), y, xTexChunk1, ty);
255 for (int xc=0; xc<xChunkCount; ++xc) {
256 float xx = m_innerRect.x() + xChunkSize * xc;
257 v++->set(xx, y, xTexChunk1, ty);
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
264 v->set(m_targetRect.width() - rightBorder, y, t, ty);
266 v->set(xx + xChunkSize, y, xTexChunk2, ty);
271 v++->set(m_targetRect.width() - rightBorder, y, xTexChunk2, ty);
272 v++->set(m_targetRect.width(), y, 1, ty);