Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 19ef4a9

Browse files
[Impeller] ensure that srcOver to src conversion takes stroke coverage into account. (#54817)
When computing whether or not an entity can be coerced into src blend from srcOver, take into account that stroke geometries may modulate with alpha to emulate stroke widths less than 1.0. While we're at it, update several computations to use max basis XY (from flutter/flutter#153451 ) since that can lead to incorrect stroke width computations in the presence of canvas scaling. Most of the changes update the APIs that used to take a ` const Entity& entity` to instead use the `const Matrix& transform`, since that is all we used the entity for. See screeenshots in flutter/flutter#154178 (comment) Fixes flutter/flutter#154178
1 parent bbb85cf commit 19ef4a9

29 files changed

+228
-105
lines changed

impeller/aiks/aiks_unittests.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ TEST_P(AiksTest, OpaqueEntitiesGetCoercedToSource) {
314314
});
315315

316316
ASSERT_TRUE(entity.size() >= 1);
317-
ASSERT_TRUE(contents->IsOpaque());
317+
ASSERT_TRUE(contents->IsOpaque({}));
318318
ASSERT_EQ(entity[0].GetBlendMode(), BlendMode::kSource);
319319
}
320320

impeller/aiks/experimental_canvas.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -673,7 +673,7 @@ void ExperimentalCanvas::AddRenderEntityToCurrentPass(Entity entity,
673673
entity.GetTransform());
674674
entity.SetInheritedOpacity(transform_stack_.back().distributed_opacity);
675675
if (entity.GetBlendMode() == BlendMode::kSourceOver &&
676-
entity.GetContents()->IsOpaque()) {
676+
entity.GetContents()->IsOpaque(entity.GetTransform())) {
677677
entity.SetBlendMode(BlendMode::kSource);
678678
}
679679

impeller/entity/contents/color_source_contents.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,9 @@ void ColorSourceContents::SetInheritedOpacity(Scalar opacity) {
5454
inherited_opacity_ = opacity;
5555
}
5656

57+
bool ColorSourceContents::AppliesAlphaForStrokeCoverage(
58+
const Matrix& transform) const {
59+
return GetGeometry() && GetGeometry()->ComputeAlphaCoverage(transform) < 1.0;
60+
}
61+
5762
} // namespace impeller

impeller/entity/contents/color_source_contents.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ class ColorSourceContents : public Contents {
126126
return geom.GetPositionBuffer(renderer, entity, pass);
127127
}
128128

129+
/// @brief Whether the entity should be treated as non-opaque due to stroke
130+
/// geometry requiring alpha for coverage.
131+
bool AppliesAlphaForStrokeCoverage(const Matrix& transform) const;
132+
129133
template <typename VertexShaderT>
130134
bool DrawGeometry(const ContentContext& renderer,
131135
const Entity& entity,

impeller/entity/contents/conical_gradient_contents.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ bool ConicalGradientContents::RenderSSBO(const ContentContext& renderer,
8080
frag_info.tile_mode = static_cast<Scalar>(tile_mode_);
8181
frag_info.decal_border_color = decal_border_color_;
8282
frag_info.alpha =
83-
GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity);
83+
GetOpacityFactor() *
84+
GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());
8485
if (focus_) {
8586
frag_info.focus = focus_.value();
8687
frag_info.focus_radius = focus_radius_;
@@ -137,7 +138,8 @@ bool ConicalGradientContents::RenderTexture(const ContentContext& renderer,
137138
frag_info.texture_sampler_y_coord_scale =
138139
gradient_texture->GetYCoordScale();
139140
frag_info.alpha =
140-
GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity);
141+
GetOpacityFactor() *
142+
GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());
141143
frag_info.half_texel =
142144
Vector2(0.5 / gradient_texture->GetSize().width,
143145
0.5 / gradient_texture->GetSize().height);

impeller/entity/contents/contents.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Contents::Contents() = default;
4949

5050
Contents::~Contents() = default;
5151

