16 using VS = SolidFillVertexShader;
23 static constexpr
Scalar kMinStrokeSizeMSAA = 0.5f;
25 static constexpr
Scalar kMinStrokeSize = 1.0f;
27 template <
typename VertexWriter>
28 using CapProc = std::function<void(
VertexWriter& vtx_builder,
29 const Point& position,
34 template <
typename VertexWriter>
35 using JoinProc = std::function<void(
VertexWriter& vtx_builder,
36 const Point& position,
37 const Point& start_offset,
38 const Point& end_offset,
42 class PositionWriter {
44 void AppendVertex(
const Point& point) {
45 data_.emplace_back(SolidFillVertexShader::PerVertexData{.position = point});
48 const std::vector<SolidFillVertexShader::PerVertexData>& GetData()
const {
53 std::vector<SolidFillVertexShader::PerVertexData> data_ = {};
56 template <
typename VertexWriter>
57 class StrokeGenerator {
60 const Scalar p_stroke_width,
61 const Scalar p_scaled_miter_limit,
62 const JoinProc<VertexWriter>& p_join_proc,
63 const CapProc<VertexWriter>& p_cap_proc,
73 for (
size_t contour_i = 0; contour_i <
polyline.contours.size();
76 size_t contour_start_point_i, contour_end_point_i;
77 std::tie(contour_start_point_i, contour_end_point_i) =
78 polyline.GetContourPointBounds(contour_i);
80 auto contour_delta = contour_end_point_i - contour_start_point_i;
81 if (contour_delta == 1) {
88 }
else if (contour_delta == 0) {
93 offset = ComputeOffset(contour_start_point_i, contour_start_point_i,
94 contour_end_point_i, contour);
95 const Point contour_first_offset =
offset.GetVector();
105 vtx.position =
polyline.GetPoint(contour_start_point_i - 1);
109 vtx_builder.AppendVertex(
vtx.position);
110 vtx_builder.AppendVertex(
vtx.position);
112 vtx.position =
polyline.GetPoint(contour_start_point_i);
115 vtx_builder.AppendVertex(
vtx.position);
116 vtx_builder.AppendVertex(
vtx.position);
120 if (!
polyline.contours[contour_i].is_closed) {
125 cap_offset,
scale,
true);
128 for (
size_t contour_component_i = 0;
129 contour_component_i < contour.
components.size();
130 contour_component_i++) {
133 bool is_last_component =
134 contour_component_i == contour.
components.size() - 1;
137 size_t component_end_index =
138 is_last_component ? contour_end_point_i - 1
140 .component_start_index;
142 AddVerticesForCurveComponent(
143 vtx_builder, component_start_index, component_end_index,
144 contour_start_point_i, contour_end_point_i, contour);
146 AddVerticesForLinearComponent(
147 vtx_builder, component_start_index, component_end_index,
148 contour_start_point_i, contour_end_point_i, contour);
158 cap_offset,
scale,
false);
171 const size_t contour_start_point_i,
172 const size_t contour_end_point_i,
175 if (point_i >= contour_end_point_i) {
177 }
else if (point_i <= contour_start_point_i) {
187 void AddVerticesForLinearComponent(
VertexWriter& vtx_builder,
188 const size_t component_start_index,
189 const size_t component_end_index,
190 const size_t contour_start_point_i,
191 const size_t contour_end_point_i,
193 bool is_last_component = component_start_index ==
194 contour.
components.back().component_start_index;
196 for (
size_t point_i = component_start_index; point_i < component_end_index;
198 bool is_end_of_component = point_i == component_end_index - 1;
202 vtx.position =
polyline.GetPoint(point_i) + offset_vector;
203 vtx_builder.AppendVertex(
vtx.position);
204 vtx.position =
polyline.GetPoint(point_i) - offset_vector;
205 vtx_builder.AppendVertex(
vtx.position);
209 vtx.position =
polyline.GetPoint(point_i + 1) + offset_vector;
210 vtx_builder.AppendVertex(
vtx.position);
211 vtx.position =
polyline.GetPoint(point_i + 1) - offset_vector;
212 vtx_builder.AppendVertex(
vtx.position);
215 offset = ComputeOffset(point_i + 2, contour_start_point_i,
216 contour_end_point_i, contour);
217 if (!is_last_component && is_end_of_component) {
226 void AddVerticesForCurveComponent(
VertexWriter& vtx_builder,
227 const size_t component_start_index,
228 const size_t component_end_index,
229 const size_t contour_start_point_i,
230 const size_t contour_end_point_i,
232 bool is_last_component = component_start_index ==
233 contour.
components.back().component_start_index;
235 for (
size_t point_i = component_start_index; point_i < component_end_index;
237 bool is_end_of_component = point_i == component_end_index - 1;
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);
249 if (!is_end_of_component) {
250 constexpr
Scalar kAngleThreshold = 10 *
kPi / 180;
253 constexpr
Scalar kAlignmentThreshold =
259 Scalar angle = kAngleThreshold;
263 while (angle < std::abs(angle_total)) {
264 Scalar signed_angle = angle_total < 0 ? -angle : angle;
268 vtx_builder.AppendVertex(
vtx.position);
270 vtx_builder.AppendVertex(
vtx.position);
272 angle += kAngleThreshold;
279 if (is_end_of_component) {
286 Point last_component_offset = is_last_component
289 vtx.position =
polyline.GetPoint(point_i + 1) + last_component_offset;
290 vtx_builder.AppendVertex(
vtx.position);
291 vtx.position =
polyline.GetPoint(point_i + 1) - last_component_offset;
292 vtx_builder.AppendVertex(
vtx.position);
294 if (!is_last_component) {
312 SolidFillVertexShader::PerVertexData
vtx;
315 template <
typename VertexWriter>
316 void CreateButtCap(VertexWriter& vtx_builder,
317 const Point& position,
322 VS::PerVertexData
vtx;
323 vtx.position = position + orientation;
324 vtx_builder.AppendVertex(
vtx.position);
325 vtx.position = position - orientation;
326 vtx_builder.AppendVertex(
vtx.position);
329 template <
typename VertexWriter>
330 void CreateRoundCap(VertexWriter& vtx_builder,
331 const Point& position,
339 CubicPathComponent arc;
341 arc = CubicPathComponent(
346 arc = CubicPathComponent(
353 vtx_builder.AppendVertex(
vtx);
354 vtx = position - orientation;
355 vtx_builder.AppendVertex(
vtx);
357 arc.ToLinearPathComponents(
scale, [&vtx_builder, &
vtx, forward_normal,
358 position](
const Point& point) {
359 vtx = position + point;
360 vtx_builder.AppendVertex(
vtx);
361 vtx = position + (-point).Reflect(forward_normal);
362 vtx_builder.AppendVertex(
vtx);
366 template <
typename VertexWriter>
367 void CreateSquareCap(VertexWriter& vtx_builder,
368 const Point& position,
376 vtx_builder.AppendVertex(
vtx);
377 vtx = position - orientation;
378 vtx_builder.AppendVertex(
vtx);
379 vtx = position + orientation + forward;
380 vtx_builder.AppendVertex(
vtx);
381 vtx = position - orientation + forward;
382 vtx_builder.AppendVertex(
vtx);
385 template <
typename VertexWriter>
386 Scalar CreateBevelAndGetDirection(VertexWriter& vtx_builder,
387 const Point& position,
388 const Point& start_offset,
389 const Point& end_offset) {
391 vtx_builder.AppendVertex(
vtx);
393 Scalar dir = start_offset.Cross(end_offset) > 0 ? -1 : 1;
394 vtx = position + start_offset * dir;
395 vtx_builder.AppendVertex(
vtx);
396 vtx = position + end_offset * dir;
397 vtx_builder.AppendVertex(
vtx);
402 template <
typename VertexWriter>
403 void CreateMiterJoin(VertexWriter& vtx_builder,
404 const Point& position,
405 const Point& start_offset,
406 const Point& end_offset,
413 Scalar alignment = (start_normal.Dot(end_normal) + 1) / 2;
418 Scalar direction = CreateBevelAndGetDirection(vtx_builder, position,
419 start_offset, end_offset);
421 Point miter_point = (((start_offset + end_offset) / 2) / alignment);
422 if (miter_point.GetDistanceSquared({0, 0}) > miter_limit * miter_limit) {
427 VS::PerVertexData
vtx;
428 vtx.position = position + miter_point * direction;
429 vtx_builder.AppendVertex(
vtx.position);
432 template <
typename VertexWriter>
433 void CreateRoundJoin(VertexWriter& vtx_builder,
434 const Point& position,
435 const Point& start_offset,
436 const Point& end_offset,
443 Scalar alignment = 1 - (start_normal.Dot(end_normal) + 1) / 2;
448 Scalar direction = CreateBevelAndGetDirection(vtx_builder, position,
449 start_offset, end_offset);
452 (start_offset + end_offset).Normalize() * start_offset.
GetLength();
455 Point middle_handle = middle +
Point(-middle.y, middle.x) *
457 alignment * direction;
458 Point start_handle = start_offset +
Point(start_offset.y, -start_offset.x) *
460 alignment * direction;
462 VS::PerVertexData
vtx;
463 CubicPathComponent(start_offset, start_handle, middle_handle, middle)
464 .ToLinearPathComponents(
scale, [&vtx_builder, direction, &
vtx, position,
465 middle_normal](
const Point& point) {
466 vtx.position = position + point * direction;
467 vtx_builder.AppendVertex(
vtx.position);
468 vtx.position = position + (-point * direction).Reflect(middle_normal);
469 vtx_builder.AppendVertex(
vtx.position);
473 template <
typename VertexWriter>
474 void CreateBevelJoin(VertexWriter& vtx_builder,
475 const Point& position,
476 const Point& start_offset,
477 const Point& end_offset,
480 CreateBevelAndGetDirection(vtx_builder, position, start_offset, end_offset);
483 template <
typename VertexWriter>
484 void CreateSolidStrokeVertices(VertexWriter& vtx_builder,
489 const CapProc<VertexWriter>&
cap_proc,
493 stroke_generator.Generate(vtx_builder);
497 template <
typename VertexWriter>
498 JoinProc<VertexWriter> GetJoinProc(
Join stroke_join) {
499 switch (stroke_join) {
501 return &CreateBevelJoin<VertexWriter>;
503 return &CreateMiterJoin<VertexWriter>;
505 return &CreateRoundJoin<VertexWriter>;
509 template <
typename VertexWriter>
510 CapProc<VertexWriter> GetCapProc(
Cap stroke_cap) {
511 switch (stroke_cap) {
513 return &CreateButtCap<VertexWriter>;
515 return &CreateRoundCap<VertexWriter>;
517 return &CreateSquareCap<VertexWriter>;
522 std::vector<SolidFillVertexShader::PerVertexData>
523 StrokePathGeometry::GenerateSolidStrokeVertices(
const Path::Polyline&
polyline,
530 auto join_proc = GetJoinProc<PositionWriter>(stroke_join);
531 auto cap_proc = GetCapProc<PositionWriter>(stroke_cap);
534 PositionWriter vtx_builder;
535 stroke_generator.Generate(vtx_builder);
536 return vtx_builder.GetData();
539 StrokePathGeometry::StrokePathGeometry(
const Path& path,
546 miter_limit_(miter_limit),
547 stroke_cap_(stroke_cap),
548 stroke_join_(stroke_join) {}
553 return stroke_width_;
569 Scalar scaled_stroke_width =
573 if (scaled_stroke_width == 0.0 || scaled_stroke_width >= kMinStrokeSizeMSAA) {
577 return std::clamp(scaled_stroke_width * 20.0f, 0.f, 1.f);
584 if (stroke_width_ < 0.0) {
588 if (determinant == 0) {
595 sqrt(std::abs(determinant));
601 PositionWriter position_writer;
604 miter_limit_ * stroke_width_ * 0.5f,
605 GetJoinProc<PositionWriter>(stroke_join_),
606 GetCapProc<PositionWriter>(stroke_cap_),
scale);
609 host_buffer.Emplace(position_writer.GetData().data(),
610 position_writer.GetData().size() *
611 sizeof(SolidFillVertexShader::PerVertexData),
612 alignof(SolidFillVertexShader::PerVertexData));
614 return GeometryResult{
619 .vertex_count = position_writer.GetData().size(),
630 std::optional<Rect> StrokePathGeometry::GetCoverage(
633 if (!path_bounds.has_value()) {
639 max_radius = max_radius *
kSqrt2;
642 max_radius = std::max(max_radius, miter_limit_ * 0.5f);
645 if (determinant == 0) {
649 Scalar min_size = kMinStrokeSize / sqrt(std::abs(determinant));
650 max_radius *= std::max(stroke_width_, min_size);
651 return path_bounds->Expand(max_radius).TransformBounds(
transform);