From 260685d1ee170a9160b4538e2d20ed606263922f Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Tue, 18 Jun 2024 08:47:25 +0200 Subject: [PATCH] Make it possible to create distance field with specific size The distance field size calculation is floating point math and sensitive to numerical errors. When used in Qt Quick, it is vital that the allocated memory is the same as what we calculate in the cache, otherwise we will get memory corruption. In certain cases, numerical errors would cause a qCeil() to round what was actually an integer up, and we would end up with an allocated size that was off by one. To avoid this, we allow the user to specify the size to allocate, so that we can do the calculation in a single place. Pick-to: 6.7 6.5 6.2 5.15 Task-number: QTBUG-124572 Change-Id: I14e381952fdf331286f1ae4b51bb5fef9e39d704 Reviewed-by: Eirik Aavitsland (cherry picked from commit b959b8f5ed537f530221e0174b723ea39a34cd58) Reviewed-by: Qt Cherry-pick Bot --- src/gui/text/qdistancefield.cpp | 29 ++++++++++++++++++++++------- src/gui/text/qdistancefield_p.h | 2 ++ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/gui/text/qdistancefield.cpp b/src/gui/text/qdistancefield.cpp index fe230188c66..33d3589a3ed 100644 --- a/src/gui/text/qdistancefield.cpp +++ b/src/gui/text/qdistancefield.cpp @@ -839,19 +839,24 @@ QDistanceFieldData *QDistanceFieldData::create(const QSize &size) return data; } +QDistanceFieldData *QDistanceFieldData::create(QSize size, const QPainterPath &path, bool doubleResolution) +{ + QDistanceFieldData *data = create(size); + makeDistanceField(data, + path, + QT_DISTANCEFIELD_SCALE(doubleResolution), + QT_DISTANCEFIELD_RADIUS(doubleResolution) / QT_DISTANCEFIELD_SCALE(doubleResolution)); + return data; +} + + QDistanceFieldData *QDistanceFieldData::create(const QPainterPath &path, bool doubleResolution) { int dfMargin = QT_DISTANCEFIELD_RADIUS(doubleResolution) / QT_DISTANCEFIELD_SCALE(doubleResolution); int glyphWidth = qCeil(path.boundingRect().width() / QT_DISTANCEFIELD_SCALE(doubleResolution)) + dfMargin * 2; int glyphHeight = qCeil(path.boundingRect().height() / QT_DISTANCEFIELD_SCALE(doubleResolution)) + dfMargin * 2; - QDistanceFieldData *data = create(QSize(glyphWidth, glyphHeight)); - - makeDistanceField(data, - path, - QT_DISTANCEFIELD_SCALE(doubleResolution), - QT_DISTANCEFIELD_RADIUS(doubleResolution) / QT_DISTANCEFIELD_SCALE(doubleResolution)); - return data; + return create(QSize(glyphWidth, glyphHeight), path, doubleResolution); } @@ -875,6 +880,16 @@ QDistanceField::QDistanceField(QFontEngine *fontEngine, glyph_t glyph, bool doub setGlyph(fontEngine, glyph, doubleResolution); } +QDistanceField::QDistanceField(QSize size, const QPainterPath &path, glyph_t glyph, bool doubleResolution) +{ + QPainterPath dfPath = path; + dfPath.translate(-dfPath.boundingRect().topLeft()); + dfPath.setFillRule(Qt::WindingFill); + + d = QDistanceFieldData::create(size, dfPath, doubleResolution); + d->glyph = glyph; +} + QDistanceField::QDistanceField(const QPainterPath &path, glyph_t glyph, bool doubleResolution) { QPainterPath dfPath = path; diff --git a/src/gui/text/qdistancefield_p.h b/src/gui/text/qdistancefield_p.h index dbc7a0abb5c..ec00e5b0f8e 100644 --- a/src/gui/text/qdistancefield_p.h +++ b/src/gui/text/qdistancefield_p.h @@ -42,6 +42,7 @@ public: static QDistanceFieldData *create(const QSize &size); static QDistanceFieldData *create(const QPainterPath &path, bool doubleResolution); + static QDistanceFieldData *create(QSize size, const QPainterPath &path, bool doubleResolution); glyph_t glyph; int width; @@ -58,6 +59,7 @@ public: QDistanceField(const QRawFont &font, glyph_t glyph, bool doubleResolution = false); QDistanceField(QFontEngine *fontEngine, glyph_t glyph, bool doubleResolution = false); QDistanceField(const QPainterPath &path, glyph_t glyph, bool doubleResolution = false); + QDistanceField(QSize size, const QPainterPath &path, glyph_t glyph, bool doubleResolution = false); bool isNull() const;