Update to 5.0.0-beta1
[profile/ivi/qtdeclarative.git] / src / quick / scenegraph / qsgdefaultimagenode.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qsgdefaultimagenode_p.h"
43
44 #include <QtCore/qvarlengtharray.h>
45 #include <QtCore/qmath.h>
46 #include <QtGui/qopenglfunctions.h>
47
48 #include <qsgtexturematerial.h>
49 #include <private/qsgtexturematerial_p.h>
50 #include <qsgmaterial.h>
51
52 QT_BEGIN_NAMESPACE
53
54 namespace
55 {
56     struct SmoothVertex
57     {
58         float x, y, u, v;
59         float dx, dy, du, dv;
60     };
61
62     const QSGGeometry::AttributeSet &smoothAttributeSet()
63     {
64         static QSGGeometry::Attribute data[] = {
65             QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true),
66             QSGGeometry::Attribute::create(1, 2, GL_FLOAT, false),
67             QSGGeometry::Attribute::create(2, 2, GL_FLOAT, false),
68             QSGGeometry::Attribute::create(3, 2, GL_FLOAT, false)
69         };
70         static QSGGeometry::AttributeSet attrs = { 4, sizeof(SmoothVertex), data };
71         return attrs;
72     }
73 }
74
75 class SmoothTextureMaterialShader : public QSGTextureMaterialShader
76 {
77 public:
78     virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);
79     virtual char const *const *attributeNames() const;
80
81 protected:
82     virtual void initialize();
83     virtual const char *vertexShader() const;
84     virtual const char *fragmentShader() const;
85
86     int m_pixelSizeLoc;
87 };
88
89
90 SmoothTextureMaterial::SmoothTextureMaterial()
91 {
92     setFlag(RequiresFullMatrixExceptTranslate, true);
93     setFlag(Blending, true);
94 }
95
96 void SmoothTextureMaterial::setTexture(QSGTexture *texture)
97 {
98     m_texture = texture;
99 }
100
101 QSGMaterialType *SmoothTextureMaterial::type() const
102 {
103     static QSGMaterialType type;
104     return &type;
105 }
106
107 QSGMaterialShader *SmoothTextureMaterial::createShader() const
108 {
109     return new SmoothTextureMaterialShader;
110 }
111
112 void SmoothTextureMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
113 {
114     if (oldEffect == 0) {
115         // The viewport is constant, so set the pixel size uniform only once.
116         QRect r = state.viewportRect();
117         program()->setUniformValue(m_pixelSizeLoc, 2.0f / r.width(), 2.0f / r.height());
118     }
119     QSGTextureMaterialShader::updateState(state, newEffect, oldEffect);
120 }
121
122 char const *const *SmoothTextureMaterialShader::attributeNames() const
123 {
124     static char const *const attributes[] = {
125         "vertex",
126         "multiTexCoord",
127         "vertexOffset",
128         "texCoordOffset",
129         0
130     };
131     return attributes;
132 }
133
134 void SmoothTextureMaterialShader::initialize()
135 {
136     m_pixelSizeLoc = program()->uniformLocation("pixelSize");
137     QSGTextureMaterialShader::initialize();
138 }
139
140 const char *SmoothTextureMaterialShader::vertexShader() const
141 {
142     return
143             "uniform highp vec2 pixelSize; \n"
144             "uniform highp mat4 qt_Matrix; \n"
145             "uniform lowp float opacity; \n"
146             "attribute highp vec4 vertex; \n"
147             "attribute highp vec2 multiTexCoord; \n"
148             "attribute highp vec2 vertexOffset; \n"
149             "attribute highp vec2 texCoordOffset; \n"
150             "varying highp vec2 texCoord; \n"
151             "varying lowp float vertexOpacity; \n"
152             "void main() { \n"
153             "    highp vec4 pos = qt_Matrix * vertex; \n"
154             "    gl_Position = pos; \n"
155             "    texCoord = multiTexCoord; \n"
156
157             "    if (vertexOffset.x != 0.) { \n"
158             "        highp vec4 delta = qt_Matrix[0] * vertexOffset.x; \n"
159             "        highp vec2 dir = delta.xy * pos.w - pos.xy * delta.w; \n"
160             "        highp vec2 ndir = .5 * pixelSize * normalize(dir / pixelSize);  \n"
161             "        dir -= ndir * delta.w * pos.w; \n"
162             "        highp float scale = min(1., dot(dir, ndir * pos.w * pos.w) / dot(dir, dir)); \n"
163             "        if (scale < 0.) scale = 1.; \n"
164             "        gl_Position += scale * delta; \n"
165             "        texCoord.x += scale * texCoordOffset.x; \n"
166             "    } \n"
167
168             "    if (vertexOffset.y != 0.) { \n"
169             "        highp vec4 delta = qt_Matrix[1] * vertexOffset.y; \n"
170             "        highp vec2 dir = delta.xy * pos.w - pos.xy * delta.w; \n"
171             "        highp vec2 ndir = .5 * pixelSize * normalize(dir / pixelSize);  \n"
172             "        dir -= ndir * delta.w * pos.w; \n"
173             "        highp float scale = min(1., dot(dir, ndir * pos.w * pos.w) / dot(dir, dir)); \n"
174             "        if (scale < 0.) scale = 1.; \n"
175             "        gl_Position += scale * delta; \n"
176             "        texCoord.y += scale * texCoordOffset.y; \n"
177             "    } \n"
178
179             "    bool onEdge = any(notEqual(vertexOffset, vec2(0.))); \n"
180             "    bool outerEdge = all(equal(texCoordOffset, vec2(0.))); \n"
181             "    vertexOpacity = onEdge && outerEdge ? 0. : opacity; \n"
182             "}";
183 }
184
185 const char *SmoothTextureMaterialShader::fragmentShader() const
186 {
187     return
188             "uniform sampler2D qt_Texture; \n"
189             "varying highp vec2 texCoord; \n"
190             "varying lowp float vertexOpacity; \n"
191             "void main() { \n"
192             "    gl_FragColor = texture2D(qt_Texture, texCoord) * vertexOpacity; \n"
193             "}";
194 }
195
196 QSGDefaultImageNode::QSGDefaultImageNode()
197     : m_innerSourceRect(0, 0, 1, 1)
198     , m_subSourceRect(0, 0, 1, 1)
199     , m_antialiasing(false)
200     , m_mirror(false)
201     , m_dirtyGeometry(false)
202     , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4)
203 {
204     setMaterial(&m_materialO);
205     setOpaqueMaterial(&m_material);
206     setGeometry(&m_geometry);
207
208 #ifdef QML_RUNTIME_TESTING
209     description = QLatin1String("image");
210 #endif
211 }
212
213 void QSGDefaultImageNode::setTargetRect(const QRectF &rect)
214 {
215     if (rect == m_targetRect)
216         return;
217     m_targetRect = rect;
218     m_dirtyGeometry = true;
219 }
220
221 void QSGDefaultImageNode::setInnerTargetRect(const QRectF &rect)
222 {
223     if (rect == m_innerTargetRect)
224         return;
225     m_innerTargetRect = rect;
226     m_dirtyGeometry = true;
227 }
228
229 void QSGDefaultImageNode::setInnerSourceRect(const QRectF &rect)
230 {
231     if (rect == m_innerSourceRect)
232         return;
233     m_innerSourceRect = rect;
234     m_dirtyGeometry = true;
235 }
236
237 void QSGDefaultImageNode::setSubSourceRect(const QRectF &rect)
238 {
239     if (rect == m_subSourceRect)
240         return;
241     m_subSourceRect = rect;
242     m_dirtyGeometry = true;
243 }
244
245 void QSGDefaultImageNode::setFiltering(QSGTexture::Filtering filtering)
246 {
247     if (m_material.filtering() == filtering)
248         return;
249
250     m_material.setFiltering(filtering);
251     m_materialO.setFiltering(filtering);
252     m_smoothMaterial.setFiltering(filtering);
253     markDirty(DirtyMaterial);
254 }
255
256
257 void QSGDefaultImageNode::setMipmapFiltering(QSGTexture::Filtering filtering)
258 {
259     if (m_material.mipmapFiltering() == filtering)
260         return;
261
262     m_material.setMipmapFiltering(filtering);
263     m_materialO.setMipmapFiltering(filtering);
264     m_smoothMaterial.setMipmapFiltering(filtering);
265     markDirty(DirtyMaterial);
266 }
267
268 void QSGDefaultImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode)
269 {
270     if (m_material.verticalWrapMode() == wrapMode)
271         return;
272
273     m_material.setVerticalWrapMode(wrapMode);
274     m_materialO.setVerticalWrapMode(wrapMode);
275     m_smoothMaterial.setVerticalWrapMode(wrapMode);
276     markDirty(DirtyMaterial);
277 }
278
279 void QSGDefaultImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode)
280 {
281     if (m_material.horizontalWrapMode() == wrapMode)
282         return;
283
284     m_material.setHorizontalWrapMode(wrapMode);
285     m_materialO.setHorizontalWrapMode(wrapMode);
286     m_smoothMaterial.setHorizontalWrapMode(wrapMode);
287     markDirty(DirtyMaterial);
288 }
289
290
291 void QSGDefaultImageNode::setTexture(QSGTexture *texture)
292 {
293     if (texture == m_material.texture())
294         return;
295
296     m_material.setTexture(texture);
297     m_materialO.setTexture(texture);
298     m_smoothMaterial.setTexture(texture);
299     // Texture cleanup
300 //    if (!texture.isNull())
301 //        m_material.setBlending(texture->hasAlphaChannel());
302     markDirty(DirtyMaterial);
303
304     // Because the texture can be a different part of the atlas, we need to update it...
305     m_dirtyGeometry = true;
306 }
307
308 void QSGDefaultImageNode::setAntialiasing(bool antialiasing)
309 {
310     if (antialiasing == m_antialiasing)
311         return;
312     m_antialiasing = antialiasing;
313     if (m_antialiasing) {
314         setMaterial(&m_smoothMaterial);
315         setOpaqueMaterial(0);
316         setGeometry(new QSGGeometry(smoothAttributeSet(), 0));
317         setFlag(OwnsGeometry, true);
318     } else {
319         setMaterial(&m_materialO);
320         setOpaqueMaterial(&m_material);
321         setGeometry(&m_geometry);
322         setFlag(OwnsGeometry, false);
323     }
324     m_dirtyGeometry = true;
325 }
326
327 void QSGDefaultImageNode::setMirror(bool mirror)
328 {
329     if (mirror == m_mirror)
330         return;
331     m_mirror = mirror;
332     m_dirtyGeometry = true;
333 }
334
335
336 void QSGDefaultImageNode::update()
337 {
338     if (m_dirtyGeometry)
339         updateGeometry();
340 }
341
342 void QSGDefaultImageNode::preprocess()
343 {
344     bool doDirty = false;
345     QSGDynamicTexture *t = qobject_cast<QSGDynamicTexture *>(m_material.texture());
346     if (t) {
347         doDirty = t->updateTexture();
348         updateGeometry();
349     }
350 // ### texture cleanup
351 //    bool alpha = m_material.blending();
352 //    if (!m_material->texture().isNull() && alpha != m_material.texture()->hasAlphaChannel()) {
353 //        m_material.setBlending(!alpha);
354 //        doDirty = true;
355 //    }
356
357     if (doDirty)
358         markDirty(DirtyMaterial);
359 }
360
361 inline static bool isPowerOfTwo(int x)
362 {
363     // Assumption: x >= 1
364     return x == (x & -x);
365 }
366
367 namespace {
368     struct X { float x, tx; };
369     struct Y { float y, ty; };
370 }
371
372 static inline void appendQuad(quint16 **indices, quint16 topLeft, quint16 topRight,
373                               quint16 bottomLeft, quint16 bottomRight)
374 {
375     *(*indices)++ = topLeft;
376     *(*indices)++ = bottomLeft;
377     *(*indices)++ = bottomRight;
378     *(*indices)++ = bottomRight;
379     *(*indices)++ = topRight;
380     *(*indices)++ = topLeft;
381 }
382
383 void QSGDefaultImageNode::updateGeometry()
384 {
385     Q_ASSERT(!m_targetRect.isEmpty());
386     const QSGTexture *t = m_material.texture();
387     if (!t) {
388         QSGGeometry *g = geometry();
389         g->allocate(4);
390         g->setDrawingMode(GL_TRIANGLE_STRIP);
391         memset(g->vertexData(), 0, g->sizeOfVertex() * 4);
392     } else {
393         QRectF sourceRect = t->normalizedTextureSubRect();
394
395         QRectF innerSourceRect(sourceRect.x() + m_innerSourceRect.x() * sourceRect.width(),
396                                sourceRect.y() + m_innerSourceRect.y() * sourceRect.height(),
397                                m_innerSourceRect.width() * sourceRect.width(),
398                                m_innerSourceRect.height() * sourceRect.height());
399
400         bool hasMargins = m_targetRect != m_innerTargetRect;
401
402         int floorLeft = qFloor(m_subSourceRect.left());
403         int ceilRight = qCeil(m_subSourceRect.right());
404         int floorTop = qFloor(m_subSourceRect.top());
405         int ceilBottom = qCeil(m_subSourceRect.bottom());
406         int hTiles = ceilRight - floorLeft;
407         int vTiles = ceilBottom - floorTop;
408
409         bool hasTiles = hTiles != 1 || vTiles != 1;
410         bool fullTexture = innerSourceRect == QRectF(0, 0, 1, 1);
411
412 #ifdef QT_OPENGL_ES_2
413         QOpenGLContext *ctx = QOpenGLContext::currentContext();
414         bool npotSupported = ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat);
415         QSize size = t->textureSize();
416         bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height());
417         bool wrapSupported = npotSupported || !isNpot;
418 #else
419         bool wrapSupported = true;
420 #endif
421
422         // An image can be rendered as a single quad if:
423         // - There are no margins, and either:
424         //   - the image isn't repeated
425         //   - the source rectangle fills the entire texture so that texture wrapping can be used,
426         //     and NPOT is supported
427         if (!hasMargins && (!hasTiles || (fullTexture && wrapSupported))) {
428             QRectF sr;
429             if (!fullTexture) {
430                 sr = QRectF(innerSourceRect.x() + (m_subSourceRect.left() - floorLeft) * innerSourceRect.width(),
431                             innerSourceRect.y() + (m_subSourceRect.top() - floorTop) * innerSourceRect.height(),
432                             m_subSourceRect.width() * innerSourceRect.width(),
433                             m_subSourceRect.height() * innerSourceRect.height());
434             } else {
435                 sr = QRectF(m_subSourceRect.left() - floorLeft, m_subSourceRect.top() - floorTop,
436                             m_subSourceRect.width(), m_subSourceRect.height());
437             }
438             if (m_mirror) {
439                 qreal oldLeft = sr.left();
440                 sr.setLeft(sr.right());
441                 sr.setRight(oldLeft);
442             }
443
444             if (m_antialiasing) {
445                 QSGGeometry *g = geometry();
446                 Q_ASSERT(g != &m_geometry);
447                 g->allocate(8, 14);
448                 g->setDrawingMode(GL_TRIANGLE_STRIP);
449                 SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData());
450                 float delta = float(qAbs(m_targetRect.width()) < qAbs(m_targetRect.height())
451                         ? m_targetRect.width() : m_targetRect.height()) * 0.5f;
452                 float sx = float(sr.width() / m_targetRect.width());
453                 float sy = float(sr.height() / m_targetRect.height());
454                 for (int d = -1; d <= 1; d += 2) {
455                     for (int j = 0; j < 2; ++j) {
456                         for (int i = 0; i < 2; ++i, ++vertices) {
457                             vertices->x = m_targetRect.x() + i * m_targetRect.width();
458                             vertices->y = m_targetRect.y() + j * m_targetRect.height();
459                             vertices->u = sr.x() + i * sr.width();
460                             vertices->v = sr.y() + j * sr.height();
461                             vertices->dx = (i == 0 ? delta : -delta) * d;
462                             vertices->dy = (j == 0 ? delta : -delta) * d;
463                             vertices->du = (d < 0 ? 0 : vertices->dx * sx);
464                             vertices->dv = (d < 0 ? 0 : vertices->dy * sy);
465                         }
466                     }
467                 }
468                 Q_ASSERT(vertices - g->vertexCount() == g->vertexData());
469                 static const quint16 indices[] = {
470                     0, 4, 1, 5, 3, 7, 2, 6, 0, 4,
471                     4, 6, 5, 7
472                 };
473                 Q_ASSERT(g->sizeOfIndex() * g->indexCount() == sizeof(indices));
474                 memcpy(g->indexDataAsUShort(), indices, sizeof(indices));
475             } else {
476                 m_geometry.allocate(4);
477                 m_geometry.setDrawingMode(GL_TRIANGLE_STRIP);
478                 QSGGeometry::updateTexturedRectGeometry(&m_geometry, m_targetRect, sr);
479             }
480         } else {
481             int hCells = hTiles;
482             int vCells = vTiles;
483             if (m_innerTargetRect.width() == 0)
484                 hCells = 0;
485             if (m_innerTargetRect.left() != m_targetRect.left())
486                 ++hCells;
487             if (m_innerTargetRect.right() != m_targetRect.right())
488                 ++hCells;
489             if (m_innerTargetRect.height() == 0)
490                 vCells = 0;
491             if (m_innerTargetRect.top() != m_targetRect.top())
492                 ++vCells;
493             if (m_innerTargetRect.bottom() != m_targetRect.bottom())
494                 ++vCells;
495             QVarLengthArray<X, 32> xData(2 * hCells);
496             QVarLengthArray<Y, 32> yData(2 * vCells);
497             X *xs = xData.data();
498             Y *ys = yData.data();
499
500             if (m_innerTargetRect.left() != m_targetRect.left()) {
501                 xs[0].x = m_targetRect.left();
502                 xs[0].tx = sourceRect.left();
503                 xs[1].x = m_innerTargetRect.left();
504                 xs[1].tx = innerSourceRect.left();
505                 xs += 2;
506             }
507             if (m_innerTargetRect.width() != 0) {
508                 xs[0].x = m_innerTargetRect.left();
509                 xs[0].tx = innerSourceRect.x() + (m_subSourceRect.left() - floorLeft) * innerSourceRect.width();
510                 ++xs;
511                 float b = m_innerTargetRect.width() / m_subSourceRect.width();
512                 float a = m_innerTargetRect.x() - m_subSourceRect.x() * b;
513                 for (int i = floorLeft + 1; i <= ceilRight - 1; ++i) {
514                     xs[0].x = xs[1].x = a + b * i;
515                     xs[0].tx = innerSourceRect.right();
516                     xs[1].tx = innerSourceRect.left();
517                     xs += 2;
518                 }
519                 xs[0].x = m_innerTargetRect.right();
520                 xs[0].tx = innerSourceRect.x() + (m_subSourceRect.right() - ceilRight + 1) * innerSourceRect.width();
521                 ++xs;
522             }
523             if (m_innerTargetRect.right() != m_targetRect.right()) {
524                 xs[0].x = m_innerTargetRect.right();
525                 xs[0].tx = innerSourceRect.right();
526                 xs[1].x = m_targetRect.right();
527                 xs[1].tx = sourceRect.right();
528                 xs += 2;
529             }
530             Q_ASSERT(xs == xData.data() + xData.size());
531             if (m_mirror) {
532                 float leftPlusRight = m_targetRect.left() + m_targetRect.right();
533                 int count = xData.size();
534                 xs = xData.data();
535                 for (int i = 0; i < count >> 1; ++i)
536                     qSwap(xs[i], xs[count - 1 - i]);
537                 for (int i = 0; i < count; ++i)
538                     xs[i].x = leftPlusRight - xs[i].x;
539             }
540
541             if (m_innerTargetRect.top() != m_targetRect.top()) {
542                 ys[0].y = m_targetRect.top();
543                 ys[0].ty = sourceRect.top();
544                 ys[1].y = m_innerTargetRect.top();
545                 ys[1].ty = innerSourceRect.top();
546                 ys += 2;
547             }
548             if (m_innerTargetRect.height() != 0) {
549                 ys[0].y = m_innerTargetRect.top();
550                 ys[0].ty = innerSourceRect.y() + (m_subSourceRect.top() - floorTop) * innerSourceRect.height();
551                 ++ys;
552                 float b = m_innerTargetRect.height() / m_subSourceRect.height();
553                 float a = m_innerTargetRect.y() - m_subSourceRect.y() * b;
554                 for (int i = floorTop + 1; i <= ceilBottom - 1; ++i) {
555                     ys[0].y = ys[1].y = a + b * i;
556                     ys[0].ty = innerSourceRect.bottom();
557                     ys[1].ty = innerSourceRect.top();
558                     ys += 2;
559                 }
560                 ys[0].y = m_innerTargetRect.bottom();
561                 ys[0].ty = innerSourceRect.y() + (m_subSourceRect.bottom() - ceilBottom + 1) * innerSourceRect.height();
562                 ++ys;
563             }
564             if (m_innerTargetRect.bottom() != m_targetRect.bottom()) {
565                 ys[0].y = m_innerTargetRect.bottom();
566                 ys[0].ty = innerSourceRect.bottom();
567                 ys[1].y = m_targetRect.bottom();
568                 ys[1].ty = sourceRect.bottom();
569                 ys += 2;
570             }
571             Q_ASSERT(ys == yData.data() + yData.size());
572
573             if (m_antialiasing) {
574                 QSGGeometry *g = geometry();
575                 Q_ASSERT(g != &m_geometry);
576
577                 g->allocate(hCells * vCells * 4 + (hCells + vCells - 1) * 4,
578                             hCells * vCells * 6 + (hCells + vCells) * 12);
579                 g->setDrawingMode(GL_TRIANGLES);
580                 SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData());
581                 memset(vertices, 0, g->vertexCount() * g->sizeOfVertex());
582                 quint16 *indices = g->indexDataAsUShort();
583
584                 // The deltas are how much the fuzziness can reach into the image.
585                 // Only the border vertices are moved by the vertex shader, so the fuzziness
586                 // can't reach further into the image than the closest interior vertices.
587                 float leftDx = xData.at(1).x - xData.at(0).x;
588                 float rightDx = xData.at(xData.size() - 1).x - xData.at(xData.size() - 2).x;
589                 float topDy = yData.at(1).y - yData.at(0).y;
590                 float bottomDy = yData.at(yData.size() - 1).y - yData.at(yData.size() - 2).y;
591
592                 float leftDu = xData.at(1).tx - xData.at(0).tx;
593                 float rightDu = xData.at(xData.size() - 1).tx - xData.at(xData.size() - 2).tx;
594                 float topDv = yData.at(1).ty - yData.at(0).ty;
595                 float bottomDv = yData.at(yData.size() - 1).ty - yData.at(yData.size() - 2).ty;
596
597                 if (hCells == 1) {
598                     leftDx = rightDx *= 0.5f;
599                     leftDu = rightDu *= 0.5f;
600                 }
601                 if (vCells == 1) {
602                     topDy = bottomDy *= 0.5f;
603                     topDv = bottomDv *= 0.5f;
604                 }
605
606                 // This delta is how much the fuzziness can reach out from the image.
607                 float delta = float(qAbs(m_targetRect.width()) < qAbs(m_targetRect.height())
608                                     ? m_targetRect.width() : m_targetRect.height()) * 0.5f;
609
610                 quint16 index = 0;
611                 ys = yData.data();
612                 for (int j = 0; j < vCells; ++j, ys += 2) {
613                     xs = xData.data();
614                     bool isTop = j == 0;
615                     bool isBottom = j == vCells - 1;
616                     for (int i = 0; i < hCells; ++i, xs += 2) {
617                         bool isLeft = i == 0;
618                         bool isRight = i == hCells - 1;
619
620                         SmoothVertex *v = vertices + index;
621
622                         quint16 topLeft = index;
623                         for (int k = (isTop || isLeft ? 2 : 1); k--; ++v, ++index) {
624                             v->x = xs[0].x;
625                             v->u = xs[0].tx;
626                             v->y = ys[0].y;
627                             v->v = ys[0].ty;
628                         }
629
630                         quint16 topRight = index;
631                         for (int k = (isTop || isRight ? 2 : 1); k--; ++v, ++index) {
632                             v->x = xs[1].x;
633                             v->u = xs[1].tx;
634                             v->y = ys[0].y;
635                             v->v = ys[0].ty;
636                         }
637
638                         quint16 bottomLeft = index;
639                         for (int k = (isBottom || isLeft ? 2 : 1); k--; ++v, ++index) {
640                             v->x = xs[0].x;
641                             v->u = xs[0].tx;
642                             v->y = ys[1].y;
643                             v->v = ys[1].ty;
644                         }
645
646                         quint16 bottomRight = index;
647                         for (int k = (isBottom || isRight ? 2 : 1); k--; ++v, ++index) {
648                             v->x = xs[1].x;
649                             v->u = xs[1].tx;
650                             v->y = ys[1].y;
651                             v->v = ys[1].ty;
652                         }
653
654                         appendQuad(&indices, topLeft, topRight, bottomLeft, bottomRight);
655
656                         if (isTop) {
657                             vertices[topLeft].dy = vertices[topRight].dy = topDy;
658                             vertices[topLeft].dv = vertices[topRight].dv = topDv;
659                             vertices[topLeft + 1].dy = vertices[topRight + 1].dy = -delta;
660                             appendQuad(&indices, topLeft + 1, topRight + 1, topLeft, topRight);
661                         }
662
663                         if (isBottom) {
664                             vertices[bottomLeft].dy = vertices[bottomRight].dy = -bottomDy;
665                             vertices[bottomLeft].dv = vertices[bottomRight].dv = -bottomDv;
666                             vertices[bottomLeft + 1].dy = vertices[bottomRight + 1].dy = delta;
667                             appendQuad(&indices, bottomLeft, bottomRight, bottomLeft + 1, bottomRight + 1);
668                         }
669
670                         if (isLeft) {
671                             vertices[topLeft].dx = vertices[bottomLeft].dx = leftDx;
672                             vertices[topLeft].du = vertices[bottomLeft].du = leftDu;
673                             vertices[topLeft + 1].dx = vertices[bottomLeft + 1].dx = -delta;
674                             appendQuad(&indices, topLeft + 1, topLeft, bottomLeft + 1, bottomLeft);
675                         }
676
677                         if (isRight) {
678                             vertices[topRight].dx = vertices[bottomRight].dx = -rightDx;
679                             vertices[topRight].du = vertices[bottomRight].du = -rightDu;
680                             vertices[topRight + 1].dx = vertices[bottomRight + 1].dx = delta;
681                             appendQuad(&indices, topRight, topRight + 1, bottomRight, bottomRight + 1);
682                         }
683                     }
684                 }
685
686                 Q_ASSERT(index == g->vertexCount());
687                 Q_ASSERT(indices - g->indexCount() == g->indexData());
688             } else {
689                 m_geometry.allocate(hCells * vCells * 4, hCells * vCells * 6);
690                 m_geometry.setDrawingMode(GL_TRIANGLES);
691                 QSGGeometry::TexturedPoint2D *vertices = m_geometry.vertexDataAsTexturedPoint2D();
692                 ys = yData.data();
693                 for (int j = 0; j < vCells; ++j, ys += 2) {
694                     xs = xData.data();
695                     for (int i = 0; i < hCells; ++i, xs += 2) {
696                         vertices[0].x = vertices[2].x = xs[0].x;
697                         vertices[0].tx = vertices[2].tx = xs[0].tx;
698                         vertices[1].x = vertices[3].x = xs[1].x;
699                         vertices[1].tx = vertices[3].tx = xs[1].tx;
700
701                         vertices[0].y = vertices[1].y = ys[0].y;
702                         vertices[0].ty = vertices[1].ty = ys[0].ty;
703                         vertices[2].y = vertices[3].y = ys[1].y;
704                         vertices[2].ty = vertices[3].ty = ys[1].ty;
705
706                         vertices += 4;
707                     }
708                 }
709
710                 quint16 *indices = m_geometry.indexDataAsUShort();
711                 for (int i = 0; i < 4 * vCells * hCells; i += 4)
712                     appendQuad(&indices, i, i + 1, i + 2, i + 3);
713             }
714         }
715     }
716     markDirty(DirtyGeometry);
717     m_dirtyGeometry = false;
718 }
719
720 QT_END_NAMESPACE