Skip to content

Commit

Permalink
[Impeller] ensure that srcOver to src conversion takes stroke coverag…
Browse files Browse the repository at this point in the history
…e 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
  • Loading branch information
jonahwilliams authored Aug 28, 2024
1 parent bbb85cf commit 19ef4a9
Show file tree
Hide file tree
Showing 29 changed files with 228 additions and 105 deletions.
2 changes: 1 addition & 1 deletion impeller/aiks/aiks_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ TEST_P(AiksTest, OpaqueEntitiesGetCoercedToSource) {
});

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

Expand Down
2 changes: 1 addition & 1 deletion impeller/aiks/experimental_canvas.cc
Original file line number Diff line number Diff line change
Expand Up @@ -673,7 +673,7 @@ void ExperimentalCanvas::AddRenderEntityToCurrentPass(Entity entity,
entity.GetTransform());
entity.SetInheritedOpacity(transform_stack_.back().distributed_opacity);
if (entity.GetBlendMode() == BlendMode::kSourceOver &&
entity.GetContents()->IsOpaque()) {
entity.GetContents()->IsOpaque(entity.GetTransform())) {
entity.SetBlendMode(BlendMode::kSource);
}

Expand Down
5 changes: 5 additions & 0 deletions impeller/entity/contents/color_source_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,9 @@ void ColorSourceContents::SetInheritedOpacity(Scalar opacity) {
inherited_opacity_ = opacity;
}

bool ColorSourceContents::AppliesAlphaForStrokeCoverage(
const Matrix& transform) const {
return GetGeometry() && GetGeometry()->ComputeAlphaCoverage(transform) < 1.0;
}

} // namespace impeller
4 changes: 4 additions & 0 deletions impeller/entity/contents/color_source_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ class ColorSourceContents : public Contents {
return geom.GetPositionBuffer(renderer, entity, pass);
}

/// @brief Whether the entity should be treated as non-opaque due to stroke
/// geometry requiring alpha for coverage.
bool AppliesAlphaForStrokeCoverage(const Matrix& transform) const;

template <typename VertexShaderT>
bool DrawGeometry(const ContentContext& renderer,
const Entity& entity,
Expand Down
6 changes: 4 additions & 2 deletions impeller/entity/contents/conical_gradient_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ bool ConicalGradientContents::RenderSSBO(const ContentContext& renderer,
frag_info.tile_mode = static_cast<Scalar>(tile_mode_);
frag_info.decal_border_color = decal_border_color_;
frag_info.alpha =
GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity);
GetOpacityFactor() *
GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());
if (focus_) {
frag_info.focus = focus_.value();
frag_info.focus_radius = focus_radius_;
Expand Down Expand Up @@ -137,7 +138,8 @@ bool ConicalGradientContents::RenderTexture(const ContentContext& renderer,
frag_info.texture_sampler_y_coord_scale =
gradient_texture->GetYCoordScale();
frag_info.alpha =
GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity);
GetOpacityFactor() *
GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());
frag_info.half_texel =
Vector2(0.5 / gradient_texture->GetSize().width,
0.5 / gradient_texture->GetSize().height);
Expand Down
2 changes: 1 addition & 1 deletion impeller/entity/contents/contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Contents::Contents() = default;

Contents::~Contents() = default;

