Skip to content

Commit db0bdfd

Browse files
authored
[Impeller] add support for rational bezier conics to Path (#163282)
Add basic support for storing rational bezier conics in impeller::Path. The support is very thin and just degrades the conics into a pair of quadratic curves just as Impeller has always done for the conic segments that it receives via SkPath objects, but it puts in place the framework for eventually handling the conics more directly and allows the unit tests to be rewritten on top of Impeller paths rather than SkPaths, paving the way for reduced internal API dependencies.
1 parent 7e155bc commit db0bdfd

11 files changed

Lines changed: 924 additions & 266 deletions

File tree

engine/src/flutter/display_list/geometry/dl_geometry_types.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,10 @@ inline const SkPoint* ToSkPoints(const DlPoint* points) {
139139
return points == nullptr ? nullptr : reinterpret_cast<const SkPoint*>(points);
140140
}
141141

142+
inline SkPoint* ToSkPoints(DlPoint* points) {
143+
return points == nullptr ? nullptr : reinterpret_cast<SkPoint*>(points);
144+
}
145+
142146
inline const SkRect& ToSkRect(const DlRect& rect) {
143147
return *reinterpret_cast<const SkRect*>(&rect);
144148
}

engine/src/flutter/display_list/geometry/dl_path.cc

Lines changed: 43 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -265,39 +265,46 @@ SkPath DlPath::ConvertToSkiaPath(const Path& path, const DlPoint& shift) {
265265
}
266266
};
267267

