Rendering of continuous strokes.

This commit is contained in:
Chinmay Garde 2021-11-18 11:42:00 -08:00 committed by Dan Field
parent 76b8ca7ca1
commit 75daec6e7b
14 changed files with 164 additions and 7 deletions

View File

@ -81,6 +81,18 @@ TEST_F(AiksTest, CanRenderStrokes) {
Paint paint;
paint.color = Color::Red();
paint.stroke_width = 20.0;
paint.style = Paint::Style::kStroke;
canvas.DrawPath(PathBuilder{}.AddLine({200, 100}, {800, 100}).CreatePath(),
paint);
// ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
TEST_F(AiksTest, CanRenderCurvedStrokes) {
Canvas canvas;
Paint paint;
paint.color = Color::Blue();
paint.stroke_width = 25.0;
paint.style = Paint::Style::kStroke;
canvas.DrawPath(PathBuilder{}.AddCircle({500, 500}, 250).CreatePath(), paint);
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

View File

@ -12,6 +12,8 @@ impeller_shaders("entity_shaders") {
"shaders/gradient_fill.vert",
"shaders/solid_fill.frag",
"shaders/solid_fill.vert",
"shaders/solid_stroke.frag",
"shaders/solid_stroke.vert",
"shaders/texture_fill.frag",
"shaders/texture_fill.vert",
]

View File

@ -15,6 +15,7 @@ ContentRenderer::ContentRenderer(std::shared_ptr<Context> context)
gradient_fill_pipeline_ = std::make_unique<GradientFillPipeline>(*context_);
solid_fill_pipeline_ = std::make_unique<SolidFillPipeline>(*context_);
texture_pipeline_ = std::make_unique<TexturePipeline>(*context_);
solid_stroke_pipeline_ = std::make_unique<SolidStrokePipeline>(*context_);
is_valid_ = true;
}
@ -52,4 +53,12 @@ std::shared_ptr<Pipeline> ContentRenderer::GetTexturePipeline() const {
return texture_pipeline_->WaitAndGet();
}
std::shared_ptr<Pipeline> ContentRenderer::GetSolidStrokePipeline() const {
if (!IsValid()) {
return nullptr;
}
return solid_stroke_pipeline_->WaitAndGet();
}
} // namespace impeller

View File

