Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2499,6 +2499,59 @@ TEST_P(AiksTest, CanRenderFilledRoundSuperellipses) {
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

TEST_P(AiksTest, CanRenderAsymmetricRoundSuperellipses) {
DisplayListBuilder builder;
builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
DlPaint paint;
paint.setColor(DlColor::kBlue());

builder.DrawRoundSuperellipse(
DlRoundSuperellipse::MakeRectRadii(
/*rect=*/DlRect::MakeXYWH(50, 50, 440, 440), /*radii=*/
{
.top_left = Size(60.0f, 240.0f),
.top_right = Size(200.0f, 40.0f),
.bottom_left = Size(180.0f, 20.0f),
.bottom_right = Size(40.0f, 200.0f),
}),
paint);

builder.DrawRoundSuperellipse(
DlRoundSuperellipse::MakeRectRadii(
/*rect=*/DlRect::MakeXYWH(550, 50, 440, 440), /*radii=*/
{
.top_left = Size(240.0f, 40.0f),
.top_right = Size(40.0f, 240.0f),
.bottom_left = Size(40.0f, 240.0f),
.bottom_right = Size(240.0f, 40.0f),
}),
paint);

builder.DrawRoundSuperellipse(
DlRoundSuperellipse::MakeRectRadii(
/*rect=*/DlRect::MakeXYWH(50, 550, 400, 400), /*radii=*/
{
.top_left = Size(240.0f, 240.0f),
.top_right = Size(40.0f, 40.0f),
.bottom_left = Size(40.0f, 40.0f),
.bottom_right = Size(40.0f, 40.0f),
}),
paint);

builder.DrawRoundSuperellipse(
DlRoundSuperellipse::MakeRectRadii(
/*rect=*/DlRect::MakeXYWH(550, 550, 400, 400), /*radii=*/
{
.top_left = Size(240.0f, 240.0f),
.top_right = Size(40.0f, 40.0f),
.bottom_left = Size(40.0f, 40.0f),
.bottom_right = Size(240.0f, 240.0f),
}),
paint);

ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

TEST_P(AiksTest, CanRenderStrokedRoundSuperellipses) {
DisplayListBuilder builder;
builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
Expand Down
69 changes: 41 additions & 28 deletions engine/src/flutter/impeller/display_list/canvas.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "impeller/entity/contents/circle_contents.h"
#include "impeller/entity/contents/clip_contents.h"
#include "impeller/entity/contents/color_source_contents.h"
#include "impeller/entity/contents/complex_rse_contents.h"
#include "impeller/entity/contents/content_context.h"
#include "impeller/entity/contents/filters/filter_contents.h"
#include "impeller/entity/contents/framebuffer_blend_contents.h"
Expand Down Expand Up @@ -1036,38 +1037,50 @@ void Canvas::DrawRoundSuperellipse(const RoundSuperellipse& round_superellipse,
entity.SetBlendMode(paint.blend_mode);

if (renderer_.GetContext()->GetFlags().use_sdfs &&
IsCompatibleWithSDFRendering(paint) &&
round_superellipse.GetRadii().AreAllCornersSame()) {
IsCompatibleWithSDFRendering(paint)) {
auto round_superellipse_params = RoundSuperellipseParam::MakeBoundsRadii(
round_superellipse.GetBounds(), round_superellipse.GetRadii());

RoundSuperellipseParam::Octant octant_top =
round_superellipse_params.top_right.top;
RoundSuperellipseParam::Octant octant_right =
round_superellipse_params.top_right.right;

auto adjusted_radii = RoundingRadii::MakeRadii(
Size(octant_top.circle_radius, octant_right.circle_radius));

auto params = UberSDFParameters::MakeRoundedSuperellipse(
/*color=*/paint.color,
/*bounds=*/round_superellipse.GetBounds(),
/*superellipse_degree=*/Point(octant_top.se_n, octant_right.se_n),
/*superellipse_a=*/Point(octant_top.se_a, octant_right.se_a),
/*radii=*/adjusted_radii,
/*corner_angle_span=*/
Point(octant_top.circle_max_angle.radians,
octant_right.circle_max_angle.radians),
/*corner_circle_center_top=*/octant_top.circle_center,
/*corner_circle_center_right=*/octant_right.circle_center,
/*superellipse_c=*/octant_top.se_a - octant_right.se_a,
/*superellipse_scale=*/
Point(round_superellipse_params.top_right.signed_scale.Abs()),
/*stroke=*/paint.GetStroke());

AddRenderSDFEntityToCurrentPass(paint, params);
if (round_superellipse_params.all_corners_same) {
auto params = UberSDFParameters::MakeRoundedSuperellipse(
/*color=*/paint.color,
/*bounds=*/round_superellipse.GetBounds(),
/*round_superellipse_params=*/round_superellipse_params,
/*stroke=*/paint.GetStroke());

return;
AddRenderSDFEntityToCurrentPass(paint, params);
return;
} else {
auto contents = ComplexRoundedSuperellipseContents::Make(
/*color=*/paint.color_source ? Color::White() : paint.color,
/*bounds=*/round_superellipse.GetBounds(),
/*round_superellipse_params=*/round_superellipse_params,
/*stroke=*/paint.GetStroke());

const Geometry* geom = contents->GetGeometry();

if (paint.color_source) {
std::shared_ptr<Contents> color_source_contents =
paint.CreateContents(renderer_, geom);
std::shared_ptr<Contents> final_contents =
ColorFilterContents::MakeBlend(
BlendMode::kSrcIn, {FilterInput::Make(std::move(contents)),
FilterInput::Make(color_source_contents)});

Paint new_paint = paint;
new_paint.color_source = nullptr;
AddRenderEntityWithFiltersToCurrentPass(entity, geom, new_paint,
/*reuse_depth=*/false,
/*override_contents=*/
std::move(final_contents));
} else {
AddRenderEntityWithFiltersToCurrentPass(entity, geom, paint,
/*reuse_depth=*/false,
/*override_contents=*/
std::move(contents));
}
return;
}
}

if (paint.style == Paint::Style::kFill) {
Expand Down
3 changes: 3 additions & 0 deletions engine/src/flutter/impeller/entity/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ impeller_shaders("entity_shaders") {
"shaders/blending/advanced_blend.frag",
"shaders/circle.frag",
"shaders/circle.vert",
"shaders/complex_rse.frag",
"shaders/uber_sdf.frag",
"shaders/clip.frag",
"shaders/clip.vert",
Expand Down Expand Up @@ -126,6 +127,8 @@ impeller_component("entity") {
"contents/clip_contents.h",
"contents/color_source_contents.cc",
"contents/color_source_contents.h",
"contents/complex_rse_contents.cc",
"contents/complex_rse_contents.h",
"contents/conical_gradient_contents.cc",
"contents/conical_gradient_contents.h",
"contents/content_context.cc",
Expand Down
172 changes: 172 additions & 0 deletions engine/src/flutter/impeller/entity/contents/complex_rse_contents.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "impeller/entity/contents/complex_rse_contents.h"

#include "impeller/entity/contents/color_source_contents.h"
#include "impeller/entity/contents/content_context.h"
#include "impeller/entity/contents/pipelines.h"
#include "impeller/entity/geometry/geometry.h"

namespace impeller {

namespace {

using PipelineBuilderCallback =
std::function<PipelineRef(ContentContextOptions)>;

using VS = ComplexRSEPipeline::VertexShader;
using FS = ComplexRSEPipeline::FragmentShader;

} // namespace

std::unique_ptr<ComplexRoundedSuperellipseContents>
ComplexRoundedSuperellipseContents::Make(
Color color,
const Rect& bounds,
const RoundSuperellipseParam& round_superellipse_params,
std::optional<StrokeParameters> stroke) {
return std::unique_ptr<ComplexRoundedSuperellipseContents>(
new ComplexRoundedSuperellipseContents(
color, bounds, round_superellipse_params, stroke));
}

ComplexRoundedSuperellipseContents::ComplexRoundedSuperellipseContents(
Color color,
const Rect& bounds,
const RoundSuperellipseParam& round_superellipse_params,
std::optional<StrokeParameters> stroke)
: color_(color),
bounds_(bounds),
round_superellipse_params_(round_superellipse_params),
stroke_(stroke) {
if (stroke) {
geometry_ = Geometry::MakeRect(bounds_.Expand(stroke->width / 2.0f));
} else {
geometry_ = Geometry::MakeRect(bounds_);
}
}

bool ComplexRoundedSuperellipseContents::Render(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const {
auto& data_host_buffer = renderer.GetTransientsDataBuffer();

Point center = bounds_.GetCenter();

RoundSuperellipseParam::Quadrant top_right =
round_superellipse_params_.top_right;
RoundSuperellipseParam::Quadrant bottom_right =
round_superellipse_params_.bottom_right;
RoundSuperellipseParam::Quadrant bottom_left =
round_superellipse_params_.bottom_left;
RoundSuperellipseParam::Quadrant top_left =
round_superellipse_params_.top_left;

Point top_right_center_relative = top_right.offset - center;
Point bottom_right_center_relative = bottom_right.offset - center;
Point bottom_left_center_relative = bottom_left.offset - center;
Point top_left_center_relative = top_left.offset - center;

Point size = Point(bounds_.GetSize() * 0.5f);

VS::FrameInfo frame_info;
FS::FragInfo frag_info;

frag_info.color = color_.WithAlpha(color_.alpha * GetOpacityFactor());
frag_info.center = center;
frag_info.size = size;
frag_info.stroked = stroke_ ? 1.0f : 0.0f;
frag_info.stroke_width = stroke_ ? stroke_->width : 0.0f;

frag_info.superellipse_degrees_top =
Vector4(bottom_right.top.se_n, top_right.top.se_n, bottom_left.top.se_n,
top_left.top.se_n);
frag_info.superellipse_degrees_right =
Vector4(bottom_right.right.se_n, top_right.right.se_n,
bottom_left.right.se_n, top_left.right.se_n);
frag_info.superellipse_semi_axes_top =
Vector4(bottom_right.top.se_a, top_right.top.se_a, bottom_left.top.se_a,
top_left.top.se_a);
frag_info.superellipse_semi_axes_right =
Vector4(bottom_right.right.se_a, top_right.right.se_a,
bottom_left.right.se_a, top_left.right.se_a);
frag_info.angle_spans_top = Vector4(bottom_right.top.circle_max_angle.radians,
top_right.top.circle_max_angle.radians,
bottom_left.top.circle_max_angle.radians,
top_left.top.circle_max_angle.radians);
frag_info.angle_spans_right =
Vector4(bottom_right.right.circle_max_angle.radians,
top_right.right.circle_max_angle.radians,
bottom_left.right.circle_max_angle.radians,
top_left.right.circle_max_angle.radians);
frag_info.octant_offsets_c =
Vector4(bottom_right.top.se_a - bottom_right.right.se_a,
top_right.top.se_a - top_right.right.se_a,
bottom_left.top.se_a - bottom_left.right.se_a,
top_left.top.se_a - top_left.right.se_a);
frag_info.radii_width =
Vector4(bottom_right.top.circle_radius, top_right.top.circle_radius,
bottom_left.top.circle_radius, top_left.top.circle_radius);
frag_info.radii_height =
Vector4(bottom_right.right.circle_radius, top_right.right.circle_radius,
bottom_left.right.circle_radius, top_left.right.circle_radius);
frag_info.circle_centers_top_x =
Vector4(bottom_right.top.circle_center.x, top_right.top.circle_center.x,
bottom_left.top.circle_center.x, top_left.top.circle_center.x);
frag_info.circle_centers_top_y =
Vector4(bottom_right.top.circle_center.y, top_right.top.circle_center.y,
bottom_left.top.circle_center.y, top_left.top.circle_center.y);
frag_info.circle_centers_right_x = Vector4(
bottom_right.right.circle_center.x, top_right.right.circle_center.x,
bottom_left.right.circle_center.x, top_left.right.circle_center.x);
frag_info.circle_centers_right_y = Vector4(
bottom_right.right.circle_center.y, top_right.right.circle_center.y,
bottom_left.right.circle_center.y, top_left.right.circle_center.y);
frag_info.superellipse_scales_x =
Vector4(bottom_right.signed_scale.Abs().x, top_right.signed_scale.Abs().x,
bottom_left.signed_scale.Abs().x, top_left.signed_scale.Abs().x);
frag_info.superellipse_scales_y =
Vector4(bottom_right.signed_scale.Abs().y, top_right.signed_scale.Abs().y,
bottom_left.signed_scale.Abs().y, top_left.signed_scale.Abs().y);
frag_info.quadrant_centers_x =
Vector4(bottom_right_center_relative.x, top_right_center_relative.x,
bottom_left_center_relative.x, top_left_center_relative.x);
frag_info.quadrant_centers_y =
Vector4(bottom_right_center_relative.y, top_right_center_relative.y,
bottom_left_center_relative.y, top_left_center_relative.y);
frag_info.quadrant_splits =
Vector4(round_superellipse_params_.top_split - center.x,
round_superellipse_params_.bottom_split - center.x,
round_superellipse_params_.left_split - center.y,
round_superellipse_params_.right_split - center.y);

auto geometry_result =
GetGeometry()->GetPositionBuffer(renderer, entity, pass);

PipelineBuilderCallback pipeline_callback =
[&renderer](ContentContextOptions options) {
return renderer.GetComplexRSEPipeline(options);
};

return ColorSourceContents::DrawGeometry<VS>(
this, GetGeometry(), renderer, entity, pass, pipeline_callback,
frame_info,
/*bind_fragment_callback=*/
[&frag_info, &data_host_buffer](RenderPass& pass) {
FS::BindFragInfo(pass, data_host_buffer.EmplaceUniform(frag_info));
return true;
});
}

std::optional<Rect> ComplexRoundedSuperellipseContents::GetCoverage(
const Entity& entity) const {
return GetGeometry()->GetCoverage(entity.GetTransform());
}

const Geometry* ComplexRoundedSuperellipseContents::GetGeometry() const {
return geometry_.get();
}

} // namespace impeller
62 changes: 62 additions & 0 deletions engine/src/flutter/impeller/entity/contents/complex_rse_contents.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef FLUTTER_IMPELLER_ENTITY_CONTENTS_COMPLEX_RSE_CONTENTS_H_
#define FLUTTER_IMPELLER_ENTITY_CONTENTS_COMPLEX_RSE_CONTENTS_H_

#include <memory>
#include <optional>

#include "flutter/impeller/entity/contents/color_source_contents.h"
#include "flutter/impeller/entity/contents/contents.h"
#include "impeller/entity/geometry/geometry.h"
#include "impeller/geometry/color.h"
#include "impeller/geometry/rect.h"
#include "impeller/geometry/round_superellipse_param.h"
#include "impeller/geometry/stroke_parameters.h"

namespace impeller {

/// A Contents class that renders asymmetric rounded superellipses using SDFs.
///
/// Separated from 'UberSDFContents' to reduce uniform bloat
class ComplexRoundedSuperellipseContents : public ColorSourceContents {
Comment thread
walley892 marked this conversation as resolved.
public:
static std::unique_ptr<ComplexRoundedSuperellipseContents> Make(
Color color,
const Rect& bounds,
const RoundSuperellipseParam& round_superellipse_params,
std::optional<StrokeParameters> stroke);

bool Render(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const override;

std::optional<Rect> GetCoverage(const Entity& entity) const override;

const Geometry* GetGeometry() const override;

private:
explicit ComplexRoundedSuperellipseContents(
Color color,
const Rect& bounds,
const RoundSuperellipseParam& round_superellipse_params,
std::optional<StrokeParameters> stroke);

Color color_;
Rect bounds_;
RoundSuperellipseParam round_superellipse_params_;
std::optional<StrokeParameters> stroke_;
std::unique_ptr<Geometry> geometry_;

ComplexRoundedSuperellipseContents(
const ComplexRoundedSuperellipseContents&) = delete;

ComplexRoundedSuperellipseContents& operator=(
const ComplexRoundedSuperellipseContents&) = delete;
};

} // namespace impeller

#endif // FLUTTER_IMPELLER_ENTITY_CONTENTS_COMPLEX_RSE_CONTENTS_H_
Loading
Loading