From 4f43d91decbec9cf5118c1b7ab8b948810fc3692 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Fri, 31 May 2024 13:04:05 -0700 Subject: [PATCH] [Impeller] make strokes < 0.5 physical pixels visually thinner. --- .../contents/conical_gradient_contents.cc | 10 ++++++---- .../contents/linear_gradient_contents.cc | 11 +++++++---- .../contents/radial_gradient_contents.cc | 10 ++++++---- .../entity/contents/solid_color_contents.cc | 3 ++- .../entity/contents/sweep_gradient_contents.cc | 10 ++++++---- .../entity/contents/tiled_texture_contents.cc | 8 +++++--- impeller/entity/geometry/geometry.h | 4 ++++ impeller/entity/geometry/geometry_unittests.cc | 18 ++++++++++++++++++ .../entity/geometry/stroke_path_geometry.cc | 15 +++++++++++++-- .../entity/geometry/stroke_path_geometry.h | 2 ++ 10 files changed, 69 insertions(+), 22 deletions(-) diff --git a/impeller/entity/contents/conical_gradient_contents.cc b/impeller/entity/contents/conical_gradient_contents.cc index 54bbaf6230781..691995bf07860 100644 --- a/impeller/entity/contents/conical_gradient_contents.cc +++ b/impeller/entity/contents/conical_gradient_contents.cc @@ -73,13 +73,14 @@ bool ConicalGradientContents::RenderSSBO(const ContentContext& renderer, }; return ColorSourceContents::DrawGeometry( renderer, entity, pass, pipeline_callback, frame_info, - [this, &renderer](RenderPass& pass) { + [this, &renderer, &entity](RenderPass& pass) { FS::FragInfo frag_info; frag_info.center = center_; frag_info.radius = radius_; frag_info.tile_mode = static_cast(tile_mode_); frag_info.decal_border_color = decal_border_color_; - frag_info.alpha = GetOpacityFactor(); + frag_info.alpha = + GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity); if (focus_) { frag_info.focus = focus_.value(); frag_info.focus_radius = focus_radius_; @@ -127,7 +128,7 @@ bool ConicalGradientContents::RenderTexture(const ContentContext& renderer, }; return ColorSourceContents::DrawGeometry( renderer, entity, pass, pipeline_callback, frame_info, - [this, &renderer, &gradient_texture](RenderPass& pass) { + [this, &renderer, &gradient_texture, &entity](RenderPass& pass) { FS::FragInfo frag_info; frag_info.center = center_; frag_info.radius = radius_; @@ -135,7 +136,8 @@ bool ConicalGradientContents::RenderTexture(const ContentContext& renderer, frag_info.decal_border_color = decal_border_color_; frag_info.texture_sampler_y_coord_scale = gradient_texture->GetYCoordScale(); - frag_info.alpha = GetOpacityFactor(); + frag_info.alpha = + GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity); frag_info.half_texel = Vector2(0.5 / gradient_texture->GetSize().width, 0.5 / gradient_texture->GetSize().height); diff --git a/impeller/entity/contents/linear_gradient_contents.cc b/impeller/entity/contents/linear_gradient_contents.cc index 891f643998101..a61185654e9e2 100644 --- a/impeller/entity/contents/linear_gradient_contents.cc +++ b/impeller/entity/contents/linear_gradient_contents.cc @@ -77,7 +77,7 @@ bool LinearGradientContents::RenderTexture(const ContentContext& renderer, }; return ColorSourceContents::DrawGeometry( renderer, entity, pass, pipeline_callback, frame_info, - [this, &renderer](RenderPass& pass) { + [this, &renderer, &entity](RenderPass& pass) { auto gradient_data = CreateGradientBuffer(colors_, stops_); auto gradient_texture = CreateGradientTexture(gradient_data, renderer.GetContext()); @@ -92,7 +92,9 @@ bool LinearGradientContents::RenderTexture(const ContentContext& renderer, frag_info.decal_border_color = decal_border_color_; frag_info.texture_sampler_y_coord_scale = gradient_texture->GetYCoordScale(); - frag_info.alpha = GetOpacityFactor(); + frag_info.alpha = + GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity); + ; frag_info.half_texel = Vector2(0.5 / gradient_texture->GetSize().width, 0.5 / gradient_texture->GetSize().height); @@ -137,13 +139,14 @@ bool LinearGradientContents::RenderSSBO(const ContentContext& renderer, }; return ColorSourceContents::DrawGeometry( renderer, entity, pass, pipeline_callback, frame_info, - [this, &renderer](RenderPass& pass) { + [this, &renderer, &entity](RenderPass& pass) { FS::FragInfo frag_info; frag_info.start_point = start_point_; frag_info.end_point = end_point_; frag_info.tile_mode = static_cast(tile_mode_); frag_info.decal_border_color = decal_border_color_; - frag_info.alpha = GetOpacityFactor(); + frag_info.alpha = + GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity); frag_info.start_to_end = end_point_ - start_point_; frag_info.inverse_dot_start_to_end = CalculateInverseDotStartToEnd(start_point_, end_point_); diff --git a/impeller/entity/contents/radial_gradient_contents.cc b/impeller/entity/contents/radial_gradient_contents.cc index f2d58c998ae48..64998af951876 100644 --- a/impeller/entity/contents/radial_gradient_contents.cc +++ b/impeller/entity/contents/radial_gradient_contents.cc @@ -79,13 +79,14 @@ bool RadialGradientContents::RenderSSBO(const ContentContext& renderer, }; return ColorSourceContents::DrawGeometry( renderer, entity, pass, pipeline_callback, frame_info, - [this, &renderer](RenderPass& pass) { + [this, &renderer, &entity](RenderPass& pass) { FS::FragInfo frag_info; frag_info.center = center_; frag_info.radius = radius_; frag_info.tile_mode = static_cast(tile_mode_); frag_info.decal_border_color = decal_border_color_; - frag_info.alpha = GetOpacityFactor(); + frag_info.alpha = + GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity); auto& host_buffer = renderer.GetTransientsBuffer(); auto colors = CreateGradientColors(colors_, stops_); @@ -126,7 +127,7 @@ bool RadialGradientContents::RenderTexture(const ContentContext& renderer, }; return ColorSourceContents::DrawGeometry( renderer, entity, pass, pipeline_callback, frame_info, - [this, &renderer, &gradient_texture](RenderPass& pass) { + [this, &renderer, &gradient_texture, &entity](RenderPass& pass) { FS::FragInfo frag_info; frag_info.center = center_; frag_info.radius = radius_; @@ -134,7 +135,8 @@ bool RadialGradientContents::RenderTexture(const ContentContext& renderer, frag_info.decal_border_color = decal_border_color_; frag_info.texture_sampler_y_coord_scale = gradient_texture->GetYCoordScale(); - frag_info.alpha = GetOpacityFactor(); + frag_info.alpha = + GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity); frag_info.half_texel = Vector2(0.5 / gradient_texture->GetSize().width, 0.5 / gradient_texture->GetSize().height); diff --git a/impeller/entity/contents/solid_color_contents.cc b/impeller/entity/contents/solid_color_contents.cc index 0c99d47990b7b..a823b603179b9 100644 --- a/impeller/entity/contents/solid_color_contents.cc +++ b/impeller/entity/contents/solid_color_contents.cc @@ -51,7 +51,8 @@ bool SolidColorContents::Render(const ContentContext& renderer, using VS = SolidFillPipeline::VertexShader; VS::FrameInfo frame_info; - frame_info.color = GetColor().Premultiply(); + frame_info.color = + GetColor().Premultiply() * GetGeometry()->ComputeAlphaCoverage(entity); PipelineBuilderCallback pipeline_callback = [&renderer](ContentContextOptions options) { diff --git a/impeller/entity/contents/sweep_gradient_contents.cc b/impeller/entity/contents/sweep_gradient_contents.cc index 3586a14b9fc75..9cc65847bd30f 100644 --- a/impeller/entity/contents/sweep_gradient_contents.cc +++ b/impeller/entity/contents/sweep_gradient_contents.cc @@ -87,14 +87,15 @@ bool SweepGradientContents::RenderSSBO(const ContentContext& renderer, }; return ColorSourceContents::DrawGeometry( renderer, entity, pass, pipeline_callback, frame_info, - [this, &renderer](RenderPass& pass) { + [this, &renderer, &entity](RenderPass& pass) { FS::FragInfo frag_info; frag_info.center = center_; frag_info.bias = bias_; frag_info.scale = scale_; frag_info.tile_mode = static_cast(tile_mode_); frag_info.decal_border_color = decal_border_color_; - frag_info.alpha = GetOpacityFactor(); + frag_info.alpha = + GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity); auto& host_buffer = renderer.GetTransientsBuffer(); auto colors = CreateGradientColors(colors_, stops_); @@ -136,7 +137,7 @@ bool SweepGradientContents::RenderTexture(const ContentContext& renderer, }; return ColorSourceContents::DrawGeometry( renderer, entity, pass, pipeline_callback, frame_info, - [this, &renderer, &gradient_texture](RenderPass& pass) { + [this, &renderer, &gradient_texture, &entity](RenderPass& pass) { FS::FragInfo frag_info; frag_info.center = center_; frag_info.bias = bias_; @@ -145,7 +146,8 @@ bool SweepGradientContents::RenderTexture(const ContentContext& renderer, gradient_texture->GetYCoordScale(); frag_info.tile_mode = static_cast(tile_mode_); frag_info.decal_border_color = decal_border_color_; - frag_info.alpha = GetOpacityFactor(); + frag_info.alpha = + GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity); frag_info.half_texel = Vector2(0.5 / gradient_texture->GetSize().width, 0.5 / gradient_texture->GetSize().height); diff --git a/impeller/entity/contents/tiled_texture_contents.cc b/impeller/entity/contents/tiled_texture_contents.cc index 3f6d4a3715203..f1c6330578d3c 100644 --- a/impeller/entity/contents/tiled_texture_contents.cc +++ b/impeller/entity/contents/tiled_texture_contents.cc @@ -150,7 +150,7 @@ bool TiledTextureContents::Render(const ContentContext& renderer, }; return ColorSourceContents::DrawGeometry( renderer, entity, pass, pipeline_callback, frame_info, - [this, &renderer, &is_external_texture](RenderPass& pass) { + [this, &renderer, &is_external_texture, &entity](RenderPass& pass) { auto& host_buffer = renderer.GetTransientsBuffer(); pass.SetCommandLabel("TextureFill"); @@ -159,13 +159,15 @@ bool TiledTextureContents::Render(const ContentContext& renderer, FSExternal::FragInfo frag_info; frag_info.x_tile_mode = static_cast(x_tile_mode_); frag_info.y_tile_mode = static_cast(y_tile_mode_); - frag_info.alpha = GetOpacityFactor(); + frag_info.alpha = + GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity); FSExternal::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info)); } else { FS::FragInfo frag_info; frag_info.x_tile_mode = static_cast(x_tile_mode_); frag_info.y_tile_mode = static_cast(y_tile_mode_); - frag_info.alpha = GetOpacityFactor(); + frag_info.alpha = + GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity); FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info)); } diff --git a/impeller/entity/geometry/geometry.h b/impeller/entity/geometry/geometry.h index 40dc0815584c5..12cbd8f08de37 100644 --- a/impeller/entity/geometry/geometry.h +++ b/impeller/entity/geometry/geometry.h @@ -107,6 +107,10 @@ class Geometry { virtual bool CanApplyMaskFilter() const; + virtual Scalar ComputeAlphaCoverage(const Entity& entitys) const { + return 1.0; + } + protected: static GeometryResult ComputePositionGeometry( const ContentContext& renderer, diff --git a/impeller/entity/geometry/geometry_unittests.cc b/impeller/entity/geometry/geometry_unittests.cc index 09061eb2628ae..b433828347a54 100644 --- a/impeller/entity/geometry/geometry_unittests.cc +++ b/impeller/entity/geometry/geometry_unittests.cc @@ -4,9 +4,11 @@ #include #include "flutter/testing/testing.h" +#include "gtest/gtest.h" #include "impeller/entity/contents/content_context.h" #include "impeller/entity/geometry/geometry.h" #include "impeller/entity/geometry/stroke_path_geometry.h" +#include "impeller/geometry/constants.h" #include "impeller/geometry/geometry_asserts.h" #include "impeller/geometry/path_builder.h" #include "impeller/renderer/testing/mocks.h" @@ -134,5 +136,21 @@ TEST(EntityGeometryTest, GeometryResultHasReasonableDefaults) { EXPECT_EQ(result.mode, GeometryResult::Mode::kNormal); } +TEST(EntityGeometryTest, AlphaCoverageStrokePaths) { + Entity entity; + entity.SetTransform(Matrix::MakeScale(Vector2{3.0, 3.0})); + EXPECT_EQ(Geometry::MakeStrokePath({}, 0.5)->ComputeAlphaCoverage(entity), 1); + EXPECT_EQ(Geometry::MakeStrokePath({}, 0.1)->ComputeAlphaCoverage(entity), 1); + EXPECT_EQ(Geometry::MakeStrokePath({}, 0.05)->ComputeAlphaCoverage(entity), + 1); + EXPECT_NEAR(Geometry::MakeStrokePath({}, 0.01)->ComputeAlphaCoverage(entity), + 0.6, 0.1); + EXPECT_NEAR( + Geometry::MakeStrokePath({}, 0.0000005)->ComputeAlphaCoverage(entity), + 1e-05, 0.001); + EXPECT_EQ(Geometry::MakeStrokePath({}, 0)->ComputeAlphaCoverage(entity), 1); + EXPECT_EQ(Geometry::MakeStrokePath({}, 40)->ComputeAlphaCoverage(entity), 1); +} + } // namespace testing } // namespace impeller diff --git a/impeller/entity/geometry/stroke_path_geometry.cc b/impeller/entity/geometry/stroke_path_geometry.cc index ba9a92bb9c1e1..5b55472c107e0 100644 --- a/impeller/entity/geometry/stroke_path_geometry.cc +++ b/impeller/entity/geometry/stroke_path_geometry.cc @@ -525,6 +525,18 @@ Join StrokePathGeometry::GetStrokeJoin() const { return stroke_join_; } +Scalar StrokePathGeometry::ComputeAlphaCoverage(const Entity& entity) const { + Scalar scaled_stroke_width = + entity.GetTransform().GetMaxBasisLengthXY() * stroke_width_; + // If the stroke width is 0 or greater than kMinStrokeSizeMSAA, don't apply + // any additional alpha. This is intended to match Skia behavior. + if (scaled_stroke_width == 0.0 || scaled_stroke_width >= kMinStrokeSizeMSAA) { + return 1.0; + } + // This scalling is eyeballed from Skia. + return std::clamp(scaled_stroke_width * 20.0f, 0.f, 1.f); +} + GeometryResult StrokePathGeometry::GetPositionBuffer( const ContentContext& renderer, const Entity& entity, @@ -568,8 +580,7 @@ GeometryResult StrokePathGeometry::GetPositionBuffer( .index_type = IndexType::kNone, }, .transform = entity.GetShaderTransform(pass), - .mode = GeometryResult::Mode::kPreventOverdraw, - }; + .mode = GeometryResult::Mode::kPreventOverdraw}; } GeometryResult::Mode StrokePathGeometry::GetResultMode() const { diff --git a/impeller/entity/geometry/stroke_path_geometry.h b/impeller/entity/geometry/stroke_path_geometry.h index 3c26e8d55ee99..69bf18774bc76 100644 --- a/impeller/entity/geometry/stroke_path_geometry.h +++ b/impeller/entity/geometry/stroke_path_geometry.h @@ -28,6 +28,8 @@ class StrokePathGeometry final : public Geometry { Join GetStrokeJoin() const; + Scalar ComputeAlphaCoverage(const Entity& entity) const override; + private: // |Geometry| GeometryResult GetPositionBuffer(const ContentContext& renderer,