diff options
| author | Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io> | 2026-09-22 13:05:35 +0200 |
|---|---|---|
| committer | Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io> | 2026-09-28 09:11:38 +0000 |
| commit | c65fd911632dae6b9d3ffc10c06df2368006dd66 (patch) | |
| tree | f2d8944aff668885d354c4d982ad0ce6ed66bfbb /tests | |
| parent | 37af73c76b7f99202538916cb19d5834c6bda605 (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.ttf | bin | 0 -> 32896 bytes | |||
| -rw-r--r-- | tests/auto/quick/scenegraph/data/neg_single.ttf | bin | 0 -> 32876 bytes | |||
| -rw-r--r-- | tests/auto/quick/scenegraph/data/ok_double.ttf | bin | 0 -> 32896 bytes | |||
| -rw-r--r-- | tests/auto/quick/scenegraph/data/ok_single.ttf | bin | 0 -> 28780 bytes | |||
| -rw-r--r-- | tests/auto/quick/scenegraph/data/pregeneratedDistanceFieldCache.qml | 21 | ||||
| -rw-r--r-- | tests/auto/quick/scenegraph/data/qtdf_invalidgeometry_5_12.ttf | bin | 0 -> 32492 bytes | |||
| -rw-r--r-- | tests/auto/quick/scenegraph/data/qtdf_multitexture_5_12.ttf | bin | 0 -> 32476 bytes | |||
| -rw-r--r-- | tests/auto/quick/scenegraph/data/wild.ttf | bin | 0 -> 32896 bytes | |||
| -rw-r--r-- | tests/auto/quick/scenegraph/tst_scenegraph.cpp | 73 |
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 Binary files differnew file mode 100644 index 0000000000..88e80a10c5 --- /dev/null +++ b/tests/auto/quick/scenegraph/data/neg_double.ttf diff --git a/tests/auto/quick/scenegraph/data/neg_single.ttf b/tests/auto/quick/scenegraph/data/neg_single.ttf Binary files differnew file mode 100644 index 0000000000..96d1861ad5 --- /dev/null +++ b/tests/auto/quick/scenegraph/data/neg_single.ttf diff --git a/tests/auto/quick/scenegraph/data/ok_double.ttf b/tests/auto/quick/scenegraph/data/ok_double.ttf Binary files differnew file mode 100644 index 0000000000..d95da04c1c --- /dev/null +++ b/tests/auto/quick/scenegraph/data/ok_double.ttf diff --git a/tests/auto/quick/scenegraph/data/ok_single.ttf b/tests/auto/quick/scenegraph/data/ok_single.ttf Binary files differnew file mode 100644 index 0000000000..7f16fcdb64 --- /dev/null +++ b/tests/auto/quick/scenegraph/data/ok_single.ttf 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 Binary files differnew file mode 100644 index 0000000000..57159c0c72 --- /dev/null +++ b/tests/auto/quick/scenegraph/data/qtdf_invalidgeometry_5_12.ttf diff --git a/tests/auto/quick/scenegraph/data/qtdf_multitexture_5_12.ttf b/tests/auto/quick/scenegraph/data/qtdf_multitexture_5_12.ttf Binary files differnew file mode 100644 index 0000000000..e9f336b5a5 --- /dev/null +++ b/tests/auto/quick/scenegraph/data/qtdf_multitexture_5_12.ttf diff --git a/tests/auto/quick/scenegraph/data/wild.ttf b/tests/auto/quick/scenegraph/data/wild.ttf Binary files differnew file mode 100644 index 0000000000..b2c92fbffa --- /dev/null +++ b/tests/auto/quick/scenegraph/data/wild.ttf 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 |
