[Impeller] give geometry classes similar API as entity / implement strokepathgeom (flutter/engine#36693)
This commit is contained in:
@@ -1481,13 +1481,9 @@ TEST_P(AiksTest, SolidStrokesRenderCorrectly) {
|
||||
canvas.ClipPath(PathBuilder{}.AddCircle(middle, radius).TakePath());
|
||||
}
|
||||
|
||||
for (auto join :
|
||||
{SolidStrokeContents::Join::kBevel, SolidStrokeContents::Join::kRound,
|
||||
SolidStrokeContents::Join::kMiter}) {
|
||||
for (auto join : {Join::kBevel, Join::kRound, Join::kMiter}) {
|
||||
paint.stroke_join = join;
|
||||
for (auto cap :
|
||||
{SolidStrokeContents::Cap::kButt, SolidStrokeContents::Cap::kSquare,
|
||||
SolidStrokeContents::Cap::kRound}) {
|
||||
for (auto cap : {Cap::kButt, Cap::kSquare, Cap::kRound}) {
|
||||
paint.stroke_cap = cap;
|
||||
canvas.DrawPath(path, paint);
|
||||
canvas.Translate({80, 0});
|
||||
|
||||
@@ -209,7 +209,7 @@ void Canvas::DrawCircle(Point center, Scalar radius, Paint paint) {
|
||||
|
||||
void Canvas::ClipPath(Path path, Entity::ClipOperation clip_op) {
|
||||
auto contents = std::make_shared<ClipContents>();
|
||||
contents->SetGeometry(Geometry::MakePath(std::move(path)));
|
||||
contents->SetGeometry(Geometry::MakeFillPath(std::move(path)));
|
||||
contents->SetClipOperation(clip_op);
|
||||
|
||||
Entity entity;
|
||||
|
||||
@@ -15,7 +15,7 @@ std::shared_ptr<Contents> Paint::CreateContentsForEntity(Path path,
|
||||
auto& source = color_source.value();
|
||||
auto contents = source();
|
||||
contents->SetGeometry(cover ? Geometry::MakeCover()
|
||||
: Geometry::MakePath(std::move(path)));
|
||||
: Geometry::MakeFillPath(std::move(path)));
|
||||
contents->SetAlpha(color.alpha);
|
||||
return contents;
|
||||
}
|
||||
@@ -24,18 +24,16 @@ std::shared_ptr<Contents> Paint::CreateContentsForEntity(Path path,
|
||||
case Style::kFill: {
|
||||
auto solid_color = std::make_shared<SolidColorContents>();
|
||||
solid_color->SetGeometry(cover ? Geometry::MakeCover()
|
||||
: Geometry::MakePath(std::move(path)));
|
||||
: Geometry::MakeFillPath(std::move(path)));
|
||||
solid_color->SetColor(color);
|
||||
return solid_color;
|
||||
}
|
||||
case Style::kStroke: {
|
||||
auto solid_stroke = std::make_shared<SolidStrokeContents>();
|
||||
solid_stroke->SetPath(std::move(path));
|
||||
solid_stroke->SetGeometry(
|
||||
Geometry::MakeStrokePath(std::move(path), stroke_width, stroke_miter,
|
||||
stroke_cap, stroke_join));
|
||||
solid_stroke->SetColor(color);
|
||||
solid_stroke->SetStrokeSize(stroke_width);
|
||||
solid_stroke->SetStrokeMiter(stroke_miter);
|
||||
solid_stroke->SetStrokeCap(stroke_cap);
|
||||
solid_stroke->SetStrokeJoin(stroke_join);
|
||||
return solid_stroke;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "impeller/entity/contents/solid_stroke_contents.h"
|
||||
#include "impeller/entity/contents/sweep_gradient_contents.h"
|
||||
#include "impeller/entity/entity.h"
|
||||
#include "impeller/entity/geometry.h"
|
||||
#include "impeller/geometry/color.h"
|
||||
|
||||
namespace impeller {
|
||||
@@ -50,8 +51,8 @@ struct Paint {
|
||||
std::optional<ColorSourceProc> color_source;
|
||||
|
||||
Scalar stroke_width = 0.0;
|
||||
SolidStrokeContents::Cap stroke_cap = SolidStrokeContents::Cap::kButt;
|
||||
SolidStrokeContents::Join stroke_join = SolidStrokeContents::Join::kMiter;
|
||||
Cap stroke_cap = Cap::kButt;
|
||||
Join stroke_join = Join::kMiter;
|
||||
Scalar stroke_miter = 4.0;
|
||||
Style style = Style::kFill;
|
||||
BlendMode blend_mode = BlendMode::kSourceOver;
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "impeller/entity/contents/sweep_gradient_contents.h"
|
||||
#include "impeller/entity/contents/tiled_texture_contents.h"
|
||||
#include "impeller/entity/entity.h"
|
||||
#include "impeller/entity/geometry.h"
|
||||
#include "impeller/geometry/path.h"
|
||||
#include "impeller/geometry/path_builder.h"
|
||||
#include "impeller/geometry/scalar.h"
|
||||
@@ -227,13 +228,13 @@ void DisplayListDispatcher::setStrokeMiter(SkScalar limit) {
|
||||
void DisplayListDispatcher::setStrokeCap(flutter::DlStrokeCap cap) {
|
||||
switch (cap) {
|
||||
case flutter::DlStrokeCap::kButt:
|
||||
paint_.stroke_cap = SolidStrokeContents::Cap::kButt;
|
||||
paint_.stroke_cap = Cap::kButt;
|
||||
break;
|
||||
case flutter::DlStrokeCap::kRound:
|
||||
paint_.stroke_cap = SolidStrokeContents::Cap::kRound;
|
||||
paint_.stroke_cap = Cap::kRound;
|
||||
break;
|
||||
case flutter::DlStrokeCap::kSquare:
|
||||
paint_.stroke_cap = SolidStrokeContents::Cap::kSquare;
|
||||
paint_.stroke_cap = Cap::kSquare;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -242,13 +243,13 @@ void DisplayListDispatcher::setStrokeCap(flutter::DlStrokeCap cap) {
|
||||
void DisplayListDispatcher::setStrokeJoin(flutter::DlStrokeJoin join) {
|
||||
switch (join) {
|
||||
case flutter::DlStrokeJoin::kMiter:
|
||||
paint_.stroke_join = SolidStrokeContents::Join::kMiter;
|
||||
paint_.stroke_join = Join::kMiter;
|
||||
break;
|
||||
case flutter::DlStrokeJoin::kRound:
|
||||
paint_.stroke_join = SolidStrokeContents::Join::kRound;
|
||||
paint_.stroke_join = Join::kRound;
|
||||
break;
|
||||
case flutter::DlStrokeJoin::kBevel:
|
||||
paint_.stroke_join = SolidStrokeContents::Join::kBevel;
|
||||
paint_.stroke_join = Join::kBevel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1010,8 +1011,8 @@ void DisplayListDispatcher::drawPoints(SkCanvas::PointMode mode,
|
||||
paint.style = Paint::Style::kStroke;
|
||||
switch (mode) {
|
||||
case SkCanvas::kPoints_PointMode:
|
||||
if (paint.stroke_cap == SolidStrokeContents::Cap::kButt) {
|
||||
paint.stroke_cap = SolidStrokeContents::Cap::kSquare;
|
||||
if (paint.stroke_cap == Cap::kButt) {
|
||||
paint.stroke_cap = Cap::kSquare;
|
||||
}
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
Point p0 = ToPoint(points[i]);
|
||||
|
||||
@@ -117,12 +117,8 @@ bool ClipContents::Render(const ContentContext& renderer,
|
||||
|
||||
cmd.pipeline = renderer.GetClipPipeline(options);
|
||||
|
||||
auto& host_buffer = pass.GetTransientsBuffer();
|
||||
auto allocator = renderer.GetContext()->GetResourceAllocator();
|
||||
auto geometry_result = geometry_->GetPositionBuffer(
|
||||
allocator, host_buffer, renderer.GetTessellator(),
|
||||
pass.GetRenderTargetSize(),
|
||||
entity.GetTransformation().GetMaxBasisLength());
|
||||
auto geometry_result = geometry_->GetPositionBuffer(renderer, entity, pass);
|
||||
cmd.BindVertices(geometry_result.vertex_buffer);
|
||||
cmd.primitive_type = geometry_result.type;
|
||||
info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
|
||||
|
||||
@@ -236,7 +236,7 @@ static std::optional<Snapshot> PipelineBlend(
|
||||
|
||||
if (foreground_color.has_value()) {
|
||||
auto contents = std::make_shared<SolidColorContents>();
|
||||
contents->SetGeometry(Geometry::MakePath(
|
||||
contents->SetGeometry(Geometry::MakeFillPath(
|
||||
PathBuilder{}
|
||||
.AddRect(Rect::MakeSize(pass.GetRenderTargetSize()))
|
||||
.TakePath()));
|
||||
|
||||
@@ -76,12 +76,9 @@ bool LinearGradientContents::Render(const ContentContext& renderer,
|
||||
OptionsFromPassAndEntity(pass, entity));
|
||||
cmd.stencil_reference = entity.GetStencilDepth();
|
||||
|
||||
auto& host_buffer = pass.GetTransientsBuffer();
|
||||
auto allocator = renderer.GetContext()->GetResourceAllocator();
|
||||
auto geometry_result = GetGeometry()->GetPositionBuffer(
|
||||
allocator, host_buffer, renderer.GetTessellator(),
|
||||
pass.GetRenderTargetSize(),
|
||||
entity.GetTransformation().GetMaxBasisLength());
|
||||
auto geometry_result =
|
||||
GetGeometry()->GetPositionBuffer(renderer, entity, pass);
|
||||
cmd.BindVertices(geometry_result.vertex_buffer);
|
||||
cmd.primitive_type = geometry_result.type;
|
||||
FS::BindGradientInfo(
|
||||
|
||||
@@ -75,12 +75,10 @@ bool RadialGradientContents::Render(const ContentContext& renderer,
|
||||
cmd.pipeline = renderer.GetRadialGradientFillPipeline(
|
||||
OptionsFromPassAndEntity(pass, entity));
|
||||
cmd.stencil_reference = entity.GetStencilDepth();
|
||||
auto& host_buffer = pass.GetTransientsBuffer();
|
||||
|
||||
auto allocator = renderer.GetContext()->GetResourceAllocator();
|
||||
auto geometry_result = GetGeometry()->GetPositionBuffer(
|
||||
allocator, host_buffer, renderer.GetTessellator(),
|
||||
pass.GetRenderTargetSize(),
|
||||
entity.GetTransformation().GetMaxBasisLength());
|
||||
auto geometry_result =
|
||||
GetGeometry()->GetPositionBuffer(renderer, entity, pass);
|
||||
cmd.BindVertices(geometry_result.vertex_buffer);
|
||||
cmd.primitive_type = geometry_result.type;
|
||||
FS::BindGradientInfo(
|
||||
|
||||
@@ -78,10 +78,8 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer,
|
||||
/// Resolve geometry.
|
||||
///
|
||||
|
||||
auto geometry_result = GetGeometry()->GetPositionBuffer(
|
||||
context->GetResourceAllocator(), pass.GetTransientsBuffer(),
|
||||
renderer.GetTessellator(), pass.GetRenderTargetSize(),
|
||||
entity.GetTransformation().GetMaxBasisLength());
|
||||
auto geometry_result =
|
||||
GetGeometry()->GetPositionBuffer(renderer, entity, pass);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/// Get or create runtime stage pipeline.
|
||||
|
||||
@@ -59,12 +59,7 @@ bool SolidColorContents::Render(const ContentContext& renderer,
|
||||
renderer.GetSolidFillPipeline(OptionsFromPassAndEntity(pass, entity));
|
||||
cmd.stencil_reference = entity.GetStencilDepth();
|
||||
|
||||
auto& host_buffer = pass.GetTransientsBuffer();
|
||||
auto allocator = renderer.GetContext()->GetResourceAllocator();
|
||||
auto geometry_result = geometry_->GetPositionBuffer(
|
||||
allocator, host_buffer, renderer.GetTessellator(),
|
||||
pass.GetRenderTargetSize(),
|
||||
entity.GetTransformation().GetMaxBasisLength());
|
||||
auto geometry_result = geometry_->GetPositionBuffer(renderer, entity, pass);
|
||||
cmd.BindVertices(geometry_result.vertex_buffer);
|
||||
cmd.primitive_type = geometry_result.type;
|
||||
|
||||
@@ -87,7 +82,7 @@ bool SolidColorContents::Render(const ContentContext& renderer,
|
||||
std::unique_ptr<SolidColorContents> SolidColorContents::Make(Path path,
|
||||
Color color) {
|
||||
auto contents = std::make_unique<SolidColorContents>();
|
||||
contents->SetGeometry(Geometry::MakePath(std::move(path)));
|
||||
contents->SetGeometry(Geometry::MakeFillPath(std::move(path)));
|
||||
contents->SetColor(color);
|
||||
return contents;
|
||||
}
|
||||
|
||||
@@ -9,15 +9,14 @@
|
||||
#include "impeller/entity/contents/clip_contents.h"
|
||||
#include "impeller/entity/contents/content_context.h"
|
||||
#include "impeller/entity/entity.h"
|
||||
#include "impeller/entity/solid_fill.frag.h"
|
||||
#include "impeller/entity/solid_fill.vert.h"
|
||||
#include "impeller/geometry/path_builder.h"
|
||||
#include "impeller/renderer/render_pass.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
SolidStrokeContents::SolidStrokeContents() {
|
||||
SetStrokeCap(Cap::kButt);
|
||||
SetStrokeJoin(Join::kMiter);
|
||||
}
|
||||
SolidStrokeContents::SolidStrokeContents(){};
|
||||
|
||||
SolidStrokeContents::~SolidStrokeContents() = default;
|
||||
|
||||
@@ -29,8 +28,8 @@ const Color& SolidStrokeContents::GetColor() const {
|
||||
return color_;
|
||||
}
|
||||
|
||||
void SolidStrokeContents::SetPath(Path path) {
|
||||
path_ = std::move(path);
|
||||
void SolidStrokeContents::SetGeometry(std::unique_ptr<Geometry> geometry) {
|
||||
geometry_ = std::move(geometry);
|
||||
}
|
||||
|
||||
std::optional<Rect> SolidStrokeContents::GetCoverage(
|
||||
@@ -38,386 +37,46 @@ std::optional<Rect> SolidStrokeContents::GetCoverage(
|
||||
if (color_.IsTransparent()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto path_bounds = path_.GetBoundingBox();
|
||||
if (!path_bounds.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
auto path_coverage = path_bounds->TransformBounds(entity.GetTransformation());
|
||||
|
||||
Scalar max_radius = 0.5;
|
||||
if (cap_ == Cap::kSquare) {
|
||||
max_radius = max_radius * kSqrt2;
|
||||
}
|
||||
if (join_ == Join::kMiter) {
|
||||
max_radius = std::max(max_radius, miter_limit_ * 0.5f);
|
||||
}
|
||||
Scalar determinant = entity.GetTransformation().GetDeterminant();
|
||||
if (determinant == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
Scalar min_size = 1.0f / sqrt(std::abs(determinant));
|
||||
Vector2 max_radius_xy = entity.GetTransformation().TransformDirection(
|
||||
Vector2(max_radius, max_radius) * std::max(stroke_size_, min_size));
|
||||
|
||||
return Rect(path_coverage.origin - max_radius_xy,
|
||||
Size(path_coverage.size.width + max_radius_xy.x * 2,
|
||||
path_coverage.size.height + max_radius_xy.y * 2));
|
||||
}
|
||||
|
||||
static VertexBuffer CreateSolidStrokeVertices(
|
||||
const Path& path,
|
||||
HostBuffer& buffer,
|
||||
Scalar stroke_width,
|
||||
const SolidStrokeContents::CapProc& cap_proc,
|
||||
const SolidStrokeContents::JoinProc& join_proc,
|
||||
Scalar miter_limit,
|
||||
Scalar tolerance) {
|
||||
using VS = SolidFillVertexShader;
|
||||
|
||||
VertexBufferBuilder<VS::PerVertexData> vtx_builder;
|
||||
auto polyline = path.CreatePolyline();
|
||||
|
||||
VS::PerVertexData vtx;
|
||||
|
||||
// Offset state.
|
||||
Point offset;
|
||||
Point previous_offset; // Used for computing joins.
|
||||
|
||||
auto compute_offset = [&polyline, &offset, &previous_offset,
|
||||
&stroke_width](size_t point_i) {
|
||||
previous_offset = offset;
|
||||
Point direction =
|
||||
(polyline.points[point_i] - polyline.points[point_i - 1]).Normalize();
|
||||
offset = Vector2{-direction.y, direction.x} * stroke_width * 0.5;
|
||||
};
|
||||
|
||||
for (size_t contour_i = 0; contour_i < polyline.contours.size();
|
||||
contour_i++) {
|
||||
size_t contour_start_point_i, contour_end_point_i;
|
||||
std::tie(contour_start_point_i, contour_end_point_i) =
|
||||
polyline.GetContourPointBounds(contour_i);
|
||||
|
||||
switch (contour_end_point_i - contour_start_point_i) {
|
||||
case 1: {
|
||||
Point p = polyline.points[contour_start_point_i];
|
||||
cap_proc(vtx_builder, p, {-stroke_width * 0.5f, 0}, tolerance);
|
||||
cap_proc(vtx_builder, p, {stroke_width * 0.5f, 0}, tolerance);
|
||||
continue;
|
||||
}
|
||||
case 0:
|
||||
continue; // This contour has no renderable content.
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// The first point's offset is always the same as the second point.
|
||||
compute_offset(contour_start_point_i + 1);
|
||||
const Point contour_first_offset = offset;
|
||||
|
||||
if (contour_i > 0) {
|
||||
// This branch only executes when we've just finished drawing a contour
|
||||
// and are switching to a new one.
|
||||
// We're drawing a triangle strip, so we need to "pick up the pen" by
|
||||
// appending two vertices at the end of the previous contour and two
|
||||
// vertices at the start of the new contour (thus connecting the two
|
||||
// contours with two zero volume triangles, which will be discarded by the
|
||||
// rasterizer).
|
||||
vtx.position = polyline.points[contour_start_point_i - 1];
|
||||
// Append two vertices when "picking up" the pen so that the triangle
|
||||
// drawn when moving to the beginning of the new contour will have zero
|
||||
// volume.
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
|
||||
vtx.position = polyline.points[contour_start_point_i];
|
||||
// Append two vertices at the beginning of the new contour, which appends
|
||||
// two triangles of zero area.
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
}
|
||||
|
||||
// Generate start cap.
|
||||
if (!polyline.contours[contour_i].is_closed) {
|
||||
cap_proc(vtx_builder, polyline.points[contour_start_point_i], -offset,
|
||||
tolerance);
|
||||
}
|
||||
|
||||
// Generate contour geometry.
|
||||
for (size_t point_i = contour_start_point_i; point_i < contour_end_point_i;
|
||||
point_i++) {
|
||||
if (point_i > contour_start_point_i) {
|
||||
// Generate line rect.
|
||||
vtx.position = polyline.points[point_i - 1] + offset;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
vtx.position = polyline.points[point_i - 1] - offset;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
vtx.position = polyline.points[point_i] + offset;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
vtx.position = polyline.points[point_i] - offset;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
|
||||
if (point_i < contour_end_point_i - 1) {
|
||||
compute_offset(point_i + 1);
|
||||
|
||||
// Generate join from the current line to the next line.
|
||||
join_proc(vtx_builder, polyline.points[point_i], previous_offset,
|
||||
offset, miter_limit, tolerance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate end cap or join.
|
||||
if (!polyline.contours[contour_i].is_closed) {
|
||||
cap_proc(vtx_builder, polyline.points[contour_end_point_i - 1], offset,
|
||||
tolerance);
|
||||
} else {
|
||||
join_proc(vtx_builder, polyline.points[contour_start_point_i], offset,
|
||||
contour_first_offset, miter_limit, tolerance);
|
||||
}
|
||||
}
|
||||
|
||||
return vtx_builder.CreateVertexBuffer(buffer);
|
||||
return geometry_->GetCoverage(entity.GetTransformation());
|
||||
}
|
||||
|
||||
bool SolidStrokeContents::Render(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) const {
|
||||
if (stroke_size_ < 0.0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
using VS = SolidFillVertexShader;
|
||||
using FS = SolidFillFragmentShader;
|
||||
VS::VertInfo vert_info;
|
||||
vert_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
|
||||
entity.GetTransformation();
|
||||
Scalar determinant = entity.GetTransformation().GetDeterminant();
|
||||
if (determinant == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
FS::FragInfo frag_info;
|
||||
frag_info.color = color_.Premultiply();
|
||||
|
||||
Command cmd;
|
||||
cmd.primitive_type = PrimitiveType::kTriangleStrip;
|
||||
cmd.label = "Solid Stroke";
|
||||
cmd.stencil_reference = entity.GetStencilDepth();
|
||||
|
||||
auto geometry_result = geometry_->GetPositionBuffer(renderer, entity, pass);
|
||||
|
||||
auto options = OptionsFromPassAndEntity(pass, entity);
|
||||
if (!color_.IsOpaque()) {
|
||||
if (geometry_result.prevent_overdraw) {
|
||||
options.stencil_compare = CompareFunction::kEqual;
|
||||
options.stencil_operation = StencilOperation::kIncrementClamp;
|
||||
}
|
||||
cmd.pipeline = renderer.GetSolidFillPipeline(options);
|
||||
cmd.stencil_reference = entity.GetStencilDepth();
|
||||
|
||||
auto tolerance =
|
||||
kDefaultCurveTolerance /
|
||||
(stroke_size_ * entity.GetTransformation().GetMaxBasisLength());
|
||||
|
||||
Scalar min_size = 1.0f / sqrt(std::abs(determinant));
|
||||
Scalar stroke_width = std::max(stroke_size_, min_size);
|
||||
cmd.BindVertices(CreateSolidStrokeVertices(
|
||||
path_, pass.GetTransientsBuffer(), stroke_width, cap_proc_, join_proc_,
|
||||
miter_limit_ * stroke_width * 0.5, tolerance));
|
||||
cmd.BindVertices(geometry_result.vertex_buffer);
|
||||
cmd.primitive_type = geometry_result.type;
|
||||
VS::BindVertInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(vert_info));
|
||||
FS::BindFragInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frag_info));
|
||||
|
||||
pass.AddCommand(cmd);
|
||||
|
||||
if (!color_.IsOpaque()) {
|
||||
if (geometry_result.prevent_overdraw) {
|
||||
return ClipRestoreContents().Render(renderer, entity, pass);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SolidStrokeContents::SetStrokeSize(Scalar size) {
|
||||
stroke_size_ = size;
|
||||
}
|
||||
|
||||
Scalar SolidStrokeContents::GetStrokeSize() const {
|
||||
return stroke_size_;
|
||||
}
|
||||
|
||||
void SolidStrokeContents::SetStrokeMiter(Scalar miter_limit) {
|
||||
if (miter_limit < 0) {
|
||||
return; // Skia behaves like this.
|
||||
}
|
||||
miter_limit_ = miter_limit;
|
||||
}
|
||||
|
||||
Scalar SolidStrokeContents::GetStrokeMiter() {
|
||||
return miter_limit_;
|
||||
}
|
||||
|
||||
void SolidStrokeContents::SetStrokeCap(Cap cap) {
|
||||
cap_ = cap;
|
||||
|
||||
switch (cap) {
|
||||
case Cap::kButt:
|
||||
cap_proc_ = [](VertexBufferBuilder<VS::PerVertexData>& vtx_builder,
|
||||
const Point& position, const Point& offset,
|
||||
Scalar tolerance) {};
|
||||
break;
|
||||
case Cap::kRound:
|
||||
cap_proc_ = [](VertexBufferBuilder<VS::PerVertexData>& vtx_builder,
|
||||
const Point& position, const Point& offset,
|
||||
Scalar tolerance) {
|
||||
VS::PerVertexData vtx;
|
||||
|
||||
Point forward(offset.y, -offset.x);
|
||||
Point forward_normal = forward.Normalize();
|
||||
|
||||
auto arc_points =
|
||||
CubicPathComponent(
|
||||
offset, offset + forward * PathBuilder::kArcApproximationMagic,
|
||||
forward + offset * PathBuilder::kArcApproximationMagic, forward)
|
||||
.CreatePolyline(tolerance);
|
||||
|
||||
vtx.position = position + offset;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
vtx.position = position - offset;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
for (const auto& point : arc_points) {
|
||||
vtx.position = position + point;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
vtx.position = position + (-point).Reflect(forward_normal);
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
}
|
||||
};
|
||||
break;
|
||||
case Cap::kSquare:
|
||||
cap_proc_ = [](VertexBufferBuilder<VS::PerVertexData>& vtx_builder,
|
||||
const Point& position, const Point& offset,
|
||||
Scalar tolerance) {
|
||||
VS::PerVertexData vtx;
|
||||
vtx.position = position;
|
||||
|
||||
Point forward(offset.y, -offset.x);
|
||||
|
||||
vtx.position = position + offset;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
vtx.position = position - offset;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
vtx.position = position + offset + forward;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
vtx.position = position - offset + forward;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SolidStrokeContents::Cap SolidStrokeContents::GetStrokeCap() {
|
||||
return cap_;
|
||||
}
|
||||
|
||||
static Scalar CreateBevelAndGetDirection(
|
||||
VertexBufferBuilder<SolidFillVertexShader::PerVertexData>& vtx_builder,
|
||||
const Point& position,
|
||||
const Point& start_offset,
|
||||
const Point& end_offset) {
|
||||
SolidFillVertexShader::PerVertexData vtx;
|
||||
vtx.position = position;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
|
||||
Scalar dir = start_offset.Cross(end_offset) > 0 ? -1 : 1;
|
||||
vtx.position = position + start_offset * dir;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
vtx.position = position + end_offset * dir;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
void SolidStrokeContents::SetStrokeJoin(Join join) {
|
||||
join_ = join;
|
||||
|
||||
switch (join) {
|
||||
case Join::kBevel:
|
||||
join_proc_ = [](VertexBufferBuilder<VS::PerVertexData>& vtx_builder,
|
||||
const Point& position, const Point& start_offset,
|
||||
const Point& end_offset, Scalar miter_limit,
|
||||
Scalar tolerance) {
|
||||
CreateBevelAndGetDirection(vtx_builder, position, start_offset,
|
||||
end_offset);
|
||||
};
|
||||
break;
|
||||
case Join::kMiter:
|
||||
join_proc_ = [](VertexBufferBuilder<VS::PerVertexData>& vtx_builder,
|
||||
const Point& position, const Point& start_offset,
|
||||
const Point& end_offset, Scalar miter_limit,
|
||||
Scalar tolerance) {
|
||||
Point start_normal = start_offset.Normalize();
|
||||
Point end_normal = end_offset.Normalize();
|
||||
|
||||
// 1 for no joint (straight line), 0 for max joint (180 degrees).
|
||||
Scalar alignment = (start_normal.Dot(end_normal) + 1) / 2;
|
||||
if (ScalarNearlyEqual(alignment, 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Scalar dir = CreateBevelAndGetDirection(vtx_builder, position,
|
||||
start_offset, end_offset);
|
||||
|
||||
Point miter_point = (start_offset + end_offset) / 2 / alignment;
|
||||
if (miter_point.GetDistanceSquared({0, 0}) >
|
||||
miter_limit * miter_limit) {
|
||||
return; // Convert to bevel when we exceed the miter limit.
|
||||
}
|
||||
|
||||
// Outer miter point.
|
||||
VS::PerVertexData vtx;
|
||||
vtx.position = position + miter_point * dir;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
};
|
||||
break;
|
||||
case Join::kRound:
|
||||
join_proc_ = [](VertexBufferBuilder<VS::PerVertexData>& vtx_builder,
|
||||
const Point& position, const Point& start_offset,
|
||||
const Point& end_offset, Scalar miter_limit,
|
||||
Scalar tolerance) {
|
||||
Point start_normal = start_offset.Normalize();
|
||||
Point end_normal = end_offset.Normalize();
|
||||
|
||||
// 0 for no joint (straight line), 1 for max joint (180 degrees).
|
||||
Scalar alignment = 1 - (start_normal.Dot(end_normal) + 1) / 2;
|
||||
if (ScalarNearlyEqual(alignment, 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Scalar dir = CreateBevelAndGetDirection(vtx_builder, position,
|
||||
start_offset, end_offset);
|
||||
|
||||
Point middle =
|
||||
(start_offset + end_offset).Normalize() * start_offset.GetLength();
|
||||
Point middle_normal = middle.Normalize();
|
||||
|
||||
Point middle_handle = middle + Point(-middle.y, middle.x) *
|
||||
PathBuilder::kArcApproximationMagic *
|
||||
alignment * dir;
|
||||
Point start_handle =
|
||||
start_offset + Point(start_offset.y, -start_offset.x) *
|
||||
PathBuilder::kArcApproximationMagic * alignment *
|
||||
dir;
|
||||
|
||||
auto arc_points = CubicPathComponent(start_offset, start_handle,
|
||||
middle_handle, middle)
|
||||
.CreatePolyline(tolerance);
|
||||
|
||||
VS::PerVertexData vtx;
|
||||
for (const auto& point : arc_points) {
|
||||
vtx.position = position + point * dir;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
vtx.position = position + (-point * dir).Reflect(middle_normal);
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SolidStrokeContents::Join SolidStrokeContents::GetStrokeJoin() {
|
||||
return join_;
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "impeller/entity/contents/content_context.h"
|
||||
#include "impeller/entity/contents/contents.h"
|
||||
#include "impeller/entity/solid_fill.frag.h"
|
||||
#include "impeller/entity/solid_fill.vert.h"
|
||||
#include "impeller/entity/geometry.h"
|
||||
#include "impeller/geometry/color.h"
|
||||
#include "impeller/geometry/path_component.h"
|
||||
#include "impeller/geometry/point.h"
|
||||
@@ -20,61 +19,17 @@
|
||||
namespace impeller {
|
||||
|
||||
class SolidStrokeContents final : public Contents {
|
||||
using VS = SolidFillVertexShader;
|
||||
using FS = SolidFillFragmentShader;
|
||||
|
||||
public:
|
||||
enum class Cap {
|
||||
kButt,
|
||||
kRound,
|
||||
kSquare,
|
||||
};
|
||||
|
||||
enum class Join {
|
||||
kMiter,
|
||||
kRound,
|
||||
kBevel,
|
||||
};
|
||||
|
||||
using CapProc =
|
||||
std::function<void(VertexBufferBuilder<VS::PerVertexData>& vtx_builder,
|
||||
const Point& position,
|
||||
const Point& offset,
|
||||
Scalar tolerance)>;
|
||||
using JoinProc =
|
||||
std::function<void(VertexBufferBuilder<VS::PerVertexData>& vtx_builder,
|
||||
const Point& position,
|
||||
const Point& start_offset,
|
||||
const Point& end_offset,
|
||||
Scalar miter_limit,
|
||||
Scalar tolerance)>;
|
||||
|
||||
SolidStrokeContents();
|
||||
|
||||
~SolidStrokeContents() override;
|
||||
|
||||
void SetPath(Path path);
|
||||
void SetGeometry(std::unique_ptr<Geometry> geometry);
|
||||
|
||||
void SetColor(Color color);
|
||||
|
||||
const Color& GetColor() const;
|
||||
|
||||
void SetStrokeSize(Scalar size);
|
||||
|
||||
Scalar GetStrokeSize() const;
|
||||
|
||||
void SetStrokeMiter(Scalar miter_limit);
|
||||
|
||||
Scalar GetStrokeMiter();
|
||||
|
||||
void SetStrokeCap(Cap cap);
|
||||
|
||||
Cap GetStrokeCap();
|
||||
|
||||
void SetStrokeJoin(Join join);
|
||||
|
||||
Join GetStrokeJoin();
|
||||
|
||||
// |Contents|
|
||||
std::optional<Rect> GetCoverage(const Entity& entity) const override;
|
||||
|
||||
@@ -84,17 +39,8 @@ class SolidStrokeContents final : public Contents {
|
||||
RenderPass& pass) const override;
|
||||
|
||||
private:
|
||||
Path path_;
|
||||
|
||||
std::unique_ptr<Geometry> geometry_;
|
||||
Color color_;
|
||||
Scalar stroke_size_ = 0.0;
|
||||
Scalar miter_limit_ = 4.0;
|
||||
|
||||
Cap cap_;
|
||||
CapProc cap_proc_;
|
||||
|
||||
Join join_;
|
||||
JoinProc join_proc_;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(SolidStrokeContents);
|
||||
};
|
||||
|
||||
@@ -81,12 +81,10 @@ bool SweepGradientContents::Render(const ContentContext& renderer,
|
||||
cmd.pipeline = renderer.GetSweepGradientFillPipeline(
|
||||
OptionsFromPassAndEntity(pass, entity));
|
||||
cmd.stencil_reference = entity.GetStencilDepth();
|
||||
auto& host_buffer = pass.GetTransientsBuffer();
|
||||
|
||||
auto allocator = renderer.GetContext()->GetResourceAllocator();
|
||||
auto geometry_result = GetGeometry()->GetPositionBuffer(
|
||||
allocator, host_buffer, renderer.GetTessellator(),
|
||||
pass.GetRenderTargetSize(),
|
||||
entity.GetTransformation().GetMaxBasisLength());
|
||||
auto geometry_result =
|
||||
GetGeometry()->GetPositionBuffer(renderer, entity, pass);
|
||||
cmd.BindVertices(geometry_result.vertex_buffer);
|
||||
cmd.primitive_type = geometry_result.type;
|
||||
FS::BindGradientInfo(
|
||||
|
||||
@@ -67,11 +67,9 @@ bool TiledTextureContents::Render(const ContentContext& renderer,
|
||||
cmd.pipeline =
|
||||
renderer.GetTiledTexturePipeline(OptionsFromPassAndEntity(pass, entity));
|
||||
cmd.stencil_reference = entity.GetStencilDepth();
|
||||
auto allocator = renderer.GetContext()->GetResourceAllocator();
|
||||
auto geometry_result = GetGeometry()->GetPositionBuffer(
|
||||
allocator, host_buffer, renderer.GetTessellator(),
|
||||
pass.GetRenderTargetSize(),
|
||||
entity.GetTransformation().GetMaxBasisLength());
|
||||
|
||||
auto geometry_result =
|
||||
GetGeometry()->GetPositionBuffer(renderer, entity, pass);
|
||||
cmd.BindVertices(geometry_result.vertex_buffer);
|
||||
cmd.primitive_type = geometry_result.type;
|
||||
VS::BindVertInfo(cmd, host_buffer.EmplaceUniform(vert_info));
|
||||
|
||||
@@ -41,7 +41,6 @@ bool VerticesContents::Render(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) const {
|
||||
auto& host_buffer = pass.GetTransientsBuffer();
|
||||
auto allocator = renderer.GetContext()->GetResourceAllocator();
|
||||
auto vertex_type = geometry_->GetVertexType();
|
||||
|
||||
Command cmd;
|
||||
@@ -53,8 +52,7 @@ bool VerticesContents::Render(const ContentContext& renderer,
|
||||
using VS = GeometryColorPipeline::VertexShader;
|
||||
|
||||
auto geometry_result = geometry_->GetPositionColorBuffer(
|
||||
allocator, host_buffer, renderer.GetTessellator(), color_,
|
||||
blend_mode_);
|
||||
renderer, entity, pass, color_, blend_mode_);
|
||||
cmd.pipeline = renderer.GetGeometryColorPipeline(
|
||||
OptionsFromPassAndEntity(pass, entity));
|
||||
cmd.primitive_type = geometry_result.type;
|
||||
@@ -70,10 +68,8 @@ bool VerticesContents::Render(const ContentContext& renderer,
|
||||
case GeometryVertexType::kPosition: {
|
||||
using VS = GeometryPositionPipeline::VertexShader;
|
||||
|
||||
auto geometry_result = geometry_->GetPositionBuffer(
|
||||
allocator, host_buffer, renderer.GetTessellator(),
|
||||
pass.GetRenderTargetSize(),
|
||||
entity.GetTransformation().GetMaxBasisLength());
|
||||
auto geometry_result =
|
||||
geometry_->GetPositionBuffer(renderer, entity, pass);
|
||||
cmd.pipeline = renderer.GetGeometryPositionPipeline(
|
||||
OptionsFromPassAndEntity(pass, entity));
|
||||
cmd.primitive_type = geometry_result.type;
|
||||
|
||||
@@ -200,9 +200,8 @@ TEST_P(EntityTest, ThreeStrokesInOnePath) {
|
||||
Entity entity;
|
||||
entity.SetTransformation(Matrix::MakeScale(GetContentScale()));
|
||||
auto contents = std::make_unique<SolidStrokeContents>();
|
||||
contents->SetPath(std::move(path));
|
||||
contents->SetGeometry(Geometry::MakeStrokePath(std::move(path), 5.0));
|
||||
contents->SetColor(Color::Red());
|
||||
contents->SetStrokeSize(5.0);
|
||||
entity.SetContents(std::move(contents));
|
||||
ASSERT_TRUE(OpenPlaygroundHere(entity));
|
||||
}
|
||||
@@ -240,9 +239,8 @@ TEST_P(EntityTest, TriangleInsideASquare) {
|
||||
Entity entity;
|
||||
entity.SetTransformation(Matrix::MakeScale(GetContentScale()));
|
||||
auto contents = std::make_unique<SolidStrokeContents>();
|
||||
contents->SetPath(std::move(path));
|
||||
contents->SetGeometry(Geometry::MakeStrokePath(std::move(path), 20.0));
|
||||
contents->SetColor(Color::Red());
|
||||
contents->SetStrokeSize(20.0);
|
||||
entity.SetContents(std::move(contents));
|
||||
|
||||
return entity.Render(context, pass);
|
||||
@@ -282,15 +280,11 @@ TEST_P(EntityTest, StrokeCapAndJoinTest) {
|
||||
|
||||
auto world_matrix = Matrix::MakeScale(GetContentScale());
|
||||
auto render_path = [width = width, &context, &pass, &world_matrix](
|
||||
Path path, SolidStrokeContents::Cap cap,
|
||||
SolidStrokeContents::Join join) {
|
||||
Path path, Cap cap, Join join) {
|
||||
auto contents = std::make_unique<SolidStrokeContents>();
|
||||
contents->SetPath(path);
|
||||
contents->SetGeometry(
|
||||
Geometry::MakeStrokePath(path, width, miter_limit, cap, join));
|
||||
contents->SetColor(Color::Red());
|
||||
contents->SetStrokeSize(width);
|
||||
contents->SetStrokeCap(cap);
|
||||
contents->SetStrokeJoin(join);
|
||||
contents->SetStrokeMiter(miter_limit);
|
||||
|
||||
Entity entity;
|
||||
entity.SetTransformation(world_matrix);
|
||||
@@ -299,7 +293,7 @@ TEST_P(EntityTest, StrokeCapAndJoinTest) {
|
||||
auto coverage = entity.GetCoverage();
|
||||
if (coverage.has_value()) {
|
||||
auto bounds_contents = std::make_unique<SolidColorContents>();
|
||||
bounds_contents->SetGeometry(Geometry::MakePath(
|
||||
bounds_contents->SetGeometry(Geometry::MakeFillPath(
|
||||
PathBuilder{}.AddRect(entity.GetCoverage().value()).TakePath()));
|
||||
bounds_contents->SetColor(Color::Green().WithAlpha(0.5));
|
||||
Entity bounds_entity;
|
||||
@@ -321,8 +315,7 @@ TEST_P(EntityTest, StrokeCapAndJoinTest) {
|
||||
auto [c, d] = IMPELLER_PLAYGROUND_LINE(off + c_def, off + d_def, r,
|
||||
Color::Black(), Color::White());
|
||||
render_path(PathBuilder{}.AddCubicCurve(a, b, d, c).TakePath(),
|
||||
SolidStrokeContents::Cap::kButt,
|
||||
SolidStrokeContents::Join::kBevel);
|
||||
Cap::kButt, Join::kBevel);
|
||||
}
|
||||
|
||||
// Cap::kSquare demo.
|
||||
@@ -333,8 +326,7 @@ TEST_P(EntityTest, StrokeCapAndJoinTest) {
|
||||
auto [c, d] = IMPELLER_PLAYGROUND_LINE(off + c_def, off + d_def, r,
|
||||
Color::Black(), Color::White());
|
||||
render_path(PathBuilder{}.AddCubicCurve(a, b, d, c).TakePath(),
|
||||
SolidStrokeContents::Cap::kSquare,
|
||||
SolidStrokeContents::Join::kBevel);
|
||||
Cap::kSquare, Join::kBevel);
|
||||
}
|
||||
|
||||
// Cap::kRound demo.
|
||||
@@ -345,8 +337,7 @@ TEST_P(EntityTest, StrokeCapAndJoinTest) {
|
||||
auto [c, d] = IMPELLER_PLAYGROUND_LINE(off + c_def, off + d_def, r,
|
||||
Color::Black(), Color::White());
|
||||
render_path(PathBuilder{}.AddCubicCurve(a, b, d, c).TakePath(),
|
||||
SolidStrokeContents::Cap::kRound,
|
||||
SolidStrokeContents::Join::kBevel);
|
||||
Cap::kRound, Join::kBevel);
|
||||
}
|
||||
|
||||
// Join::kBevel demo.
|
||||
@@ -357,7 +348,7 @@ TEST_P(EntityTest, StrokeCapAndJoinTest) {
|
||||
Point c = IMPELLER_PLAYGROUND_POINT(off + c_def, r, Color::White());
|
||||
render_path(
|
||||
PathBuilder{}.MoveTo(a).LineTo(b).LineTo(c).Close().TakePath(),
|
||||
SolidStrokeContents::Cap::kButt, SolidStrokeContents::Join::kBevel);
|
||||
Cap::kButt, Join::kBevel);
|
||||
}
|
||||
|
||||
// Join::kMiter demo.
|
||||
@@ -368,7 +359,7 @@ TEST_P(EntityTest, StrokeCapAndJoinTest) {
|
||||
Point c = IMPELLER_PLAYGROUND_POINT(off + c_def, r, Color::White());
|
||||
render_path(
|
||||
PathBuilder{}.MoveTo(a).LineTo(b).LineTo(c).Close().TakePath(),
|
||||
SolidStrokeContents::Cap::kButt, SolidStrokeContents::Join::kMiter);
|
||||
Cap::kButt, Join::kMiter);
|
||||
}
|
||||
|
||||
// Join::kRound demo.
|
||||
@@ -379,7 +370,7 @@ TEST_P(EntityTest, StrokeCapAndJoinTest) {
|
||||
Point c = IMPELLER_PLAYGROUND_POINT(off + c_def, r, Color::White());
|
||||
render_path(
|
||||
PathBuilder{}.MoveTo(a).LineTo(b).LineTo(c).Close().TakePath(),
|
||||
SolidStrokeContents::Cap::kButt, SolidStrokeContents::Join::kRound);
|
||||
Cap::kButt, Join::kRound);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -639,34 +630,44 @@ TEST_P(EntityTest, CubicCurveAndOverlapTest) {
|
||||
|
||||
TEST_P(EntityTest, SolidStrokeContentsSetStrokeCapsAndJoins) {
|
||||
{
|
||||
SolidStrokeContents stroke;
|
||||
auto geometry = Geometry::MakeStrokePath(Path{});
|
||||
auto path_geometry = static_cast<StrokePathGeometry*>(geometry.get());
|
||||
// Defaults.
|
||||
ASSERT_EQ(stroke.GetStrokeCap(), SolidStrokeContents::Cap::kButt);
|
||||
ASSERT_EQ(stroke.GetStrokeJoin(), SolidStrokeContents::Join::kMiter);
|
||||
ASSERT_EQ(path_geometry->GetStrokeCap(), Cap::kButt);
|
||||
ASSERT_EQ(path_geometry->GetStrokeJoin(), Join::kMiter);
|
||||
}
|
||||
|
||||
{
|
||||
SolidStrokeContents stroke;
|
||||
stroke.SetStrokeCap(SolidStrokeContents::Cap::kSquare);
|
||||
ASSERT_EQ(stroke.GetStrokeCap(), SolidStrokeContents::Cap::kSquare);
|
||||
auto geometry = Geometry::MakeStrokePath(Path{}, 1.0, 4.0, Cap::kSquare);
|
||||
auto path_geometry = static_cast<StrokePathGeometry*>(geometry.get());
|
||||
ASSERT_EQ(path_geometry->GetStrokeCap(), Cap::kSquare);
|
||||
}
|
||||
|
||||
{
|
||||
SolidStrokeContents stroke;
|
||||
stroke.SetStrokeCap(SolidStrokeContents::Cap::kRound);
|
||||
ASSERT_EQ(stroke.GetStrokeCap(), SolidStrokeContents::Cap::kRound);
|
||||
auto geometry = Geometry::MakeStrokePath(Path{}, 1.0, 4.0, Cap::kRound);
|
||||
auto path_geometry = static_cast<StrokePathGeometry*>(geometry.get());
|
||||
ASSERT_EQ(path_geometry->GetStrokeCap(), Cap::kRound);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(EntityTest, SolidStrokeContentsSetMiter) {
|
||||
SolidStrokeContents contents;
|
||||
ASSERT_FLOAT_EQ(contents.GetStrokeMiter(), 4);
|
||||
TEST_P(EntityTest, SolidStrokeContentsSetMiterLimit) {
|
||||
{
|
||||
auto geometry = Geometry::MakeStrokePath(Path{});
|
||||
auto path_geometry = static_cast<StrokePathGeometry*>(geometry.get());
|
||||
ASSERT_FLOAT_EQ(path_geometry->GetMiterLimit(), 4);
|
||||
}
|
||||
|
||||
contents.SetStrokeMiter(8);
|
||||
ASSERT_FLOAT_EQ(contents.GetStrokeMiter(), 8);
|
||||
{
|
||||
auto geometry = Geometry::MakeStrokePath(Path{}, 1.0, /*miter_limit=*/8.0);
|
||||
auto path_geometry = static_cast<StrokePathGeometry*>(geometry.get());
|
||||
ASSERT_FLOAT_EQ(path_geometry->GetMiterLimit(), 8);
|
||||
}
|
||||
|
||||
contents.SetStrokeMiter(-1);
|
||||
ASSERT_FLOAT_EQ(contents.GetStrokeMiter(), 8);
|
||||
{
|
||||
auto geometry = Geometry::MakeStrokePath(Path{}, 1.0, /*miter_limit=*/-1.0);
|
||||
auto path_geometry = static_cast<StrokePathGeometry*>(geometry.get());
|
||||
ASSERT_FLOAT_EQ(path_geometry->GetMiterLimit(), 4);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(EntityTest, BlendingModeOptions) {
|
||||
@@ -955,7 +956,7 @@ TEST_P(EntityTest, GaussianBlurFilter) {
|
||||
auto fill = std::make_shared<SolidColorContents>();
|
||||
fill->SetColor(input_color);
|
||||
fill->SetGeometry(
|
||||
Geometry::MakePath(PathBuilder{}.AddRect(input_rect).TakePath()));
|
||||
Geometry::MakeFillPath(PathBuilder{}.AddRect(input_rect).TakePath()));
|
||||
|
||||
input = fill;
|
||||
input_size = input_rect.size;
|
||||
@@ -1140,12 +1141,13 @@ TEST_P(EntityTest, ContentsGetBoundsForEmptyPathReturnsNullopt) {
|
||||
|
||||
TEST_P(EntityTest, SolidStrokeCoverageIsCorrect) {
|
||||
{
|
||||
auto geometry = Geometry::MakeStrokePath(
|
||||
PathBuilder{}.AddLine({0, 0}, {10, 10}).TakePath(), 4.0, 4.0,
|
||||
Cap::kButt, Join::kBevel);
|
||||
|
||||
Entity entity;
|
||||
auto contents = std::make_unique<SolidStrokeContents>();
|
||||
contents->SetPath(PathBuilder{}.AddLine({0, 0}, {10, 10}).TakePath());
|
||||
contents->SetStrokeCap(SolidStrokeContents::Cap::kButt);
|
||||
contents->SetStrokeJoin(SolidStrokeContents::Join::kBevel);
|
||||
contents->SetStrokeSize(4);
|
||||
contents->SetGeometry(std::move(geometry));
|
||||
contents->SetColor(Color::Black());
|
||||
entity.SetContents(std::move(contents));
|
||||
auto actual = entity.GetCoverage();
|
||||
@@ -1156,12 +1158,13 @@ TEST_P(EntityTest, SolidStrokeCoverageIsCorrect) {
|
||||
|
||||
// Cover the Cap::kSquare case.
|
||||
{
|
||||
auto geometry = Geometry::MakeStrokePath(
|
||||
PathBuilder{}.AddLine({0, 0}, {10, 10}).TakePath(), 4.0, 4.0,
|
||||
Cap::kSquare, Join::kBevel);
|
||||
|
||||
Entity entity;
|
||||
auto contents = std::make_unique<SolidStrokeContents>();
|
||||
contents->SetPath(PathBuilder{}.AddLine({0, 0}, {10, 10}).TakePath());
|
||||
contents->SetStrokeCap(SolidStrokeContents::Cap::kSquare);
|
||||
contents->SetStrokeJoin(SolidStrokeContents::Join::kBevel);
|
||||
contents->SetStrokeSize(4);
|
||||
contents->SetGeometry(std::move(geometry));
|
||||
contents->SetColor(Color::Black());
|
||||
entity.SetContents(std::move(contents));
|
||||
auto actual = entity.GetCoverage();
|
||||
@@ -1173,13 +1176,13 @@ TEST_P(EntityTest, SolidStrokeCoverageIsCorrect) {
|
||||
|
||||
// Cover the Join::kMiter case.
|
||||
{
|
||||
auto geometry = Geometry::MakeStrokePath(
|
||||
PathBuilder{}.AddLine({0, 0}, {10, 10}).TakePath(), 4.0, 2.0,
|
||||
Cap::kSquare, Join::kMiter);
|
||||
|
||||
Entity entity;
|
||||
auto contents = std::make_unique<SolidStrokeContents>();
|
||||
contents->SetPath(PathBuilder{}.AddLine({0, 0}, {10, 10}).TakePath());
|
||||
contents->SetStrokeCap(SolidStrokeContents::Cap::kSquare);
|
||||
contents->SetStrokeJoin(SolidStrokeContents::Join::kMiter);
|
||||
contents->SetStrokeSize(4);
|
||||
contents->SetStrokeMiter(2);
|
||||
contents->SetGeometry(std::move(geometry));
|
||||
contents->SetColor(Color::Black());
|
||||
entity.SetContents(std::move(contents));
|
||||
auto actual = entity.GetCoverage();
|
||||
@@ -1191,7 +1194,7 @@ TEST_P(EntityTest, SolidStrokeCoverageIsCorrect) {
|
||||
|
||||
TEST_P(EntityTest, BorderMaskBlurCoverageIsCorrect) {
|
||||
auto fill = std::make_shared<SolidColorContents>();
|
||||
fill->SetGeometry(Geometry::MakePath(
|
||||
fill->SetGeometry(Geometry::MakeFillPath(
|
||||
PathBuilder{}.AddRect(Rect::MakeXYWH(0, 0, 300, 400)).TakePath()));
|
||||
fill->SetColor(Color::CornflowerBlue());
|
||||
auto border_mask_blur = FilterContents::MakeBorderMaskBlur(
|
||||
@@ -1424,7 +1427,7 @@ TEST_P(EntityTest, SolidFillCoverageIsCorrect) {
|
||||
fill->SetColor(Color::CornflowerBlue());
|
||||
auto expected = Rect::MakeLTRB(100, 110, 200, 220);
|
||||
fill->SetGeometry(
|
||||
Geometry::MakePath(PathBuilder{}.AddRect(expected).TakePath()));
|
||||
Geometry::MakeFillPath(PathBuilder{}.AddRect(expected).TakePath()));
|
||||
|
||||
auto coverage = fill->GetCoverage({});
|
||||
ASSERT_TRUE(coverage.has_value());
|
||||
@@ -1435,7 +1438,7 @@ TEST_P(EntityTest, SolidFillCoverageIsCorrect) {
|
||||
{
|
||||
auto fill = std::make_shared<SolidColorContents>();
|
||||
fill->SetColor(Color::CornflowerBlue());
|
||||
fill->SetGeometry(Geometry::MakePath(
|
||||
fill->SetGeometry(Geometry::MakeFillPath(
|
||||
PathBuilder{}.AddRect(Rect::MakeLTRB(100, 110, 200, 220)).TakePath()));
|
||||
|
||||
Entity entity;
|
||||
@@ -1452,7 +1455,7 @@ TEST_P(EntityTest, SolidFillCoverageIsCorrect) {
|
||||
{
|
||||
auto fill = std::make_shared<SolidColorContents>();
|
||||
fill->SetColor(Color::WhiteTransparent());
|
||||
fill->SetGeometry(Geometry::MakePath(
|
||||
fill->SetGeometry(Geometry::MakeFillPath(
|
||||
PathBuilder{}.AddRect(Rect::MakeLTRB(100, 110, 200, 220)).TakePath()));
|
||||
|
||||
auto coverage = fill->GetCoverage({});
|
||||
@@ -1474,7 +1477,7 @@ TEST_P(EntityTest, SolidFillShouldRenderIsCorrect) {
|
||||
{
|
||||
auto fill = std::make_shared<SolidColorContents>();
|
||||
fill->SetColor(Color::CornflowerBlue());
|
||||
fill->SetGeometry(Geometry::MakePath(
|
||||
fill->SetGeometry(Geometry::MakeFillPath(
|
||||
PathBuilder{}.AddRect(Rect::MakeLTRB(0, 0, 100, 100)).TakePath()));
|
||||
ASSERT_TRUE(fill->ShouldRender(Entity{}, Rect::MakeSize(Size{100, 100})));
|
||||
ASSERT_FALSE(
|
||||
@@ -1499,7 +1502,7 @@ TEST_P(EntityTest, ClipContentsShouldRenderIsCorrect) {
|
||||
{
|
||||
auto clip = std::make_shared<ClipContents>();
|
||||
ASSERT_TRUE(clip->ShouldRender(Entity{}, Rect::MakeSize(Size{100, 100})));
|
||||
clip->SetGeometry(Geometry::MakePath(
|
||||
clip->SetGeometry(Geometry::MakeFillPath(
|
||||
PathBuilder{}.AddRect(Rect::MakeLTRB(0, 0, 100, 100)).TakePath()));
|
||||
ASSERT_TRUE(clip->ShouldRender(Entity{}, Rect::MakeSize(Size{100, 100})));
|
||||
ASSERT_TRUE(
|
||||
@@ -1559,7 +1562,7 @@ TEST_P(EntityTest, RRectShadowTest) {
|
||||
auto coverage = entity.GetCoverage();
|
||||
if (show_coverage && coverage.has_value()) {
|
||||
auto bounds_contents = std::make_unique<SolidColorContents>();
|
||||
bounds_contents->SetGeometry(Geometry::MakePath(
|
||||
bounds_contents->SetGeometry(Geometry::MakeFillPath(
|
||||
PathBuilder{}.AddRect(entity.GetCoverage().value()).TakePath()));
|
||||
bounds_contents->SetColor(coverage_color.Premultiply());
|
||||
Entity bounds_entity;
|
||||
@@ -1575,7 +1578,7 @@ TEST_P(EntityTest, RRectShadowTest) {
|
||||
TEST_P(EntityTest, ColorMatrixFilterCoverageIsCorrect) {
|
||||
// Set up a simple color background.
|
||||
auto fill = std::make_shared<SolidColorContents>();
|
||||
fill->SetGeometry(Geometry::MakePath(
|
||||
fill->SetGeometry(Geometry::MakeFillPath(
|
||||
PathBuilder{}.AddRect(Rect::MakeXYWH(0, 0, 300, 400)).TakePath()));
|
||||
fill->SetColor(Color::Coral());
|
||||
|
||||
@@ -1669,7 +1672,7 @@ TEST_P(EntityTest, ColorMatrixFilterEditable) {
|
||||
TEST_P(EntityTest, LinearToSrgbFilterCoverageIsCorrect) {
|
||||
// Set up a simple color background.
|
||||
auto fill = std::make_shared<SolidColorContents>();
|
||||
fill->SetGeometry(Geometry::MakePath(
|
||||
fill->SetGeometry(Geometry::MakeFillPath(
|
||||
PathBuilder{}.AddRect(Rect::MakeXYWH(0, 0, 300, 400)).TakePath()));
|
||||
fill->SetColor(Color::MintCream());
|
||||
|
||||
@@ -1721,7 +1724,7 @@ TEST_P(EntityTest, LinearToSrgbFilter) {
|
||||
TEST_P(EntityTest, SrgbToLinearFilterCoverageIsCorrect) {
|
||||
// Set up a simple color background.
|
||||
auto fill = std::make_shared<SolidColorContents>();
|
||||
fill->SetGeometry(Geometry::MakePath(
|
||||
fill->SetGeometry(Geometry::MakeFillPath(
|
||||
PathBuilder{}.AddRect(Rect::MakeXYWH(0, 0, 300, 400)).TakePath()));
|
||||
fill->SetColor(Color::DeepPink());
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "impeller/entity/position_color.vert.h"
|
||||
#include "impeller/geometry/path_builder.h"
|
||||
#include "impeller/renderer/device_buffer.h"
|
||||
#include "impeller/renderer/render_pass.h"
|
||||
#include "impeller/tessellator/tessellator.h"
|
||||
|
||||
namespace impeller {
|
||||
@@ -20,8 +21,21 @@ std::unique_ptr<Geometry> Geometry::MakeVertices(Vertices vertices) {
|
||||
return std::make_unique<VerticesGeometry>(std::move(vertices));
|
||||
}
|
||||
|
||||
std::unique_ptr<Geometry> Geometry::MakePath(Path path) {
|
||||
return std::make_unique<PathGeometry>(std::move(path));
|
||||
std::unique_ptr<Geometry> Geometry::MakeFillPath(Path path) {
|
||||
return std::make_unique<FillPathGeometry>(std::move(path));
|
||||
}
|
||||
|
||||
std::unique_ptr<Geometry> Geometry::MakeStrokePath(Path path,
|
||||
Scalar stroke_width,
|
||||
Scalar miter_limit,
|
||||
Cap stroke_cap,
|
||||
Join stroke_join) {
|
||||
// Skia behaves like this.
|
||||
if (miter_limit < 0) {
|
||||
miter_limit = 4.0;
|
||||
}
|
||||
return std::make_unique<StrokePathGeometry>(path, stroke_width, miter_limit,
|
||||
stroke_cap, stroke_join);
|
||||
}
|
||||
|
||||
std::unique_ptr<Geometry> Geometry::MakeCover() {
|
||||
@@ -45,11 +59,9 @@ static PrimitiveType GetPrimitiveType(const Vertices& vertices) {
|
||||
}
|
||||
|
||||
GeometryResult VerticesGeometry::GetPositionBuffer(
|
||||
std::shared_ptr<Allocator> device_allocator,
|
||||
HostBuffer& host_buffer,
|
||||
std::shared_ptr<Tessellator> tessellator,
|
||||
ISize render_target_size,
|
||||
Scalar max_basis_length) {
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) {
|
||||
if (!vertices_.IsValid()) {
|
||||
return {};
|
||||
}
|
||||
@@ -62,7 +74,8 @@ GeometryResult VerticesGeometry::GetPositionBuffer(
|
||||
buffer_desc.size = total_vtx_bytes + total_idx_bytes;
|
||||
buffer_desc.storage_mode = StorageMode::kHostVisible;
|
||||
|
||||
auto buffer = device_allocator->CreateBuffer(buffer_desc);
|
||||
auto buffer =
|
||||
renderer.GetContext()->GetResourceAllocator()->CreateBuffer(buffer_desc);
|
||||
|
||||
const auto& positions = vertices_.GetPositions();
|
||||
if (!buffer->CopyHostBuffer(
|
||||
@@ -93,9 +106,9 @@ GeometryResult VerticesGeometry::GetPositionBuffer(
|
||||
}
|
||||
|
||||
GeometryResult VerticesGeometry::GetPositionColorBuffer(
|
||||
std::shared_ptr<Allocator> device_allocator,
|
||||
HostBuffer& host_buffer,
|
||||
std::shared_ptr<Tessellator> tessellator,
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass,
|
||||
Color paint_color,
|
||||
BlendMode blend_mode) {
|
||||
using VS = GeometryColorPipeline::VertexShader;
|
||||
@@ -125,7 +138,8 @@ GeometryResult VerticesGeometry::GetPositionColorBuffer(
|
||||
buffer_desc.size = total_vtx_bytes + total_idx_bytes;
|
||||
buffer_desc.storage_mode = StorageMode::kHostVisible;
|
||||
|
||||
auto buffer = device_allocator->CreateBuffer(buffer_desc);
|
||||
auto buffer =
|
||||
renderer.GetContext()->GetResourceAllocator()->CreateBuffer(buffer_desc);
|
||||
|
||||
if (!buffer->CopyHostBuffer(reinterpret_cast<uint8_t*>(vertex_data.data()),
|
||||
Range{0, total_vtx_bytes}, 0)) {
|
||||
@@ -154,41 +168,39 @@ GeometryResult VerticesGeometry::GetPositionColorBuffer(
|
||||
}
|
||||
|
||||
GeometryResult VerticesGeometry::GetPositionUVBuffer(
|
||||
std::shared_ptr<Allocator> device_allocator,
|
||||
HostBuffer& host_buffer,
|
||||
std::shared_ptr<Tessellator> tessellator,
|
||||
ISize render_target_size) {
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) {
|
||||
// TODO(jonahwilliams): support texture coordinates in vertices.
|
||||
return {};
|
||||
}
|
||||
|
||||
GeometryVertexType VerticesGeometry::GetVertexType() {
|
||||
GeometryVertexType VerticesGeometry::GetVertexType() const {
|
||||
if (vertices_.GetColors().size()) {
|
||||
return GeometryVertexType::kColor;
|
||||
}
|
||||
return GeometryVertexType::kPosition;
|
||||
}
|
||||
|
||||
std::optional<Rect> VerticesGeometry::GetCoverage(Matrix transform) {
|
||||
std::optional<Rect> VerticesGeometry::GetCoverage(
|
||||
const Matrix& transform) const {
|
||||
return vertices_.GetTransformedBoundingBox(transform);
|
||||
}
|
||||
|
||||
/////// Path Geometry ///////
|
||||
|
||||
PathGeometry::PathGeometry(Path path) : path_(std::move(path)) {}
|
||||
FillPathGeometry::FillPathGeometry(Path path) : path_(std::move(path)) {}
|
||||
|
||||
PathGeometry::~PathGeometry() = default;
|
||||
FillPathGeometry::~FillPathGeometry() = default;
|
||||
|
||||
GeometryResult PathGeometry::GetPositionBuffer(
|
||||
std::shared_ptr<Allocator> device_allocator,
|
||||
HostBuffer& host_buffer,
|
||||
std::shared_ptr<Tessellator> tessellator,
|
||||
ISize render_target_size,
|
||||
Scalar max_basis_length) {
|
||||
GeometryResult FillPathGeometry::GetPositionBuffer(
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) {
|
||||
VertexBuffer vertex_buffer;
|
||||
auto tesselation_result = tessellator->Tessellate(
|
||||
path_.GetFillType(),
|
||||
path_.CreatePolyline(kDefaultCurveTolerance / max_basis_length),
|
||||
auto& host_buffer = pass.GetTransientsBuffer();
|
||||
auto tesselation_result = renderer.GetTessellator()->Tessellate(
|
||||
path_.GetFillType(), path_.CreatePolyline(),
|
||||
[&vertex_buffer, &host_buffer](
|
||||
const float* vertices, size_t vertices_count, const uint16_t* indices,
|
||||
size_t indices_count) {
|
||||
@@ -210,47 +222,439 @@ GeometryResult PathGeometry::GetPositionBuffer(
|
||||
};
|
||||
}
|
||||
|
||||
GeometryResult PathGeometry::GetPositionColorBuffer(
|
||||
std::shared_ptr<Allocator> device_allocator,
|
||||
HostBuffer& host_buffer,
|
||||
std::shared_ptr<Tessellator> tessellator,
|
||||
GeometryResult FillPathGeometry::GetPositionColorBuffer(
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass,
|
||||
Color paint_color,
|
||||
BlendMode blend_mode) {
|
||||
// TODO(jonahwilliams): support per-color vertex in path geometry.
|
||||
return {};
|
||||
}
|
||||
|
||||
GeometryResult PathGeometry::GetPositionUVBuffer(
|
||||
std::shared_ptr<Allocator> device_allocator,
|
||||
HostBuffer& host_buffer,
|
||||
std::shared_ptr<Tessellator> tessellator,
|
||||
ISize render_target_size) {
|
||||
GeometryResult FillPathGeometry::GetPositionUVBuffer(
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) {
|
||||
// TODO(jonahwilliams): support texture coordinates in path geometry.
|
||||
return {};
|
||||
}
|
||||
|
||||
GeometryVertexType PathGeometry::GetVertexType() {
|
||||
GeometryVertexType FillPathGeometry::GetVertexType() const {
|
||||
return GeometryVertexType::kPosition;
|
||||
}
|
||||
|
||||
std::optional<Rect> PathGeometry::GetCoverage(Matrix transform) {
|
||||
std::optional<Rect> FillPathGeometry::GetCoverage(
|
||||
const Matrix& transform) const {
|
||||
return path_.GetTransformedBoundingBox(transform);
|
||||
}
|
||||
|
||||
///// Stroke Geometry //////
|
||||
|
||||
StrokePathGeometry::StrokePathGeometry(Path path,
|
||||
Scalar stroke_width,
|
||||
Scalar miter_limit,
|
||||
Cap stroke_cap,
|
||||
Join stroke_join)
|
||||
: path_(std::move(path)),
|
||||
stroke_width_(stroke_width),
|
||||
miter_limit_(miter_limit),
|
||||
stroke_cap_(stroke_cap),
|
||||
stroke_join_(stroke_join) {}
|
||||
|
||||
StrokePathGeometry::~StrokePathGeometry() = default;
|
||||
|
||||
Scalar StrokePathGeometry::GetStrokeWidth() const {
|
||||
return stroke_width_;
|
||||
}
|
||||
|
||||
Scalar StrokePathGeometry::GetMiterLimit() const {
|
||||
return miter_limit_;
|
||||
}
|
||||
|
||||
Cap StrokePathGeometry::GetStrokeCap() const {
|
||||
return stroke_cap_;
|
||||
}
|
||||
|
||||
Join StrokePathGeometry::GetStrokeJoin() const {
|
||||
return stroke_join_;
|
||||
}
|
||||
|
||||
// static
|
||||
Scalar StrokePathGeometry::CreateBevelAndGetDirection(
|
||||
VertexBufferBuilder<SolidFillVertexShader::PerVertexData>& vtx_builder,
|
||||
const Point& position,
|
||||
const Point& start_offset,
|
||||
const Point& end_offset) {
|
||||
SolidFillVertexShader::PerVertexData vtx;
|
||||
vtx.position = position;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
|
||||
Scalar dir = start_offset.Cross(end_offset) > 0 ? -1 : 1;
|
||||
vtx.position = position + start_offset * dir;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
vtx.position = position + end_offset * dir;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
// static
|
||||
StrokePathGeometry::JoinProc StrokePathGeometry::GetJoinProc(Join stroke_join) {
|
||||
using VS = SolidFillVertexShader;
|
||||
StrokePathGeometry::JoinProc join_proc;
|
||||
switch (stroke_join) {
|
||||
case Join::kBevel:
|
||||
join_proc = [](VertexBufferBuilder<VS::PerVertexData>& vtx_builder,
|
||||
const Point& position, const Point& start_offset,
|
||||
const Point& end_offset, Scalar miter_limit,
|
||||
Scalar tolerance) {
|
||||
CreateBevelAndGetDirection(vtx_builder, position, start_offset,
|
||||
end_offset);
|
||||
};
|
||||
break;
|
||||
case Join::kMiter:
|
||||
join_proc = [](VertexBufferBuilder<VS::PerVertexData>& vtx_builder,
|
||||
const Point& position, const Point& start_offset,
|
||||
const Point& end_offset, Scalar miter_limit,
|
||||
Scalar tolerance) {
|
||||
Point start_normal = start_offset.Normalize();
|
||||
Point end_normal = end_offset.Normalize();
|
||||
|
||||
// 1 for no joint (straight line), 0 for max joint (180 degrees).
|
||||
Scalar alignment = (start_normal.Dot(end_normal) + 1) / 2;
|
||||
if (ScalarNearlyEqual(alignment, 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Scalar dir = CreateBevelAndGetDirection(vtx_builder, position,
|
||||
start_offset, end_offset);
|
||||
|
||||
Point miter_point = (start_offset + end_offset) / 2 / alignment;
|
||||
if (miter_point.GetDistanceSquared({0, 0}) >
|
||||
miter_limit * miter_limit) {
|
||||
return; // Convert to bevel when we exceed the miter limit.
|
||||
}
|
||||
|
||||
// Outer miter point.
|
||||
VS::PerVertexData vtx;
|
||||
vtx.position = position + miter_point * dir;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
};
|
||||
break;
|
||||
case Join::kRound:
|
||||
join_proc = [](VertexBufferBuilder<VS::PerVertexData>& vtx_builder,
|
||||
const Point& position, const Point& start_offset,
|
||||
const Point& end_offset, Scalar miter_limit,
|
||||
Scalar tolerance) {
|
||||
Point start_normal = start_offset.Normalize();
|
||||
Point end_normal = end_offset.Normalize();
|
||||
|
||||
// 0 for no joint (straight line), 1 for max joint (180 degrees).
|
||||
Scalar alignment = 1 - (start_normal.Dot(end_normal) + 1) / 2;
|
||||
if (ScalarNearlyEqual(alignment, 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Scalar dir = CreateBevelAndGetDirection(vtx_builder, position,
|
||||
start_offset, end_offset);
|
||||
|
||||
Point middle =
|
||||
(start_offset + end_offset).Normalize() * start_offset.GetLength();
|
||||
Point middle_normal = middle.Normalize();
|
||||
|
||||
Point middle_handle = middle + Point(-middle.y, middle.x) *
|
||||
PathBuilder::kArcApproximationMagic *
|
||||
alignment * dir;
|
||||
Point start_handle =
|
||||
start_offset + Point(start_offset.y, -start_offset.x) *
|
||||
PathBuilder::kArcApproximationMagic * alignment *
|
||||
dir;
|
||||
|
||||
auto arc_points = CubicPathComponent(start_offset, start_handle,
|
||||
middle_handle, middle)
|
||||
.CreatePolyline(tolerance);
|
||||
|
||||
VS::PerVertexData vtx;
|
||||
for (const auto& point : arc_points) {
|
||||
vtx.position = position + point * dir;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
vtx.position = position + (-point * dir).Reflect(middle_normal);
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
return join_proc;
|
||||
}
|
||||
|
||||
// static
|
||||
StrokePathGeometry::CapProc StrokePathGeometry::GetCapProc(Cap stroke_cap) {
|
||||
using VS = SolidFillVertexShader;
|
||||
StrokePathGeometry::CapProc cap_proc;
|
||||
switch (stroke_cap) {
|
||||
case Cap::kButt:
|
||||
cap_proc = [](VertexBufferBuilder<VS::PerVertexData>& vtx_builder,
|
||||
const Point& position, const Point& offset,
|
||||
Scalar tolerance) {};
|
||||
break;
|
||||
case Cap::kRound:
|
||||
cap_proc = [](VertexBufferBuilder<VS::PerVertexData>& vtx_builder,
|
||||
const Point& position, const Point& offset,
|
||||
Scalar tolerance) {
|
||||
VS::PerVertexData vtx;
|
||||
|
||||
Point forward(offset.y, -offset.x);
|
||||
Point forward_normal = forward.Normalize();
|
||||
|
||||
auto arc_points =
|
||||
CubicPathComponent(
|
||||
offset, offset + forward * PathBuilder::kArcApproximationMagic,
|
||||
forward + offset * PathBuilder::kArcApproximationMagic, forward)
|
||||
.CreatePolyline(tolerance);
|
||||
|
||||
vtx.position = position + offset;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
vtx.position = position - offset;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
for (const auto& point : arc_points) {
|
||||
vtx.position = position + point;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
vtx.position = position + (-point).Reflect(forward_normal);
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
}
|
||||
};
|
||||
break;
|
||||
case Cap::kSquare:
|
||||
cap_proc = [](VertexBufferBuilder<VS::PerVertexData>& vtx_builder,
|
||||
const Point& position, const Point& offset,
|
||||
Scalar tolerance) {
|
||||
VS::PerVertexData vtx;
|
||||
vtx.position = position;
|
||||
|
||||
Point forward(offset.y, -offset.x);
|
||||
|
||||
vtx.position = position + offset;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
vtx.position = position - offset;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
vtx.position = position + offset + forward;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
vtx.position = position - offset + forward;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
};
|
||||
break;
|
||||
}
|
||||
return cap_proc;
|
||||
}
|
||||
|
||||
// static
|
||||
VertexBuffer StrokePathGeometry::CreateSolidStrokeVertices(
|
||||
const Path& path,
|
||||
HostBuffer& buffer,
|
||||
Scalar stroke_width,
|
||||
Scalar scaled_miter_limit,
|
||||
const StrokePathGeometry::JoinProc& join_proc,
|
||||
const StrokePathGeometry::CapProc& cap_proc,
|
||||
Scalar tolerance) {
|
||||
VertexBufferBuilder<VS::PerVertexData> vtx_builder;
|
||||
auto polyline = path.CreatePolyline();
|
||||
|
||||
VS::PerVertexData vtx;
|
||||
|
||||
// Offset state.
|
||||
Point offset;
|
||||
Point previous_offset; // Used for computing joins.
|
||||
|
||||
auto compute_offset = [&polyline, &offset, &previous_offset,
|
||||
&stroke_width](size_t point_i) {
|
||||
previous_offset = offset;
|
||||
Point direction =
|
||||
(polyline.points[point_i] - polyline.points[point_i - 1]).Normalize();
|
||||
offset = Vector2{-direction.y, direction.x} * stroke_width * 0.5;
|
||||
};
|
||||
|
||||
for (size_t contour_i = 0; contour_i < polyline.contours.size();
|
||||
contour_i++) {
|
||||
size_t contour_start_point_i, contour_end_point_i;
|
||||
std::tie(contour_start_point_i, contour_end_point_i) =
|
||||
polyline.GetContourPointBounds(contour_i);
|
||||
|
||||
switch (contour_end_point_i - contour_start_point_i) {
|
||||
case 1: {
|
||||
Point p = polyline.points[contour_start_point_i];
|
||||
cap_proc(vtx_builder, p, {-stroke_width * 0.5f, 0}, tolerance);
|
||||
cap_proc(vtx_builder, p, {stroke_width * 0.5f, 0}, tolerance);
|
||||
continue;
|
||||
}
|
||||
case 0:
|
||||
continue; // This contour has no renderable content.
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// The first point's offset is always the same as the second point.
|
||||
compute_offset(contour_start_point_i + 1);
|
||||
const Point contour_first_offset = offset;
|
||||
|
||||
if (contour_i > 0) {
|
||||
// This branch only executes when we've just finished drawing a contour
|
||||
// and are switching to a new one.
|
||||
// We're drawing a triangle strip, so we need to "pick up the pen" by
|
||||
// appending two vertices at the end of the previous contour and two
|
||||
// vertices at the start of the new contour (thus connecting the two
|
||||
// contours with two zero volume triangles, which will be discarded by
|
||||
// the rasterizer).
|
||||
vtx.position = polyline.points[contour_start_point_i - 1];
|
||||
// Append two vertices when "picking up" the pen so that the triangle
|
||||
// drawn when moving to the beginning of the new contour will have zero
|
||||
// volume.
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
|
||||
vtx.position = polyline.points[contour_start_point_i];
|
||||
// Append two vertices at the beginning of the new contour, which
|
||||
// appends two triangles of zero area.
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
}
|
||||
|
||||
// Generate start cap.
|
||||
if (!polyline.contours[contour_i].is_closed) {
|
||||
cap_proc(vtx_builder, polyline.points[contour_start_point_i], -offset,
|
||||
tolerance);
|
||||
}
|
||||
|
||||
// Generate contour geometry.
|
||||
for (size_t point_i = contour_start_point_i; point_i < contour_end_point_i;
|
||||
point_i++) {
|
||||
if (point_i > contour_start_point_i) {
|
||||
// Generate line rect.
|
||||
vtx.position = polyline.points[point_i - 1] + offset;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
vtx.position = polyline.points[point_i - 1] - offset;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
vtx.position = polyline.points[point_i] + offset;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
vtx.position = polyline.points[point_i] - offset;
|
||||
vtx_builder.AppendVertex(vtx);
|
||||
|
||||
if (point_i < contour_end_point_i - 1) {
|
||||
compute_offset(point_i + 1);
|
||||
|
||||
// Generate join from the current line to the next line.
|
||||
join_proc(vtx_builder, polyline.points[point_i], previous_offset,
|
||||
offset, scaled_miter_limit, tolerance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate end cap or join.
|
||||
if (!polyline.contours[contour_i].is_closed) {
|
||||
cap_proc(vtx_builder, polyline.points[contour_end_point_i - 1], offset,
|
||||
tolerance);
|
||||
} else {
|
||||
join_proc(vtx_builder, polyline.points[contour_start_point_i], offset,
|
||||
contour_first_offset, scaled_miter_limit, tolerance);
|
||||
}
|
||||
}
|
||||
|
||||
return vtx_builder.CreateVertexBuffer(buffer);
|
||||
}
|
||||
|
||||
GeometryResult StrokePathGeometry::GetPositionBuffer(
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) {
|
||||
if (stroke_width_ < 0.0) {
|
||||
return {};
|
||||
}
|
||||
auto determinant = entity.GetTransformation().GetDeterminant();
|
||||
if (determinant == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Scalar min_size = 1.0f / sqrt(std::abs(determinant));
|
||||
Scalar stroke_width = std::max(stroke_width_, min_size);
|
||||
|
||||
auto tolerance =
|
||||
kDefaultCurveTolerance /
|
||||
(stroke_width_ * entity.GetTransformation().GetMaxBasisLength());
|
||||
|
||||
auto& host_buffer = pass.GetTransientsBuffer();
|
||||
auto vertex_buffer = CreateSolidStrokeVertices(
|
||||
path_, host_buffer, stroke_width, miter_limit_ * stroke_width_ * 0.5,
|
||||
GetJoinProc(stroke_join_), GetCapProc(stroke_cap_), tolerance);
|
||||
|
||||
return GeometryResult{
|
||||
.type = PrimitiveType::kTriangleStrip,
|
||||
.vertex_buffer = vertex_buffer,
|
||||
.prevent_overdraw = true,
|
||||
};
|
||||
}
|
||||
|
||||
GeometryResult StrokePathGeometry::GetPositionColorBuffer(
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass,
|
||||
Color paint_color,
|
||||
BlendMode blend_mode) {
|
||||
// TODO(jonahwilliams): support per-color vertex in path geometry.
|
||||
return {};
|
||||
}
|
||||
|
||||
GeometryResult StrokePathGeometry::GetPositionUVBuffer(
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) {
|
||||
// TODO(jonahwilliams): support texture coordinates in path geometry.
|
||||
return {};
|
||||
}
|
||||
|
||||
GeometryVertexType StrokePathGeometry::GetVertexType() const {
|
||||
return GeometryVertexType::kPosition;
|
||||
}
|
||||
|
||||
std::optional<Rect> StrokePathGeometry::GetCoverage(
|
||||
const Matrix& transform) const {
|
||||
auto path_bounds = path_.GetBoundingBox();
|
||||
if (!path_bounds.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
auto path_coverage = path_bounds->TransformBounds(transform);
|
||||
|
||||
Scalar max_radius = 0.5;
|
||||
if (stroke_cap_ == Cap::kSquare) {
|
||||
max_radius = max_radius * kSqrt2;
|
||||
}
|
||||
if (stroke_join_ == Join::kMiter) {
|
||||
max_radius = std::max(max_radius, miter_limit_ * 0.5f);
|
||||
}
|
||||
Scalar determinant = transform.GetDeterminant();
|
||||
if (determinant == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
Scalar min_size = 1.0f / sqrt(std::abs(determinant));
|
||||
Vector2 max_radius_xy = transform.TransformDirection(
|
||||
Vector2(max_radius, max_radius) * std::max(stroke_width_, min_size));
|
||||
|
||||
return Rect(path_coverage.origin - max_radius_xy,
|
||||
Size(path_coverage.size.width + max_radius_xy.x * 2,
|
||||
path_coverage.size.height + max_radius_xy.y * 2));
|
||||
}
|
||||
|
||||
/////// Cover Geometry ///////
|
||||
|
||||
CoverGeometry::CoverGeometry() = default;
|
||||
|
||||
CoverGeometry::~CoverGeometry() = default;
|
||||
|
||||
GeometryResult CoverGeometry::GetPositionBuffer(
|
||||
std::shared_ptr<Allocator> device_allocator,
|
||||
HostBuffer& host_buffer,
|
||||
std::shared_ptr<Tessellator> tessellator,
|
||||
ISize render_target_size,
|
||||
Scalar max_basis_length) {
|
||||
auto rect = Rect(Size(render_target_size));
|
||||
GeometryResult CoverGeometry::GetPositionBuffer(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) {
|
||||
auto rect = Rect(Size(pass.GetRenderTargetSize()));
|
||||
constexpr uint16_t kRectIndicies[4] = {0, 1, 2, 3};
|
||||
auto& host_buffer = pass.GetTransientsBuffer();
|
||||
return GeometryResult{
|
||||
.type = PrimitiveType::kTriangleStrip,
|
||||
.vertex_buffer = {.vertex_buffer = host_buffer.Emplace(
|
||||
@@ -266,9 +670,9 @@ GeometryResult CoverGeometry::GetPositionBuffer(
|
||||
}
|
||||
|
||||
GeometryResult CoverGeometry::GetPositionColorBuffer(
|
||||
std::shared_ptr<Allocator> device_allocator,
|
||||
HostBuffer& host_buffer,
|
||||
std::shared_ptr<Tessellator> tessellator,
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass,
|
||||
Color paint_color,
|
||||
BlendMode blend_mode) {
|
||||
// TODO(jonahwilliams): support per-color vertex in cover geometry.
|
||||
@@ -276,19 +680,18 @@ GeometryResult CoverGeometry::GetPositionColorBuffer(
|
||||
}
|
||||
|
||||
GeometryResult CoverGeometry::GetPositionUVBuffer(
|
||||
std::shared_ptr<Allocator> device_allocator,
|
||||
HostBuffer& host_buffer,
|
||||
std::shared_ptr<Tessellator> tessellator,
|
||||
ISize render_target_size) {
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) {
|
||||
// TODO(jonahwilliams): support texture coordinates in cover geometry.
|
||||
return {};
|
||||
}
|
||||
|
||||
GeometryVertexType CoverGeometry::GetVertexType() {
|
||||
GeometryVertexType CoverGeometry::GetVertexType() const {
|
||||
return GeometryVertexType::kPosition;
|
||||
}
|
||||
|
||||
std::optional<Rect> CoverGeometry::GetCoverage(Matrix transform) {
|
||||
std::optional<Rect> CoverGeometry::GetCoverage(const Matrix& transform) const {
|
||||
return Rect::MakeMaximum();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "impeller/entity/contents/contents.h"
|
||||
#include "impeller/entity/entity.h"
|
||||
#include "impeller/entity/solid_fill.vert.h"
|
||||
#include "impeller/geometry/color.h"
|
||||
#include "impeller/geometry/path.h"
|
||||
#include "impeller/geometry/vertices.h"
|
||||
@@ -27,6 +30,18 @@ enum GeometryVertexType {
|
||||
kUV,
|
||||
};
|
||||
|
||||
enum class Cap {
|
||||
kButt,
|
||||
kRound,
|
||||
kSquare,
|
||||
};
|
||||
|
||||
enum class Join {
|
||||
kMiter,
|
||||
kRound,
|
||||
kBevel,
|
||||
};
|
||||
|
||||
class Geometry {
|
||||
public:
|
||||
Geometry();
|
||||
@@ -35,115 +50,191 @@ class Geometry {
|
||||
|
||||
static std::unique_ptr<Geometry> MakeVertices(Vertices vertices);
|
||||
|
||||
static std::unique_ptr<Geometry> MakePath(Path path);
|
||||
static std::unique_ptr<Geometry> MakeFillPath(Path path);
|
||||
|
||||
static std::unique_ptr<Geometry> MakeStrokePath(
|
||||
Path path,
|
||||
Scalar stroke_width = 0.0,
|
||||
Scalar miter_limit = 4.0,
|
||||
Cap stroke_cap = Cap::kButt,
|
||||
Join stroke_join = Join::kMiter);
|
||||
|
||||
static std::unique_ptr<Geometry> MakeCover();
|
||||
|
||||
virtual GeometryResult GetPositionBuffer(
|
||||
std::shared_ptr<Allocator> device_allocator,
|
||||
HostBuffer& host_buffer,
|
||||
std::shared_ptr<Tessellator> tessellator,
|
||||
ISize render_target_size,
|
||||
Scalar max_basis_length) = 0;
|
||||
virtual GeometryResult GetPositionBuffer(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) = 0;
|
||||
|
||||
virtual GeometryResult GetPositionColorBuffer(
|
||||
std::shared_ptr<Allocator> device_allocator,
|
||||
HostBuffer& host_buffer,
|
||||
std::shared_ptr<Tessellator> tessellator,
|
||||
Color paint_color,
|
||||
BlendMode blend_mode) = 0;
|
||||
virtual GeometryResult GetPositionColorBuffer(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass,
|
||||
Color paint_color,
|
||||
BlendMode blend_mode) = 0;
|
||||
|
||||
virtual GeometryResult GetPositionUVBuffer(
|
||||
std::shared_ptr<Allocator> device_allocator,
|
||||
HostBuffer& host_buffer,
|
||||
std::shared_ptr<Tessellator> tessellator,
|
||||
ISize render_target_size) = 0;
|
||||
virtual GeometryResult GetPositionUVBuffer(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) = 0;
|
||||
|
||||
virtual GeometryVertexType GetVertexType() = 0;
|
||||
virtual GeometryVertexType GetVertexType() const = 0;
|
||||
|
||||
virtual std::optional<Rect> GetCoverage(Matrix transform) = 0;
|
||||
virtual std::optional<Rect> GetCoverage(const Matrix& transform) const = 0;
|
||||
};
|
||||
|
||||
/// @brief A geometry that is created from a vertices object.
|
||||
class VerticesGeometry : public Geometry {
|
||||
public:
|
||||
VerticesGeometry(Vertices vertices);
|
||||
explicit VerticesGeometry(Vertices vertices);
|
||||
|
||||
~VerticesGeometry();
|
||||
|
||||
private:
|
||||
// |Geometry|
|
||||
GeometryResult GetPositionBuffer(std::shared_ptr<Allocator> device_allocator,
|
||||
HostBuffer& host_buffer,
|
||||
std::shared_ptr<Tessellator> tessellator,
|
||||
ISize render_target_size,
|
||||
Scalar max_basis_length) override;
|
||||
GeometryResult GetPositionBuffer(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) override;
|
||||
|
||||
// |Geometry|
|
||||
GeometryResult GetPositionColorBuffer(
|
||||
std::shared_ptr<Allocator> device_allocator,
|
||||
HostBuffer& host_buffer,
|
||||
std::shared_ptr<Tessellator> tessellator,
|
||||
Color paint_color,
|
||||
BlendMode blend_mode) override;
|
||||
GeometryResult GetPositionColorBuffer(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass,
|
||||
Color paint_color,
|
||||
BlendMode blend_mode) override;
|
||||
|
||||
// |Geometry|
|
||||
GeometryResult GetPositionUVBuffer(
|
||||
std::shared_ptr<Allocator> device_allocator,
|
||||
HostBuffer& host_buffer,
|
||||
std::shared_ptr<Tessellator> tessellator,
|
||||
ISize render_target_size) override;
|
||||
GeometryResult GetPositionUVBuffer(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) override;
|
||||
|
||||
// |Geometry|
|
||||
GeometryVertexType GetVertexType() override;
|
||||
GeometryVertexType GetVertexType() const override;
|
||||
|
||||
// |Geometry|
|
||||
std::optional<Rect> GetCoverage(Matrix transform) override;
|
||||
std::optional<Rect> GetCoverage(const Matrix& transform) const override;
|
||||
|
||||
Vertices vertices_;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(VerticesGeometry);
|
||||
};
|
||||
|
||||
/// @brief A geometry that is created from a path object.
|
||||
class PathGeometry : public Geometry {
|
||||
/// @brief A geometry that is created from a filled path object.
|
||||
class FillPathGeometry : public Geometry {
|
||||
public:
|
||||
PathGeometry(Path path);
|
||||
explicit FillPathGeometry(Path path);
|
||||
|
||||
~PathGeometry();
|
||||
~FillPathGeometry();
|
||||
|
||||
private:
|
||||
// |Geometry|
|
||||
GeometryResult GetPositionBuffer(std::shared_ptr<Allocator> device_allocator,
|
||||
HostBuffer& host_buffer,
|
||||
std::shared_ptr<Tessellator> tessellator,
|
||||
ISize render_target_size,
|
||||
Scalar max_basis_length) override;
|
||||
GeometryResult GetPositionBuffer(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) override;
|
||||
|
||||
// |Geometry|
|
||||
GeometryResult GetPositionColorBuffer(
|
||||
std::shared_ptr<Allocator> device_allocator,
|
||||
HostBuffer& host_buffer,
|
||||
std::shared_ptr<Tessellator> tessellator,
|
||||
Color paint_color,
|
||||
BlendMode blend_mode) override;
|
||||
GeometryResult GetPositionColorBuffer(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass,
|
||||
Color paint_color,
|
||||
BlendMode blend_mode) override;
|
||||
|
||||
// |Geometry|
|
||||
GeometryResult GetPositionUVBuffer(
|
||||
std::shared_ptr<Allocator> device_allocator,
|
||||
HostBuffer& host_buffer,
|
||||
std::shared_ptr<Tessellator> tessellator,
|
||||
ISize render_target_size) override;
|
||||
GeometryResult GetPositionUVBuffer(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) override;
|
||||
|
||||
// |Geometry|
|
||||
GeometryVertexType GetVertexType() override;
|
||||
GeometryVertexType GetVertexType() const override;
|
||||
|
||||
// |Geometry|
|
||||
std::optional<Rect> GetCoverage(Matrix transform) override;
|
||||
std::optional<Rect> GetCoverage(const Matrix& transform) const override;
|
||||
|
||||
Path path_;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(PathGeometry);
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(FillPathGeometry);
|
||||
};
|
||||
|
||||
/// @brief A geometry that is created from a stroked path object.
|
||||
class StrokePathGeometry : public Geometry {
|
||||
public:
|
||||
StrokePathGeometry(Path path,
|
||||
Scalar stroke_width,
|
||||
Scalar miter_limit,
|
||||
Cap stroke_cap,
|
||||
Join stroke_join);
|
||||
|
||||
~StrokePathGeometry();
|
||||
|
||||
Scalar GetStrokeWidth() const;
|
||||
|
||||
Scalar GetMiterLimit() const;
|
||||
|
||||
Cap GetStrokeCap() const;
|
||||
|
||||
Join GetStrokeJoin() const;
|
||||
|
||||
private:
|
||||
using VS = SolidFillVertexShader;
|
||||
|
||||
using CapProc =
|
||||
std::function<void(VertexBufferBuilder<VS::PerVertexData>& vtx_builder,
|
||||
const Point& position,
|
||||
const Point& offset,
|
||||
Scalar tolerance)>;
|
||||
using JoinProc =
|
||||
std::function<void(VertexBufferBuilder<VS::PerVertexData>& vtx_builder,
|
||||
const Point& position,
|
||||
const Point& start_offset,
|
||||
const Point& end_offset,
|
||||
Scalar miter_limit,
|
||||
Scalar tolerance)>;
|
||||
|
||||
// |Geometry|
|
||||
GeometryResult GetPositionBuffer(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) override;
|
||||
|
||||
// |Geometry|
|
||||
GeometryResult GetPositionColorBuffer(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass,
|
||||
Color paint_color,
|
||||
BlendMode blend_mode) override;
|
||||
|
||||
// |Geometry|
|
||||
GeometryResult GetPositionUVBuffer(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) override;
|
||||
|
||||
// |Geometry|
|
||||
GeometryVertexType GetVertexType() const override;
|
||||
|
||||
// |Geometry|
|
||||
std::optional<Rect> GetCoverage(const Matrix& transform) const override;
|
||||
|
||||
static Scalar CreateBevelAndGetDirection(
|
||||
VertexBufferBuilder<SolidFillVertexShader::PerVertexData>& vtx_builder,
|
||||
const Point& position,
|
||||
const Point& start_offset,
|
||||
const Point& end_offset);
|
||||
|
||||
static VertexBuffer CreateSolidStrokeVertices(const Path& path,
|
||||
HostBuffer& buffer,
|
||||
Scalar stroke_width,
|
||||
Scalar scaled_miter_limit,
|
||||
const JoinProc& join_proc,
|
||||
const CapProc& cap_proc,
|
||||
Scalar tolerance);
|
||||
|
||||
static StrokePathGeometry::JoinProc GetJoinProc(Join stroke_join);
|
||||
|
||||
static StrokePathGeometry::CapProc GetCapProc(Cap stroke_cap);
|
||||
|
||||
Path path_;
|
||||
Scalar stroke_width_;
|
||||
Scalar miter_limit_;
|
||||
Cap stroke_cap_;
|
||||
Join stroke_join_;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(StrokePathGeometry);
|
||||
};
|
||||
|
||||
/// @brief A geometry that implements "drawPaint" like behavior by covering
|
||||
@@ -156,32 +247,27 @@ class CoverGeometry : public Geometry {
|
||||
|
||||
private:
|
||||
// |Geometry|
|
||||
GeometryResult GetPositionBuffer(std::shared_ptr<Allocator> device_allocator,
|
||||
HostBuffer& host_buffer,
|
||||
std::shared_ptr<Tessellator> tessellator,
|
||||
ISize render_target_size,
|
||||
Scalar max_basis_length) override;
|
||||
GeometryResult GetPositionBuffer(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) override;
|
||||
|
||||
// |Geometry|
|
||||
GeometryResult GetPositionColorBuffer(
|
||||
std::shared_ptr<Allocator> device_allocator,
|
||||
HostBuffer& host_buffer,
|
||||
std::shared_ptr<Tessellator> tessellator,
|
||||
Color paint_color,
|
||||
BlendMode blend_mode) override;
|
||||
GeometryResult GetPositionColorBuffer(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass,
|
||||
Color paint_color,
|
||||
BlendMode blend_mode) override;
|
||||
|
||||
// |Geometry|
|
||||
GeometryResult GetPositionUVBuffer(
|
||||
std::shared_ptr<Allocator> device_allocator,
|
||||
HostBuffer& host_buffer,
|
||||
std::shared_ptr<Tessellator> tessellator,
|
||||
ISize render_target_size) override;
|
||||
GeometryResult GetPositionUVBuffer(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) override;
|
||||
|
||||
// |Geometry|
|
||||
GeometryVertexType GetVertexType() override;
|
||||
GeometryVertexType GetVertexType() const override;
|
||||
|
||||
// |Geometry|
|
||||
std::optional<Rect> GetCoverage(Matrix transform) override;
|
||||
std::optional<Rect> GetCoverage(const Matrix& transform) const override;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(CoverGeometry);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user