diff --git a/impeller/entity/contents/text_contents.cc b/impeller/entity/contents/text_contents.cc index 1aafcd4ae360e..e6d0d7ac54abe 100644 --- a/impeller/entity/contents/text_contents.cc +++ b/impeller/entity/contents/text_contents.cc @@ -78,8 +78,18 @@ static bool CommonRender( VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); SamplerDescriptor sampler_desc; - sampler_desc.min_filter = MinMagFilter::kLinear; - sampler_desc.mag_filter = MinMagFilter::kLinear; + if (entity.GetTransformation().IsTranslationScaleOnly()) { + sampler_desc.min_filter = MinMagFilter::kNearest; + sampler_desc.mag_filter = MinMagFilter::kNearest; + } else { + // Currently, we only propagate the scale of the transform to the atlas + // renderer, so if the transform has more than just a translation, we turn + // on linear sampling to prevent crunchiness caused by the pixel grid not + // being perfectly aligned. + // The downside is that this slightly over-blurs rotated/skewed text. + sampler_desc.min_filter = MinMagFilter::kLinear; + sampler_desc.mag_filter = MinMagFilter::kLinear; + } sampler_desc.mip_filter = MipFilter::kNone; typename FS::FragInfo frag_info; @@ -146,10 +156,10 @@ static bool CommonRender( for (const auto& point : unit_points) { typename VS::PerVertexData vtx; vtx.unit_position = point; - vtx.destination_position = offset_glyph_position + Point(0.5, 0.5); + vtx.destination_position = offset_glyph_position; vtx.destination_size = Point(glyph_position.glyph.bounds.size); - vtx.source_position = atlas_position + Point(0.5, 0.5); - vtx.source_glyph_size = atlas_glyph_size - Point(1.0, 1.0); + vtx.source_position = atlas_position; + vtx.source_glyph_size = atlas_glyph_size; if constexpr (std::is_same_v) { vtx.has_color = glyph_position.glyph.type == Glyph::Type::kBitmap ? 1.0 : 0.0; diff --git a/impeller/entity/shaders/glyph_atlas.frag b/impeller/entity/shaders/glyph_atlas.frag index 007748704f1d7..acfed85a3204b 100644 --- a/impeller/entity/shaders/glyph_atlas.frag +++ b/impeller/entity/shaders/glyph_atlas.frag @@ -21,13 +21,14 @@ out vec4 frag_color; void main() { vec2 uv_size = v_source_glyph_size / frag_info.atlas_size; - vec2 offset = v_source_position / frag_info.atlas_size; + vec2 uv_position = v_source_position / frag_info.atlas_size; if (v_has_color == 1.0) { frag_color = - texture(glyph_atlas_sampler, v_unit_position * uv_size + offset); + texture(glyph_atlas_sampler, v_unit_position * uv_size + uv_position); } else { frag_color = - texture(glyph_atlas_sampler, v_unit_position * uv_size + offset).aaaa * + texture(glyph_atlas_sampler, v_unit_position * uv_size + uv_position) + .aaaa * frag_info.text_color; } } diff --git a/impeller/entity/shaders/glyph_atlas.vert b/impeller/entity/shaders/glyph_atlas.vert index bd527655c8984..514f0b67dbb34 100644 --- a/impeller/entity/shaders/glyph_atlas.vert +++ b/impeller/entity/shaders/glyph_atlas.vert @@ -26,7 +26,8 @@ void main() { gl_Position = IPPositionForGlyphPosition( frame_info.mvp, unit_position, destination_position, destination_size); v_unit_position = unit_position; - v_source_position = source_position; + // Pixel snap the source (sampling) start position. + v_source_position = round(source_position); v_source_glyph_size = source_glyph_size; v_has_color = has_color; } diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 72d09f1ab5b21..2d40b7eac8ed7 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -424,6 +424,32 @@ TEST(GeometryTest, MatrixIsAligned) { } } +TEST(GeometryTest, MatrixTranslationScaleOnly) { + { + auto m = Matrix(); + bool result = m.IsTranslationScaleOnly(); + ASSERT_TRUE(result); + } + + { + auto m = Matrix::MakeScale(Vector3(2, 3, 4)); + bool result = m.IsTranslationScaleOnly(); + ASSERT_TRUE(result); + } + + { + auto m = Matrix::MakeTranslation(Vector3(2, 3, 4)); + bool result = m.IsTranslationScaleOnly(); + ASSERT_TRUE(result); + } + + { + auto m = Matrix::MakeRotationZ(Degrees(10)); + bool result = m.IsTranslationScaleOnly(); + ASSERT_FALSE(result); + } +} + TEST(GeometryTest, MatrixLookAt) { { auto m = Matrix::MakeLookAt(Vector3(0, 0, -1), Vector3(0, 0, 1), diff --git a/impeller/geometry/matrix.h b/impeller/geometry/matrix.h index 2d50f3045007b..30d6636f9af79 100644 --- a/impeller/geometry/matrix.h +++ b/impeller/geometry/matrix.h @@ -322,6 +322,19 @@ struct Matrix { ); } + /// @brief Returns true if the matrix has a scale-only basis and is + /// non-projective. Note that an identity matrix meets this criteria. + constexpr bool IsTranslationScaleOnly() const { + return ( + // clang-format off + m[0] != 0.0 && m[1] == 0.0 && m[2] == 0.0 && m[3] == 0.0 && + m[4] == 0.0 && m[5] != 0.0 && m[6] == 0.0 && m[7] == 0.0 && + m[8] == 0.0 && m[9] == 0.0 && m[10] != 0.0 && m[11] == 0.0 && + m[15] == 1.0 + // clang-format on + ); + } + std::optional Decompose() const; constexpr bool operator==(const Matrix& m) const {