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 auto arc_points = CubicPathComponent(start_offset, start_handle,
132 middle_handle, middle)
133 .CreatePolyline(scale);
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 for (
const auto& point : arc.CreatePolyline(scale)) {
196 vtx.position = position + point;
197 vtx_builder.AppendVertex(vtx);
198 vtx.position = position + (-point).Reflect(forward_normal);
199 vtx_builder.AppendVertex(vtx);
204 cap_proc = [](VertexBufferBuilder<VS::PerVertexData>& vtx_builder,
207 Point orientation = offset * (reverse ? -1 : 1);
209 VS::PerVertexData vtx;
211 Point forward(offset.y, -offset.x);
213 vtx.position = position + orientation;
214 vtx_builder.AppendVertex(vtx);
215 vtx.position = position - orientation;
216 vtx_builder.AppendVertex(vtx);
217 vtx.position = position + orientation + forward;
218 vtx_builder.AppendVertex(vtx);
219 vtx.position = position - orientation + forward;
220 vtx_builder.AppendVertex(vtx);
228 VertexBufferBuilder<SolidFillVertexShader::PerVertexData>
229 StrokePathGeometry::CreateSolidStrokeVertices(
232 Scalar scaled_miter_limit,
233 const StrokePathGeometry::JoinProc& join_proc,
234 const StrokePathGeometry::CapProc& cap_proc,
236 VertexBufferBuilder<VS::PerVertexData> vtx_builder;
237 auto polyline = path.CreatePolyline(scale);
239 VS::PerVertexData vtx;
243 Point previous_offset;
245 auto compute_offset = [&polyline, &offset, &previous_offset,
246 &stroke_width](
size_t point_i) {
247 previous_offset = offset;
249 (polyline.points[point_i] - polyline.points[point_i - 1]).Normalize();
250 offset =
Vector2{-direction.
y, direction.x} * stroke_width * 0.5;
253 for (
size_t contour_i = 0; contour_i < polyline.contours.size();
255 auto contour = polyline.contours[contour_i];
256 size_t contour_start_point_i, contour_end_point_i;
257 std::tie(contour_start_point_i, contour_end_point_i) =
258 polyline.GetContourPointBounds(contour_i);
260 switch (contour_end_point_i - contour_start_point_i) {
262 Point p = polyline.points[contour_start_point_i];
263 cap_proc(vtx_builder, p, {-stroke_width * 0.5f, 0}, scale,
false);
264 cap_proc(vtx_builder, p, {stroke_width * 0.5f, 0}, scale,
false);
274 compute_offset(contour_start_point_i + 1);
275 const Point contour_first_offset = offset;
285 vtx.position = polyline.points[contour_start_point_i - 1];
289 vtx_builder.AppendVertex(vtx);
290 vtx_builder.AppendVertex(vtx);
292 vtx.position = polyline.points[contour_start_point_i];
295 vtx_builder.AppendVertex(vtx);
296 vtx_builder.AppendVertex(vtx);
300 if (!polyline.contours[contour_i].is_closed) {
302 Vector2(-contour.start_direction.y, contour.start_direction.x) *
304 cap_proc(vtx_builder, polyline.points[contour_start_point_i], cap_offset,
309 for (
size_t point_i = contour_start_point_i + 1;
310 point_i < contour_end_point_i; point_i++) {
312 vtx.position = polyline.points[point_i - 1] + offset;
313 vtx_builder.AppendVertex(vtx);
314 vtx.position = polyline.points[point_i - 1] - offset;
315 vtx_builder.AppendVertex(vtx);
316 vtx.position = polyline.points[point_i] + offset;
317 vtx_builder.AppendVertex(vtx);
318 vtx.position = polyline.points[point_i] - offset;
319 vtx_builder.AppendVertex(vtx);
321 if (point_i < contour_end_point_i - 1) {
322 compute_offset(point_i + 1);
325 join_proc(vtx_builder, polyline.points[point_i], previous_offset,
326 offset, scaled_miter_limit, scale);
331 if (!polyline.contours[contour_i].is_closed) {
333 Vector2(-contour.end_direction.y, contour.end_direction.x) *
335 cap_proc(vtx_builder, polyline.points[contour_end_point_i - 1],
336 cap_offset, scale,
false);
338 join_proc(vtx_builder, polyline.points[contour_start_point_i], offset,
339 contour_first_offset, scaled_miter_limit, scale);
346 GeometryResult StrokePathGeometry::GetPositionBuffer(
347 const ContentContext& renderer,
348 const Entity& entity,
350 if (stroke_width_ < 0.0) {
353 auto determinant = entity.GetTransformation().GetDeterminant();
354 if (determinant == 0) {
358 Scalar min_size = 1.0f / sqrt(std::abs(determinant));
359 Scalar stroke_width = std::max(stroke_width_, min_size);
361 auto& host_buffer = pass.GetTransientsBuffer();
362 auto vertex_builder = CreateSolidStrokeVertices(
363 path_, stroke_width, miter_limit_ * stroke_width_ * 0.5,
364 GetJoinProc(stroke_join_), GetCapProc(stroke_cap_),
365 entity.GetTransformation().GetMaxBasisLength());
367 return GeometryResult{
369 .vertex_buffer = vertex_builder.CreateVertexBuffer(host_buffer),
371 entity.GetTransformation(),
372 .prevent_overdraw =
true,
376 GeometryResult StrokePathGeometry::GetPositionUVBuffer(
377 Rect texture_coverage,
378 Matrix effect_transform,
379 const ContentContext& renderer,
380 const Entity& entity,
382 if (stroke_width_ < 0.0) {
386 if (determinant == 0) {
390 Scalar min_size = 1.0f / sqrt(std::abs(determinant));
391 Scalar stroke_width = std::max(stroke_width_, min_size);
393 auto& host_buffer = pass.GetTransientsBuffer();
394 auto stroke_builder = CreateSolidStrokeVertices(
395 path_, stroke_width, miter_limit_ * stroke_width_ * 0.5,
396 GetJoinProc(stroke_join_), GetCapProc(stroke_cap_),
397 entity.GetTransformation().GetMaxBasisLength());
399 stroke_builder, {0, 0}, texture_coverage.size, effect_transform);
401 return GeometryResult{
403 .vertex_buffer = vertex_builder.CreateVertexBuffer(host_buffer),
405 entity.GetTransformation(),
406 .prevent_overdraw =
true,
414 std::optional<Rect> StrokePathGeometry::GetCoverage(
415 const Matrix& transform)
const {
417 if (!path_bounds.has_value()) {
420 auto path_coverage = path_bounds->TransformBounds(transform);
424 max_radius = max_radius *
kSqrt2;
427 max_radius = std::max(max_radius, miter_limit_ * 0.5f);
429 Scalar determinant = transform.GetDeterminant();
430 if (determinant == 0) {
433 Scalar min_size = 1.0f / sqrt(std::abs(determinant));
436 .TransformDirection(
Vector2(max_radius, max_radius) *
437 std::max(stroke_width_, min_size))
439 return Rect(path_coverage.origin - max_radius_xy,
440 Size(path_coverage.size.width + max_radius_xy.x * 2,
441 path_coverage.size.height + max_radius_xy.y * 2));