From b5403efcb103423f44c707db4b07df0ff5c2203b Mon Sep 17 00:00:00 2001
From: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@nokia.com>
Date: Thu, 6 Nov 2014 12:10:00 +0100
Subject: [PATCH] Add padding to distance field glyph cache

Linear filtering can cause pixels outside the glyph's bounding rect
to be sampled. On drivers where uninitialized texture data is actually
uninitialized, this could cause artifacts in rendering for glyphs at
the right edge of the initiliazed area since they would sometimes sample
random pixels. To avoid this, we add padding between the glyphs in the
cache.

[ChangeLog][Qt Quick] Fixed uncommon artifacts in rendering of distance
field glyphs.

Task-number: QTBUG-42148
Change-Id: I6982b4a150d9459185d50a4362e1ead588d3860f
Reviewed-by: Yoann Lopes <yoann.lopes@theqtcompany.com>
---
 .../qsgdefaultdistancefieldglyphcache.cpp     | 28 +++++++++++++------
 .../qsgdefaultdistancefieldglyphcache_p.h     |  3 +-
 2 files changed, 22 insertions(+), 9 deletions(-)

diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
index 093e84b618..3d958245d2 100644
--- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
+++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
@@ -48,6 +48,10 @@ QT_BEGIN_NAMESPACE
 
 DEFINE_BOOL_CONFIG_OPTION(qmlUseGlyphCacheWorkaround, QML_USE_GLYPHCACHE_WORKAROUND)
 
+#if !defined(QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING)
+#  define QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING 2
+#endif
+
 QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font)
     : QSGDistanceFieldGlyphCache(man, c, font)
     , m_maxTextureSize(0)
@@ -90,8 +94,9 @@ void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyph
     for (QSet<glyph_t>::const_iterator it = glyphs.constBegin(); it != glyphs.constEnd() ; ++it) {
         glyph_t glyphIndex = *it;
 
+        int padding = QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING;
         int glyphWidth = qCeil(glyphData(glyphIndex).boundingRect.width()) + distanceFieldRadius() * 2;
-        QSize glyphSize(glyphWidth, QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution()));
+        QSize glyphSize(glyphWidth + padding * 2, QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution()) + padding * 2);
         QRect alloc = m_areaAllocator->allocate(glyphSize);
 
         if (alloc.isNull()) {
@@ -101,7 +106,10 @@ void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyph
 
                 TexCoord unusedCoord = glyphTexCoord(unusedGlyph);
                 int unusedGlyphWidth = qCeil(glyphData(unusedGlyph).boundingRect.width()) + distanceFieldRadius() * 2;
-                m_areaAllocator->deallocate(QRect(unusedCoord.x, unusedCoord.y, unusedGlyphWidth, QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution())));
+                m_areaAllocator->deallocate(QRect(unusedCoord.x - padding,
+                                                  unusedCoord.y - padding,
+                                                  padding * 2 + unusedGlyphWidth,
+                                                  padding * 2 + QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution())));
 
                 m_unusedGlyphs.remove(unusedGlyph);
                 m_glyphsTexture.remove(unusedGlyph);
@@ -117,11 +125,14 @@ void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyph
 
         TextureInfo *tex = textureInfo(alloc.y() / maxTextureSize());
         alloc = QRect(alloc.x(), alloc.y() % maxTextureSize(), alloc.width(), alloc.height());
+
         tex->allocatedArea |= alloc;
+        Q_ASSERT(tex->padding == padding || tex->padding < 0);
+        tex->padding = padding;
 
         GlyphPosition p;
         p.glyph = glyphIndex;
-        p.position = alloc.topLeft();
+        p.position = alloc.topLeft() + QPoint(padding, padding);
 
         glyphPositions.append(p);
         glyphsToRender.append(glyphIndex);
@@ -153,13 +164,14 @@ void QSGDefaultDistanceFieldGlyphCache::storeGlyphs(const QList<QDistanceField>
 
         glyphTextures[texInfo].append(glyphIndex);
 
+        int padding = texInfo->padding;
         int expectedWidth = qCeil(c.width + c.xMargin * 2);
-        if (glyph.width() != expectedWidth)
-            glyph = glyph.copy(0, 0, expectedWidth, glyph.height());
+        glyph = glyph.copy(-padding, -padding,
+                           expectedWidth + padding  * 2, glyph.height() + padding * 2);
 
         if (useTextureResizeWorkaround()) {
             uchar *inBits = glyph.scanLine(0);
-            uchar *outBits = texInfo->image.scanLine(int(c.y)) + int(c.x);
+            uchar *outBits = texInfo->image.scanLine(int(c.y) - padding) + int(c.x) - padding;
             for (int y = 0; y < glyph.height(); ++y) {
                 memcpy(outBits, inBits, glyph.width());
                 inBits += glyph.width();
@@ -175,13 +187,13 @@ void QSGDefaultDistanceFieldGlyphCache::storeGlyphs(const QList<QDistanceField>
         if (useTextureUploadWorkaround()) {
             for (int i = 0; i < glyph.height(); ++i) {
                 m_funcs->glTexSubImage2D(GL_TEXTURE_2D, 0,
-                                         c.x, c.y + i, glyph.width(),1,
+                                         c.x - padding, c.y + i - padding, glyph.width(),1,
                                          format, GL_UNSIGNED_BYTE,
                                          glyph.scanLine(i));
             }
         } else {
             m_funcs->glTexSubImage2D(GL_TEXTURE_2D, 0,
-                                     c.x, c.y, glyph.width(), glyph.height(),
+                                     c.x - padding, c.y - padding, glyph.width(), glyph.height(),
                                      format, GL_UNSIGNED_BYTE,
                                      glyph.constBits());
         }
diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h
index f7314776d6..2f9331f6d8 100644
--- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h
+++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h
@@ -73,8 +73,9 @@ private:
         QSize size;
         QRect allocatedArea;
         QDistanceField image;
+        int padding;
 
-        TextureInfo() : texture(0)
+        TextureInfo() : texture(0), padding(-1)
         { }
     };
 
-- 
GitLab