blob: 810c76e386a4943cfca73d925a0b5e8c01dcf87a [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.10
part of engine;
/// An implementation of [ui.Path] which is backed by an `SkPath`.
///
/// The `SkPath` is required for `CkCanvas` methods which take a path.
class CkPath implements ui.Path {
final SkPath _skPath;
CkPath()
: _skPath = SkPath(),
_fillType = ui.PathFillType.nonZero {
_skPath.setFillType(toSkFillType(_fillType));
}
CkPath.from(CkPath other)
: _skPath = SkPath(other._skPath),
_fillType = other.fillType {
_skPath.setFillType(toSkFillType(_fillType));
}
CkPath._fromSkPath(SkPath skPath, this._fillType) : _skPath = skPath {
_skPath.setFillType(toSkFillType(_fillType));
}
ui.PathFillType _fillType;
@override
ui.PathFillType get fillType => _fillType;
@override
set fillType(ui.PathFillType newFillType) {
if (_fillType == newFillType) {
return;
}
_fillType = newFillType;
_skPath.setFillType(toSkFillType(newFillType));
}
@override
void addArc(ui.Rect oval, double startAngle, double sweepAngle) {
const double toDegrees = 180.0 / math.pi;
_skPath.addArc(
toSkRect(oval),
startAngle * toDegrees,
sweepAngle * toDegrees,
);
}
@override
void addOval(ui.Rect oval) {
_skPath.addOval(toSkRect(oval), false, 1);
}
@override
void addPath(ui.Path path, ui.Offset offset, {Float64List? matrix4}) {
List<double> skMatrix;
if (matrix4 == null) {
skMatrix = toSkMatrixFromFloat32(
Matrix4.translationValues(offset.dx, offset.dy, 0.0).storage);
} else {
skMatrix = toSkMatrixFromFloat64(matrix4);
skMatrix[2] += offset.dx;
skMatrix[5] += offset.dy;
}
final CkPath otherPath = path as CkPath;
_skPath.addPath(
otherPath._skPath,
skMatrix[0],
skMatrix[1],
skMatrix[2],
skMatrix[3],
skMatrix[4],
skMatrix[5],
skMatrix[6],
skMatrix[7],
skMatrix[8],
false,
);
}
@override
void addPolygon(List<ui.Offset> points, bool close) {
assert(points != null); // ignore: unnecessary_null_comparison
final SkFloat32List encodedPoints = toMallocedSkPoints(points);
_skPath.addPoly(encodedPoints.toTypedArray(), close);
freeFloat32List(encodedPoints);
}
@override
void addRRect(ui.RRect rrect) {
_skPath.addRRect(
toSkRRect(rrect),
false,
);
}
@override
void addRect(ui.Rect rect) {
_skPath.addRect(toSkRect(rect));
}
@override
void arcTo(
ui.Rect rect, double startAngle, double sweepAngle, bool forceMoveTo) {
const double toDegrees = 180.0 / math.pi;
_skPath.arcToOval(
toSkRect(rect),
startAngle * toDegrees,
sweepAngle * toDegrees,
forceMoveTo,
);
}
@override
void arcToPoint(ui.Offset arcEnd,
{ui.Radius radius = ui.Radius.zero,
double rotation = 0.0,
bool largeArc = false,
bool clockwise = true}) {
_skPath.arcToRotated(
radius.x,
radius.y,
rotation,
!largeArc,
!clockwise,
arcEnd.dx,
arcEnd.dy,
);
}
@override
void close() {
_skPath.close();
}
@override
ui.PathMetrics computeMetrics({bool forceClosed = false}) {
return CkPathMetrics(this, forceClosed);
}
@override
void conicTo(double x1, double y1, double x2, double y2, double w) {
_skPath.conicTo(x1, y1, x2, y2, w);
}
@override
bool contains(ui.Offset point) {
return _skPath.contains(point.dx, point.dy);
}
@override
void cubicTo(
double x1, double y1, double x2, double y2, double x3, double y3) {
_skPath.cubicTo(x1, y1, x2, y2, x3, y3);
}
@override
void extendWithPath(ui.Path path, ui.Offset offset, {Float64List? matrix4}) {
List<double> skMatrix;
if (matrix4 == null) {
skMatrix = toSkMatrixFromFloat32(
Matrix4.translationValues(offset.dx, offset.dy, 0.0).storage);
} else {
skMatrix = toSkMatrixFromFloat64(matrix4);
skMatrix[2] += offset.dx;
skMatrix[5] += offset.dy;
}
final CkPath otherPath = path as CkPath;
_skPath.addPath(
otherPath._skPath,
skMatrix[0],
skMatrix[1],
skMatrix[2],
skMatrix[3],
skMatrix[4],
skMatrix[5],
skMatrix[6],
skMatrix[7],
skMatrix[8],
true,
);
}
@override
ui.Rect getBounds() => fromSkRect(_skPath.getBounds());
@override
void lineTo(double x, double y) {
_skPath.lineTo(x, y);
}
@override
void moveTo(double x, double y) {
_skPath.moveTo(x, y);
}
@override
void quadraticBezierTo(double x1, double y1, double x2, double y2) {
_skPath.quadTo(x1, y1, x2, y2);
}
@override
void relativeArcToPoint(ui.Offset arcEndDelta,
{ui.Radius radius = ui.Radius.zero,
double rotation = 0.0,
bool largeArc = false,
bool clockwise = true}) {
_skPath.rArcTo(
radius.x,
radius.y,
rotation,
!largeArc,
!clockwise,
arcEndDelta.dx,
arcEndDelta.dy,
);
}
@override
void relativeConicTo(double x1, double y1, double x2, double y2, double w) {
_skPath.rConicTo(x1, y1, x2, y2, w);
}
@override
void relativeCubicTo(
double x1, double y1, double x2, double y2, double x3, double y3) {
_skPath.rCubicTo(x1, y1, x2, y2, x3, y3);
}
@override
void relativeLineTo(double dx, double dy) {
_skPath.rLineTo(dx, dy);
}
@override
void relativeMoveTo(double dx, double dy) {
_skPath.rMoveTo(dx, dy);
}
@override
void relativeQuadraticBezierTo(double x1, double y1, double x2, double y2) {
_skPath.rQuadTo(x1, y1, x2, y2);
}
@override
void reset() {
// Only reset the local field. Skia will reset its internal state via
// SkPath.reset() below.
_fillType = ui.PathFillType.nonZero;
_skPath.reset();
}
@override
ui.Path shift(ui.Offset offset) {
// Since CanvasKit does not expose `SkPath.offset`, create a copy of this
// path and call `transform` on it.
final SkPath newPath = _skPath.copy();
newPath.transform(1.0, 0.0, offset.dx, 0.0, 1.0, offset.dy, 0.0, 0.0, 0.0);
return CkPath._fromSkPath(newPath, _fillType);
}
static CkPath combine(
ui.PathOperation operation,
ui.Path uiPath1,
ui.Path uiPath2,
) {
final CkPath path1 = uiPath1 as CkPath;
final CkPath path2 = uiPath2 as CkPath;
final SkPath newPath = canvasKit.MakePathFromOp(
path1._skPath,
path2._skPath,
toSkPathOp(operation),
);
return CkPath._fromSkPath(newPath, path1._fillType);
}
@override
ui.Path transform(Float64List matrix4) {
final SkPath newPath = _skPath.copy();
final Float32List m = toSkMatrixFromFloat64(matrix4);
newPath.transform(
m[0],
m[1],
m[2],
m[3],
m[4],
m[5],
m[6],
m[7],
m[8],
);
return CkPath._fromSkPath(newPath, _fillType);
}
String? toSvgString() {
return _skPath.toSVGString();
}
/// Return `true` if this path contains no segments.
bool get isEmpty {
return _skPath.isEmpty();
}
}