16 : path_(
std::move(path)),
17 stroke_width_(stroke_width),
18 miter_limit_(miter_limit),
19 stroke_cap_(stroke_cap),
20 stroke_join_(stroke_join) {}
41 Scalar StrokePathGeometry::CreateBevelAndGetDirection(
43 const Point& position,
44 const Point& start_offset,
45 const Point& end_offset) {
46 SolidFillVertexShader::PerVertexData vtx;
47 vtx.position = position;
50 Scalar dir = start_offset.
Cross(end_offset) > 0 ? -1 : 1;
51 vtx.position = position + start_offset * dir;
53 vtx.position = position + end_offset * dir;
60 StrokePathGeometry::JoinProc StrokePathGeometry::GetJoinProc(
Join stroke_join) {
61 using VS = SolidFillVertexShader;
62 StrokePathGeometry::JoinProc join_proc;
63 switch (stroke_join) {
65 join_proc = [](VertexBufferBuilder<VS::PerVertexData>& vtx_builder,
66 const Point& position,
const Point& start_offset,
69 CreateBevelAndGetDirection(vtx_builder, position, start_offset,
74 join_proc = [](VertexBufferBuilder<VS::PerVertexData>& vtx_builder,
75 const Point& position,
const Point& start_offset,
82 Scalar alignment = (start_normal.Dot(end_normal) + 1) / 2;
87 Scalar dir = CreateBevelAndGetDirection(vtx_builder, position,
88 start_offset, end_offset);
90 Point miter_point = (start_offset + end_offset) / 2 / alignment;
91 if (miter_point.GetDistanceSquared({0, 0}) >
92 miter_limit * miter_limit) {
97 VS::PerVertexData vtx;
98 vtx.position = position + miter_point * dir;
99 vtx_builder.AppendVertex(vtx);
103 join_proc = [](VertexBufferBuilder<VS::PerVertexData>& vtx_builder,
104 const Point& position,
const Point& start_offset,
111 Scalar alignment = 1 - (start_normal.Dot(end_normal) + 1) / 2;
116 Scalar dir = CreateBevelAndGetDirection(vtx_builder, position,
117 start_offset, end_offset);
120 (start_offset + end_offset).Normalize() * start_offset.
GetLength();
123 Point middle_handle = middle +
Point(-middle.y, middle.x) *
127 start_offset +
Point(start_offset.y, -start_offset.x) *
131 std::vector<Point> arc_points;
132 CubicPathComponent(start_offset, start_handle, middle_handle, middle)
133 .AppendPolylinePoints(scale, arc_points);
135 VS::PerVertexData vtx;
136 for (
const auto& point : arc_points) {
137 vtx.position = position + point * dir;
138 vtx_builder.AppendVertex(vtx);
139 vtx.position = position + (-point * dir).Reflect(middle_normal);
140 vtx_builder.AppendVertex(vtx);
149 StrokePathGeometry::CapProc StrokePathGeometry::GetCapProc(
Cap stroke_cap) {
150 using VS = SolidFillVertexShader;
151 StrokePathGeometry::CapProc cap_proc;
152 switch (stroke_cap) {
154 cap_proc = [](VertexBufferBuilder<VS::PerVertexData>& vtx_builder,
157 Point orientation = offset * (reverse ? -1 : 1);
158 VS::PerVertexData vtx;
159 vtx.position = position + orientation;
160 vtx_builder.AppendVertex(vtx);
161 vtx.position = position - orientation;
162 vtx_builder.AppendVertex(vtx);
166 cap_proc = [](VertexBufferBuilder<VS::PerVertexData>& vtx_builder,
169 Point orientation = offset * (reverse ? -1 : 1);
171 VS::PerVertexData vtx;
173 Point forward(offset.y, -offset.x);
176 CubicPathComponent arc;
178 arc = CubicPathComponent(
184 arc = CubicPathComponent(
191 vtx.position = position + orientation;
192 vtx_builder.AppendVertex(vtx);
193 vtx.position = position - orientation;
194 vtx_builder.AppendVertex(vtx);
195 std::vector<Point> arc_points;
196 arc.AppendPolylinePoints(scale, arc_points);
197 for (
const auto& point : arc_points) {
198 vtx.position = position + point;
199 vtx_builder.AppendVertex(vtx);
200 vtx.position = position + (-point).Reflect(forward_normal);
201 vtx_builder.AppendVertex(vtx);
206 cap_proc = [](VertexBufferBuilder<VS::PerVertexData>& vtx_builder,
209 Point orientation = offset * (reverse ? -1 : 1);
211 VS::PerVertexData vtx;
213 Point forward(offset.y, -offset.x);
215 vtx.position = position + orientation;
216 vtx_builder.AppendVertex(vtx);
217 vtx.position = position - orientation;
218 vtx_builder.AppendVertex(vtx);
219 vtx.position = position + orientation + forward;
220 vtx_builder.AppendVertex(vtx);
221 vtx.position = position - orientation + forward;
222 vtx_builder.AppendVertex(vtx);
230 VertexBufferBuilder<SolidFillVertexShader::PerVertexData>
231 StrokePathGeometry::CreateSolidStrokeVertices(
234 Scalar scaled_miter_limit,
235 const StrokePathGeometry::JoinProc& join_proc,
236 const StrokePathGeometry::CapProc& cap_proc,
238 VertexBufferBuilder<VS::PerVertexData> vtx_builder;
239 auto point_buffer = std::make_unique<std::vector<Point>>();
243 point_buffer->reserve(512);
244 auto polyline = path.CreatePolyline(scale, std::move(point_buffer));
246 VS::PerVertexData vtx;
250 Point previous_offset;
255 auto compute_offset = [&polyline, &offset, &previous_offset, &stroke_width](
256 const size_t point_i,
257 const size_t contour_start_point_i,
258 const size_t contour_end_point_i,
259 const Path::PolylineContour& contour) {
261 if (point_i >= contour_end_point_i) {
262 direction = contour.end_direction;
263 }
else if (point_i <= contour_start_point_i) {
264 direction = -contour.start_direction;
266 direction = (polyline.GetPoint(point_i) - polyline.GetPoint(point_i - 1))
269 previous_offset = offset;
270 offset =
Vector2{-direction.
y, direction.x} * stroke_width * 0.5;
273 auto add_vertices_for_linear_component =
274 [&vtx_builder, &offset, &previous_offset, &vtx, &polyline,
275 &compute_offset, scaled_miter_limit, scale, &join_proc](
276 const size_t component_start_index,
const size_t component_end_index,
277 const size_t contour_start_point_i,
const size_t contour_end_point_i,
278 const Path::PolylineContour& contour) {
279 auto is_last_component =
280 component_start_index ==
281 contour.components.back().component_start_index;
283 for (
size_t point_i = component_start_index;
284 point_i < component_end_index; point_i++) {
285 auto is_end_of_component = point_i == component_end_index - 1;
286 vtx.position = polyline.GetPoint(point_i) + offset;
287 vtx_builder.AppendVertex(vtx);
288 vtx.position = polyline.GetPoint(point_i) - offset;
289 vtx_builder.AppendVertex(vtx);
293 vtx.position = polyline.GetPoint(point_i + 1) + offset;
294 vtx_builder.AppendVertex(vtx);
295 vtx.position = polyline.GetPoint(point_i + 1) - offset;
296 vtx_builder.AppendVertex(vtx);
298 compute_offset(point_i + 2, contour_start_point_i,
299 contour_end_point_i, contour);
300 if (!is_last_component && is_end_of_component) {
302 join_proc(vtx_builder, polyline.GetPoint(point_i + 1),
303 previous_offset, offset, scaled_miter_limit, scale);
308 auto add_vertices_for_curve_component =
309 [&vtx_builder, &offset, &previous_offset, &vtx, &polyline,
310 &compute_offset, scaled_miter_limit, scale, &join_proc](
311 const size_t component_start_index,
const size_t component_end_index,
312 const size_t contour_start_point_i,
const size_t contour_end_point_i,
313 const Path::PolylineContour& contour) {
314 auto is_last_component =
315 component_start_index ==
316 contour.components.back().component_start_index;
318 for (
size_t point_i = component_start_index;
319 point_i < component_end_index; point_i++) {
320 auto is_end_of_component = point_i == component_end_index - 1;
322 vtx.position = polyline.GetPoint(point_i) + offset;
323 vtx_builder.AppendVertex(vtx);
324 vtx.position = polyline.GetPoint(point_i) - offset;
325 vtx_builder.AppendVertex(vtx);
327 compute_offset(point_i + 2, contour_start_point_i,
328 contour_end_point_i, contour);
331 if (is_end_of_component) {
332 vtx.position = polyline.GetPoint(point_i + 1) + offset;
333 vtx_builder.AppendVertex(vtx);
334 vtx.position = polyline.GetPoint(point_i + 1) - offset;
335 vtx_builder.AppendVertex(vtx);
337 if (!is_last_component) {
338 join_proc(vtx_builder, polyline.GetPoint(point_i + 1),
339 previous_offset, offset, scaled_miter_limit, scale);
345 for (
size_t contour_i = 0; contour_i < polyline.contours.size();
347 auto contour = polyline.contours[contour_i];
348 size_t contour_start_point_i, contour_end_point_i;
349 std::tie(contour_start_point_i, contour_end_point_i) =
350 polyline.GetContourPointBounds(contour_i);
352 switch (contour_end_point_i - contour_start_point_i) {
354 Point p = polyline.GetPoint(contour_start_point_i);
355 cap_proc(vtx_builder, p, {-stroke_width * 0.5f, 0}, scale,
false);
356 cap_proc(vtx_builder, p, {stroke_width * 0.5f, 0}, scale,
false);
365 compute_offset(contour_start_point_i, contour_start_point_i,
366 contour_end_point_i, contour);
367 const Point contour_first_offset = offset;
377 vtx.position = polyline.GetPoint(contour_start_point_i - 1);
381 vtx_builder.AppendVertex(vtx);
382 vtx_builder.AppendVertex(vtx);
384 vtx.position = polyline.GetPoint(contour_start_point_i);
387 vtx_builder.AppendVertex(vtx);
388 vtx_builder.AppendVertex(vtx);
392 if (!polyline.contours[contour_i].is_closed) {
394 Vector2(-contour.start_direction.y, contour.start_direction.x) *
396 cap_proc(vtx_builder, polyline.GetPoint(contour_start_point_i),
397 cap_offset, scale,
true);
400 for (
size_t contour_component_i = 0;
401 contour_component_i < contour.components.size();
402 contour_component_i++) {
403 auto component = contour.components[contour_component_i];
404 auto is_last_component =
405 contour_component_i == contour.components.size() - 1;
407 auto component_start_index = component.component_start_index;
408 auto component_end_index =
409 is_last_component ? contour_end_point_i - 1
410 : contour.components[contour_component_i + 1]
411 .component_start_index;
412 if (component.is_curve) {
413 add_vertices_for_curve_component(
414 component_start_index, component_end_index, contour_start_point_i,
415 contour_end_point_i, contour);
417 add_vertices_for_linear_component(
418 component_start_index, component_end_index, contour_start_point_i,
419 contour_end_point_i, contour);
424 if (!contour.is_closed) {
426 Vector2(-contour.end_direction.y, contour.end_direction.x) *
428 cap_proc(vtx_builder, polyline.GetPoint(contour_end_point_i - 1),
429 cap_offset, scale,
false);
431 join_proc(vtx_builder, polyline.GetPoint(contour_start_point_i), offset,
432 contour_first_offset, scaled_miter_limit, scale);
439 GeometryResult StrokePathGeometry::GetPositionBuffer(
440 const ContentContext& renderer,
441 const Entity& entity,
442 RenderPass& pass)
const {
443 if (stroke_width_ < 0.0) {
446 auto determinant = entity.GetTransform().GetDeterminant();
447 if (determinant == 0) {
451 Scalar min_size = 1.0f / sqrt(std::abs(determinant));
452 Scalar stroke_width = std::max(stroke_width_, min_size);
454 auto& host_buffer = pass.GetTransientsBuffer();
455 auto vertex_builder = CreateSolidStrokeVertices(
456 path_, stroke_width, miter_limit_ * stroke_width_ * 0.5,
457 GetJoinProc(stroke_join_), GetCapProc(stroke_cap_),
458 entity.GetTransform().GetMaxBasisLength());
460 return GeometryResult{
462 .vertex_buffer = vertex_builder.CreateVertexBuffer(host_buffer),
463 .transform = pass.GetOrthographicTransform() * entity.GetTransform(),
464 .prevent_overdraw =
true,
468 GeometryResult StrokePathGeometry::GetPositionUVBuffer(
469 Rect texture_coverage,
470 Matrix effect_transform,
471 const ContentContext& renderer,
472 const Entity& entity,
473 RenderPass& pass)
const {
474 if (stroke_width_ < 0.0) {
477 auto determinant = entity.GetTransform().GetDeterminant();
478 if (determinant == 0) {
482 Scalar min_size = 1.0f / sqrt(std::abs(determinant));
483 Scalar stroke_width = std::max(stroke_width_, min_size);
485 auto& host_buffer = pass.GetTransientsBuffer();
486 auto stroke_builder = CreateSolidStrokeVertices(
487 path_, stroke_width, miter_limit_ * stroke_width_ * 0.5,
488 GetJoinProc(stroke_join_), GetCapProc(stroke_cap_),
489 entity.GetTransform().GetMaxBasisLength());
491 stroke_builder, {0, 0}, texture_coverage.GetSize(), effect_transform);
493 return GeometryResult{
495 .vertex_buffer = vertex_builder.CreateVertexBuffer(host_buffer),
496 .transform = pass.GetOrthographicTransform() * entity.GetTransform(),
497 .prevent_overdraw =
true,
505 std::optional<Rect> StrokePathGeometry::GetCoverage(
506 const Matrix& transform)
const {
508 if (!path_bounds.has_value()) {
511 auto path_coverage = path_bounds->TransformBounds(transform);
515 max_radius = max_radius *
kSqrt2;
518 max_radius = std::max(max_radius, miter_limit_ * 0.5f);
520 Scalar determinant = transform.GetDeterminant();
521 if (determinant == 0) {
524 Scalar min_size = 1.0f / sqrt(std::abs(determinant));
527 .TransformDirection(
Vector2(max_radius, max_radius) *
528 std::max(stroke_width_, min_size))
530 return path_coverage.Expand(max_radius_xy);