aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>2026-09-22 13:05:35 +0200
committerEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>2026-09-28 09:11:38 +0000
commitc65fd911632dae6b9d3ffc10c06df2368006dd66 (patch)
treef2d8944aff668885d354c4d982ad0ce6ed66bfbb /tests
parent37af73c76b7f99202538916cb19d5834c6bda605 (diff)
Fix bug when reading qtdf files with multiple textures
If the pre-generated distance field cache spans multiple textures, then the bottom most glyphs will expand beyond what is reported as the maximum texture size, due to a hot fix made for QTBUG-76528. When fixing QTBUG-149508, a stronger restriction was added on the glyph rects, ensuring that they do not expand beyond the texture. This was to protect against malformed qtdf tables and the main purpose was to keep the values from overflowing into negative numbers. Checking the height against maxSize * 2 instead ensures that we also accept glyphs that expand beyond the texture size while keeping the fix for QTBUG-149508 for any reasonable texture size. This adds tests for both QTBUG-149508 and QTBUG-150766 to ensure both problems are covered. Fixes: QTBUG-150766 Change-Id: I59e793b6bee32f83999f597c92275ddbdba1bd38 Reviewed-by: Andy Nichols <andy.nichols@qt.io> (cherry picked from commit c2a8d0ff939122a5c1e8e6c5c59dae53c7b30389)
Diffstat (limited to 'tests')
-rw-r--r--tests/auto/quick/scenegraph/data/neg_double.ttfbin0 -> 32896 bytes
-rw-r--r--tests/auto/quick/scenegraph/data/neg_single.ttfbin0 -> 32876 bytes
-rw-r--r--tests/auto/quick/scenegraph/data/ok_double.ttfbin0 -> 32896 bytes
-rw-r--r--tests/auto/quick/scenegraph/data/ok_single.ttfbin0 -> 28780 bytes
-rw-r--r--tests/auto/quick/scenegraph/data/pregeneratedDistanceFieldCache.qml21
-rw-r--r--tests/auto/quick/scenegraph/data/qtdf_invalidgeometry_5_12.ttfbin0 -> 32492 bytes
-rw-r--r--tests/auto/quick/scenegraph/data/qtdf_multitexture_5_12.ttfbin0 -> 32476 bytes
-rw-r--r--tests/auto/quick/scenegraph/data/wild.ttfbin0 -> 32896 bytes
-rw-r--r--tests/auto/quick/scenegraph/tst_scenegraph.cpp73
9 files changed, 94 insertions, 0 deletions
diff --git a/tests/auto/quick/scenegraph/data/neg_double.ttf b/tests/auto/quick/scenegraph/data/neg_double.ttf
new file mode 100644
index 0000000000..88e80a10c5
--- /dev/null
+++ b/tests/auto/quick/scenegraph/data/neg_double.ttf
Binary files differ
diff --git a/tests/auto/quick/scenegraph/data/neg_single.ttf b/tests/auto/quick/scenegraph/data/neg_single.ttf
new file mode 100644
index 0000000000..96d1861ad5
--- /dev/null
+++ b/tests/auto/quick/scenegraph/data/neg_single.ttf
Binary files differ
diff --git a/tests/auto/quick/scenegraph/data/ok_double.ttf b/tests/auto/quick/scenegraph/data/ok_double.ttf
new file mode 100644
index 0000000000..d95da04c1c
--- /dev/null
+++ b/tests/auto/quick/scenegraph/data/ok_double.ttf
Binary files differ
diff --git a/tests/auto/quick/scenegraph/data/ok_single.ttf b/tests/auto/quick/scenegraph/data/ok_single.ttf
new file mode 100644
index 0000000000..7f16fcdb64
--- /dev/null
+++ b/tests/auto/quick/scenegraph/data/ok_single.ttf
Binary files differ
diff --git a/tests/auto/quick/scenegraph/data/pregeneratedDistanceFieldCache.qml b/tests/auto/quick/scenegraph/data/pregeneratedDistanceFieldCache.qml
new file mode 100644
index 0000000000..d7b72a2849
--- /dev/null
+++ b/tests/auto/quick/scenegraph/data/pregeneratedDistanceFieldCache.qml
@@ -0,0 +1,21 @@
+// Copyright (C) 2026 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Rectangle {
+ width: 320
+ height: 200
+ color: "white"
+
+ property alias fontFamily: text.font.family
+
+ Text {
+ id: text
+ anchors.centerIn: parent
+ renderType: Text.QtRendering
+ text: "ABC"
+ color: "black"
+ font.pixelSize: 64
+ }
+}
diff --git a/tests/auto/quick/scenegraph/data/qtdf_invalidgeometry_5_12.ttf b/tests/auto/quick/scenegraph/data/qtdf_invalidgeometry_5_12.ttf
new file mode 100644
index 0000000000..57159c0c72
--- /dev/null
+++ b/tests/auto/quick/scenegraph/data/qtdf_invalidgeometry_5_12.ttf
Binary files differ
diff --git a/tests/auto/quick/scenegraph/data/qtdf_multitexture_5_12.ttf b/tests/auto/quick/scenegraph/data/qtdf_multitexture_5_12.ttf
new file mode 100644
index 0000000000..e9f336b5a5
--- /dev/null
+++ b/tests/auto/quick/scenegraph/data/qtdf_multitexture_5_12.ttf
Binary files differ
diff --git a/tests/auto/quick/scenegraph/data/wild.ttf b/tests/auto/quick/scenegraph/data/wild.ttf
new file mode 100644
index 0000000000..b2c92fbffa
--- /dev/null
+++ b/tests/auto/quick/scenegraph/data/wild.ttf
Binary files differ
diff --git a/tests/auto/quick/scenegraph/tst_scenegraph.cpp b/tests/auto/quick/scenegraph/tst_scenegraph.cpp
index 4e0ebc2877..e32733fd4a 100644
--- a/tests/auto/quick/scenegraph/tst_scenegraph.cpp
+++ b/tests/auto/quick/scenegraph/tst_scenegraph.cpp
@@ -108,6 +108,8 @@ private slots:
void resizeTextureFromImage();
void textureNativeInterface();
void distanceFieldCacheInvalidation();
+ void pregeneratedDistanceFieldCache_data();
+ void pregeneratedDistanceFieldCache();
void unexposeDuringPolish();
#ifdef QT_BUILD_INTERNAL
@@ -909,6 +911,77 @@ void tst_SceneGraph::distanceFieldCacheInvalidation()
}
}
+void tst_SceneGraph::pregeneratedDistanceFieldCache_data()
+{
+ QTest::addColumn<QString>("fontFileName");
+ QTest::addColumn<bool>("expectLoaded");
+
+ // Various tests for
+ QTest::newRow("control, one 64x64 texture") << QStringLiteral("ok_single.ttf") << true;
+ QTest::newRow("control, two 64x64 textures") << QStringLiteral("ok_double.ttf") << true;
+ QTest::newRow("control, two 64x64 textures") << QStringLiteral("ok_double.ttf") << true;
+ QTest::newRow("allocatedWidth -1, height 256 -> size -256") << QStringLiteral("neg_single.ttf") << true;
+ QTest::newRow("allocatedWidth -1, height 1024 -> size -1024, pointer slides back 1 KB") << QStringLiteral("neg_double.ttf") << true;
+ QTest::newRow("allocatedWidth INT_MIN -> size -2147483648") << QStringLiteral("wild.ttf") << true;
+
+ // In the 5.12 version of the qtdf format, the texture size in the header
+ // is the slice height of the stacked area allocator, and the texture
+ // holding a glyph that crosses a slice boundary is taller than this.
+ QTest::newRow("multiple textures, version 5.12")
+ << QStringLiteral("qtdf_multitexture_5_12.ttf") << true;
+
+ // QTBUG-149508: an allocated height with the high bit set becomes
+ // negative when narrowed to int, defeating the payload bounds check.
+ // Such geometry must be rejected.
+ QTest::newRow("invalid geometry, version 5.12")
+ << QStringLiteral("qtdf_invalidgeometry_5_12.ttf") << false;
+}
+
+void tst_SceneGraph::pregeneratedDistanceFieldCache()
+{
+ if (!isRunningOnRhi())
+ QSKIP("Skipping complex rendering tests due to not running with QRhi");
+
+ QFETCH(QString, fontFileName);
+ QFETCH(bool, expectLoaded);
+
+ const int fontId = QFontDatabase::addApplicationFont(testFile(fontFileName));
+ QVERIFY(fontId >= 0);
+ const auto cleanup = qScopeGuard([fontId] { QFontDatabase::removeApplicationFont(fontId); });
+
+ const QStringList families = QFontDatabase::applicationFontFamilies(fontId);
+ QVERIFY(!families.isEmpty());
+
+ // Verify that the font resolves and carries a pregenerated cache, so that
+ // the text below cannot accidentally pass by using another font or by
+ // generating distance fields dynamically.
+ QCOMPARE(QFontInfo(QFont(families.first())).family(), families.first());
+ {
+ const QRawFont rawFont(testFile(fontFileName), 32.0);
+ QVERIFY(rawFont.isValid());
+ QVERIFY(!rawFont.fontTable("qtdf").isEmpty());
+ }
+
+ // The scene renders "ABC" with the given font. The pregenerated cache
+ // contains the glyphs for "A" and "B" in the first texture, where "B"
+ // crosses the boundary into the next slice, and "C" in the second
+ // texture. The distance field data marks the glyphs' texture rects as
+ // fully inside, so if (and only if) the pregenerated cache is loaded, the
+ // glyphs are rendered as solid boxes.
+ QQuickView view;
+ view.setInitialProperties({{ QStringLiteral("fontFamily"), families.first() }});
+ view.setSource(testFileUrl(QLatin1String("pregeneratedDistanceFieldCache.qml")));
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+
+ const QImage content = view.grabWindow();
+ QVERIFY(!content.isNull());
+ if (expectLoaded)
+ QVERIFY(containsSomethingOtherThanWhite(content));
+ else
+ QVERIFY(!containsSomethingOtherThanWhite(content));
+}
+
class NotificationItem : public QQuickItem
{
Q_OBJECT