Downscale textures which exceed the GL texture limit
authorGunnar Sletta <gunnar.sletta@jollamobile.com>
Fri, 15 Aug 2014 07:26:16 +0000 (09:26 +0200)
committerGunnar Sletta <gunnar.sletta@jollamobile.com>
Sat, 23 Aug 2014 06:00:43 +0000 (08:00 +0200)
This way they will at least render.

[ChangeLog][QtQuick] Images exceeding GL_MAX_TEXTURE_SIZE will be
downscaled to fit so they will still show.

Change-Id: I169ecac768036812b8e14265ec1a0a8902655666
Reviewed-by: Laszlo Agocs <laszlo.agocs@digia.com>
src/quick/scenegraph/qsgcontext.cpp
src/quick/scenegraph/qsgcontext_p.h
src/quick/scenegraph/util/qsgtexture.cpp
tests/auto/quick/qquickimage/data/hugeImages.qml [new file with mode: 0644]
tests/auto/quick/qquickimage/data/tootall.png [new file with mode: 0644]
tests/auto/quick/qquickimage/data/toowide.png [new file with mode: 0644]
tests/auto/quick/qquickimage/tst_qquickimage.cpp

index 6ebc479..4e76857 100644 (file)
@@ -253,6 +253,7 @@ void QSGContext::renderContextInitialized(QSGRenderContext *renderContext)
         QSet<QByteArray> exts = renderContext->openglContext()->extensions();
         QByteArray all; foreach (const QByteArray &e, exts) all += ' ' + e;
         qCDebug(QSG_LOG_INFO) << "GL_EXTENSIONS:    " << all.constData();
+        qCDebug(QSG_LOG_INFO) << "Max Texture Size: " << renderContext->maxTextureSize();
     }
 
     d->mutex.unlock();
@@ -392,6 +393,7 @@ QSGRenderContext::QSGRenderContext(QSGContext *context)
     , m_atlasManager(0)
     , m_depthStencilManager(0)
     , m_distanceFieldCacheManager(0)
+    , m_maxTextureSize(0)
     , m_brokenIBOs(false)
     , m_serializedRender(false)
     , m_attachToGLContext(true)
@@ -493,6 +495,9 @@ void QSGRenderContext::registerFontengineForCleanup(QFontEngine *engine)
  */
 void QSGRenderContext::initialize(QOpenGLContext *context)
 {
+    QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
+    funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
+
     // Sanity check the surface format, in case it was overridden by the application
     QSurfaceFormat requested = m_sg->defaultSurfaceFormat();
     QSurfaceFormat actual = context->format();
@@ -513,7 +518,6 @@ void QSGRenderContext::initialize(QOpenGLContext *context)
     m_sg->renderContextInitialized(this);
 
 #ifdef Q_OS_LINUX
-    QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
     const char *vendor = (const char *) funcs->glGetString(GL_VENDOR);
     if (strstr(vendor, "nouveau"))
         m_brokenIBOs = true;
index b5271f9..7020226 100644 (file)
@@ -126,6 +126,7 @@ public:
     static QSGRenderContext *from(QOpenGLContext *context);
 
     bool hasBrokenIndexBufferObjects() const { return m_brokenIBOs; }
+    int maxTextureSize() const { return m_maxTextureSize; }
 
 Q_SIGNALS:
     void initialized();
@@ -147,7 +148,7 @@ protected:
     QSGDistanceFieldGlyphCacheManager *m_distanceFieldCacheManager;
 
     QSet<QFontEngine *> m_fontEnginesToClean;
-
+    int m_maxTextureSize;
     bool m_brokenIBOs;
     bool m_serializedRender;
     bool m_attachToGLContext;
index 69edfab..abd9d48 100644 (file)
@@ -671,12 +671,28 @@ void QSGPlainTexture::bind()
         bindTime = qsg_renderer_timer.nsecsElapsed();
 
     // ### TODO: check for out-of-memory situations...
-    int w = m_image.width();
-    int h = m_image.height();
 
     QImage tmp = (m_image.format() == QImage::Format_RGB32 || m_image.format() == QImage::Format_ARGB32_Premultiplied)
                  ? m_image
                  : m_image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+
+    // Downscale the texture to fit inside the max texture limit if it is too big.
+    // It would be better if the image was already downscaled to the right size,
+    // but this information is not always available at that time, so as a last
+    // resort we can do it here. Texture coordinates are normalized, so it
+    // won't cause any problems and actual texture sizes will be written
+    // based on QSGTexture::textureSize which is updated after this, so that
+    // should be ok.
+    int max;
+    if (QSGRenderContext *rc = QSGRenderContext::from(context))
+        max = rc->maxTextureSize();
+    else
+        glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
+    if (tmp.width() > max || tmp.height() > max) {
+        tmp = tmp.scaled(qMin(max, tmp.width()), qMin(max, tmp.height()), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+        m_texture_size = tmp.size();
+    }
+
     if (tmp.width() * 4 != tmp.bytesPerLine())
         tmp = tmp.copy();
 
@@ -726,7 +742,7 @@ void QSGPlainTexture::bind()
     if (profileFrames)
         swizzleTime = qsg_renderer_timer.nsecsElapsed();
 
-    funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, w, h, 0, externalFormat, GL_UNSIGNED_BYTE, tmp.constBits());
+    funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_texture_size.width(), m_texture_size.height(), 0, externalFormat, GL_UNSIGNED_BYTE, tmp.constBits());
 
     qint64 uploadTime = 0;
     if (profileFrames)
