From 9a9ad483a3e8b51f60c9bce46c99a030bf0b8a10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 17 Oct 2023 19:16:41 +0200 Subject: [PATCH] Text: round the flushed DistanceFieldGlyphCache image range. To a multiple of the input/output size ratio to satisfy the TextureTools::DistanceField assertion and to avoid potential strange misalignments. --- src/Magnum/Text/DistanceFieldGlyphCache.cpp | 34 ++++++++++++++----- .../Test/DistanceFieldGlyphCacheGLTest.cpp | 7 ++++ 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/Magnum/Text/DistanceFieldGlyphCache.cpp b/src/Magnum/Text/DistanceFieldGlyphCache.cpp index c79b3a7d2..02d0fa516 100644 --- a/src/Magnum/Text/DistanceFieldGlyphCache.cpp +++ b/src/Magnum/Text/DistanceFieldGlyphCache.cpp @@ -83,11 +83,14 @@ void DistanceFieldGlyphCache::doSetImage(const Vector2i& offset, const ImageView .setMinificationFilter(GL::SamplerFilter::Linear) .setMagnificationFilter(GL::SamplerFilter::Linear); - /* Upload the input texture and create a distance field from it */ - const Vector2 scale = Vector2{_size}/Vector2{size().xy()}; - - /* On ES2 without EXT_unpack_subimage and on WebGL 1 there's no possibility - to upload just a slice of the input, upload the whole image instead by + /* The constructor already checked that the ratio is an integer multiple, + so this division should lead to no information loss */ + CORRADE_INTERNAL_ASSERT(size().xy() % _size == Vector2i{0}); + const Vector2i ratio = size().xy()/_size; + + /* Upload the input texture and create a distance field from it. On ES2 + without EXT_unpack_subimage and on WebGL 1 there's no possibility to + upload just a slice of the input, upload the whole image instead by ignoring the PixelStorage properties of the input and also process it as a whole. */ #ifdef MAGNUM_TARGET_GLES2 @@ -96,7 +99,7 @@ void DistanceFieldGlyphCache::doSetImage(const Vector2i& offset, const ImageView #endif { input.setImage(0, GL::textureFormat(image.format()), ImageView2D{image.format(), size().xy(), image.data()}); - _distanceField(input, texture(), {{}, size().xy()*scale}, size().xy()); + _distanceField(input, texture(), {{}, size().xy()/ratio}, size().xy()); #ifdef MAGNUM_TARGET_WEBGL static_cast(offset); #endif @@ -122,15 +125,28 @@ void DistanceFieldGlyphCache::doSetImage(const Vector2i& offset, const ImageView image.storage().skip().xy() - padding()); const Vector2i paddedMax = Math::min(size().xy(), image.size() + image.storage().skip().xy() + padding()); + + /* TextureTools::DistanceField expects the input size and output + rectangle size ratio to be a multiple of 2 in order for the shader + to perform pixel addressing correctly. That might not always be the + case with the rectangle passed to flushImage(), so round the + paddedMin *down* to a multiple of the ratio and paddedMax *up* to a + multiple of the ratio. */ + const Vector2i paddedMinRounded = ratio*(paddedMin/ratio); + const Vector2i paddedMaxRounded = ratio*((paddedMax + ratio - Vector2i{1})/ratio); + /* As the size is also a multiple of ratio, the resulting size should + not get larger. */ + CORRADE_INTERNAL_ASSERT(paddedMaxRounded <= size().xy()); + const ImageView2D paddedImage{ PixelStorage{image.storage()} - .setSkip({paddedMin, image.storage().skip().z()}), + .setSkip({paddedMinRounded, image.storage().skip().z()}), image.format(), - paddedMax - paddedMin, + paddedMaxRounded - paddedMinRounded, image.data()}; input.setImage(0, GL::textureFormat(paddedImage.format()), paddedImage); - _distanceField(input, texture(), Range2Di::fromSize(paddedMin*scale, paddedImage.size()*scale), paddedImage.size()); + _distanceField(input, texture(), Range2Di::fromSize(paddedMinRounded/ratio, paddedImage.size()/ratio), paddedImage.size()); } #endif } diff --git a/src/Magnum/Text/Test/DistanceFieldGlyphCacheGLTest.cpp b/src/Magnum/Text/Test/DistanceFieldGlyphCacheGLTest.cpp index 1f496fd25..b2993e99c 100644 --- a/src/Magnum/Text/Test/DistanceFieldGlyphCacheGLTest.cpp +++ b/src/Magnum/Text/Test/DistanceFieldGlyphCacheGLTest.cpp @@ -90,6 +90,13 @@ const struct { distance field algorithm as if the whole image was processed. */ {{48, 48}, {208, 208}}, {}}, + {"tight flush rectangle, ratio not a multiple of 2", + /* Like above, but the flush range isn't satisfying the "multiple of 2" + assertion and the code needs to round it to a larger rectangle that + satisfies it */ + {256, 256}, {64, 64}, {}, + {{47, 48}, {208, 209}}, + {}}, }; DistanceFieldGlyphCacheGLTest::DistanceFieldGlyphCacheGLTest() {