From eae27f210bd7b811c9da59ea7723b0fb15e94abc Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Tue, 28 May 2024 14:04:21 -0700 Subject: [PATCH] [Impeller] make strokes slightly lighter. (flutter/engine#53067) We can set the minimum stroke width to 0.5 to guarantee at least one hit of 4x MSAA coverage. This doesn't fix stroke fidelity issues but it does make it a bit better. https://github.com/flutter/flutter/issues/144313 --- .../flutter/impeller/aiks/aiks_path_unittests.cc | 12 ++++++++++++ .../entity/geometry/stroke_path_geometry.cc | 15 +++++++++++++-- .../testing/impeller_golden_tests_output.txt | 3 +++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/engine/src/flutter/impeller/aiks/aiks_path_unittests.cc b/engine/src/flutter/impeller/aiks/aiks_path_unittests.cc index 0aa9ed99033..1cfb658d769 100644 --- a/engine/src/flutter/impeller/aiks/aiks_path_unittests.cc +++ b/engine/src/flutter/impeller/aiks/aiks_path_unittests.cc @@ -48,6 +48,18 @@ TEST_P(AiksTest, CanRenderThickCurvedStrokes) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_P(AiksTest, CanRenderThinCurvedStrokes) { + Canvas canvas; + Paint paint; + paint.color = Color::Red(); + // Impeller doesn't support hairlines yet, but size this guarantees + // the smallest possible stroke width. + paint.stroke_width = 0.01; + paint.style = Paint::Style::kStroke; + canvas.DrawPath(PathBuilder{}.AddCircle({100, 100}, 50).TakePath(), paint); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + TEST_P(AiksTest, CanRenderStrokePathThatEndsAtSharpTurn) { Canvas canvas; diff --git a/engine/src/flutter/impeller/entity/geometry/stroke_path_geometry.cc b/engine/src/flutter/impeller/entity/geometry/stroke_path_geometry.cc index 3ccf294cbb3..ba9a92bb9c1 100644 --- a/engine/src/flutter/impeller/entity/geometry/stroke_path_geometry.cc +++ b/engine/src/flutter/impeller/entity/geometry/stroke_path_geometry.cc @@ -15,6 +15,13 @@ using VS = SolidFillVertexShader; namespace { +/// @brief The minimum stroke size can be less than one physical pixel because +/// of MSAA, but no less that half a physical pixel otherwise we might +/// not hit one of the sample positions. +static constexpr Scalar kMinStrokeSizeMSAA = 0.5f; + +static constexpr Scalar kMinStrokeSize = 1.0f; + template using CapProc = std::function StrokePathGeometry::GetCoverage( if (determinant == 0) { return std::nullopt; } - Scalar min_size = 1.0f / sqrt(std::abs(determinant)); + // Use the most conervative coverage setting. + Scalar min_size = kMinStrokeSize / sqrt(std::abs(determinant)); max_radius *= std::max(stroke_width_, min_size); return path_bounds->Expand(max_radius).TransformBounds(transform); } diff --git a/engine/src/flutter/testing/impeller_golden_tests_output.txt b/engine/src/flutter/testing/impeller_golden_tests_output.txt index 8adb5b267b3..cee7f44d467 100644 --- a/engine/src/flutter/testing/impeller_golden_tests_output.txt +++ b/engine/src/flutter/testing/impeller_golden_tests_output.txt @@ -460,6 +460,9 @@ impeller_Play_AiksTest_CanRenderTextWithLargePerspectiveTransform_Vulkan.png impeller_Play_AiksTest_CanRenderThickCurvedStrokes_Metal.png impeller_Play_AiksTest_CanRenderThickCurvedStrokes_OpenGLES.png impeller_Play_AiksTest_CanRenderThickCurvedStrokes_Vulkan.png +impeller_Play_AiksTest_CanRenderThinCurvedStrokes_Metal.png +impeller_Play_AiksTest_CanRenderThinCurvedStrokes_OpenGLES.png +impeller_Play_AiksTest_CanRenderThinCurvedStrokes_Vulkan.png impeller_Play_AiksTest_CanRenderTiledTextureClampWithTranslate_Metal.png impeller_Play_AiksTest_CanRenderTiledTextureClampWithTranslate_OpenGLES.png impeller_Play_AiksTest_CanRenderTiledTextureClampWithTranslate_Vulkan.png