268-
size_t count = path.GetComponentCount();
269-
for (size_t i = 0; i < count; i++) {
270-
switch (path.GetComponentTypeAtIndex(i)) {
268+
for (auto it = path.begin(), end = path.end(); it != end; ++it) {
269+
switch (it.type()) {
271270
case ComponentType::kContour: {
272-
impeller::ContourComponent contour;
273-
path.GetContourComponentAtIndex(i, contour);
271+
const impeller::ContourComponent* contour = it.contour();
272+
FML_DCHECK(contour != nullptr);
274273
if (subpath_needs_close) {
275274
sk_path.close();
276275
}
277-
pending_moveto = contour.destination;
278-
subpath_needs_close = contour.IsClosed();
276+
pending_moveto = contour->destination;
277+
subpath_needs_close = contour->IsClosed();
279278
break;
280279
}
281280
case ComponentType::kLinear: {
282-
impeller::LinearPathComponent linear;
283-
path.GetLinearComponentAtIndex(i, linear);
281+
const impeller::LinearPathComponent* linear = it.linear();
282+
FML_DCHECK(linear != nullptr);
284283
resolve_moveto();
285-
sk_path.lineTo(ToSkPoint(linear.p2));
284+
sk_path.lineTo(ToSkPoint(linear->p2));
286285
break;
287286
}
288287
case ComponentType::kQuadratic: {
289-
impeller::QuadraticPathComponent quadratic;
290-
path.GetQuadraticComponentAtIndex(i, quadratic);
288+
const impeller::QuadraticPathComponent* quadratic = it.quadratic();
289+
FML_DCHECK(quadratic != nullptr);
291290
resolve_moveto();
292-
sk_path.quadTo(ToSkPoint(quadratic.cp), ToSkPoint(quadratic.p2));
291+
sk_path.quadTo(ToSkPoint(quadratic->cp), ToSkPoint(quadratic->p2));
292+
break;
293+
}
294+
case ComponentType::kConic: {
295+
const impeller::ConicPathComponent* conic = it.conic();
296+
FML_DCHECK(conic != nullptr);
297+
resolve_moveto();
298+
sk_path.conicTo(ToSkPoint(conic->cp), ToSkPoint(conic->p2),
299+
conic->weight.x);
293300
break;
294301
}
295302
case ComponentType::kCubic: {
296-
impeller::CubicPathComponent cubic;
297-
path.GetCubicComponentAtIndex(i, cubic);
303+
const impeller::CubicPathComponent* cubic = it.cubic();
304+
FML_DCHECK(cubic != nullptr);
298305
resolve_moveto();
299-
sk_path.cubicTo(ToSkPoint(cubic.cp1), ToSkPoint(cubic.cp2),
300-
ToSkPoint(cubic.p2));
306+
sk_path.cubicTo(ToSkPoint(cubic->cp1), ToSkPoint(cubic->cp2),
307+
ToSkPoint(cubic->p2));
301308
break;
302309
}
303310
}
@@ -339,27 +346,26 @@ Path DlPath::ConvertToImpellerPath(const SkPath& path, const DlPoint& shift) {
339346
builder.QuadraticCurveTo(ToDlPoint(data.points[1]),
340347
ToDlPoint(data.points[2]));
341348
break;
342-
case SkPath::kConic_Verb: {
343-
constexpr auto kPow2 = 1; // Only works for sweeps up to 90 degrees.
344-
constexpr auto kQuadCount = 1 + (2 * (1 << kPow2));
345-
SkPoint points[kQuadCount];
346-
const auto curve_count =
347-
SkPath::ConvertConicToQuads(data.points[0], //
348-
data.points[1], //
349-
data.points[2], //
350-
iterator.conicWeight(), //
351-
points, //
352-
kPow2 //
353-
);
354-
355-
for (int curve_index = 0, point_index = 0; //
356-
curve_index < curve_count; //
357-
curve_index++, point_index += 2 //
358-
) {
359-
builder.QuadraticCurveTo(ToDlPoint(points[point_index + 1]),
360-
ToDlPoint(points[point_index + 2]));
349+
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]));
361367
}
362-
} break;
368+
break;
363369
case SkPath::kCubic_Verb:
364370
builder.CubicCurveTo(ToDlPoint(data.points[1]),
365371
ToDlPoint(data.points[2]),

engine/src/flutter/display_list/skia/dl_sk_conversions_unittests.cc

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "flutter/display_list/effects/dl_color_sources.h"
1212
#include "flutter/display_list/effects/dl_image_filters.h"
1313
#include "flutter/display_list/skia/dl_sk_conversions.h"
14+
#include "flutter/impeller/geometry/path_component.h"
1415
#include "gtest/gtest.h"
1516
#include "third_party/skia/include/core/SkColorSpace.h"
1617
#include "third_party/skia/include/core/SkSamplingOptions.h"
@@ -372,5 +373,92 @@ TEST(DisplayListSkConversions, ToSkRSTransform) {
372373
}
373374
}
374375

376+
// This tests the new conic subdivision code in the Impeller conic path
377+
// component object vs the code we used to rely on inside Skia
378+
TEST(DisplayListSkConversions, ConicToQuads) {
379+
SkScalar weights[4] = {
380+
0.02f,
381+
0.5f,
382+
SK_ScalarSqrt2 * 0.5f,
383+
1.0f,
384+
};
385+
386+
for (SkScalar weight : weights) {
387+
SkPoint sk_points[5];
388+
int ncurves = SkPath::ConvertConicToQuads(
389+
SkPoint::Make(10, 10), SkPoint::Make(20, 10), SkPoint::Make(20, 20),
390+
weight, sk_points, 1);
391+
ASSERT_EQ(ncurves, 2) << "weight: " << weight;
392+
393+
std::array<DlPoint, 5> i_points;
394+
impeller::ConicPathComponent i_conic(DlPoint(10, 10), DlPoint(20, 10),
395+
DlPoint(20, 20), weight);
396+
i_conic.SubdivideToQuadraticPoints(i_points);
397+
398+
for (int i = 0; i < 5; i++) {
399+
EXPECT_FLOAT_EQ(sk_points[i].fX, i_points[i].x)
400+
<< "weight: " << weight << "point[" << i << "].x";
401+
EXPECT_FLOAT_EQ(sk_points[i].fY, i_points[i].y)
402+
<< "weight: " << weight << "point[" << i << "].y";
403+
}
404+
}
405+
}
406+
407+
// This tests the new conic subdivision code in the Impeller conic path
408+
// component object vs the code we used to rely on inside Skia
409+
TEST(DisplayListSkConversions, ConicPathToQuads) {
410+
// If we execute conicTo with a weight of exactly 1.0, SkPath will turn
411+
// it into a quadTo, so we avoid that by using 0.999
412+
SkScalar weights[4] = {
413+
0.02f,
414+
0.5f,
415+
SK_ScalarSqrt2 * 0.5f,
416+
1.0f - kEhCloseEnough,
417+
};
418+
419+
for (SkScalar weight : weights) {
420+
SkPath sk_path;
421+
sk_path.moveTo(10, 10);
422+
sk_path.conicTo(20, 10, 20, 20, weight);
423+
424+
DlPath dl_path(sk_path);
425+
impeller::Path i_path = dl_path.GetPath();
426+
427+
auto it = i_path.begin();
428+
ASSERT_EQ(it.type(), impeller::Path::ComponentType::kContour);
429+
++it;
430+
431+
ASSERT_EQ(it.type(), impeller::Path::ComponentType::kQuadratic);
432+
auto quad1 = it.quadratic();
433+
ASSERT_NE(quad1, nullptr);
434+
++it;
435+
436+
ASSERT_EQ(it.type(), impeller::Path::ComponentType::kQuadratic);
437+
auto quad2 = it.quadratic();
438+
ASSERT_NE(quad2, nullptr);
439+
++it;
440+
441+
SkPoint sk_points[5];
442+
int ncurves = SkPath::ConvertConicToQuads(
443+
SkPoint::Make(10, 10), SkPoint::Make(20, 10), SkPoint::Make(20, 20),
444+
weight, sk_points, 1);
445+
ASSERT_EQ(ncurves, 2);
446+
447+
EXPECT_FLOAT_EQ(sk_points[0].fX, quad1->p1.x) << "weight: " << weight;
448+
EXPECT_FLOAT_EQ(sk_points[0].fY, quad1->p1.y) << "weight: " << weight;
449+
EXPECT_FLOAT_EQ(sk_points[1].fX, quad1->cp.x) << "weight: " << weight;
450+
EXPECT_FLOAT_EQ(sk_points[1].fY, quad1->cp.y) << "weight: " << weight;
451+
EXPECT_FLOAT_EQ(sk_points[2].fX, quad1->p2.x) << "weight: " << weight;
452+
EXPECT_FLOAT_EQ(sk_points[2].fY, quad1->p2.y) << "weight: " << weight;
453+
454+
EXPECT_FLOAT_EQ(sk_points[2].fX, quad2->p1.x) << "weight: " << weight;
455+
EXPECT_FLOAT_EQ(sk_points[2].fY, quad2->p1.y) << "weight: " << weight;
456+
EXPECT_FLOAT_EQ(sk_points[3].fX, quad2->cp.x) << "weight: " << weight;
457+
EXPECT_FLOAT_EQ(sk_points[3].fY, quad2->cp.y) << "weight: " << weight;
458+
EXPECT_FLOAT_EQ(sk_points[4].fX, quad2->p2.x) << "weight: " << weight;
459+
EXPECT_FLOAT_EQ(sk_points[4].fY, quad2->p2.y) << "weight: " << weight;
460+
}
461+
}
462+
375463
} // namespace testing
376464
} // namespace flutter

engine/src/flutter/impeller/geometry/constants.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ constexpr float k2OverSqrtPi = 1.12837916709551257390f;
4646
// sqrt(2)
4747
constexpr float kSqrt2 = 1.41421356237309504880f;
4848

49-
// 1/sqrt(2)
49+
// sqrt(2) / 2 == 1/sqrt(2)
5050
constexpr float k1OverSqrt2 = 0.70710678118654752440f;
51+
constexpr float kSqrt2Over2 = 0.70710678118654752440f;
5152

5253
// phi
5354
constexpr float kPhi = 1.61803398874989484820f;

0 commit comments

Comments
 (0)