45 static constexpr Scalar kFractionUninitialized = -1.0f;
80 uint16_t umbra_index = 0u;
89 Scalar umbra_fraction = kFractionUninitialized;
94 UmbraPin* p_next =
nullptr;
95 UmbraPin* p_prev =
nullptr;
100 bool IsFractionInitialized()
const {
101 return umbra_fraction > kFractionUninitialized;
112struct DirectionDetector {
113 Scalar last_direction_ = 0.0f;
114 size_t change_count = 0u;
118 void AccumulateDirection(Scalar new_direction) {
119 if (last_direction_ == 0.0f || last_direction_ * new_direction < 0.0f) {
120 last_direction_ = std::copysign(1.0f, new_direction);
126 bool IsConcave()
const {
129 return change_count > 3u;
145class UmbraPinAccumulator :
public PathTessellator::VertexWriter {
153 static constexpr Scalar kSubPixelCount = 16.0f;
154 static constexpr Scalar kSubPixelScale = (1.0f / kSubPixelCount);
158 enum class PathStatus {
175 UmbraPinAccumulator() =
default;
176 ~UmbraPinAccumulator() =
default;
180 void Reserve(
size_t vertex_count) { pins_.reserve(vertex_count); }
184 PathStatus GetStatus() {
return GetResults().status; }
188 std::vector<UmbraPin>& GetPins() {
return pins_; }
192 Point GetCentroid() {
return GetResults().centroid; }
196 Scalar GetDirection() {
return GetResults().path_direction; }
213 Scalar path_direction = 0.0f;
217 void Write(Point point)
override;
220 void EndContour()
override;
223 static Point ToDeviceGrid(Point point);
227 std::vector<UmbraPin> pins_;
232 bool first_contour_ended_ =
false;
237 bool has_multiple_contours_ =
false;
241 std::optional<PathResults> results_;
245 PathResults& GetResults() {
246 if (results_.has_value()) {
247 return results_.value();
249 return (results_ = FinalizePath()).value();
254 PathResults FinalizePath();
267 static constexpr Scalar GetTrigRadiusForHeight(Scalar occluder_height) {
268 return GetPenumbraSizeForHeight(occluder_height);
273 explicit PolygonInfo(Scalar occluder_height);
290 const std::shared_ptr<ShadowVertices> CalculateConvexShadowMesh(
293 const Tessellator::Trigs& trigs);
299 static constexpr Scalar GetPenumbraSizeForHeight(Scalar occluder_height) {
300 return occluder_height;
306 static constexpr Scalar GetUmbraSizeForHeight(Scalar occluder_height) {
307 return occluder_height;
312 static constexpr Scalar kMinSubPixelDistanceSquared =
313 UmbraPinAccumulator::kSubPixelScale * UmbraPinAccumulator::kSubPixelScale;
316 const Scalar occluder_height_;
320 Scalar umbra_gaussian_ = 1.0f;
328 std::vector<Point> vertices_;
332 std::vector<uint16_t> indices_;
337 std::vector<Scalar> gaussians_;
342 void ComputePinDirectionsAndMinDistanceToCentroid(std::vector<UmbraPin>& pins,
343 const Point& centroid,
351 struct UmbraPinLinkedList {
352 UmbraPin* p_head_pin =
nullptr;
353 size_t pin_count = 0u;
355 bool IsNull() {
return p_head_pin ==
nullptr; }
363 UmbraPinLinkedList ResolveUmbraIntersections(std::vector<UmbraPin>& pins,
372 struct PinIntersection {
384 static std::optional<PinIntersection> ComputeIntersection(UmbraPin& pin0,
389 static constexpr Scalar kCrossTolerance = 1.0f / 2048.0f;
390 static constexpr Scalar kIntersectionTolerance = 1.0e-6f;
394 static constexpr Scalar FiniteVectorLengthSquared(Vector2 v) {
395 return !v.IsFinite() ? -1.0f : v.Dot(v);
402 static constexpr inline bool OutsideInterval(Scalar numer,
404 bool denom_positive) {
405 return (denom_positive && (numer < 0 || numer > denom)) ||
406 (!denom_positive && (numer > 0 || numer < denom));
418 static void RemovePin(UmbraPin* p_pin, UmbraPin** p_head);
424 static int ComputeSide(
const Point& p0,
const Vector2& v,
const Point& p);
443 void ComputeMesh(std::vector<UmbraPin>& pins,
444 const Point& centroid,
445 UmbraPinLinkedList& list,
457 void PopulateUmbraVertices(std::vector<UmbraPin>& pins,
458 UmbraPinLinkedList& list,
459 const Point centroid);
477 uint16_t AppendFan(
const UmbraPin* p_curr_pin,
478 const Point& fan_start,
479 const Point& fan_end,
480 uint16_t start_index,
486 uint16_t AppendVertex(
const Point& vertex, Scalar gaussian);
489 void AddTriangle(uint16_t v0, uint16_t v1, uint16_t v2);
492PolygonInfo::PolygonInfo(Scalar occluder_height)
493 : occluder_height_(occluder_height) {}
495const std::shared_ptr<ShadowVertices> PolygonInfo::CalculateConvexShadowMesh(
497 const Matrix& matrix,
498 const Tessellator::Trigs& trigs) {
499 if (!matrix.IsInvertible()) {
500 return ShadowVertices::kEmpty;
503 Scalar scale = matrix.GetMaxBasisLengthXY();
505 UmbraPinAccumulator pin_accumulator;
507 auto [point_count, contour_count] =
509 pin_accumulator.Reserve(point_count);
511 PathTessellator::PathToTransformedFilledVertices(source, pin_accumulator,
514 switch (pin_accumulator.GetStatus()) {
515 case UmbraPinAccumulator::PathStatus::kEmpty:
516 return ShadowVertices::kEmpty;
517 case UmbraPinAccumulator::PathStatus::kNonConvex:
518 case UmbraPinAccumulator::PathStatus::kMultipleContours:
520 case UmbraPinAccumulator::PathStatus::kConvex:
524 std::vector<UmbraPin>& pins = pin_accumulator.GetPins();
525 const Point& centroid = pin_accumulator.GetCentroid();
526 Scalar direction = pin_accumulator.GetDirection();
528 ComputePinDirectionsAndMinDistanceToCentroid(pins, centroid, direction);
530 UmbraPinLinkedList list = ResolveUmbraIntersections(pins, direction);
543 ComputeMesh(pins, centroid, list, trigs, direction);
545 Matrix inverted_matrix = matrix.Invert();
546 for (Point& vertex : vertices_) {
547 vertex = inverted_matrix * vertex;
549 return ShadowVertices::Make(std::move(vertices_), std::move(indices_),
550 std::move(gaussians_));
558void UmbraPinAccumulator::Write(Point point) {
560 if (first_contour_ended_) {
561 has_multiple_contours_ =
true;
566 point = ToDeviceGrid(point);
568 if (!pins_.empty()) {
571 Point prev = pins_.back().path_vertex;
580 if (pins_.size() >= 2u) {
582 Point prev_prev = pins_.end()[-2].path_vertex;
584 Vector2 v1 = point - prev_prev;
585 Scalar cross = v0.Cross(v1);
592 if (point == prev_prev) {
604 pins_.emplace_back(point);
616void UmbraPinAccumulator::EndContour() {
618 if (first_contour_ended_) {
619 has_multiple_contours_ =
true;
626 FML_DCHECK(pins_.front().path_vertex == pins_.back().path_vertex);
628 first_contour_ended_ =
true;
632Point UmbraPinAccumulator::ToDeviceGrid(Point point) {
633 return (point * kSubPixelCount).
Round() * kSubPixelScale;
653UmbraPinAccumulator::PathResults UmbraPinAccumulator::FinalizePath() {
656 if (has_multiple_contours_) {
657 return {.status = PathStatus::kMultipleContours};
660 if (pins_.size() < 3u) {
661 return {.status = PathStatus::kEmpty};
664 DirectionDetector x_direction_detector;
665 DirectionDetector y_direction_detector;
667 Point relative_centroid;
668 Scalar path_direction = 0.0f;
671 Point prev = pins_.back().path_vertex;
672 Point prev_prev = pins_.end()[-2].path_vertex;
673 Point first = pins_.front().path_vertex;
674 for (UmbraPin& pin : pins_) {
675 Point new_point = pin.path_vertex;
679 Vector2 delta = new_point - prev;
680 x_direction_detector.AccumulateDirection(delta.x);
681 y_direction_detector.AccumulateDirection(delta.y);
682 if (x_direction_detector.IsConcave() ||
683 y_direction_detector.IsConcave()) {
684 return {.status = PathStatus::kNonConvex};
689 if (path_direction != 0.0f) {
691 Vector2 v1 = new_point - prev_prev;
692 Scalar cross = v0.Cross(v1);
695 if (cross * path_direction < 0.0f) {
696 return {.status = PathStatus::kNonConvex};
703 Vector2 v1 = new_point - first;
704 Scalar quad_area = v0.Cross(v1);
705 if (quad_area != 0) {
709 if (path_direction == 0) {
710 path_direction = std::copysign(1.0f, quad_area);
711 }
else if (quad_area * path_direction < 0) {
712 return {.status = PathStatus::kNonConvex};
715 relative_centroid += (v0 + v1) * quad_area;
716 path_area += quad_area;
724 if (path_direction == 0.0f) {
726 return {.status = PathStatus::kEmpty};
766 relative_centroid /= 3.0f * path_area;
771 .status = PathStatus::kConvex,
772 .centroid = pins_[0].path_vertex + relative_centroid,
773 .path_direction = path_direction,
777void PolygonInfo::ComputePinDirectionsAndMinDistanceToCentroid(
778 std::vector<UmbraPin>& pins,
779 const Point& centroid,
781 Scalar desired_umbra_size = GetUmbraSizeForHeight(occluder_height_);
782 Scalar min_umbra_squared = desired_umbra_size * desired_umbra_size;
783 FML_DCHECK(direction == 1.0f || direction == -1.0f);
792 UmbraPin* p_prev_pin = &pins.back();
793 for (UmbraPin& pin : pins) {
794 UmbraPin* p_curr_pin = &pin;
797 Scalar distance_squared = centroid.GetDistanceToSegmentSquared(
798 p_prev_pin->path_vertex, p_curr_pin->path_vertex);
799 min_umbra_squared = std::min(min_umbra_squared, distance_squared);
801 p_prev_pin = p_curr_pin;
804 static constexpr auto kTolerance = 1.0e-2f;
805 Scalar umbra_size = std::sqrt(min_umbra_squared);
806 if (umbra_size < desired_umbra_size + kTolerance) {
809 auto newInset = umbra_size - kTolerance;
810 auto ratio = 0.5f * (newInset / desired_umbra_size + 1);
813 umbra_gaussian_ = ratio;
814 umbra_size = newInset;
823 Scalar penumbra_scale = -GetPenumbraSizeForHeight(occluder_height_);
824 p_prev_pin = &pins.back();
825 for (UmbraPin& pin : pins) {
826 UmbraPin* p_curr_pin = &pin;
827 p_curr_pin->p_prev = p_prev_pin;
828 p_prev_pin->p_next = p_curr_pin;
834 p_prev_pin->path_delta = p_curr_pin->path_vertex - p_prev_pin->path_vertex;
835 Vector2 pin_direction = p_prev_pin
838 .PerpendicularRight() *
841 p_prev_pin->penumbra_delta = pin_direction * penumbra_scale;
842 p_prev_pin->umbra_vertex =
843 p_prev_pin->pin_tip =
844 p_prev_pin->path_vertex + pin_direction * umbra_size;
846 p_prev_pin = p_curr_pin;
860std::optional<PolygonInfo::PinIntersection> PolygonInfo::ComputeIntersection(
865 Vector2 tip_delta = pin1.pin_tip - pin0.pin_tip;
867 Scalar denom = pin0.path_delta.Cross(pin1.path_delta);
868 bool denom_positive = (denom > 0);
869 Scalar numerator0, numerator1;
896 Scalar v0_length_squared = FiniteVectorLengthSquared(v0);
897 if (v0_length_squared <= 0.0f) {
899 Scalar v1_length_squared = FiniteVectorLengthSquared(v1);
900 if (v1_length_squared <= 0.0f) {
902 if (w.IsFinite() && !w.IsZero()) {
904 .intersection = pin0.pin_tip,
914 numerator1 = v1.Dot(-w);
915 denom = v1_length_squared;
916 if (OutsideInterval(numerator1, denom,
true)) {
922 numerator0 = v0.Dot(w);
923 denom = v0_length_squared;
925 if (OutsideInterval(numerator0, denom,
true)) {
928 Scalar v1_length_squared = FiniteVectorLengthSquared(v1);
929 if (v1_length_squared <= 0.0f) {
934 Scalar old_numerator0 = numerator0;
935 numerator0 = v0.Dot(w + v1);
937 if (OutsideInterval(numerator0, denom,
true)) {
941 if (numerator0 * old_numerator0 > 0) {
946 numerator1 = v1.Dot(-w);
947 denom = v1_length_squared;
952 numerator0 = w.Cross(v1);
953 if (OutsideInterval(numerator0, denom, denom_positive)) {
956 numerator1 = w.Cross(v0);
957 if (OutsideInterval(numerator1, denom, denom_positive)) {
962 Scalar fraction0 = numerator0 / denom;
963 Scalar fraction1 = numerator1 / denom;
966 .intersection = pin0.pin_tip + v0 * fraction0,
967 .fraction0 = fraction0,
968 .fraction1 = fraction1,
972void PolygonInfo::RemovePin(UmbraPin* p_pin, UmbraPin** p_head) {
973 UmbraPin* p_next = p_pin->p_next;
974 UmbraPin* p_prev = p_pin->p_prev;
975 p_prev->p_next = p_next;
976 p_next->p_prev = p_prev;
977 if (*p_head == p_pin) {
978 *p_head = (p_next == p_pin) ?
nullptr : p_next;
985int PolygonInfo::ComputeSide(
const Point& p0,
989 Scalar cross = v.Cross(w);
991 return ((cross > 0) ? 1 : -1);
1000PolygonInfo::UmbraPinLinkedList PolygonInfo::ResolveUmbraIntersections(
1001 std::vector<UmbraPin>& pins,
1003 UmbraPin* p_head_pin = &pins.front();
1004 UmbraPin* p_curr_pin = p_head_pin;
1005 UmbraPin* p_prev_pin = p_curr_pin->p_prev;
1006 size_t umbra_vertex_count = pins.size();
1009 size_t allowed_iterations = pins.size() * pins.size() + 1u;
1011 while (p_head_pin && p_prev_pin != p_curr_pin) {
1012 if (--allowed_iterations == 0) {
1016 std::optional<PinIntersection> intersection =
1017 ComputeIntersection(*p_prev_pin, *p_curr_pin);
1018 if (intersection.has_value()) {
1021 if (intersection->fraction0 < p_prev_pin->umbra_fraction) {
1023 RemovePin(p_prev_pin, &p_head_pin);
1024 --umbra_vertex_count;
1026 p_prev_pin = p_prev_pin->p_prev;
1027 }
else if (p_curr_pin->IsFractionInitialized() &&
1028 p_curr_pin->umbra_vertex.GetDistanceSquared(
1029 intersection->intersection) < kIntersectionTolerance) {
1035 p_curr_pin->umbra_vertex = intersection->intersection;
1036 p_curr_pin->umbra_fraction = intersection->fraction1;
1039 p_prev_pin = p_curr_pin;
1040 p_curr_pin = p_curr_pin->p_next;
1044 int side = direction * ComputeSide(p_curr_pin->pin_tip,
1045 p_curr_pin->path_delta,
1046 p_prev_pin->pin_tip);
1048 side == direction * ComputeSide(p_curr_pin->pin_tip,
1049 p_curr_pin->path_delta,
1050 p_prev_pin->pin_tip +
1051 p_prev_pin->path_delta)) {
1053 RemovePin(p_prev_pin, &p_head_pin);
1054 --umbra_vertex_count;
1056 p_prev_pin = p_prev_pin->p_prev;
1059 RemovePin(p_curr_pin, &p_head_pin);
1060 --umbra_vertex_count;
1061 p_curr_pin = p_curr_pin->p_next;
1072 p_prev_pin = p_head_pin;
1073 p_curr_pin = p_head_pin->p_next;
1074 size_t umbra_vertices = 1u;
1075 while (p_curr_pin != p_head_pin) {
1076 if (p_prev_pin->umbra_vertex.GetDistanceSquared(p_curr_pin->umbra_vertex) <
1077 kMinSubPixelDistanceSquared) {
1078 RemovePin(p_curr_pin, &p_head_pin);
1079 p_curr_pin = p_curr_pin->p_next;
1082 p_prev_pin = p_curr_pin;
1083 p_curr_pin = p_curr_pin->p_next;
1085 FML_DCHECK(p_curr_pin == p_prev_pin->p_next);
1086 FML_DCHECK(p_prev_pin == p_curr_pin->p_prev);
1089 if (umbra_vertices < 3u) {
1093 return {p_head_pin, umbra_vertices};
1127void PolygonInfo::ComputeMesh(std::vector<UmbraPin>& pins,
1128 const Point& centroid,
1129 UmbraPinLinkedList& list,
1133 size_t vertex_count = list.pin_count + 1u;
1134 size_t triangle_count = list.pin_count;
1137 size_t penumbra_count = pins.size() * 2;
1138 penumbra_count += trigs.
size() * 4;
1139 vertex_count += penumbra_count;
1140 triangle_count += penumbra_count;
1142 vertices_.reserve(vertex_count);
1143 gaussians_.reserve(vertex_count);
1144 indices_.reserve(triangle_count * 3);
1157 PopulateUmbraVertices(pins, list, centroid);
1182 const UmbraPin* p_prev_pin = &pins.back();
1190 Point last_penumbra_point =
1191 p_prev_pin->path_vertex + p_prev_pin->penumbra_delta;
1192 uint16_t last_penumbra_index = AppendVertex(last_penumbra_point, 0.0f);
1194 for (
const UmbraPin& pin : pins) {
1195 const UmbraPin* p_curr_pin = &pin;
1203 if (p_prev_pin->umbra_index != p_curr_pin->umbra_index) {
1221 AddTriangle(last_penumbra_index,
1222 p_prev_pin->umbra_index, p_curr_pin->umbra_index);
1227 Point new_penumbra_point =
1228 p_curr_pin->path_vertex + p_prev_pin->penumbra_delta;
1229 uint16_t new_penumbra_index = AppendVertex(new_penumbra_point, 0.0f);
1231 if (last_penumbra_index != new_penumbra_index) {
1232 AddTriangle(p_curr_pin->umbra_index, last_penumbra_index,
1233 new_penumbra_index);
1236 last_penumbra_point = new_penumbra_point;
1237 last_penumbra_index = new_penumbra_index;
1242 new_penumbra_point = p_curr_pin->path_vertex + p_curr_pin->penumbra_delta;
1243 new_penumbra_index =
1244 AppendFan(p_curr_pin, last_penumbra_point, new_penumbra_point,
1245 last_penumbra_index, trigs, direction);
1247 last_penumbra_point = new_penumbra_point;
1248 last_penumbra_index = new_penumbra_index;
1249 p_prev_pin = p_curr_pin;
1256void PolygonInfo::PopulateUmbraVertices(std::vector<UmbraPin>& pins,
1257 UmbraPinLinkedList& list,
1258 const Point centroid) {
1268 uint16_t last_umbra_index = AppendVertex(centroid, umbra_gaussian_);
1276 UmbraPin* p_next_umbra_pin = list.p_head_pin;
1277 UmbraPin* p_curr_umbra_pin = p_next_umbra_pin->p_prev;
1278 for (UmbraPin& pin : pins) {
1279 if (p_next_umbra_pin == &pin ||
1280 (pin.path_vertex.GetDistanceSquared(p_curr_umbra_pin->umbra_vertex) >
1281 pin.path_vertex.GetDistanceSquared(p_next_umbra_pin->umbra_vertex))) {
1285 p_curr_umbra_pin = p_next_umbra_pin;
1286 p_next_umbra_pin = p_next_umbra_pin->p_next;
1289 uint16_t new_umbra_index =
1290 AppendVertex(p_curr_umbra_pin->umbra_vertex, umbra_gaussian_);
1291 p_curr_umbra_pin->umbra_index = new_umbra_index;
1292 if (last_umbra_index != 0u) {
1293 AddTriangle(0u, last_umbra_index, new_umbra_index);
1295 last_umbra_index = new_umbra_index;
1297 if (p_curr_umbra_pin != &pin) {
1298 pin.umbra_vertex = p_curr_umbra_pin->umbra_vertex;
1299 pin.umbra_index = last_umbra_index;
1303 if (last_umbra_index != pins.front().umbra_index) {
1304 AddTriangle(0u, last_umbra_index, pins.front().umbra_index);
1311uint16_t PolygonInfo::AppendFan(
const UmbraPin* p_curr_pin,
1312 const Vector2& start,
1314 uint16_t start_index,
1318 uint16_t center_index = p_curr_pin->umbra_index;
1319 uint16_t prev_index = start_index;
1323 size_t trig_count = trigs.
size();
1324 for (
size_t i = 1u;
i < trig_count;
i++) {
1325 Trig trig = trigs[
i];
1326 Point fan_delta = (direction >= 0 ? trig : -trig) * start_delta;
1327 if (fan_delta.Cross(end_delta) * direction <= 0) {
1330 uint16_t cur_index = AppendVertex(center + fan_delta, 0.0f);
1331 if (prev_index != cur_index) {
1332 AddTriangle(center_index, prev_index, cur_index);
1333 prev_index = cur_index;
1335 if (
i == trig_count - 1) {
1344 start_delta = fan_delta;
1347 uint16_t cur_index = AppendVertex(center + end_delta, 0.0f);
1348 if (prev_index != cur_index) {
1349 AddTriangle(center_index, prev_index, cur_index);
1356uint16_t PolygonInfo::AppendVertex(
const Point& vertex, Scalar gaussian) {
1357 FML_DCHECK(gaussian >= 0.0f && gaussian <= 1.0f);
1358 uint16_t index = vertices_.size();
1361 FML_DCHECK(index <= std::numeric_limits<uint16_t>::max());
1362 if (gaussian == gaussians_.back() && vertex == vertices_.back()) {
1365 vertices_.push_back(vertex);
1366 gaussians_.push_back(gaussian);
1371void PolygonInfo::AddTriangle(uint16_t v0, uint16_t v1, uint16_t v2) {
1372 FML_DCHECK(std::max(std::max(v0, v1), v2) < vertices_.size());
1373 indices_.push_back(v0);
1374 indices_.push_back(v1);
1375 indices_.push_back(v2);
1383 std::make_shared<ShadowVertices>();
1393 : shadow_vertices_(MakeAmbientShadowVertices(tessellator,
1399 return shadow_vertices_ !=
nullptr;
1403 return shadow_vertices_ !=
nullptr && shadow_vertices_->IsEmpty();
1408 return shadow_vertices_;
1412 return std::move(shadow_vertices_);
1418 using VS = ShadowVerticesVertexShader;
1423 vertex_count *
sizeof(VS::PerVertexData),
alignof(VS::PerVertexData),
1424 [&](uint8_t*
data) {
1425 VS::PerVertexData* vtx_contents =
1426 reinterpret_cast<VS::PerVertexData*
>(
data);
1427 for (
size_t i = 0u;
i < vertex_count;
i++) {
1429 .position = vertices_[
i],
1430 .gaussian = gaussians_[
i],
1436 const uint16_t* indices_data =
GetIndices().data();
1439 indices_data, index_count *
sizeof(uint16_t),
alignof(uint16_t));
1445 .vertex_buffer = vertex_buffer,
1446 .index_buffer = index_buffer,
1447 .vertex_count = index_count,
1459 Scalar trig_radius = PolygonInfo::GetTrigRadiusForHeight(occluder_height);
1462 PolygonInfo polygon(occluder_height);
1464 return polygon.CalculateConvexShadowMesh(source, matrix, trigs);
HostBuffer & GetTransientsDataBuffer() const
Retrieve the current host buffer for transient storage of other non-index data.
HostBuffer & GetTransientsIndexesBuffer() const
Retrieve the current host buffer for transient storage of indexes used for indexed draws.
Matrix GetShaderTransform(const RenderPass &pass) const
BufferView Emplace(const BufferType &buffer, size_t alignment=0)
Emplace non-uniform data (like contiguous vertices) onto the host buffer.
static std::pair< size_t, size_t > CountFillStorage(const PathSource &source, Scalar scale)
Render passes encode render commands directed as one specific render target into an underlying comman...
const std::shared_ptr< ShadowVertices > & GetShadowVertices() const
ShadowPathGeometry(Tessellator &tessellator, const Matrix &matrix, const PathSource &source, Scalar occluder_height)
bool IsEmpty() const
Returns true if this shadow has no effect, is not visible.
const std::shared_ptr< ShadowVertices > TakeShadowVertices()
static std::shared_ptr< ShadowVertices > MakeAmbientShadowVertices(Tessellator &tessellator, const PathSource &source, Scalar occluder_height, const Matrix &matrix)
size_t GetVertexCount() const
std::optional< Rect > GetBounds() const
static const std::shared_ptr< ShadowVertices > kEmpty
const std::vector< uint16_t > & GetIndices() const
GeometryResult GetPositionBuffer(const ContentContext &renderer, const Entity &entity, RenderPass &pass) const
size_t GetIndexCount() const
The count of the indices that define the mesh.
A utility that generates triangles of the specified fill type given a polyline. This happens on the C...
Trigs GetTrigsForDeviceRadius(Scalar pixel_radius)
#define FML_DCHECK(condition)
constexpr float kEhCloseEnough
constexpr bool ScalarNearlyZero(Scalar x, Scalar tolerance=kEhCloseEnough)
LinePipeline::VertexShader VS
A 4x4 matrix using column-major storage.
static constexpr TPoint Round(const TPoint< U > &other)
static constexpr std::optional< TRect > MakePointBounds(const U &value)
A structure to store the sine and cosine of an angle.
std::shared_ptr< const fml::Mapping > data