16 using VS = SolidFillVertexShader;
20 template <
typename VertexWriter>
21 using CapProc = std::function<void(
VertexWriter& vtx_builder,
22 const Point& position,
27 template <
typename VertexWriter>
28 using JoinProc = std::function<void(
VertexWriter& vtx_builder,
29 const Point& position,
30 const Point& start_offset,
31 const Point& end_offset,
35 class PositionWriter {
37 void AppendVertex(
const Point& point) {
38 data_.emplace_back(SolidFillVertexShader::PerVertexData{.position = point});
41 const std::vector<SolidFillVertexShader::PerVertexData>& GetData()
const {
46 std::vector<SolidFillVertexShader::PerVertexData> data_ = {};
49 template <
typename VertexWriter>
50 class StrokeGenerator {
53 const Scalar p_stroke_width,
54 const Scalar p_scaled_miter_limit,
55 const JoinProc<VertexWriter>& p_join_proc,
56 const CapProc<VertexWriter>& p_cap_proc,
66 for (
size_t contour_i = 0; contour_i <
polyline.contours.size();
69 size_t contour_start_point_i, contour_end_point_i;
70 std::tie(contour_start_point_i, contour_end_point_i) =
71 polyline.GetContourPointBounds(contour_i);
73 auto contour_delta = contour_end_point_i - contour_start_point_i;
74 if (contour_delta == 1) {
81 }
else if (contour_delta == 0) {
86 offset = ComputeOffset(contour_start_point_i, contour_start_point_i,
87 contour_end_point_i, contour);
88 const Point contour_first_offset =
offset.GetVector();
98 vtx.position =
polyline.GetPoint(contour_start_point_i - 1);
102 vtx_builder.AppendVertex(
vtx.position);
103 vtx_builder.AppendVertex(
vtx.position);
105 vtx.position =
polyline.GetPoint(contour_start_point_i);
108 vtx_builder.AppendVertex(
vtx.position);
109 vtx_builder.AppendVertex(
vtx.position);
113 if (!
polyline.contours[contour_i].is_closed) {
118 cap_offset,
scale,
true);
121 for (
size_t contour_component_i = 0;
122 contour_component_i < contour.
components.size();
123 contour_component_i++) {
126 bool is_last_component =
127 contour_component_i == contour.
components.size() - 1;
130 size_t component_end_index =
131 is_last_component ? contour_end_point_i - 1
133 .component_start_index;
135 AddVerticesForCurveComponent(
136 vtx_builder, component_start_index, component_end_index,
137 contour_start_point_i, contour_end_point_i, contour);
139 AddVerticesForLinearComponent(
140 vtx_builder, component_start_index, component_end_index,
141 contour_start_point_i, contour_end_point_i, contour);
151 cap_offset,
scale,
false);
164 const size_t contour_start_point_i,
165 const size_t contour_end_point_i,
168 if (point_i >= contour_end_point_i) {
170 }
else if (point_i <= contour_start_point_i) {
180 void AddVerticesForLinearComponent(
VertexWriter& vtx_builder,
181 const size_t component_start_index,
182 const size_t component_end_index,
183 const size_t contour_start_point_i,
184 const size_t contour_end_point_i,
186 bool is_last_component = component_start_index ==
187 contour.
components.back().component_start_index;
189 for (
size_t point_i = component_start_index; point_i < component_end_index;
191 bool is_end_of_component = point_i == component_end_index - 1;
195 vtx.position =
polyline.GetPoint(point_i) + offset_vector;
196 vtx_builder.AppendVertex(
vtx.position);
197 vtx.position =
polyline.GetPoint(point_i) - offset_vector;
198 vtx_builder.AppendVertex(
vtx.position);
202 vtx.position =
polyline.GetPoint(point_i + 1) + offset_vector;
203 vtx_builder.AppendVertex(
vtx.position);
204 vtx.position =
polyline.GetPoint(point_i + 1) - offset_vector;
205 vtx_builder.AppendVertex(
vtx.position);
208 offset = ComputeOffset(point_i + 2, contour_start_point_i,
209 contour_end_point_i, contour);
210 if (!is_last_component && is_end_of_component) {
219 void AddVerticesForCurveComponent(
VertexWriter& vtx_builder,
220 const size_t component_start_index,
221 const size_t component_end_index,
222 const size_t contour_start_point_i,
223 const size_t contour_end_point_i,
225 bool is_last_component = component_start_index ==
226 contour.
components.back().component_start_index;
228 for (
size_t point_i = component_start_index; point_i < component_end_index;
230 bool is_end_of_component = point_i == component_end_index - 1;
233 vtx_builder.AppendVertex(
vtx.position);
235 vtx_builder.AppendVertex(
vtx.position);
238 offset = ComputeOffset(point_i + 2, contour_start_point_i,
239 contour_end_point_i, contour);
242 if (!is_end_of_component) {
243 constexpr
Scalar kAngleThreshold = 10 *
kPi / 180;
246 constexpr
Scalar kAlignmentThreshold =
252 Scalar angle = kAngleThreshold;
256 while (angle < std::abs(angle_total)) {
257 Scalar signed_angle = angle_total < 0 ? -angle : angle;
261 vtx_builder.AppendVertex(
vtx.position);
263 vtx_builder.AppendVertex(
vtx.position);
265 angle += kAngleThreshold;
272 if (is_end_of_component) {
279 Point last_component_offset = is_last_component
282 vtx.position =
polyline.GetPoint(point_i + 1) + last_component_offset;
283 vtx_builder.AppendVertex(
vtx.position);
284 vtx.position =
polyline.GetPoint(point_i + 1) - last_component_offset;
285 vtx_builder.AppendVertex(
vtx.position);
287 if (!is_last_component) {
305 SolidFillVertexShader::PerVertexData
vtx;
308 template <
typename VertexWriter>
309 void CreateButtCap(VertexWriter& vtx_builder,
310 const Point& position,
315 VS::PerVertexData
vtx;
316 vtx.position = position + orientation;
317 vtx_builder.AppendVertex(
vtx.position);
318 vtx.position = position - orientation;
319 vtx_builder.AppendVertex(
vtx.position);
322 template <
typename VertexWriter>
323 void CreateRoundCap(VertexWriter& vtx_builder,
324 const Point& position,
332 CubicPathComponent arc;
334 arc = CubicPathComponent(
339 arc = CubicPathComponent(
346 vtx_builder.AppendVertex(
vtx);
347 vtx = position - orientation;
348 vtx_builder.AppendVertex(
vtx);
350 arc.ToLinearPathComponents(
scale, [&vtx_builder, &
vtx, forward_normal,
351 position](
const Point& point) {
352 vtx = position + point;
353 vtx_builder.AppendVertex(
vtx);
354 vtx = position + (-point).Reflect(forward_normal);
355 vtx_builder.AppendVertex(
vtx);
359 template <
typename VertexWriter>
360 void CreateSquareCap(VertexWriter& vtx_builder,
361 const Point& position,
369 vtx_builder.AppendVertex(
vtx);
370 vtx = position - orientation;
371 vtx_builder.AppendVertex(
vtx);
372 vtx = position + orientation + forward;
373 vtx_builder.AppendVertex(
vtx);
374 vtx = position - orientation + forward;
375 vtx_builder.AppendVertex(
vtx);
378 template <
typename VertexWriter>
379 Scalar CreateBevelAndGetDirection(VertexWriter& vtx_builder,
380 const Point& position,
381 const Point& start_offset,
382 const Point& end_offset) {
384 vtx_builder.AppendVertex(
vtx);
386 Scalar dir = start_offset.Cross(end_offset) > 0 ? -1 : 1;
387 vtx = position + start_offset * dir;
388 vtx_builder.AppendVertex(
vtx);
389 vtx = position + end_offset * dir;
390 vtx_builder.AppendVertex(
vtx);
395 template <
typename VertexWriter>
396 void CreateMiterJoin(VertexWriter& vtx_builder,
397 const Point& position,
398 const Point& start_offset,
399 const Point& end_offset,
406 Scalar alignment = (start_normal.Dot(end_normal) + 1) / 2;
411 Scalar direction = CreateBevelAndGetDirection(vtx_builder, position,
412 start_offset, end_offset);
414 Point miter_point = (((start_offset + end_offset) / 2) / alignment);
415 if (miter_point.GetDistanceSquared({0, 0}) > miter_limit * miter_limit) {
420 VS::PerVertexData
vtx;
421 vtx.position = position + miter_point * direction;
422 vtx_builder.AppendVertex(
vtx.position);
425 template <
typename VertexWriter>
426 void CreateRoundJoin(VertexWriter& vtx_builder,
427 const Point& position,
428 const Point& start_offset,
429 const Point& end_offset,
436 Scalar alignment = 1 - (start_normal.Dot(end_normal) + 1) / 2;
441 Scalar direction = CreateBevelAndGetDirection(vtx_builder, position,
442 start_offset, end_offset);
445 (start_offset + end_offset).Normalize() * start_offset.
GetLength();
448 Point middle_handle = middle +
Point(-middle.y, middle.x) *
450 alignment * direction;
451 Point start_handle = start_offset +
Point(start_offset.y, -start_offset.x) *
453 alignment * direction;
455 VS::PerVertexData
vtx;
456 CubicPathComponent(start_offset, start_handle, middle_handle, middle)
457 .ToLinearPathComponents(
scale, [&vtx_builder, direction, &
vtx, position,
458 middle_normal](
const Point& point) {
459 vtx.position = position + point * direction;
460 vtx_builder.AppendVertex(
vtx.position);
461 vtx.position = position + (-point * direction).Reflect(middle_normal);
462 vtx_builder.AppendVertex(
vtx.position);
466 template <
typename VertexWriter>
467 void CreateBevelJoin(VertexWriter& vtx_builder,
468 const Point& position,
469 const Point& start_offset,
470 const Point& end_offset,
473 CreateBevelAndGetDirection(vtx_builder, position, start_offset, end_offset);
476 template <
typename VertexWriter>
477 void CreateSolidStrokeVertices(VertexWriter& vtx_builder,
482 const CapProc<VertexWriter>&
cap_proc,
486 stroke_generator.Generate(vtx_builder);
490 template <
typename VertexWriter>
491 JoinProc<VertexWriter> GetJoinProc(
Join stroke_join) {
492 switch (stroke_join) {
494 return &CreateBevelJoin<VertexWriter>;
496 return &CreateMiterJoin<VertexWriter>;
498 return &CreateRoundJoin<VertexWriter>;
502 template <
typename VertexWriter>
503 CapProc<VertexWriter> GetCapProc(
Cap stroke_cap) {
504 switch (stroke_cap) {
506 return &CreateButtCap<VertexWriter>;
508 return &CreateRoundCap<VertexWriter>;
510 return &CreateSquareCap<VertexWriter>;
515 std::vector<SolidFillVertexShader::PerVertexData>
516 StrokePathGeometry::GenerateSolidStrokeVertices(
const Path::Polyline&
polyline,
523 auto join_proc = GetJoinProc<PositionWriter>(stroke_join);
524 auto cap_proc = GetCapProc<PositionWriter>(stroke_cap);
527 PositionWriter vtx_builder;
528 stroke_generator.Generate(vtx_builder);
529 return vtx_builder.GetData();
532 StrokePathGeometry::StrokePathGeometry(
const Path& path,
539 miter_limit_(miter_limit),
540 stroke_cap_(stroke_cap),
541 stroke_join_(stroke_join) {}
546 return stroke_width_;
569 if (stroke_width_ < 0.0) {
573 if (max_basis == 0) {
586 PositionWriter position_writer;
589 miter_limit_ * stroke_width_ * 0.5f,
590 GetJoinProc<PositionWriter>(stroke_join_),
591 GetCapProc<PositionWriter>(stroke_cap_),
scale);
594 host_buffer.Emplace(position_writer.GetData().data(),
595 position_writer.GetData().size() *
596 sizeof(SolidFillVertexShader::PerVertexData),
597 alignof(SolidFillVertexShader::PerVertexData));
599 return GeometryResult{
604 .vertex_count = position_writer.GetData().size(),
615 std::optional<Rect> StrokePathGeometry::GetCoverage(
618 if (!path_bounds.has_value()) {
624 max_radius = max_radius *
kSqrt2;
627 max_radius = std::max(max_radius, miter_limit_ * 0.5f);
630 if (max_basis == 0) {
635 max_radius *= std::max(stroke_width_, min_size);
636 return path_bounds->Expand(max_radius).TransformBounds(
transform);