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) {
285 Point last_component_offset =
287 vtx.position =
polyline.GetPoint(point_i + 1) + last_component_offset;
288 vtx_builder.AppendVertex(
vtx.position);
289 vtx.position =
polyline.GetPoint(point_i + 1) - last_component_offset;
290 vtx_builder.AppendVertex(
vtx.position);
292 if (!is_last_component) {
309 SolidFillVertexShader::PerVertexData
vtx;
312 template <
typename VertexWriter>
313 void CreateButtCap(VertexWriter& vtx_builder,
314 const Point& position,
319 VS::PerVertexData
vtx;
320 vtx.position = position + orientation;
321 vtx_builder.AppendVertex(
vtx.position);
322 vtx.position = position - orientation;
323 vtx_builder.AppendVertex(
vtx.position);
326 template <
typename VertexWriter>
327 void CreateRoundCap(VertexWriter& vtx_builder,
328 const Point& position,
336 CubicPathComponent arc;
338 arc = CubicPathComponent(
343 arc = CubicPathComponent(
350 vtx_builder.AppendVertex(
vtx);
351 vtx = position - orientation;
352 vtx_builder.AppendVertex(
vtx);
354 arc.ToLinearPathComponents(
scale, [&vtx_builder, &
vtx, forward_normal,
355 position](
const Point& point) {
356 vtx = position + point;
357 vtx_builder.AppendVertex(
vtx);
358 vtx = position + (-point).Reflect(forward_normal);
359 vtx_builder.AppendVertex(
vtx);
363 template <
typename VertexWriter>
364 void CreateSquareCap(VertexWriter& vtx_builder,
365 const Point& position,
373 vtx_builder.AppendVertex(
vtx);
374 vtx = position - orientation;
375 vtx_builder.AppendVertex(
vtx);
376 vtx = position + orientation + forward;
377 vtx_builder.AppendVertex(
vtx);
378 vtx = position - orientation + forward;
379 vtx_builder.AppendVertex(
vtx);
382 template <
typename VertexWriter>
383 Scalar CreateBevelAndGetDirection(VertexWriter& vtx_builder,
384 const Point& position,
385 const Point& start_offset,
386 const Point& end_offset) {
388 vtx_builder.AppendVertex(
vtx);
390 Scalar dir = start_offset.Cross(end_offset) > 0 ? -1 : 1;
391 vtx = position + start_offset * dir;
392 vtx_builder.AppendVertex(
vtx);
393 vtx = position + end_offset * dir;
394 vtx_builder.AppendVertex(
vtx);
399 template <
typename VertexWriter>
400 void CreateMiterJoin(VertexWriter& vtx_builder,
401 const Point& position,
402 const Point& start_offset,
403 const Point& end_offset,
410 Scalar alignment = (start_normal.Dot(end_normal) + 1) / 2;
415 Scalar direction = CreateBevelAndGetDirection(vtx_builder, position,
416 start_offset, end_offset);
418 Point miter_point = (((start_offset + end_offset) / 2) / alignment);
419 if (miter_point.GetDistanceSquared({0, 0}) > miter_limit * miter_limit) {
424 VS::PerVertexData
vtx;
425 vtx.position = position + miter_point * direction;
426 vtx_builder.AppendVertex(
vtx.position);
429 template <
typename VertexWriter>
430 void CreateRoundJoin(VertexWriter& vtx_builder,
431 const Point& position,
432 const Point& start_offset,
433 const Point& end_offset,
440 Scalar alignment = 1 - (start_normal.Dot(end_normal) + 1) / 2;
445 Scalar direction = CreateBevelAndGetDirection(vtx_builder, position,
446 start_offset, end_offset);
449 (start_offset + end_offset).Normalize() * start_offset.
GetLength();
452 Point middle_handle = middle +
Point(-middle.y, middle.x) *
454 alignment * direction;
455 Point start_handle = start_offset +
Point(start_offset.y, -start_offset.x) *
457 alignment * direction;
459 VS::PerVertexData
vtx;
460 CubicPathComponent(start_offset, start_handle, middle_handle, middle)
461 .ToLinearPathComponents(
scale, [&vtx_builder, direction, &
vtx, position,
462 middle_normal](
const Point& point) {
463 vtx.position = position + point * direction;
464 vtx_builder.AppendVertex(
vtx.position);
465 vtx.position = position + (-point * direction).Reflect(middle_normal);
466 vtx_builder.AppendVertex(
vtx.position);
470 template <
typename VertexWriter>
471 void CreateBevelJoin(VertexWriter& vtx_builder,
472 const Point& position,
473 const Point& start_offset,
474 const Point& end_offset,
477 CreateBevelAndGetDirection(vtx_builder, position, start_offset, end_offset);
480 template <
typename VertexWriter>
481 void CreateSolidStrokeVertices(VertexWriter& vtx_builder,
486 const CapProc<VertexWriter>&
cap_proc,
490 stroke_generator.Generate(vtx_builder);
494 template <
typename VertexWriter>
495 JoinProc<VertexWriter> GetJoinProc(
Join stroke_join) {
496 switch (stroke_join) {
498 return &CreateBevelJoin<VertexWriter>;
500 return &CreateMiterJoin<VertexWriter>;
502 return &CreateRoundJoin<VertexWriter>;
506 template <
typename VertexWriter>
507 CapProc<VertexWriter> GetCapProc(
Cap stroke_cap) {
508 switch (stroke_cap) {
510 return &CreateButtCap<VertexWriter>;
512 return &CreateRoundCap<VertexWriter>;
514 return &CreateSquareCap<VertexWriter>;
519 std::vector<SolidFillVertexShader::PerVertexData>
520 StrokePathGeometry::GenerateSolidStrokeVertices(
const Path::Polyline&
polyline,
527 auto join_proc = GetJoinProc<PositionWriter>(stroke_join);
528 auto cap_proc = GetCapProc<PositionWriter>(stroke_cap);
531 PositionWriter vtx_builder;
532 stroke_generator.Generate(vtx_builder);
533 return vtx_builder.GetData();
536 std::vector<TextureFillVertexShader::PerVertexData>
537 StrokePathGeometry::GenerateSolidStrokeVerticesUV(
544 Point texture_origin,
546 const Matrix& effect_transform) {
548 auto join_proc = GetJoinProc<PositionUVWriter>(stroke_join);
549 auto cap_proc = GetCapProc<PositionUVWriter>(stroke_cap);
552 PositionUVWriter vtx_builder(texture_origin, texture_size, effect_transform);
553 stroke_generator.Generate(vtx_builder);
554 return vtx_builder.GetData();
557 StrokePathGeometry::StrokePathGeometry(
const Path& path,
564 miter_limit_(miter_limit),
565 stroke_cap_(stroke_cap),
566 stroke_join_(stroke_join) {}
571 return stroke_width_;
590 if (stroke_width_ < 0.0) {
594 if (determinant == 0) {
598 Scalar min_size = 1.0f / sqrt(std::abs(determinant));
604 PositionWriter position_writer;
607 miter_limit_ * stroke_width_ * 0.5f,
608 GetJoinProc<PositionWriter>(stroke_join_),
609 GetCapProc<PositionWriter>(stroke_cap_),
scale);
611 BufferView buffer_view =
612 host_buffer.Emplace(position_writer.GetData().data(),
613 position_writer.GetData().size() *
614 sizeof(SolidFillVertexShader::PerVertexData),
615 alignof(SolidFillVertexShader::PerVertexData));
617 return GeometryResult{
621 .vertex_buffer = buffer_view,
622 .vertex_count = position_writer.GetData().size(),
630 GeometryResult StrokePathGeometry::GetPositionUVBuffer(
631 Rect texture_coverage,
632 Matrix effect_transform,
633 const ContentContext& renderer,
634 const Entity& entity,
635 RenderPass& pass)
const {
636 if (stroke_width_ < 0.0) {
639 auto determinant = entity.GetTransform().GetDeterminant();
640 if (determinant == 0) {
644 Scalar min_size = 1.0f / sqrt(std::abs(determinant));
647 auto& host_buffer = renderer.GetTransientsBuffer();
648 auto scale = entity.GetTransform().GetMaxBasisLength();
649 auto polyline = renderer.GetTessellator()->CreateTempPolyline(path_,
scale);
651 PositionUVWriter writer(
Point{0, 0}, texture_coverage.GetSize(),
654 miter_limit_ * stroke_width_ * 0.5f,
655 GetJoinProc<PositionUVWriter>(stroke_join_),
656 GetCapProc<PositionUVWriter>(stroke_cap_),
scale);
658 BufferView buffer_view = host_buffer.Emplace(
659 writer.GetData().data(),
660 writer.GetData().size() *
sizeof(TextureFillVertexShader::PerVertexData),
661 alignof(TextureFillVertexShader::PerVertexData));
663 return GeometryResult{
667 .vertex_buffer = buffer_view,
668 .vertex_count = writer.GetData().size(),
671 .transform = entity.GetShaderTransform(pass),
684 std::optional<Rect> StrokePathGeometry::GetCoverage(
685 const Matrix& transform)
const {
687 if (!path_bounds.has_value()) {
693 max_radius = max_radius *
kSqrt2;
696 max_radius = std::max(max_radius, miter_limit_ * 0.5f);
698 Scalar determinant = transform.GetDeterminant();
699 if (determinant == 0) {
702 Scalar min_size = 1.0f / sqrt(std::abs(determinant));
703 max_radius *= std::max(stroke_width_, min_size);
704 return path_bounds->Expand(max_radius).TransformBounds(transform);