Add padding to distance field glyph cache
authorEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@nokia.com>
Thu, 6 Nov 2014 11:10:00 +0000 (12:10 +0100)
committerEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@theqtcompany.com>
Tue, 11 Nov 2014 13:30:26 +0000 (14:30 +0100)
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>
src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h

index 093e84b..3d95824 100644 (file)
@@ -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());
         }
index f731477..2f9331f 100644 (file)
@@ -73,8 +73,9 @@ private:
         QSize size;
         QRect allocatedArea;
         QDistanceField image;
+        int padding;
 
-        TextureInfo() : texture(0)
+        TextureInfo() : texture(0), padding(-1)
         { }
     };