52-
bool Contents::IsOpaque() const {
52+
bool Contents::IsOpaque(const Matrix& transform) const {
5353
return false;
5454
}
5555

impeller/entity/contents/contents.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,9 @@ class Contents {
9191
/// properties (e.g. the blend mode), clips/visibility culling, or
9292
/// inherited opacity.
9393
///
94-
virtual bool IsOpaque() const;
94+
/// @param transform The current transform matrix of the entity that will
95+
/// render this contents.
96+
virtual bool IsOpaque(const Matrix& transform) const;
9597

9698
//----------------------------------------------------------------------------
9799
/// @brief Given the current pass space bounding rectangle of the clip

impeller/entity/contents/linear_gradient_contents.cc

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ void LinearGradientContents::SetTileMode(Entity::TileMode tile_mode) {
4444
tile_mode_ = tile_mode;
4545
}
4646

47-
bool LinearGradientContents::IsOpaque() const {
47+
bool LinearGradientContents::IsOpaque(const Matrix& transform) const {
4848
if (GetOpacityFactor() < 1 || tile_mode_ == Entity::TileMode::kDecal) {
4949
return false;
5050
}
@@ -53,7 +53,7 @@ bool LinearGradientContents::IsOpaque() const {
5353
return false;
5454
}
5555
}
56-
return true;
56+
return !AppliesAlphaForStrokeCoverage(transform);
5757
}
5858

5959
bool LinearGradientContents::CanApplyFastGradient() const {
@@ -176,7 +176,8 @@ bool LinearGradientContents::FastLinearGradient(const ContentContext& renderer,
176176

177177
FS::FragInfo frag_info;
178178
frag_info.alpha =
179-
GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity);
179+
GetOpacityFactor() *
180+
GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());
180181

181182
FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
182183

@@ -231,7 +232,8 @@ bool LinearGradientContents::RenderTexture(const ContentContext& renderer,
231232
frag_info.texture_sampler_y_coord_scale =
232233
gradient_texture->GetYCoordScale();
233234
frag_info.alpha =
234-
GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity);
235+
GetOpacityFactor() *
236+
GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());
235237
;
236238
frag_info.half_texel =
237239
Vector2(0.5 / gradient_texture->GetSize().width,
@@ -284,7 +286,8 @@ bool LinearGradientContents::RenderSSBO(const ContentContext& renderer,
284286
frag_info.tile_mode = static_cast<Scalar>(tile_mode_);
285287
frag_info.decal_border_color = decal_border_color_;
286288
frag_info.alpha =
287-
GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity);
289+
GetOpacityFactor() *
290+
GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());
288291
frag_info.start_to_end = end_point_ - start_point_;
289292
frag_info.inverse_dot_start_to_end =
290293
CalculateInverseDotStartToEnd(start_point_, end_point_);

impeller/entity/contents/linear_gradient_contents.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class LinearGradientContents final : public ColorSourceContents {
2121
~LinearGradientContents() override;
2222

2323
// |Contents|
24-
bool IsOpaque() const override;
24+
bool IsOpaque(const Matrix& transform) const override;
2525

2626
// |Contents|
2727
bool Render(const ContentContext& renderer,

impeller/entity/contents/radial_gradient_contents.cc

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ const std::vector<Scalar>& RadialGradientContents::GetStops() const {
4343
return stops_;
4444
}
4545

46-
bool RadialGradientContents::IsOpaque() const {
46+
bool RadialGradientContents::IsOpaque(const Matrix& transform) const {
4747
if (GetOpacityFactor() < 1 || tile_mode_ == Entity::TileMode::kDecal) {
4848
return false;
4949
}
@@ -52,7 +52,7 @@ bool RadialGradientContents::IsOpaque() const {
5252
return false;
5353
}
5454
}
55-
return true;
55+
return !AppliesAlphaForStrokeCoverage(transform);
5656
}
5757

5858
bool RadialGradientContents::Render(const ContentContext& renderer,
@@ -86,7 +86,8 @@ bool RadialGradientContents::RenderSSBO(const ContentContext& renderer,
8686
frag_info.tile_mode = static_cast<Scalar>(tile_mode_);
8787
frag_info.decal_border_color = decal_border_color_;
8888
frag_info.alpha =
89-
GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity);
89+
GetOpacityFactor() *
90+
GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());
9091

9192
auto& host_buffer = renderer.GetTransientsBuffer();
9293
auto colors = CreateGradientColors(colors_, stops_);
@@ -136,7 +137,8 @@ bool RadialGradientContents::RenderTexture(const ContentContext& renderer,
136137
frag_info.texture_sampler_y_coord_scale =
137138
gradient_texture->GetYCoordScale();
138139
frag_info.alpha =
139-
GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity);
140+
GetOpacityFactor() *
141+
GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());
140142
frag_info.half_texel =
141143
Vector2(0.5 / gradient_texture->GetSize().width,
142144
0.5 / gradient_texture->GetSize().height);

impeller/entity/contents/radial_gradient_contents.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class RadialGradientContents final : public ColorSourceContents {
2121
~RadialGradientContents() override;
2222

2323
// |Contents|
24-
bool IsOpaque() const override;
24+
bool IsOpaque(const Matrix& transform) const override;
2525

2626
// |Contents|
2727
bool Render(const ContentContext& renderer,

impeller/entity/contents/solid_color_contents.cc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ bool SolidColorContents::IsSolidColor() const {
2828
return true;
2929
}
3030

31-
bool SolidColorContents::IsOpaque() const {
32-
return GetColor().IsOpaque();
31+
bool SolidColorContents::IsOpaque(const Matrix& transform) const {
32+
return GetColor().IsOpaque() && !AppliesAlphaForStrokeCoverage(transform);
3333
}
3434

3535
std::optional<Rect> SolidColorContents::GetCoverage(
@@ -54,8 +54,8 @@ bool SolidColorContents::Render(const ContentContext& renderer,
5454

5555
VS::FrameInfo frame_info;
5656
FS::FragInfo frag_info;
57-
frag_info.color =
58-
GetColor().Premultiply() * GetGeometry()->ComputeAlphaCoverage(entity);
57+
frag_info.color = GetColor().Premultiply() *
58+
GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());
5959

6060
PipelineBuilderCallback pipeline_callback =
6161
[&renderer](ContentContextOptions options) {

impeller/entity/contents/solid_color_contents.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class SolidColorContents final : public ColorSourceContents {
3131
bool IsSolidColor() const override;
3232

3333
// |Contents|
34-
bool IsOpaque() const override;
34+
bool IsOpaque(const Matrix& transform) const override;
3535

3636
// |Contents|
3737
std::optional<Rect> GetCoverage(const Entity& entity) const override;

impeller/entity/contents/sweep_gradient_contents.cc

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ const std::vector<Scalar>& SweepGradientContents::GetStops() const {
4949
return stops_;
5050
}
5151

52-
bool SweepGradientContents::IsOpaque() const {
52+
bool SweepGradientContents::IsOpaque(const Matrix& transform) const {
5353
if (GetOpacityFactor() < 1 || tile_mode_ == Entity::TileMode::kDecal) {
5454
return false;
5555
}
@@ -58,7 +58,7 @@ bool SweepGradientContents::IsOpaque() const {
5858
return false;
5959
}
6060
}
61-
return true;
61+
return !AppliesAlphaForStrokeCoverage(transform);
6262
}
6363

6464
bool SweepGradientContents::Render(const ContentContext& renderer,
@@ -95,7 +95,8 @@ bool SweepGradientContents::RenderSSBO(const ContentContext& renderer,
9595
frag_info.tile_mode = static_cast<Scalar>(tile_mode_);
9696
frag_info.decal_border_color = decal_border_color_;
9797
frag_info.alpha =
98-
GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity);
98+
GetOpacityFactor() *
99+
GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());
99100

100101
auto& host_buffer = renderer.GetTransientsBuffer();
101102
auto colors = CreateGradientColors(colors_, stops_);
@@ -147,7 +148,8 @@ bool SweepGradientContents::RenderTexture(const ContentContext& renderer,
147148
frag_info.tile_mode = static_cast<Scalar>(tile_mode_);
148149
frag_info.decal_border_color = decal_border_color_;
149150
frag_info.alpha =
150-
GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity);
151+
GetOpacityFactor() *
152+
GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());
151153
frag_info.half_texel =
152154
Vector2(0.5 / gradient_texture->GetSize().width,
153155
0.5 / gradient_texture->GetSize().height);

impeller/entity/contents/sweep_gradient_contents.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class SweepGradientContents final : public ColorSourceContents {
2222
~SweepGradientContents() override;
2323

2424
// |Contents|
25-
bool IsOpaque() const override;
25+
bool IsOpaque(const Matrix& transform) const override;
2626

2727
// |Contents|
2828
bool Render(const ContentContext& renderer,

impeller/entity/contents/tiled_texture_contents.cc

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,15 @@ bool TiledTextureContents::UsesEmulatedTileMode(
9696
}
9797

9898
// |Contents|
99-
bool TiledTextureContents::IsOpaque() const {
99+
bool TiledTextureContents::IsOpaque(const Matrix& transform) const {
100100
if (GetOpacityFactor() < 1 || x_tile_mode_ == Entity::TileMode::kDecal ||
101101
y_tile_mode_ == Entity::TileMode::kDecal) {
102102
return false;
103103
}
104104
if (color_filter_) {
105105
return false;
106106
}
107-
return texture_->IsOpaque();
107+
return texture_->IsOpaque() && !AppliesAlphaForStrokeCoverage(transform);
108108
}
109109

110110
bool TiledTextureContents::Render(const ContentContext& renderer,
@@ -160,14 +160,16 @@ bool TiledTextureContents::Render(const ContentContext& renderer,
160160
frag_info.x_tile_mode = static_cast<Scalar>(x_tile_mode_);
161161
frag_info.y_tile_mode = static_cast<Scalar>(y_tile_mode_);
162162
frag_info.alpha =
163-
GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity);
163+
GetOpacityFactor() *
164+
GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());
164165
FSExternal::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
165166
} else {
166167
FS::FragInfo frag_info;
167168
frag_info.x_tile_mode = static_cast<Scalar>(x_tile_mode_);
168169
frag_info.y_tile_mode = static_cast<Scalar>(y_tile_mode_);
169170
frag_info.alpha =
170-
GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity);
171+
GetOpacityFactor() *
172+
GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());
171173
FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
172174
}
173175

impeller/entity/contents/tiled_texture_contents.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class TiledTextureContents final : public ColorSourceContents {
2828
std::function<std::shared_ptr<ColorFilterContents>(FilterInput::Ref)>;
2929

3030
// |Contents|
31-
bool IsOpaque() const override;
31+
bool IsOpaque(const Matrix& transform) const override;
3232

3333
// |Contents|
3434
bool Render(const ContentContext& renderer,

impeller/entity/entity.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@ bool Entity::CanInheritOpacity() const {
126126
if (!contents_) {
127127
return false;
128128
}
129-
if (!((blend_mode_ == BlendMode::kSource && contents_->IsOpaque()) ||
129+
if (!((blend_mode_ == BlendMode::kSource &&
130+
contents_->IsOpaque(GetTransform())) ||
130131
blend_mode_ == BlendMode::kSourceOver)) {
131132
return false;
132133
}
@@ -137,7 +138,8 @@ bool Entity::SetInheritedOpacity(Scalar alpha) {
137138
if (!CanInheritOpacity()) {
138139
return false;
139140
}
140-
if (blend_mode_ == BlendMode::kSource && contents_->IsOpaque()) {
141+
if (blend_mode_ == BlendMode::kSource &&
142+
contents_->IsOpaque(GetTransform())) {
141143
blend_mode_ = BlendMode::kSourceOver;
142144
}
143145
contents_->SetInheritedOpacity(alpha);

impeller/entity/entity_pass.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ bool EntityPass::GetBoundsLimitIsSnug() const {
104104

105105
void EntityPass::AddEntity(Entity entity) {
106106
if (entity.GetBlendMode() == BlendMode::kSourceOver &&
107-
entity.GetContents()->IsOpaque()) {
107+
entity.GetContents()->IsOpaque(entity.GetTransform())) {
108108
entity.SetBlendMode(BlendMode::kSource);
109109
}
110110

0 commit comments

Comments
 (0)