diff --git a/src/declarative/scenegraph/qsgdefaultimagenode.cpp b/src/declarative/scenegraph/qsgdefaultimagenode.cpp index aa30d009dabbb2f807bc259aa0523cf9ae86ab80..2d436107bfeba8072f64d6cacae78255cf8619de 100644 --- a/src/declarative/scenegraph/qsgdefaultimagenode.cpp +++ b/src/declarative/scenegraph/qsgdefaultimagenode.cpp @@ -43,6 +43,10 @@ #include <private/qsgtextureprovider_p.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qmath.h> +#include <QtOpenGL/qglfunctions.h> + QT_BEGIN_NAMESPACE QSGDefaultImageNode::QSGDefaultImageNode() @@ -159,19 +163,120 @@ void QSGDefaultImageNode::preprocess() markDirty(DirtyMaterial); } +inline static bool isPowerOfTwo(int x) +{ + // Assumption: x >= 1 + return x == (x & -x); +} + void QSGDefaultImageNode::updateGeometry() { const QSGTexture *t = m_material.texture(); if (!t) { + m_geometry.allocate(4); + m_geometry.setDrawingMode(GL_TRIANGLE_STRIP); QSGGeometry::updateTexturedRectGeometry(&m_geometry, QRectF(), QRectF()); } else { QRectF textureRect = t->textureSubRect(); - QRectF sr(textureRect.x() + m_sourceRect.x() * textureRect.width(), - textureRect.y() + m_sourceRect.y() * textureRect.height(), - m_sourceRect.width() * textureRect.width(), - m_sourceRect.height() * textureRect.height()); - QSGGeometry::updateTexturedRectGeometry(&m_geometry, m_targetRect, sr); + bool isSubRect = textureRect != QRectF(0, 0, 1, 1); + const int ceilRight = qCeil(m_sourceRect.right()); + const int floorLeft = qFloor(m_sourceRect.left()); + const int ceilBottom = qCeil(m_sourceRect.bottom()); + const int floorTop = qFloor(m_sourceRect.top()); + const int hCells = ceilRight - floorLeft; + const int vCells = ceilBottom - floorTop; + bool isRepeating = hCells > 1 || vCells > 1; + +#ifdef QT_OPENGL_ES_2 + const QGLContext *ctx = QGLContext::currentContext(); + bool npotSupported = ctx->functions()->hasOpenGLFeature(QGLFunctions::NPOTTextures); + + QSize size = t->textureSize(); + bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height()); + + if (isRepeating && (isSubRect || (isNpot && !npotSupported))) { +#else + if (isRepeating && isSubRect) { +#endif + m_geometry.allocate(hCells * vCells * 4, hCells * vCells * 6); + m_geometry.setDrawingMode(GL_TRIANGLES); + struct X { float x, tx; }; + struct Y { float y, ty; }; + QVarLengthArray<X, 32> xData(2 * hCells); + QVarLengthArray<Y, 32> yData(2 * vCells); + X *xs = xData.data(); + Y *ys = yData.data(); + + xs->x = m_targetRect.left(); + xs->tx = textureRect.x() + (m_sourceRect.left() - floorLeft) * textureRect.width(); + ++xs; + ys->y = m_targetRect.top(); + ys->ty = textureRect.y() + (m_sourceRect.top() - floorTop) * textureRect.height(); + ++ys; + + float a, b; + b = m_targetRect.width() / m_sourceRect.width(); + a = m_targetRect.x() - m_sourceRect.x() * b; + for (int i = floorLeft + 1; i <= ceilRight - 1; ++i) { + xs[0].x = xs[1].x = a + b * i; + xs[0].tx = 1; + xs[1].tx = 0; + xs += 2; + } + b = m_targetRect.height() / m_sourceRect.height(); + a = m_targetRect.y() - m_sourceRect.y() * b; + for (int i = floorTop + 1; i <= ceilBottom - 1; ++i) { + ys[0].y = ys[1].y = a + b * i; + ys[0].ty = 1; + ys[1].ty = 0; + ys += 2; + } + + xs->x = m_targetRect.right(); + xs->tx = textureRect.x() + (m_sourceRect.right() - ceilRight + 1) * textureRect.width(); + + ys->y = m_targetRect.bottom(); + ys->ty = textureRect.y() + (m_sourceRect.bottom() - ceilBottom + 1) * textureRect.height(); + + QSGGeometry::TexturedPoint2D *vertices = m_geometry.vertexDataAsTexturedPoint2D(); + ys = yData.data(); + for (int j = 0; j < vCells; ++j, ys += 2) { + xs = xData.data(); + for (int i = 0; i < hCells; ++i, xs += 2) { + vertices[0].x = vertices[2].x = xs[0].x; + vertices[0].tx = vertices[2].tx = xs[0].tx; + vertices[1].x = vertices[3].x = xs[1].x; + vertices[1].tx = vertices[3].tx = xs[1].tx; + + vertices[0].y = vertices[1].y = ys[0].y; + vertices[0].ty = vertices[1].ty = ys[0].ty; + vertices[2].y = vertices[3].y = ys[1].y; + vertices[2].ty = vertices[3].ty = ys[1].ty; + + vertices += 4; + } + } + + quint16 *indices = m_geometry.indexDataAsUShort(); + for (int i = 0; i < 4 * vCells * hCells; i += 4) { + *indices++ = i; + *indices++ = i + 2; + *indices++ = i + 3; + *indices++ = i + 3; + *indices++ = i + 1; + *indices++ = i; + } + } else { + QRectF sr(textureRect.x() + m_sourceRect.x() * textureRect.width(), + textureRect.y() + m_sourceRect.y() * textureRect.height(), + m_sourceRect.width() * textureRect.width(), + m_sourceRect.height() * textureRect.height()); + + m_geometry.allocate(4); + m_geometry.setDrawingMode(GL_TRIANGLE_STRIP); + QSGGeometry::updateTexturedRectGeometry(&m_geometry, m_targetRect, sr); + } } markDirty(DirtyGeometry); m_dirtyGeometry = false; diff --git a/src/declarative/scenegraph/util/qsgtexturematerial.cpp b/src/declarative/scenegraph/util/qsgtexturematerial.cpp index 1e14172de358103528264eb321fcb03c744a51c8..cdca59963c8aa42ccf44fed7a51c3340f4c06fb2 100644 --- a/src/declarative/scenegraph/util/qsgtexturematerial.cpp +++ b/src/declarative/scenegraph/util/qsgtexturematerial.cpp @@ -41,10 +41,17 @@ #include "qsgtexturematerial_p.h" -#include <qglshaderprogram.h> +#include <QtOpenGL/qglshaderprogram.h> +#include <QtOpenGL/qglfunctions.h> QT_BEGIN_NAMESPACE +inline static bool isPowerOfTwo(int x) +{ + // Assumption: x >= 1 + return x == (x & -x); +} + const char qt_scenegraph_texture_material_vertex_code[] = "uniform highp mat4 qt_Matrix; \n" "attribute highp vec4 qt_VertexPosition; \n" @@ -95,8 +102,19 @@ void QSGOpaqueTextureMaterialShader::updateState(const RenderState &state, QSGMa QSGTexture *t = tx->texture(); t->setFiltering(tx->filtering()); - t->setHorizontalWrapMode(tx->horizontalWrapMode()); - t->setVerticalWrapMode(tx->verticalWrapMode()); +#ifdef QT_OPENGL_ES_2 + bool npotSupported = state.context()->functions()->hasOpenGLFeature(QGLFunctions::NPOTTextures); + QSize size = t->textureSize(); + bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height()); + if (!npotSupported && isNpot) { + t->setHorizontalWrapMode(QSGTexture::ClampToEdge); + t->setVerticalWrapMode(QSGTexture::ClampToEdge); + } else +#endif + { + t->setHorizontalWrapMode(tx->horizontalWrapMode()); + t->setVerticalWrapMode(tx->verticalWrapMode()); + } t->setMipmapFiltering(tx->mipmapFiltering()); if (oldTx == 0 || oldTx->texture()->textureId() != t->textureId())