10 #include "impeller/entity/texture_fill.vert.h"
15 using VS = SolidFillVertexShader;
19 template <
typename VertexWriter>
20 using CapProc = std::function<void(VertexWriter& vtx_builder,
21 const Point& position,
26 template <
typename VertexWriter>
27 using JoinProc = std::function<void(VertexWriter& vtx_builder,
28 const Point& position,
29 const Point& start_offset,
30 const Point& end_offset,
34 class PositionWriter {
36 void AppendVertex(
const Point& point) {
37 data_.emplace_back(SolidFillVertexShader::PerVertexData{.position = point});
40 const std::vector<SolidFillVertexShader::PerVertexData>& GetData()
const {
45 std::vector<SolidFillVertexShader::PerVertexData> data_ = {};
48 class PositionUVWriter {
50 PositionUVWriter(
const Point& texture_origin,
51 const Size& texture_size,
52 const Matrix& effect_transform)
53 : texture_origin_(texture_origin),
54 texture_size_(texture_size),
55 effect_transform_(effect_transform) {}
57 const std::vector<TextureFillVertexShader::PerVertexData>& GetData() {
58 if (effect_transform_.IsIdentity()) {
59 auto origin = texture_origin_;
60 auto scale = 1.0 / texture_size_;
62 for (
auto& pvd : data_) {
63 pvd.texture_coords = (pvd.position - origin) *
scale;
68 texture_rect.GetNormalizingTransform() * effect_transform_;
70 for (
auto& pvd : data_) {
71 pvd.texture_coords = uv_transform * pvd.position;
77 void AppendVertex(
const Point& point) {
78 data_.emplace_back(TextureFillVertexShader::PerVertexData{
85 std::vector<TextureFillVertexShader::PerVertexData> data_ = {};
86 const Point texture_origin_;
87 const Size texture_size_;
88 const Matrix effect_transform_;
91 template <
typename VertexWriter>
92 class StrokeGenerator {
95 const Scalar p_stroke_width,
96 const Scalar p_scaled_miter_limit,
97 const JoinProc<VertexWriter>& p_join_proc,
98 const CapProc<VertexWriter>& p_cap_proc,
107 void Generate(VertexWriter& vtx_builder) {
108 for (
size_t contour_i = 0; contour_i <
polyline.contours.size();
111 size_t contour_start_point_i, contour_end_point_i;
112 std::tie(contour_start_point_i, contour_end_point_i) =
113 polyline.GetContourPointBounds(contour_i);
115 auto contour_delta = contour_end_point_i - contour_start_point_i;
116 if (contour_delta == 1) {
123 }
else if (contour_delta == 0) {
128 offset = ComputeOffset(contour_start_point_i, contour_start_point_i,
129 contour_end_point_i, contour);
140 vtx.position =
polyline.GetPoint(contour_start_point_i - 1);
144 vtx_builder.AppendVertex(
vtx.position);
145 vtx_builder.AppendVertex(
vtx.position);
147 vtx.position =
polyline.GetPoint(contour_start_point_i);
150 vtx_builder.AppendVertex(
vtx.position);
151 vtx_builder.AppendVertex(
vtx.position);
155 if (!
polyline.contours[contour_i].is_closed) {
160 cap_offset,
scale,
true);
163 for (
size_t contour_component_i = 0;
164 contour_component_i < contour.
components.size();
165 contour_component_i++) {
168 bool is_last_component =
169 contour_component_i == contour.
components.size() - 1;
172 size_t component_end_index =
173 is_last_component ? contour_end_point_i - 1
175 .component_start_index;
177 AddVerticesForCurveComponent(
178 vtx_builder, component_start_index, component_end_index,
179 contour_start_point_i, contour_end_point_i, contour);
181 AddVerticesForLinearComponent(
182 vtx_builder, component_start_index, component_end_index,
183 contour_start_point_i, contour_end_point_i, contour);
193 cap_offset,
scale,
false);
204 Point ComputeOffset(
const size_t point_i,
205 const size_t contour_start_point_i,
206 const size_t contour_end_point_i,
209 if (point_i >= contour_end_point_i) {
211 }
else if (point_i <= contour_start_point_i) {
220 void AddVerticesForLinearComponent(VertexWriter& vtx_builder,
221 const size_t component_start_index,
222 const size_t component_end_index,
223 const size_t contour_start_point_i,
224 const size_t contour_end_point_i,
226 bool is_last_component = component_start_index ==
227 contour.
components.back().component_start_index;
229 for (
size_t point_i = component_start_index; point_i < component_end_index;
231 bool is_end_of_component = point_i == component_end_index - 1;
233 vtx_builder.AppendVertex(
vtx.position);
235 vtx_builder.AppendVertex(
vtx.position);
240 vtx_builder.AppendVertex(
vtx.position);
242 vtx_builder.AppendVertex(
vtx.position);
245 offset = ComputeOffset(point_i + 2, contour_start_point_i,
246 contour_end_point_i, contour);
247 if (!is_last_component && is_end_of_component) {
255 void AddVerticesForCurveComponent(VertexWriter& vtx_builder,
256 const size_t component_start_index,
257 const size_t component_end_index,
258 const size_t contour_start_point_i,
259 const size_t contour_end_point_i,
261 bool is_last_component = component_start_index ==
262 contour.
components.back().component_start_index;
264 for (
size_t point_i = component_start_index; point_i < component_end_index;
266 bool is_end_of_component = point_i == component_end_index - 1;
269 vtx_builder.AppendVertex(
vtx.position);
271 vtx_builder.AppendVertex(
vtx.position);
274 offset = ComputeOffset(point_i + 2, contour_start_point_i,
275 contour_end_point_i, contour);
278 if (is_end_of_component) {
280 vtx_builder.AppendVertex(
vtx.position);
282 vtx_builder.AppendVertex(
vtx.position);
284 if (!is_last_component) {
301 SolidFillVertexShader::PerVertexData
vtx;
304 template <
typename VertexWriter>
305 void CreateButtCap(VertexWriter& vtx_builder,
306 const Point& position,
311 VS::PerVertexData
vtx;
312 vtx.position = position + orientation;
313 vtx_builder.AppendVertex(
vtx.position);
314 vtx.position = position - orientation;
315 vtx_builder.AppendVertex(
vtx.position);
318 template <
typename VertexWriter>
319 void CreateRoundCap(VertexWriter& vtx_builder,
320 const Point& position,
328 CubicPathComponent arc;
330 arc = CubicPathComponent(
335 arc = CubicPathComponent(
342 vtx_builder.AppendVertex(
vtx);
343 vtx = position - orientation;
344 vtx_builder.AppendVertex(
vtx);
346 arc.ToLinearPathComponents(
scale, [&vtx_builder, &
vtx, forward_normal,
347 position](
const Point& point) {
348 vtx = position + point;
349 vtx_builder.AppendVertex(
vtx);
350 vtx = position + (-point).Reflect(forward_normal);
351 vtx_builder.AppendVertex(
vtx);
355 template <
typename VertexWriter>
356 void CreateSquareCap(VertexWriter& vtx_builder,
357 const Point& position,
365 vtx_builder.AppendVertex(
vtx);
366 vtx = position - orientation;
367 vtx_builder.AppendVertex(
vtx);
368 vtx = position + orientation + forward;
369 vtx_builder.AppendVertex(
vtx);
370 vtx = position - orientation + forward;
371 vtx_builder.AppendVertex(
vtx);
374 template <
typename VertexWriter>
375 Scalar CreateBevelAndGetDirection(VertexWriter& vtx_builder,
376 const Point& position,
377 const Point& start_offset,
378 const Point& end_offset) {
380 vtx_builder.AppendVertex(
vtx);
382 Scalar dir = start_offset.Cross(end_offset) > 0 ? -1 : 1;
383 vtx = position + start_offset * dir;
384 vtx_builder.AppendVertex(
vtx);
385 vtx = position + end_offset * dir;
386 vtx_builder.AppendVertex(
vtx);
391 template <
typename VertexWriter>
392 void CreateMiterJoin(VertexWriter& vtx_builder,
393 const Point& position,
394 const Point& start_offset,
395 const Point& end_offset,
402 Scalar alignment = (start_normal.Dot(end_normal) + 1) / 2;
407 Scalar direction = CreateBevelAndGetDirection(vtx_builder, position,
408 start_offset, end_offset);
410 Point miter_point = (((start_offset + end_offset) / 2) / alignment);
411 if (miter_point.GetDistanceSquared({0, 0}) > miter_limit * miter_limit) {
416 VS::PerVertexData
vtx;
417 vtx.position = position + miter_point * direction;
418 vtx_builder.AppendVertex(
vtx.position);
421 template <
typename VertexWriter>
422 void CreateRoundJoin(VertexWriter& vtx_builder,
423 const Point& position,
424 const Point& start_offset,
425 const Point& end_offset,
432 Scalar alignment = 1 - (start_normal.Dot(end_normal) + 1) / 2;
437 Scalar direction = CreateBevelAndGetDirection(vtx_builder, position,
438 start_offset, end_offset);
441 (start_offset + end_offset).Normalize() * start_offset.
GetLength();
444 Point middle_handle = middle +
Point(-middle.y, middle.x) *
446 alignment * direction;
447 Point start_handle = start_offset +
Point(start_offset.y, -start_offset.x) *
449 alignment * direction;
451 VS::PerVertexData
vtx;
452 CubicPathComponent(start_offset, start_handle, middle_handle, middle)
453 .ToLinearPathComponents(
scale, [&vtx_builder, direction, &
vtx, position,
454 middle_normal](
const Point& point) {
455 vtx.position = position + point * direction;
456 vtx_builder.AppendVertex(
vtx.position);
457 vtx.position = position + (-point * direction).Reflect(middle_normal);
458 vtx_builder.AppendVertex(
vtx.position);
462 template <
typename VertexWriter>
463 void CreateBevelJoin(VertexWriter& vtx_builder,
464 const Point& position,
465 const Point& start_offset,
466 const Point& end_offset,
469 CreateBevelAndGetDirection(vtx_builder, position, start_offset, end_offset);
472 template <
typename VertexWriter>
473 void CreateSolidStrokeVertices(VertexWriter& vtx_builder,
478 const CapProc<VertexWriter>&
cap_proc,
482 stroke_generator.Generate(vtx_builder);
486 template <
typename VertexWriter>
487 JoinProc<VertexWriter> GetJoinProc(
Join stroke_join) {
488 switch (stroke_join) {
490 return &CreateBevelJoin<VertexWriter>;
492 return &CreateMiterJoin<VertexWriter>;
494 return &CreateRoundJoin<VertexWriter>;
498 template <
typename VertexWriter>
499 CapProc<VertexWriter> GetCapProc(
Cap stroke_cap) {
500 switch (stroke_cap) {
502 return &CreateButtCap<VertexWriter>;
504 return &CreateRoundCap<VertexWriter>;
506 return &CreateSquareCap<VertexWriter>;
511 std::vector<SolidFillVertexShader::PerVertexData>
512 StrokePathGeometry::GenerateSolidStrokeVertices(
const Path::Polyline&
polyline,
519 auto join_proc = GetJoinProc<PositionWriter>(stroke_join);
520 auto cap_proc = GetCapProc<PositionWriter>(stroke_cap);
523 PositionWriter vtx_builder;
524 stroke_generator.Generate(vtx_builder);
525 return vtx_builder.GetData();
528 std::vector<TextureFillVertexShader::PerVertexData>
529 StrokePathGeometry::GenerateSolidStrokeVerticesUV(
536 Point texture_origin,
538 const Matrix& effect_transform) {
540 auto join_proc = GetJoinProc<PositionUVWriter>(stroke_join);
541 auto cap_proc = GetCapProc<PositionUVWriter>(stroke_cap);
544 PositionUVWriter vtx_builder(texture_origin, texture_size, effect_transform);
545 stroke_generator.Generate(vtx_builder);
546 return vtx_builder.GetData();
549 StrokePathGeometry::StrokePathGeometry(
const Path& path,
556 miter_limit_(miter_limit),
557 stroke_cap_(stroke_cap),
558 stroke_join_(stroke_join) {}
563 return stroke_width_;
582 if (stroke_width_ < 0.0) {
586 if (determinant == 0) {
590 Scalar min_size = 1.0f / sqrt(std::abs(determinant));
596 PositionWriter position_writer;
599 miter_limit_ * stroke_width_ * 0.5f,
600 GetJoinProc<PositionWriter>(stroke_join_),
601 GetCapProc<PositionWriter>(stroke_cap_),
scale);
603 BufferView buffer_view =
604 host_buffer.Emplace(position_writer.GetData().data(),
605 position_writer.GetData().size() *
606 sizeof(SolidFillVertexShader::PerVertexData),
607 alignof(SolidFillVertexShader::PerVertexData));
609 return GeometryResult{
613 .vertex_buffer = buffer_view,
614 .vertex_count = position_writer.GetData().size(),
622 GeometryResult StrokePathGeometry::GetPositionUVBuffer(
623 Rect texture_coverage,
624 Matrix effect_transform,
625 const ContentContext& renderer,
626 const Entity& entity,
627 RenderPass& pass)
const {
628 if (stroke_width_ < 0.0) {
631 auto determinant = entity.GetTransform().GetDeterminant();
632 if (determinant == 0) {
636 Scalar min_size = 1.0f / sqrt(std::abs(determinant));
639 auto& host_buffer = renderer.GetTransientsBuffer();
640 auto scale = entity.GetTransform().GetMaxBasisLength();
641 auto polyline = renderer.GetTessellator()->CreateTempPolyline(path_,
scale);
643 PositionUVWriter writer(
Point{0, 0}, texture_coverage.GetSize(),
646 miter_limit_ * stroke_width_ * 0.5f,
647 GetJoinProc<PositionUVWriter>(stroke_join_),
648 GetCapProc<PositionUVWriter>(stroke_cap_),
scale);
650 BufferView buffer_view = host_buffer.Emplace(
651 writer.GetData().data(),
652 writer.GetData().size() *
sizeof(TextureFillVertexShader::PerVertexData),
653 alignof(TextureFillVertexShader::PerVertexData));
655 return GeometryResult{
659 .vertex_buffer = buffer_view,
660 .vertex_count = writer.GetData().size(),
663 .transform = entity.GetShaderTransform(pass),
676 std::optional<Rect> StrokePathGeometry::GetCoverage(
677 const Matrix& transform)
const {
679 if (!path_bounds.has_value()) {
685 max_radius = max_radius *
kSqrt2;
688 max_radius = std::max(max_radius, miter_limit_ * 0.5f);
690 Scalar determinant = transform.GetDeterminant();
691 if (determinant == 0) {
694 Scalar min_size = 1.0f / sqrt(std::abs(determinant));
695 max_radius *= std::max(stroke_width_, min_size);
696 return path_bounds->Expand(max_radius).TransformBounds(transform);