88#include " flutter/impeller/geometry/path_builder.h"
99#include " impeller/geometry/path.h"
1010
11+ namespace {
12+ inline constexpr flutter::DlPathFillType ToDlFillType (SkPathFillType sk_type) {
13+ switch (sk_type) {
14+ case SkPathFillType::kEvenOdd :
15+ return impeller::FillType::kOdd ;
16+ case SkPathFillType::kWinding :
17+ return impeller::FillType::kNonZero ;
18+ case SkPathFillType::kInverseEvenOdd :
19+ case SkPathFillType::kInverseWinding :
20+ FML_UNREACHABLE ();
21+ }
22+ }
23+
24+ inline constexpr SkPathFillType ToSkFillType (flutter::DlPathFillType dl_type) {
25+ switch (dl_type) {
26+ case impeller::FillType::kOdd :
27+ return SkPathFillType::kEvenOdd ;
28+ case impeller::FillType::kNonZero :
29+ return SkPathFillType::kWinding ;
30+ }
31+ }
32+ } // namespace
33+
1134namespace flutter {
1235
1336using Path = impeller::Path;
@@ -120,6 +143,22 @@ const Path& DlPath::GetPath() const {
120143 return path.value ();
121144}
122145
146+ void DlPath::Dispatch (DlPathReceiver& receiver) const {
147+ if (data_->sk_path_original ) {
148+ auto & sk_path = data_->sk_path ;
149+ FML_DCHECK (sk_path.has_value ());
150+ if (sk_path.has_value ()) {
151+ DispatchFromSkiaPath (sk_path.value (), receiver);
152+ }
153+ } else {
154+ auto & path = data_->path ;
155+ FML_DCHECK (path.has_value ());
156+ if (path.has_value ()) {
157+ DispatchFromImpellerPath (path.value (), receiver);
158+ }
159+ }
160+ }
161+
123162void DlPath::WillRenderSkPath () const {
124163 if (data_->render_count >= kMaxVolatileUses ) {
125164 auto & sk_path = data_->sk_path ;
@@ -252,26 +291,94 @@ DlPath DlPath::operator+(const DlPath& other) const {
252291 return DlPath (path);
253292}
254293
255- SkPath DlPath::ConvertToSkiaPath (const Path& path, const DlPoint& shift) {
256- SkPath sk_path;
257- sk_path.setFillType (ToSkFillType (path.GetFillType ()));
294+ static void ReduceConic (DlPathReceiver& receiver,
295+ const DlPoint& p1,
296+ const DlPoint& cp,
297+ const DlPoint& p2,
298+ DlScalar weight) {
299+ // We might eventually have conic conversion math that deals with
300+ // degenerate conics gracefully (or have all receivers just handle
301+ // them directly). But, until then, we will just convert them to a
302+ // pair of quads and accept the results as "close enough".
303+ if (p1 != cp) {
304+ if (cp != p2) {
305+ std::array<DlPoint, 5 > points;
306+ impeller::ConicPathComponent conic (p1, cp, p2, weight);
307+ conic.SubdivideToQuadraticPoints (points);
308+ receiver.QuadTo (points[1 ], points[2 ]);
309+ receiver.QuadTo (points[3 ], points[4 ]);
310+ } else {
311+ receiver.LineTo (cp);
312+ }
313+ } else if (cp != p2) {
314+ receiver.LineTo (p2);
315+ }
316+ }
317+
318+ namespace {
319+ class SkiaPathReceiver final : public DlPathReceiver {
320+ public:
321+ void SetPathInfo (DlPathFillType fill_type, bool is_convex) override {
322+ sk_path_.setFillType (ToSkFillType (fill_type));
323+ }
324+ void MoveTo (const DlPoint& p2) override { sk_path_.moveTo (ToSkPoint (p2)); }
325+ void LineTo (const DlPoint& p2) override { sk_path_.lineTo (ToSkPoint (p2)); }
326+ void QuadTo (const DlPoint& cp, const DlPoint& p2) override {
327+ sk_path_.quadTo (ToSkPoint (cp), ToSkPoint (p2));
328+ }
329+ bool ConicTo (const DlPoint& cp, const DlPoint& p2, DlScalar weight) override {
330+ sk_path_.conicTo (ToSkPoint (cp), ToSkPoint (p2), weight);
331+ return true ;
332+ }
333+ void CubicTo (const DlPoint& cp1,
334+ const DlPoint& cp2,
335+ const DlPoint& p2) override {
336+ sk_path_.cubicTo (ToSkPoint (cp1), ToSkPoint (cp2), ToSkPoint (p2));
337+ }
338+ void Close () override { sk_path_.close (); }
339+
340+ SkPath TakePath () { return sk_path_; }
341+
342+ private:
343+ SkPath sk_path_;
344+ };
345+ } // namespace
346+
347+ SkPath DlPath::ConvertToSkiaPath (const Path& path) {
348+ SkiaPathReceiver receiver;
349+
350+ DispatchFromImpellerPath (path, receiver);
351+
352+ return receiver.TakePath ();
353+ }
354+
355+ void DlPath::DispatchFromImpellerPath (const impeller::Path& path,
356+ DlPathReceiver& receiver) {
258357 bool subpath_needs_close = false ;
259358 std::optional<DlPoint> pending_moveto;
260359
261- auto resolve_moveto = [&pending_moveto , &sk_path ]() {
360+ auto resolve_moveto = [&receiver , &pending_moveto ]() {
262361 if (pending_moveto.has_value ()) {
263- sk_path. moveTo ( ToSkPoint ( pending_moveto.value () ));
362+ receiver. MoveTo ( pending_moveto.value ());
264363 pending_moveto.reset ();
265364 }
266365 };
267366
367+ // The Impeller Point Count is way overestimated due to duplicate
368+ // points between elements.
369+ receiver.RecommendSizes (path.GetComponentCount (), path.GetPointCount ());
370+ std::optional<DlRect> bounds = path.GetBoundingBox ();
371+ if (bounds.has_value ()) {
372+ receiver.RecommendBounds (bounds.value ());
373+ }
374+ receiver.SetPathInfo (path.GetFillType (), path.IsConvex ());
268375 for (auto it = path.begin (), end = path.end (); it != end; ++it) {
269376 switch (it.type ()) {
270377 case ComponentType::kContour : {
271378 const impeller::ContourComponent* contour = it.contour ();
272379 FML_DCHECK (contour != nullptr );
273380 if (subpath_needs_close) {
274- sk_path. close ();
381+ receiver. Close ();
275382 }
276383 pending_moveto = contour->destination ;
277384 subpath_needs_close = contour->IsClosed ();
@@ -281,45 +388,96 @@ SkPath DlPath::ConvertToSkiaPath(const Path& path, const DlPoint& shift) {
281388 const impeller::LinearPathComponent* linear = it.linear ();
282389 FML_DCHECK (linear != nullptr );
283390 resolve_moveto ();
284- sk_path. lineTo ( ToSkPoint ( linear->p2 ) );
391+ receiver. LineTo ( linear->p2 );
285392 break ;
286393 }
287394 case ComponentType::kQuadratic : {
288395 const impeller::QuadraticPathComponent* quadratic = it.quadratic ();
289396 FML_DCHECK (quadratic != nullptr );
290397 resolve_moveto ();
291- sk_path. quadTo ( ToSkPoint ( quadratic->cp ), ToSkPoint ( quadratic->p2 ) );
398+ receiver. QuadTo ( quadratic->cp , quadratic->p2 );
292399 break ;
293400 }
294401 case ComponentType::kConic : {
295402 const impeller::ConicPathComponent* conic = it.conic ();
296403 FML_DCHECK (conic != nullptr );
297404 resolve_moveto ();
298- sk_path.conicTo (ToSkPoint (conic->cp ), ToSkPoint (conic->p2 ),
299- conic->weight .x );
405+ if (!receiver.ConicTo (conic->cp , conic->p2 , conic->weight .x )) {
406+ ReduceConic (receiver, conic->p1 , conic->cp , conic->p2 ,
407+ conic->weight .x );
408+ }
300409 break ;
301410 }
302411 case ComponentType::kCubic : {
303412 const impeller::CubicPathComponent* cubic = it.cubic ();
304413 FML_DCHECK (cubic != nullptr );
305414 resolve_moveto ();
306- sk_path.cubicTo (ToSkPoint (cubic->cp1 ), ToSkPoint (cubic->cp2 ),
307- ToSkPoint (cubic->p2 ));
415+ receiver.CubicTo (cubic->cp1 , cubic->cp2 , cubic->p2 );
308416 break ;
309417 }
310418 }
311419 }
312420 if (subpath_needs_close) {
313- sk_path. close ();
421+ receiver. Close ();
314422 }
315-
316- return sk_path;
317423}
318424
319- Path DlPath::ConvertToImpellerPath (const SkPath& path, const DlPoint& shift) {
320- if (path.isEmpty () || !shift.IsFinite ()) {
425+ namespace {
426+ class ImpellerPathReceiver final : public DlPathReceiver {
427+ public:
428+ void RecommendSizes (size_t verb_count, size_t point_count) override {
429+ // Reserve a path size with some arbitrarily additional padding.
430+ builder_.Reserve (point_count + 8 , verb_count + 8 );
431+ }
432+ void RecommendBounds (const DlRect& bounds) override {
433+ builder_.SetBounds (bounds);
434+ }
435+ void SetPathInfo (DlPathFillType fill_type, bool is_convex) override {
436+ this ->fill_type_ = fill_type;
437+ builder_.SetConvexity (is_convex ? Convexity::kConvex //
438+ : Convexity::kUnknown );
439+ }
440+ void MoveTo (const DlPoint& p2) override { builder_.MoveTo (p2); }
441+ void LineTo (const DlPoint& p2) override { builder_.LineTo (p2); }
442+ void QuadTo (const DlPoint& cp, const DlPoint& p2) override {
443+ builder_.QuadraticCurveTo (cp, p2);
444+ }
445+ // For legacy compatibility we do not override ConicTo to let the dispatcher
446+ // convert conics to quads until we update Impeller for full support of
447+ // rational quadratics
448+ void CubicTo (const DlPoint& cp1,
449+ const DlPoint& cp2,
450+ const DlPoint& p2) override {
451+ builder_.CubicCurveTo (cp1, cp2, p2);
452+ }
453+ void Close () override { builder_.Close (); }
454+
455+ impeller::Path TakePath () { return builder_.TakePath (fill_type_); }
456+
457+ private:
458+ PathBuilder builder_;
459+ DlPathFillType fill_type_;
460+ };
461+ } // namespace
462+
463+ Path DlPath::ConvertToImpellerPath (const SkPath& path) {
464+ if (path.isEmpty ()) {
321465 return Path{};
322466 }
467+
468+ ImpellerPathReceiver receiver;
469+
470+ DispatchFromSkiaPath (path, receiver);
471+
472+ return receiver.TakePath ();
473+ }
474+
475+ void DlPath::DispatchFromSkiaPath (const SkPath& path,
476+ DlPathReceiver& receiver) {
477+ if (path.isEmpty ()) {
478+ return ;
479+ }
480+
323481 auto iterator = SkPath::Iter (path, false );
324482
325483 struct PathData {
@@ -328,67 +486,47 @@ Path DlPath::ConvertToImpellerPath(const SkPath& path, const DlPoint& shift) {
328486 };
329487 };
330488
331- PathBuilder builder;
332489 PathData data;
333- // Reserve a path size with some arbitrarily additional padding.
334- builder.Reserve (path.countPoints () + 8 , path.countVerbs () + 8 );
490+
491+ receiver.RecommendSizes (path.countVerbs (), path.countPoints ());
492+ receiver.RecommendBounds (ToDlRect (path.getBounds ()));
493+ receiver.SetPathInfo (ToDlFillType (path.getFillType ()), path.isConvex ());
335494 auto verb = SkPath::Verb::kDone_Verb ;
336495 do {
337496 verb = iterator.next (data.points );
338497 switch (verb) {
339498 case SkPath::kMove_Verb :
340- builder .MoveTo (ToDlPoint (data.points [0 ]));
499+ receiver .MoveTo (ToDlPoint (data.points [0 ]));
341500 break ;
342501 case SkPath::kLine_Verb :
343- builder .LineTo (ToDlPoint (data.points [1 ]));
502+ receiver .LineTo (ToDlPoint (data.points [1 ]));
344503 break ;
345504 case SkPath::kQuad_Verb :
346- builder.QuadraticCurveTo (ToDlPoint (data.points [1 ]),
347- ToDlPoint (data.points [2 ]));
505+ receiver.QuadTo (ToDlPoint (data.points [1 ]), ToDlPoint (data.points [2 ]));
348506 break ;
349507 case SkPath::kConic_Verb :
350- // We might eventually have conic conversion math that deals with
351- // degenerate conics gracefully (or just handle them directly),
352- // but until then, we will detect and ignore them.
353- if (data.points [0 ] != data.points [1 ]) {
354- if (data.points [1 ] != data.points [2 ]) {
355- std::array<DlPoint, 5 > points;
356- impeller::ConicPathComponent conic (
357- ToDlPoint (data.points [0 ]), ToDlPoint (data.points [1 ]),
358- ToDlPoint (data.points [2 ]), iterator.conicWeight ());
359- conic.SubdivideToQuadraticPoints (points);
360- builder.QuadraticCurveTo (points[1 ], points[2 ]);
361- builder.QuadraticCurveTo (points[3 ], points[4 ]);
362- } else {
363- builder.LineTo (ToDlPoint (data.points [1 ]));
364- }
365- } else if (data.points [1 ] != data.points [2 ]) {
366- builder.LineTo (ToDlPoint (data.points [2 ]));
508+ if (!receiver.ConicTo (ToDlPoint (data.points [1 ]),
509+ ToDlPoint (data.points [2 ]),
510+ iterator.conicWeight ())) {
511+ ReduceConic (receiver, //
512+ ToDlPoint (data.points [0 ]), //
513+ ToDlPoint (data.points [1 ]), //
514+ ToDlPoint (data.points [2 ]), //
515+ iterator.conicWeight ());
367516 }
368517 break ;
369518 case SkPath::kCubic_Verb :
370- builder. CubicCurveTo (ToDlPoint (data.points [1 ]),
371- ToDlPoint (data.points [2 ]),
372- ToDlPoint (data.points [3 ]));
519+ receiver. CubicTo (ToDlPoint (data.points [1 ]), //
520+ ToDlPoint (data.points [2 ]), //
521+ ToDlPoint (data.points [3 ]));
373522 break ;
374523 case SkPath::kClose_Verb :
375- builder .Close ();
524+ receiver .Close ();
376525 break ;
377526 case SkPath::kDone_Verb :
378527 break ;
379528 }
380529 } while (verb != SkPath::Verb::kDone_Verb );
381-
382- DlRect bounds = ToDlRect (path.getBounds ());
383- if (!shift.IsZero ()) {
384- builder.Shift (shift);
385- bounds = bounds.Shift (shift);
386- }
387-
388- builder.SetConvexity (path.isConvex () ? Convexity::kConvex
389- : Convexity::kUnknown );
390- builder.SetBounds (bounds);
391- return builder.TakePath (ToDlFillType (path.getFillType ()));
392530}
393531
394532} // namespace flutter
0 commit comments