@ -11,6 +11,8 @@
#include "flutter/impeller/entity/gradient_fill.vert.h"
#include "flutter/impeller/entity/solid_fill.frag.h"
#include "flutter/impeller/entity/solid_fill.vert.h"
#include "flutter/impeller/entity/solid_stroke.frag.h"
#include "flutter/impeller/entity/solid_stroke.vert.h"
#include "flutter/impeller/entity/texture_fill.frag.h"
#include "flutter/impeller/entity/texture_fill.vert.h"
#include "impeller/renderer/pipeline.h"
@ -23,6 +25,8 @@ using SolidFillPipeline =
PipelineT<SolidFillVertexShader, SolidFillFragmentShader>;
using TexturePipeline =
PipelineT<TextureFillVertexShader, TextureFillFragmentShader>;
using SolidStrokePipeline =
PipelineT<SolidStrokeVertexShader, SolidStrokeFragmentShader>;
class ContentRenderer {
public:
@ -38,6 +42,8 @@ class ContentRenderer {
std::shared_ptr<Pipeline> GetTexturePipeline() const;
std::shared_ptr<Pipeline> GetSolidStrokePipeline() const;
std::shared_ptr<Context> GetContext() const;
private:
@ -45,6 +51,7 @@ class ContentRenderer {
std::unique_ptr<GradientFillPipeline> gradient_fill_pipeline_;
std::unique_ptr<SolidFillPipeline> solid_fill_pipeline_;
std::unique_ptr<TexturePipeline> texture_pipeline_;
std::unique_ptr<SolidStrokePipeline> solid_stroke_pipeline_;
bool is_valid_ = false;
FML_DISALLOW_COPY_AND_ASSIGN(ContentRenderer);

View File

@ -10,6 +10,7 @@
#include "impeller/entity/content_renderer.h"
#include "impeller/entity/entity.h"
#include "impeller/geometry/path_builder.h"
#include "impeller/geometry/vector.h"
#include "impeller/renderer/render_pass.h"
#include "impeller/renderer/sampler_library.h"
#include "impeller/renderer/surface.h"
@ -236,13 +237,11 @@ bool TextureContents::Render(const ContentRenderer& renderer,
frame_info.mvp =
Matrix::MakeOrthographic(surface.GetSize()) * entity.GetTransformation();
auto frame_info_view = host_buffer.EmplaceUniform(frame_info);
Command cmd;
cmd.label = "TextureFill";
cmd.pipeline = renderer.GetTexturePipeline();
cmd.BindVertices(vertex_builder.CreateVertexBuffer(host_buffer));
VS::BindFrameInfo(cmd, frame_info_view);
VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info));
FS::BindTextureSampler(
cmd, texture_,
renderer.GetContext()->GetSamplerLibrary()->GetSampler({}));
@ -275,6 +274,45 @@ const Color& SolidStrokeContents::GetColor() const {
return color_;
}
static VertexBuffer CreateSolidStrokeVertices(const Path& path,
HostBuffer& buffer) {
using VS = SolidStrokeVertexShader;
VertexBufferBuilder<VS::PerVertexData> vtx_builder;
auto polyline = path.CreatePolyline();
for (size_t i = 0, polyline_size = polyline.size(); i < polyline_size; i++) {
const auto is_last_point = i == polyline_size - 1;
const auto& p1 = polyline[i];
const auto& p2 = is_last_point ? polyline[i - 1] : polyline[i + 1];
const auto diff = p2 - p1;
const Scalar direction = is_last_point ? -1.0 : 1.0;
const auto normal =
Point{-diff.y * direction, diff.x * direction}.Normalize();
VS::PerVertexData vtx;
vtx.vertex_position = p1;
if (i == 0) {
vtx.vertex_normal = -normal;
vtx_builder.AppendVertex(vtx);
vtx.vertex_normal = normal;
vtx_builder.AppendVertex(vtx);
}
vtx.vertex_normal = normal;
vtx_builder.AppendVertex(vtx);
vtx.vertex_normal = -normal;
vtx_builder.AppendVertex(vtx);
}
return vtx_builder.CreateVertexBuffer(buffer);
}
bool SolidStrokeContents::Render(const ContentRenderer& renderer,
const Entity& entity,
const Surface& surface,
@ -283,7 +321,29 @@ bool SolidStrokeContents::Render(const ContentRenderer& renderer,
return true;
}
return false;
using VS = SolidStrokeVertexShader;
VS::FrameInfo frame_info;
frame_info.mvp =
Matrix::MakeOrthographic(surface.GetSize()) * entity.GetTransformation();
VS::StrokeInfo stroke_info;
stroke_info.color = color_;
stroke_info.size = stroke_size_;
Command cmd;
cmd.primitive_type = PrimitiveType::kTriangleStrip;
cmd.label = "SolidStroke";
cmd.pipeline = renderer.GetSolidStrokePipeline();
cmd.BindVertices(
CreateSolidStrokeVertices(entity.GetPath(), pass.GetTransientsBuffer()));
VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info));
VS::BindStrokeInfo(cmd,
pass.GetTransientsBuffer().EmplaceUniform(stroke_info));
pass.AddCommand(std::move(cmd));
return true;
}
void SolidStrokeContents::SetStrokeSize(Scalar size) {

View File

@ -0,0 +1,11 @@
// 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.
in vec4 stroke_color;
out vec4 frag_color;
void main() {
frag_color = stroke_color;
}

View File

@ -0,0 +1,23 @@
// 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.
uniform FrameInfo {
mat4 mvp;
} frame_info;
uniform StrokeInfo {
vec4 color;
float size;
} stroke_info;
in vec2 vertex_position;
in vec2 vertex_normal;
out vec4 stroke_color;
void main() {
vec2 offset = vertex_normal * vec2(stroke_info.size * 0.5);
gl_Position = frame_info.mvp * vec4(vertex_position + offset, 0.0, 1.0);
stroke_color = stroke_info.color;
}

View File

@ -298,6 +298,11 @@ PathBuilder& PathBuilder::AddOval(const Rect& container) {
return *this;
}
PathBuilder& PathBuilder::AddLine(const Point& p1, const Point& p2) {
prototype_.AddLinearComponent(p1, p2);
return *this;
}
const Path& PathBuilder::GetCurrentPath() const {
return prototype_;
}

View File

@ -54,6 +54,8 @@ class PathBuilder {
PathBuilder& AddOval(const Rect& rect);
PathBuilder& AddLine(const Point& p1, const Point& p2);
struct RoundingRadii {
Scalar topLeft = 0.0;
Scalar bottomLeft = 0.0;

View File

@ -86,6 +86,18 @@ struct TPoint {
constexpr Type GetDistance(const TPoint& p) const {
return sqrt(GetDistanceSquared(p));
}
constexpr Type GetLengthSquared() const { return GetDistanceSquared({}); }
constexpr Type GetLength() const { return GetDistance({}); }
constexpr TPoint Normalize() const {
const auto length = GetLength();
if (length == 0) {
return {};
}
return {x / length, y / length};
}
};
using Point = TPoint<Scalar>;

View File

@ -4,6 +4,8 @@
#include <Metal/Metal.h>
#include <set>
#include "flutter/fml/macros.h"
#include "impeller/renderer/backend/metal/backend_cast.h"
#include "impeller/renderer/vertex_descriptor.h"
@ -28,8 +30,15 @@ class VertexDescriptorMTL {
StageInput(size_t p_location, MTLVertexFormat p_format, size_t p_length)
: location(p_location), format(p_format), length(p_length) {}
struct Compare {
constexpr bool operator()(const StageInput& lhs,
const StageInput& rhs) const {
return lhs.location < rhs.location;
}
};
};
std::vector<StageInput> stage_inputs_;
std::set<StageInput, StageInput::Compare> stage_inputs_;
FML_DISALLOW_COPY_AND_ASSIGN(VertexDescriptorMTL);
};

View File

@ -179,8 +179,9 @@ bool VertexDescriptorMTL::SetStageInputs(
FML_LOG(ERROR) << "Format for input " << input.name << " not supported.";
return false;
}
stage_inputs_.emplace_back(StageInput{
input.location, vertex_format, (input.bit_width * input.vec_size) / 8});
stage_inputs_.insert(StageInput{input.location, vertex_format,
(input.bit_width * input.vec_size) / 8});
}
return true;

View File

@ -50,6 +50,8 @@ struct ShaderUniformSlot {
struct ShaderStageIOSlot {
// Statically allocated const string containing advisory debug description.
// This may be absent in release modes and the runtime may not use this string
// for normal operation.
const char* name;
size_t location;
size_t set;

View File

@ -35,6 +35,8 @@ class VertexBufferBuilder {
bool HasVertices() const { return !vertices_.empty(); }
size_t GetVertexCount() const { return vertices_.size(); }
VertexBufferBuilder& AppendVertex(VertexType_ vertex) {
vertices_.emplace_back(std::move(vertex));
return *this;