@@ -741,7 +757,7 @@ void QSGPlainTexture::bind()
     if (profileFrames) {
         mipmapTime = qsg_renderer_timer.nsecsElapsed();
         qCDebug(QSG_LOG_TIME_TEXTURE,
-                "plain texture uploaded in: %dms (%dx%d), bind=%d, convert=%d, swizzle=%d (%s->%s), upload=%d, mipmap=%d",
+                "plain texture uploaded in: %dms (%dx%d), bind=%d, convert=%d, swizzle=%d (%s->%s), upload=%d, mipmap=%d%s",
                 int(mipmapTime / 1000000),
                 m_texture_size.width(), m_texture_size.height(),
                 int(bindTime / 1000000),
@@ -750,7 +766,8 @@ void QSGPlainTexture::bind()
                 (externalFormat == GL_BGRA ? "BGRA" : "RGBA"),
                 (internalFormat == GL_BGRA ? "BGRA" : "RGBA"),
                 int((uploadTime - swizzleTime)/1000000),
-                int((mipmapTime - uploadTime)/1000000));
+                int((mipmapTime - uploadTime)/1000000),
+                m_texture_size != m_image.size() ? " (scaled to GL_MAX_TEXTURE_SIZE)" : "");
     }
 
     Q_QUICK_SG_PROFILE(QQuickProfiler::SceneGraphTexturePrepare, (
@@ -760,7 +777,6 @@ void QSGPlainTexture::bind()
             uploadTime - swizzleTime,
             qsg_renderer_timer.nsecsElapsed() - uploadTime));
 
-    m_texture_size = QSize(w, h);
     m_texture_rect = QRectF(0, 0, 1, 1);
 
     m_dirty_bind_options = false;
diff --git a/tests/auto/quick/qquickimage/data/hugeImages.qml b/tests/auto/quick/qquickimage/data/hugeImages.qml
new file mode 100644 (file)
index 0000000..f195299
--- /dev/null
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Item {
+    width: 200
+    height: 200
+
+    Image {
+        source: "toowide.png"
+        width: 100
+        height: 100
+    }
+
+    Image {
+        x: 100
+        source: "tootall.png"
+        width: 100
+        height: 100
+    }
+}
diff --git a/tests/auto/quick/qquickimage/data/tootall.png b/tests/auto/quick/qquickimage/data/tootall.png
new file mode 100644 (file)
index 0000000..98d0f18
Binary files /dev/null and b/tests/auto/quick/qquickimage/data/tootall.png differ
diff --git a/tests/auto/quick/qquickimage/data/toowide.png b/tests/auto/quick/qquickimage/data/toowide.png
new file mode 100644 (file)
index 0000000..4a0a92c
Binary files /dev/null and b/tests/auto/quick/qquickimage/data/toowide.png differ
index a68a763..3b70c68 100644 (file)
@@ -56,6 +56,7 @@
 #include <QtGui/QPainter>
 #include <QtGui/QImageReader>
 #include <QQuickWindow>
+#include <QQuickView>
 #include <QQuickImageProvider>
 
 #include "../../shared/util.h"
@@ -106,6 +107,7 @@ private slots:
     void sourceSizeChanges();
     void correctStatus();
     void highdpi();
+    void hugeImages();
 
 private:
     QQmlEngine engine;
@@ -969,6 +971,21 @@ void tst_qquickimage::highdpi()
     delete obj;
 }
 
+void tst_qquickimage::hugeImages()
+{
+    QQuickView view;
+    view.setSource(testFileUrl("hugeImages.qml"));
+    view.setGeometry(0, 0, 200, 200);
+    view.create();
+
+    QImage contents = view.grabWindow();
+
+    QCOMPARE(contents.pixel(0, 0), qRgba(255, 0, 0, 255));
+    QCOMPARE(contents.pixel(99, 99), qRgba(255, 0, 0, 255));
+    QCOMPARE(contents.pixel(100, 0), qRgba(0, 0, 255, 255));
+    QCOMPARE(contents.pixel(199, 99), qRgba(0, 0, 255, 255));
+}
+
 QTEST_MAIN(tst_qquickimage)
 
 #include "tst_qquickimage.moc"