1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "qquickninepatchnode_p.h"
43 #include <private/qsgadaptationlayer_p.h>
44 #include <private/qmath_p.h>
46 QQuickNinePatchNode::QQuickNinePatchNode()
47 : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0)
48 , m_horizontalTileMode(QQuickBorderImage::Stretch)
49 , m_verticalTileMode(QQuickBorderImage::Stretch)
50 , m_dirtyGeometry(false)
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");
62 void QQuickNinePatchNode::setInnerRect(const QRectF &rect)
64 if (m_innerRect == rect)
67 m_dirtyGeometry = true;
70 void QQuickNinePatchNode::setRect(const QRectF &rect)
72 if (m_targetRect == rect)
75 m_dirtyGeometry = true;
78 void QQuickNinePatchNode::setHorzontalTileMode(QQuickBorderImage::TileMode mode)
80 if (mode == QQuickBorderImage::TileMode(m_horizontalTileMode))
82 m_horizontalTileMode = mode;
83 m_dirtyGeometry = true;
87 void QQuickNinePatchNode::setVerticalTileMode(QQuickBorderImage::TileMode mode)
89 if (mode == QQuickBorderImage::TileMode(m_verticalTileMode))
91 m_verticalTileMode = mode;
92 m_dirtyGeometry = true;
96 void QQuickNinePatchNode::setFiltering(QSGTexture::Filtering filtering)
98 if (m_material.filtering() == filtering)
101 m_material.setFiltering(filtering);
102 m_materialO.setFiltering(filtering);
103 markDirty(DirtyMaterial);
106 QSGTexture::Filtering QQuickNinePatchNode::filtering() const
108 return m_material.filtering();
111 void QQuickNinePatchNode::setTexture(QSGTexture *texture)
113 if (texture == m_material.texture())
115 m_material.setTexture(texture);
116 m_materialO.setTexture(texture);
117 markDirty(DirtyMaterial);
120 QSGTexture *QQuickNinePatchNode::texture() const
122 return m_material.texture();
125 void QQuickNinePatchNode::setMirror(bool m)
130 m_dirtyGeometry = true;
134 void QQuickNinePatchNode::update()
136 if (!m_dirtyGeometry)
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
143 Q_ASSERT(m_material.texture());
145 float tw = m_material.texture()->textureSize().width();
146 float th = m_material.texture()->textureSize().height();
148 QRectF textureSubRect = m_material.texture()->normalizedTextureSubRect();
149 QSize textureSize = m_material.texture()->textureSize();
151 float rightBorder = tw - m_innerRect.right();
152 float bottomBorder = th - m_innerRect.bottom();
154 // qDebug() << m_innerRect << m_targetRect << m_horizontalTileMode << m_verticalTileMode;
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
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;
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;
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;
190 int xTotalChunkCount = xChunkCount + 2;
191 int yTotalChunkCount = yChunkCount + 2;
193 int totalChunkCount = xTotalChunkCount * yTotalChunkCount;
194 int vertexCount = totalChunkCount * 4;
195 int indexCount = totalChunkCount * 6;
197 if (vertexCount != m_geometry.vertexCount() || indexCount != m_geometry.indexCount())
198 m_geometry.allocate(vertexCount, indexCount);
200 QSGGeometry::TexturedPoint2D *v = m_geometry.vertexDataAsTexturedPoint2D();
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;
208 fillRow(v, 0, 0, xChunkCount, xChunkSize, textureSubRect, textureSize);
209 fillRow(v, m_innerRect.y(), yTexChunk1, xChunkCount, xChunkSize, textureSubRect, textureSize);
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);
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
220 fillRow(v, m_targetRect.height() - bottomBorder, t, xChunkCount, xChunkSize, textureSubRect, textureSize);
222 fillRow(v, yy + yChunkSize, yTexChunk2, xChunkCount, xChunkSize, textureSubRect, textureSize);
226 fillRow(v, m_targetRect.height() - bottomBorder, yTexChunk2, xChunkCount, xChunkSize, textureSubRect, textureSize);
227 fillRow(v, m_targetRect.height(), 1, xChunkCount, xChunkSize, textureSubRect, textureSize);
230 v = m_geometry.vertexDataAsTexturedPoint2D();
231 for (int i=0; i<m_geometry.vertexCount(); ++i) {
232 v->x = m_targetRect.width() - v->x;
237 // v = m_geometry.vertexDataAsTexturedPoint2D();
238 // for (int i=0; i<m_geometry.vertexCount(); ++i) {
239 // printf("Vertex: %d: (%.3f, %.3f) - (%.3f, %.3f)\n",
241 // v->x, v->y, v->tx, v->ty);
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;
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]);
267 markDirty(QSGNode::DirtyGeometry);
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
274 ty = tsr.y() + ty * tsr.height();
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;
281 v++->set(0, y, tsr.left(), ty);
282 v++->set(m_innerRect.x(), y, xTexChunk1, ty);
284 for (int xc=0; xc<xChunkCount; ++xc) {
285 float xx = m_innerRect.x() + xChunkSize * xc;
286 v++->set(xx, y, xTexChunk1, ty);
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
293 v->set(m_targetRect.width() - rightBorder, y, t, ty);
295 v->set(xx + xChunkSize, y, xTexChunk2, ty);
300 v++->set(m_targetRect.width() - rightBorder, y, xTexChunk2, ty);
301 v++->set(m_targetRect.width(), y, tsr.right(), ty);