bool Contents::IsOpaque() const {
bool Contents::IsOpaque(const Matrix& transform) const {
return false;
}

Expand Down
4 changes: 3 additions & 1 deletion impeller/entity/contents/contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ class Contents {
/// properties (e.g. the blend mode), clips/visibility culling, or
/// inherited opacity.
///
virtual bool IsOpaque() const;
/// @param transform The current transform matrix of the entity that will
/// render this contents.
virtual bool IsOpaque(const Matrix& transform) const;

//----------------------------------------------------------------------------
/// @brief Given the current pass space bounding rectangle of the clip
Expand Down
13 changes: 8 additions & 5 deletions impeller/entity/contents/linear_gradient_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ void LinearGradientContents::SetTileMode(Entity::TileMode tile_mode) {
tile_mode_ = tile_mode;
}

bool LinearGradientContents::IsOpaque() const {
bool LinearGradientContents::IsOpaque(const Matrix& transform) const {
if (GetOpacityFactor() < 1 || tile_mode_ == Entity::TileMode::kDecal) {
return false;
}
Expand All @@ -53,7 +53,7 @@ bool LinearGradientContents::IsOpaque() const {
return false;
}
}
return true;
return !AppliesAlphaForStrokeCoverage(transform);
}

bool LinearGradientContents::CanApplyFastGradient() const {
Expand Down Expand Up @@ -176,7 +176,8 @@ bool LinearGradientContents::FastLinearGradient(const ContentContext& renderer,

FS::FragInfo frag_info;
frag_info.alpha =
GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity);
GetOpacityFactor() *
GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());

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

Expand Down Expand Up @@ -231,7 +232,8 @@ bool LinearGradientContents::RenderTexture(const ContentContext& renderer,
frag_info.texture_sampler_y_coord_scale =
gradient_texture->GetYCoordScale();
frag_info.alpha =
GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity);
GetOpacityFactor() *
GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());
;
frag_info.half_texel =
Vector2(0.5 / gradient_texture->GetSize().width,
Expand Down Expand Up @@ -284,7 +286,8 @@ bool LinearGradientContents::RenderSSBO(const ContentContext& renderer,
frag_info.tile_mode = static_cast<Scalar>(tile_mode_);
frag_info.decal_border_color = decal_border_color_;
frag_info.alpha =
GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity);
GetOpacityFactor() *
GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());
frag_info.start_to_end = end_point_ - start_point_;
frag_info.inverse_dot_start_to_end =
CalculateInverseDotStartToEnd(start_point_, end_point_);
Expand Down
2 changes: 1 addition & 1 deletion impeller/entity/contents/linear_gradient_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class LinearGradientContents final : public ColorSourceContents {
~LinearGradientContents() override;

// |Contents|
bool IsOpaque() const override;
bool IsOpaque(const Matrix& transform) const override;

// |Contents|
bool Render(const ContentContext& renderer,
Expand Down
10 changes: 6 additions & 4 deletions impeller/entity/contents/radial_gradient_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const std::vector<Scalar>& RadialGradientContents::GetStops() const {
return stops_;
}

bool RadialGradientContents::IsOpaque() const {
bool RadialGradientContents::IsOpaque(const Matrix& transform) const {
if (GetOpacityFactor() < 1 || tile_mode_ == Entity::TileMode::kDecal) {
return false;
}
Expand All @@ -52,7 +52,7 @@ bool RadialGradientContents::IsOpaque() const {
return false;
}
}
return true;
return !AppliesAlphaForStrokeCoverage(transform);
}

bool RadialGradientContents::Render(const ContentContext& renderer,
Expand Down Expand Up @@ -86,7 +86,8 @@ bool RadialGradientContents::RenderSSBO(const ContentContext& renderer,
frag_info.tile_mode = static_cast<Scalar>(tile_mode_);
frag_info.decal_border_color = decal_border_color_;
frag_info.alpha =
GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity);
GetOpacityFactor() *
GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());

auto& host_buffer = renderer.GetTransientsBuffer();
auto colors = CreateGradientColors(colors_, stops_);
Expand Down Expand Up @@ -136,7 +137,8 @@ bool RadialGradientContents::RenderTexture(const ContentContext& renderer,
frag_info.texture_sampler_y_coord_scale =
gradient_texture->GetYCoordScale();
frag_info.alpha =
GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity);
GetOpacityFactor() *
GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());
frag_info.half_texel =
Vector2(0.5 / gradient_texture->GetSize().width,
0.5 / gradient_texture->GetSize().height);
Expand Down
2 changes: 1 addition & 1 deletion impeller/entity/contents/radial_gradient_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class RadialGradientContents final : public ColorSourceContents {
~RadialGradientContents() override;

// |Contents|
bool IsOpaque() const override;
bool IsOpaque(const Matrix& transform) const override;

// |Contents|
bool Render(const ContentContext& renderer,
Expand Down
8 changes: 4 additions & 4 deletions impeller/entity/contents/solid_color_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ bool SolidColorContents::IsSolidColor() const {
return true;
}

bool SolidColorContents::IsOpaque() const {
return GetColor().IsOpaque();
bool SolidColorContents::IsOpaque(const Matrix& transform) const {
return GetColor().IsOpaque() && !AppliesAlphaForStrokeCoverage(transform);
}

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

VS::FrameInfo frame_info;
FS::FragInfo frag_info;
frag_info.color =
GetColor().Premultiply() * GetGeometry()->ComputeAlphaCoverage(entity);
frag_info.color = GetColor().Premultiply() *
GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());

PipelineBuilderCallback pipeline_callback =
[&renderer](ContentContextOptions options) {
Expand Down
2 changes: 1 addition & 1 deletion impeller/entity/contents/solid_color_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class SolidColorContents final : public ColorSourceContents {
bool IsSolidColor() const override;

// |Contents|
bool IsOpaque() const override;
bool IsOpaque(const Matrix& transform) const override;

// |Contents|
std::optional<Rect> GetCoverage(const Entity& entity) const override;
Expand Down
10 changes: 6 additions & 4 deletions impeller/entity/contents/sweep_gradient_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const std::vector<Scalar>& SweepGradientContents::GetStops() const {
return stops_;
}

bool SweepGradientContents::IsOpaque() const {
bool SweepGradientContents::IsOpaque(const Matrix& transform) const {
if (GetOpacityFactor() < 1 || tile_mode_ == Entity::TileMode::kDecal) {
return false;
}
Expand All @@ -58,7 +58,7 @@ bool SweepGradientContents::IsOpaque() const {
return false;
}
}
return true;
return !AppliesAlphaForStrokeCoverage(transform);
}

bool SweepGradientContents::Render(const ContentContext& renderer,
Expand Down Expand Up @@ -95,7 +95,8 @@ bool SweepGradientContents::RenderSSBO(const ContentContext& renderer,
frag_info.tile_mode = static_cast<Scalar>(tile_mode_);
frag_info.decal_border_color = decal_border_color_;
frag_info.alpha =
GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity);
GetOpacityFactor() *
GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());

auto& host_buffer = renderer.GetTransientsBuffer();
auto colors = CreateGradientColors(colors_, stops_);
Expand Down Expand Up @@ -147,7 +148,8 @@ bool SweepGradientContents::RenderTexture(const ContentContext& renderer,
frag_info.tile_mode = static_cast<Scalar>(tile_mode_);
frag_info.decal_border_color = decal_border_color_;
frag_info.alpha =
GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity);
GetOpacityFactor() *
GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());
frag_info.half_texel =
Vector2(0.5 / gradient_texture->GetSize().width,
0.5 / gradient_texture->GetSize().height);
Expand Down
2 changes: 1 addition & 1 deletion impeller/entity/contents/sweep_gradient_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class SweepGradientContents final : public ColorSourceContents {
~SweepGradientContents() override;

// |Contents|
bool IsOpaque() const override;
bool IsOpaque(const Matrix& transform) const override;

// |Contents|
bool Render(const ContentContext& renderer,
Expand Down
10 changes: 6 additions & 4 deletions impeller/entity/contents/tiled_texture_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,15 @@ bool TiledTextureContents::UsesEmulatedTileMode(
}

// |Contents|
bool TiledTextureContents::IsOpaque() const {
bool TiledTextureContents::IsOpaque(const Matrix& transform) const {
if (GetOpacityFactor() < 1 || x_tile_mode_ == Entity::TileMode::kDecal ||
y_tile_mode_ == Entity::TileMode::kDecal) {
return false;
}
if (color_filter_) {
return false;
}
return texture_->IsOpaque();
return texture_->IsOpaque() && !AppliesAlphaForStrokeCoverage(transform);
}

bool TiledTextureContents::Render(const ContentContext& renderer,
Expand Down Expand Up @@ -160,14 +160,16 @@ bool TiledTextureContents::Render(const ContentContext& renderer,
frag_info.x_tile_mode = static_cast<Scalar>(x_tile_mode_);
frag_info.y_tile_mode = static_cast<Scalar>(y_tile_mode_);
frag_info.alpha =
GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity);
GetOpacityFactor() *
GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());
FSExternal::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
} else {
FS::FragInfo frag_info;
frag_info.x_tile_mode = static_cast<Scalar>(x_tile_mode_);
frag_info.y_tile_mode = static_cast<Scalar>(y_tile_mode_);
frag_info.alpha =
GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity);
GetOpacityFactor() *
GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());
FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
}

Expand Down
2 changes: 1 addition & 1 deletion impeller/entity/contents/tiled_texture_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class TiledTextureContents final : public ColorSourceContents {
std::function<std::shared_ptr<ColorFilterContents>(FilterInput::Ref)>;

// |Contents|
bool IsOpaque() const override;
bool IsOpaque(const Matrix& transform) const override;

// |Contents|
bool Render(const ContentContext& renderer,
Expand Down
6 changes: 4 additions & 2 deletions impeller/entity/entity.cc
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ bool Entity::CanInheritOpacity() const {
if (!contents_) {
return false;
}
if (!((blend_mode_ == BlendMode::kSource && contents_->IsOpaque()) ||
if (!((blend_mode_ == BlendMode::kSource &&
contents_->IsOpaque(GetTransform())) ||
blend_mode_ == BlendMode::kSourceOver)) {
return false;
}
Expand All @@ -137,7 +138,8 @@ bool Entity::SetInheritedOpacity(Scalar alpha) {
if (!CanInheritOpacity()) {
return false;
}
if (blend_mode_ == BlendMode::kSource && contents_->IsOpaque()) {
if (blend_mode_ == BlendMode::kSource &&
contents_->IsOpaque(GetTransform())) {
blend_mode_ = BlendMode::kSourceOver;
}
contents_->SetInheritedOpacity(alpha);
Expand Down
2 changes: 1 addition & 1 deletion impeller/entity/entity_pass.cc
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ bool EntityPass::GetBoundsLimitIsSnug() const {

void EntityPass::AddEntity(Entity entity) {
if (entity.GetBlendMode() == BlendMode::kSourceOver &&
entity.GetContents()->IsOpaque()) {
entity.GetContents()->IsOpaque(entity.GetTransform())) {
entity.SetBlendMode(BlendMode::kSource);
}

Expand Down
Loading

0 comments on commit 19ef4a9

Please sign in to comment.