Flutter Engine
The Flutter Engine
stroke_path_geometry.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
14
15namespace impeller {
16using VS = SolidFillVertexShader;
17
18namespace {
19
20/// @brief The minimum stroke size can be less than one physical pixel because
21/// of MSAA, but no less that half a physical pixel otherwise we might
22/// not hit one of the sample positions.
23static constexpr Scalar kMinStrokeSizeMSAA = 0.5f;
24
25static constexpr Scalar kMinStrokeSize = 1.0f;
26
27template <typename VertexWriter>
28using CapProc = std::function<void(VertexWriter& vtx_builder,
29 const Point& position,
30 const Point& offset,
32 bool reverse)>;
33
34template <typename VertexWriter>
35using JoinProc = std::function<void(VertexWriter& vtx_builder,
36 const Point& position,
37 const Point& start_offset,
38 const Point& end_offset,
39 Scalar miter_limit,
40 Scalar scale)>;
41
42class PositionWriter {
43 public:
44 void AppendVertex(const Point& point) {
45 data_.emplace_back(SolidFillVertexShader::PerVertexData{.position = point});
46 }
47
48 const std::vector<SolidFillVertexShader::PerVertexData>& GetData() const {
49 return data_;
50 }
51
52 private:
53 std::vector<SolidFillVertexShader::PerVertexData> data_ = {};
54};
55
56template <typename VertexWriter>
57class StrokeGenerator {
58 public:
59 StrokeGenerator(const Path::Polyline& p_polyline,
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,
64 const Scalar p_scale)
65 : polyline(p_polyline),
66 stroke_width(p_stroke_width),
67 scaled_miter_limit(p_scaled_miter_limit),
68 join_proc(p_join_proc),
69 cap_proc(p_cap_proc),
70 scale(p_scale) {}
71
72 void Generate(VertexWriter& vtx_builder) {
73 for (size_t contour_i = 0; contour_i < polyline.contours.size();
74 contour_i++) {
75 const Path::PolylineContour& contour = polyline.contours[contour_i];
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);
79
80 auto contour_delta = contour_end_point_i - contour_start_point_i;
81 if (contour_delta == 1) {
82 Point p = polyline.GetPoint(contour_start_point_i);
83 cap_proc(vtx_builder, p, {-stroke_width * 0.5f, 0}, scale,
84 /*reverse=*/false);
85 cap_proc(vtx_builder, p, {stroke_width * 0.5f, 0}, scale,
86 /*reverse=*/false);
87 continue;
88 } else if (contour_delta == 0) {
89 continue; // This contour has no renderable content.
90 }
91
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();
96
97 if (contour_i > 0) {
98 // This branch only executes when we've just finished drawing a contour
99 // and are switching to a new one.
100 // We're drawing a triangle strip, so we need to "pick up the pen" by
101 // appending two vertices at the end of the previous contour and two
102 // vertices at the start of the new contour (thus connecting the two
103 // contours with two zero volume triangles, which will be discarded by
104 // the rasterizer).
105 vtx.position = polyline.GetPoint(contour_start_point_i - 1);
106 // Append two vertices when "picking up" the pen so that the triangle
107 // drawn when moving to the beginning of the new contour will have zero
108 // volume.
109 vtx_builder.AppendVertex(vtx.position);
110 vtx_builder.AppendVertex(vtx.position);
111
112 vtx.position = polyline.GetPoint(contour_start_point_i);
113 // Append two vertices at the beginning of the new contour, which
114 // appends two triangles of zero area.
115 vtx_builder.AppendVertex(vtx.position);
116 vtx_builder.AppendVertex(vtx.position);
117 }
118
119 // Generate start cap.
120 if (!polyline.contours[contour_i].is_closed) {
121 Point cap_offset =
122 Vector2(-contour.start_direction.y, contour.start_direction.x) *
123 stroke_width * 0.5f; // Counterclockwise normal
124 cap_proc(vtx_builder, polyline.GetPoint(contour_start_point_i),
125 cap_offset, scale, /*reverse=*/true);
126 }
127
128 for (size_t contour_component_i = 0;
129 contour_component_i < contour.components.size();
130 contour_component_i++) {
131 const Path::PolylineContour::Component& component =
132 contour.components[contour_component_i];
133 bool is_last_component =
134 contour_component_i == contour.components.size() - 1;
135
136 size_t component_start_index = component.component_start_index;
137 size_t component_end_index =
138 is_last_component ? contour_end_point_i - 1
139 : contour.components[contour_component_i + 1]
140 .component_start_index;
141 if (component.is_curve) {
142 AddVerticesForCurveComponent(
143 vtx_builder, component_start_index, component_end_index,
144 contour_start_point_i, contour_end_point_i, contour);
145 } else {
146 AddVerticesForLinearComponent(
147 vtx_builder, component_start_index, component_end_index,
148 contour_start_point_i, contour_end_point_i, contour);
149 }
150 }
151
152 // Generate end cap or join.
153 if (!contour.is_closed) {
154 auto cap_offset =
155 Vector2(-contour.end_direction.y, contour.end_direction.x) *
156 stroke_width * 0.5f; // Clockwise normal
157 cap_proc(vtx_builder, polyline.GetPoint(contour_end_point_i - 1),
158 cap_offset, scale, /*reverse=*/false);
159 } else {
160 join_proc(vtx_builder, polyline.GetPoint(contour_start_point_i),
161 offset.GetVector(), contour_first_offset, scaled_miter_limit,
162 scale);
163 }
164 }
165 }
166
167 /// Computes offset by calculating the direction from point_i - 1 to point_i
168 /// if point_i is within `contour_start_point_i` and `contour_end_point_i`;
169 /// Otherwise, it uses direction from contour.
170 SeparatedVector2 ComputeOffset(const size_t point_i,
171 const size_t contour_start_point_i,
172 const size_t contour_end_point_i,
173 const Path::PolylineContour& contour) const {
174 Point direction;
175 if (point_i >= contour_end_point_i) {
176 direction = contour.end_direction;
177 } else if (point_i <= contour_start_point_i) {
178 direction = -contour.start_direction;
179 } else {
180 direction = (polyline.GetPoint(point_i) - polyline.GetPoint(point_i - 1))
181 .Normalize();
182 }
183 return SeparatedVector2(Vector2{-direction.y, direction.x},
184 stroke_width * 0.5f);
185 }
186
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;
195
196 for (size_t point_i = component_start_index; point_i < component_end_index;
197 point_i++) {
198 bool is_end_of_component = point_i == component_end_index - 1;
199
200 Point offset_vector = offset.GetVector();
201
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);
206
207 // For line components, two additional points need to be appended
208 // prior to appending a join connecting the next component.
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);
213
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) {
218 // Generate join from the current line to the next line.
219 join_proc(vtx_builder, polyline.GetPoint(point_i + 1),
220 previous_offset.GetVector(), offset.GetVector(),
222 }
223 }
224 }
225
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;
234
235 for (size_t point_i = component_start_index; point_i < component_end_index;
236 point_i++) {
237 bool is_end_of_component = point_i == component_end_index - 1;
238
239 vtx.position = polyline.GetPoint(point_i) + offset.GetVector();
240 vtx_builder.AppendVertex(vtx.position);
241 vtx.position = polyline.GetPoint(point_i) - offset.GetVector();
242 vtx_builder.AppendVertex(vtx.position);
243
245 offset = ComputeOffset(point_i + 2, contour_start_point_i,
246 contour_end_point_i, contour);
247
248 // If the angle to the next segment is too sharp, round out the join.
249 if (!is_end_of_component) {
250 constexpr Scalar kAngleThreshold = 10 * kPi / 180;
251 // `std::cosf` is not constexpr-able, unfortunately, so we have to bake
252 // the alignment constant.
253 constexpr Scalar kAlignmentThreshold =
254 0.984807753012208; // std::cosf(kThresholdAngle) -- 10 degrees
255
256 // Use a cheap dot product to determine whether the angle is too sharp.
257 if (previous_offset.GetAlignment(offset) < kAlignmentThreshold) {
258 Scalar angle_total = previous_offset.AngleTo(offset).radians;
259 Scalar angle = kAngleThreshold;
260
261 // Bridge the large angle with additional geometry at
262 // `kAngleThreshold` interval.
263 while (angle < std::abs(angle_total)) {
264 Scalar signed_angle = angle_total < 0 ? -angle : angle;
265 Point offset =
266 previous_offset.GetVector().Rotate(Radians(signed_angle));
267 vtx.position = polyline.GetPoint(point_i) + offset;
268 vtx_builder.AppendVertex(vtx.position);
269 vtx.position = polyline.GetPoint(point_i) - offset;
270 vtx_builder.AppendVertex(vtx.position);
271
272 angle += kAngleThreshold;
273 }
274 }
275 }
276
277 // For curve components, the polyline is detailed enough such that
278 // it can avoid worrying about joins altogether.
279 if (is_end_of_component) {
280 // Append two additional vertices to close off the component. If we're
281 // on the _last_ component of the contour then we need to use the
282 // contour's end direction.
283 // `ComputeOffset` returns the contour's end direction when attempting
284 // to grab offsets past `contour_end_point_i`, so just use `offset` when
285 // we're on the last component.
286 Point last_component_offset = is_last_component
287 ? offset.GetVector()
288 : previous_offset.GetVector();
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);
293 // Generate join from the current line to the next line.
294 if (!is_last_component) {
295 join_proc(vtx_builder, polyline.GetPoint(point_i + 1),
296 previous_offset.GetVector(), offset.GetVector(),
298 }
299 }
300 }
301 }
302
303 const Path::Polyline& polyline;
306 const JoinProc<VertexWriter>& join_proc;
307 const CapProc<VertexWriter>& cap_proc;
309
310 SeparatedVector2 previous_offset;
311 SeparatedVector2 offset;
312 SolidFillVertexShader::PerVertexData vtx;
313};
314
315template <typename VertexWriter>
316void CreateButtCap(VertexWriter& vtx_builder,
317 const Point& position,
318 const Point& offset,
320 bool reverse) {
321 Point orientation = offset * (reverse ? -1 : 1);
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);
327}
328
329template <typename VertexWriter>
330void CreateRoundCap(VertexWriter& vtx_builder,
331 const Point& position,
332 const Point& offset,
334 bool reverse) {
335 Point orientation = offset * (reverse ? -1 : 1);
336 Point forward(offset.y, -offset.x);
337 Point forward_normal = forward.Normalize();
338
339 CubicPathComponent arc;
340 if (reverse) {
341 arc = CubicPathComponent(
342 forward, forward + orientation * PathBuilder::kArcApproximationMagic,
343 orientation + forward * PathBuilder::kArcApproximationMagic,
344 orientation);
345 } else {
346 arc = CubicPathComponent(
347 orientation,
348 orientation + forward * PathBuilder::kArcApproximationMagic,
349 forward + orientation * PathBuilder::kArcApproximationMagic, forward);
350 }
351
352 Point vtx = position + orientation;
353 vtx_builder.AppendVertex(vtx);
354 vtx = position - orientation;
355 vtx_builder.AppendVertex(vtx);
356
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);
363 });
364}
365
366template <typename VertexWriter>
367void CreateSquareCap(VertexWriter& vtx_builder,
368 const Point& position,
369 const Point& offset,
371 bool reverse) {
372 Point orientation = offset * (reverse ? -1 : 1);
373 Point forward(offset.y, -offset.x);
374
375 Point vtx = position + orientation;
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);
383}
384
385template <typename VertexWriter>
386Scalar CreateBevelAndGetDirection(VertexWriter& vtx_builder,
387 const Point& position,
388 const Point& start_offset,
389 const Point& end_offset) {
390 Point vtx = position;
391 vtx_builder.AppendVertex(vtx);
392
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);
398
399 return dir;
400}
401
402template <typename VertexWriter>
403void CreateMiterJoin(VertexWriter& vtx_builder,
404 const Point& position,
405 const Point& start_offset,
406 const Point& end_offset,
407 Scalar miter_limit,
408 Scalar scale) {
409 Point start_normal = start_offset.Normalize();
410 Point end_normal = end_offset.Normalize();
411
412 // 1 for no joint (straight line), 0 for max joint (180 degrees).
413 Scalar alignment = (start_normal.Dot(end_normal) + 1) / 2;
414 if (ScalarNearlyEqual(alignment, 1)) {
415 return;
416 }
417
418 Scalar direction = CreateBevelAndGetDirection(vtx_builder, position,
419 start_offset, end_offset);
420
421 Point miter_point = (((start_offset + end_offset) / 2) / alignment);
422 if (miter_point.GetDistanceSquared({0, 0}) > miter_limit * miter_limit) {
423 return; // Convert to bevel when we exceed the miter limit.
424 }
425
426 // Outer miter point.
427 VS::PerVertexData vtx;
428 vtx.position = position + miter_point * direction;
429 vtx_builder.AppendVertex(vtx.position);
430}
431
432template <typename VertexWriter>
433void CreateRoundJoin(VertexWriter& vtx_builder,
434 const Point& position,
435 const Point& start_offset,
436 const Point& end_offset,
437 Scalar miter_limit,
438 Scalar scale) {
439 Point start_normal = start_offset.Normalize();
440 Point end_normal = end_offset.Normalize();
441
442 // 0 for no joint (straight line), 1 for max joint (180 degrees).
443 Scalar alignment = 1 - (start_normal.Dot(end_normal) + 1) / 2;
444 if (ScalarNearlyEqual(alignment, 0)) {
445 return;
446 }
447
448 Scalar direction = CreateBevelAndGetDirection(vtx_builder, position,
449 start_offset, end_offset);
450
451 Point middle =
452 (start_offset + end_offset).Normalize() * start_offset.GetLength();
453 Point middle_normal = middle.Normalize();
454
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;
461
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);
470 });
471}
472
473template <typename VertexWriter>
474void CreateBevelJoin(VertexWriter& vtx_builder,
475 const Point& position,
476 const Point& start_offset,
477 const Point& end_offset,
478 Scalar miter_limit,
479 Scalar scale) {
480 CreateBevelAndGetDirection(vtx_builder, position, start_offset, end_offset);
481}
482
483template <typename VertexWriter>
484void CreateSolidStrokeVertices(VertexWriter& vtx_builder,
485 const Path::Polyline& polyline,
488 const JoinProc<VertexWriter>& join_proc,
489 const CapProc<VertexWriter>& cap_proc,
490 Scalar scale) {
491 StrokeGenerator stroke_generator(polyline, stroke_width, scaled_miter_limit,
493 stroke_generator.Generate(vtx_builder);
494}
495
496// static
497template <typename VertexWriter>
498JoinProc<VertexWriter> GetJoinProc(Join stroke_join) {
499 switch (stroke_join) {
500 case Join::kBevel:
501 return &CreateBevelJoin<VertexWriter>;
502 case Join::kMiter:
503 return &CreateMiterJoin<VertexWriter>;
504 case Join::kRound:
505 return &CreateRoundJoin<VertexWriter>;
506 }
507}
508
509template <typename VertexWriter>
510CapProc<VertexWriter> GetCapProc(Cap stroke_cap) {
511 switch (stroke_cap) {
512 case Cap::kButt:
513 return &CreateButtCap<VertexWriter>;
514 case Cap::kRound:
515 return &CreateRoundCap<VertexWriter>;
516 case Cap::kSquare:
517 return &CreateSquareCap<VertexWriter>;
518 }
519}
520} // namespace
521
522std::vector<SolidFillVertexShader::PerVertexData>
523StrokePathGeometry::GenerateSolidStrokeVertices(const Path::Polyline& polyline,
525 Scalar miter_limit,
526 Join stroke_join,
527 Cap stroke_cap,
528 Scalar scale) {
529 auto scaled_miter_limit = stroke_width * miter_limit * 0.5f;
530 auto join_proc = GetJoinProc<PositionWriter>(stroke_join);
531 auto cap_proc = GetCapProc<PositionWriter>(stroke_cap);
532 StrokeGenerator stroke_generator(polyline, stroke_width, scaled_miter_limit,
534 PositionWriter vtx_builder;
535 stroke_generator.Generate(vtx_builder);
536 return vtx_builder.GetData();
537}
538
539StrokePathGeometry::StrokePathGeometry(const Path& path,
541 Scalar miter_limit,
542 Cap stroke_cap,
543 Join stroke_join)
544 : path_(path),
545 stroke_width_(stroke_width),
546 miter_limit_(miter_limit),
547 stroke_cap_(stroke_cap),
548 stroke_join_(stroke_join) {}
549
551
553 return stroke_width_;
554}
555
557 return miter_limit_;
558}
559
561 return stroke_cap_;
562}
563
565 return stroke_join_;
566}
567
569 Scalar scaled_stroke_width =
570 entity.GetTransform().GetMaxBasisLengthXY() * stroke_width_;
571 // If the stroke width is 0 or greater than kMinStrokeSizeMSAA, don't apply
572 // any additional alpha. This is intended to match Skia behavior.
573 if (scaled_stroke_width == 0.0 || scaled_stroke_width >= kMinStrokeSizeMSAA) {
574 return 1.0;
575 }
576 // This scalling is eyeballed from Skia.
577 return std::clamp(scaled_stroke_width * 20.0f, 0.f, 1.f);
578}
579
580GeometryResult StrokePathGeometry::GetPositionBuffer(
582 const Entity& entity,
583 RenderPass& pass) const {
584 if (stroke_width_ < 0.0) {
585 return {};
586 }
587 auto determinant = entity.GetTransform().GetDeterminant();
588 if (determinant == 0) {
589 return {};
590 }
591
592 Scalar min_size =
593 (pass.GetSampleCount() == SampleCount::kCount4 ? kMinStrokeSizeMSAA
594 : kMinStrokeSize) /
595 sqrt(std::abs(determinant));
596 Scalar stroke_width = std::max(stroke_width_, min_size);
597
598 auto& host_buffer = renderer.GetTransientsBuffer();
599 auto scale = entity.GetTransform().GetMaxBasisLength();
600
601 PositionWriter position_writer;
602 auto polyline = renderer.GetTessellator()->CreateTempPolyline(path_, scale);
603 CreateSolidStrokeVertices(position_writer, polyline, stroke_width,
604 miter_limit_ * stroke_width_ * 0.5f,
605 GetJoinProc<PositionWriter>(stroke_join_),
606 GetCapProc<PositionWriter>(stroke_cap_), scale);
607
608 BufferView buffer_view =
609 host_buffer.Emplace(position_writer.GetData().data(),
610 position_writer.GetData().size() *
611 sizeof(SolidFillVertexShader::PerVertexData),
612 alignof(SolidFillVertexShader::PerVertexData));
613
614 return GeometryResult{
616 .vertex_buffer =
617 {
618 .vertex_buffer = buffer_view,
619 .vertex_count = position_writer.GetData().size(),
620 .index_type = IndexType::kNone,
621 },
622 .transform = entity.GetShaderTransform(pass),
624}
625
626GeometryResult::Mode StrokePathGeometry::GetResultMode() const {
628}
629
630std::optional<Rect> StrokePathGeometry::GetCoverage(
631 const Matrix& transform) const {
632 auto path_bounds = path_.GetBoundingBox();
633 if (!path_bounds.has_value()) {
634 return std::nullopt;
635 }
636
637 Scalar max_radius = 0.5;
638 if (stroke_cap_ == Cap::kSquare) {
639 max_radius = max_radius * kSqrt2;
640 }
641 if (stroke_join_ == Join::kMiter) {
642 max_radius = std::max(max_radius, miter_limit_ * 0.5f);
643 }
644 Scalar determinant = transform.GetDeterminant();
645 if (determinant == 0) {
646 return std::nullopt;
647 }
648 // Use the most conervative coverage setting.
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);
652}
653
654} // namespace impeller
static unsigned clamp(SkFixed fx, int max)
BufferView buffer_view
Matrix GetShaderTransform(const RenderPass &pass) const
Get the vertex shader transform used for drawing this Entity.
Definition: entity.cc:50
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
Definition: entity.cc:46
static constexpr const Scalar kArcApproximationMagic
Definition: path_builder.h:23
Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments....
Definition: path.h:52
std::optional< Rect > GetBoundingBox() const
Definition: path.cc:405
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:33
SampleCount GetSampleCount() const
The sample count of the attached render target.
Definition: render_pass.cc:23
Scalar ComputeAlphaCoverage(const Entity &entity) const override
An interface for generating a multi contour polyline as a triangle strip.
Dart_NativeFunction function
Definition: fuchsia.cc:51
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static void Normalize(char *s)
Definition: flags.cc:296
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets dir
Definition: switches.h:145
@ kNone
Does not use the index buffer.
Join
Definition: path.h:24
Point Vector2
Definition: point.h:326
constexpr float kPi
Definition: constants.h:26
float Scalar
Definition: scalar.h:18
SolidFillVertexShader VS
TPoint< Scalar > Point
Definition: point.h:322
Cap
Definition: path.h:18
constexpr float kSqrt2
Definition: constants.h:47
constexpr bool ScalarNearlyEqual(Scalar x, Scalar y, Scalar tolerance=kEhCloseEnough)
Definition: scalar.h:30
SK_API sk_sp< PrecompileColorFilter > Matrix()
SIN Vec< N, float > abs(const Vec< N, float > &x)
Definition: SkVx.h:707
SIN Vec< N, float > sqrt(const Vec< N, float > &x)
Definition: SkVx.h:706
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
Definition: p3.cpp:47
SeparatedVector2 previous_offset
SolidFillVertexShader::PerVertexData vtx
const CapProc< VertexWriter > & cap_proc
const Scalar scaled_miter_limit
const Scalar stroke_width
const Scalar scale
const JoinProc< VertexWriter > & join_proc
const Path::Polyline & polyline
SeparatedVector2 offset
constexpr Scalar GetMaxBasisLengthXY() const
Definition: matrix.h:300
Scalar GetMaxBasisLength() const
Definition: matrix.cc:196
Scalar GetDeterminant() const
Definition: matrix.cc:162
A Vector2, broken down as a separate magnitude and direction. Assumes that the direction given is nor...
constexpr TPoint Normalize() const
Definition: point.h:208