blob: 650aa231f8addea7e423f91c1ce94976550f0d61 [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.
#include "flutter/lib/ui/painting/path.h"
#include <cmath>
#include "flutter/lib/ui/painting/matrix.h"
#include "flutter/lib/ui/ui_dart_state.h"
#include "third_party/tonic/converter/dart_converter.h"
#include "third_party/tonic/dart_args.h"
#include "third_party/tonic/dart_binding_macros.h"
#include "third_party/tonic/dart_library_natives.h"
using tonic::ToDart;
namespace flutter {
typedef CanvasPath Path;
static void Path_constructor(Dart_NativeArguments args) {
UIDartState::ThrowIfUIOperationsProhibited();
DartCallConstructor(&CanvasPath::CreateNew, args);
}
IMPLEMENT_WRAPPERTYPEINFO(ui, Path);
#define FOR_EACH_BINDING(V) \
V(Path, addArc) \
V(Path, addOval) \
V(Path, addPath) \
V(Path, addPolygon) \
V(Path, addRect) \
V(Path, addRRect) \
V(Path, arcTo) \
V(Path, arcToPoint) \
V(Path, close) \
V(Path, conicTo) \
V(Path, contains) \
V(Path, cubicTo) \
V(Path, extendWithPath) \
V(Path, extendWithPathAndMatrix) \
V(Path, getFillType) \
V(Path, lineTo) \
V(Path, moveTo) \
V(Path, quadraticBezierTo) \
V(Path, relativeArcToPoint) \
V(Path, relativeConicTo) \
V(Path, relativeCubicTo) \
V(Path, relativeLineTo) \
V(Path, relativeMoveTo) \
V(Path, relativeQuadraticBezierTo) \
V(Path, reset) \
V(Path, setFillType) \
V(Path, shift) \
V(Path, transform) \
V(Path, getBounds) \
V(Path, addPathWithMatrix) \
V(Path, op) \
V(Path, clone)
FOR_EACH_BINDING(DART_NATIVE_CALLBACK)
void CanvasPath::RegisterNatives(tonic::DartLibraryNatives* natives) {
natives->Register({{"Path_constructor", Path_constructor, 1, true},
FOR_EACH_BINDING(DART_REGISTER_NATIVE)});
}
CanvasPath::CanvasPath()
: path_tracker_(UIDartState::Current()->GetVolatilePathTracker()),
tracked_path_(std::make_shared<VolatilePathTracker::TrackedPath>()) {
FML_DCHECK(path_tracker_);
resetVolatility();
}
CanvasPath::~CanvasPath() = default;
void CanvasPath::resetVolatility() {
if (!tracked_path_->tracking_volatility) {
mutable_path().setIsVolatile(true);
tracked_path_->frame_count = 0;
tracked_path_->tracking_volatility = true;
path_tracker_->Insert(tracked_path_);
}
}
void CanvasPath::ReleaseDartWrappableReference() const {
FML_DCHECK(path_tracker_);
path_tracker_->Erase(tracked_path_);
RefCountedDartWrappable::ReleaseDartWrappableReference();
}
int CanvasPath::getFillType() {
return static_cast<int>(path().getFillType());
}
void CanvasPath::setFillType(int fill_type) {
mutable_path().setFillType(static_cast<SkPathFillType>(fill_type));
resetVolatility();
}
void CanvasPath::moveTo(float x, float y) {
mutable_path().moveTo(x, y);
resetVolatility();
}
void CanvasPath::relativeMoveTo(float x, float y) {
mutable_path().rMoveTo(x, y);
resetVolatility();
}
void CanvasPath::lineTo(float x, float y) {
mutable_path().lineTo(x, y);
resetVolatility();
}
void CanvasPath::relativeLineTo(float x, float y) {
mutable_path().rLineTo(x, y);
resetVolatility();
}
void CanvasPath::quadraticBezierTo(float x1, float y1, float x2, float y2) {
mutable_path().quadTo(x1, y1, x2, y2);
resetVolatility();
}
void CanvasPath::relativeQuadraticBezierTo(float x1,
float y1,
float x2,
float y2) {
mutable_path().rQuadTo(x1, y1, x2, y2);
resetVolatility();
}
void CanvasPath::cubicTo(float x1,
float y1,
float x2,
float y2,
float x3,
float y3) {
mutable_path().cubicTo(x1, y1, x2, y2, x3, y3);
resetVolatility();
}
void CanvasPath::relativeCubicTo(float x1,
float y1,
float x2,
float y2,
float x3,
float y3) {
mutable_path().rCubicTo(x1, y1, x2, y2, x3, y3);
resetVolatility();
}
void CanvasPath::conicTo(float x1, float y1, float x2, float y2, float w) {
mutable_path().conicTo(x1, y1, x2, y2, w);
resetVolatility();
}
void CanvasPath::relativeConicTo(float x1,
float y1,
float x2,
float y2,
float w) {
mutable_path().rConicTo(x1, y1, x2, y2, w);
resetVolatility();
}
void CanvasPath::arcTo(float left,
float top,
float right,
float bottom,
float startAngle,
float sweepAngle,
bool forceMoveTo) {
mutable_path().arcTo(SkRect::MakeLTRB(left, top, right, bottom),
startAngle * 180.0 / M_PI, sweepAngle * 180.0 / M_PI,
forceMoveTo);
resetVolatility();
}
void CanvasPath::arcToPoint(float arcEndX,
float arcEndY,
float radiusX,
float radiusY,
float xAxisRotation,
bool isLargeArc,
bool isClockwiseDirection) {
const auto arcSize = isLargeArc ? SkPath::ArcSize::kLarge_ArcSize
: SkPath::ArcSize::kSmall_ArcSize;
const auto direction =
isClockwiseDirection ? SkPathDirection::kCW : SkPathDirection::kCCW;
mutable_path().arcTo(radiusX, radiusY, xAxisRotation, arcSize, direction,
arcEndX, arcEndY);
resetVolatility();
}
void CanvasPath::relativeArcToPoint(float arcEndDeltaX,
float arcEndDeltaY,
float radiusX,
float radiusY,
float xAxisRotation,
bool isLargeArc,
bool isClockwiseDirection) {
const auto arcSize = isLargeArc ? SkPath::ArcSize::kLarge_ArcSize
: SkPath::ArcSize::kSmall_ArcSize;
const auto direction =
isClockwiseDirection ? SkPathDirection::kCW : SkPathDirection::kCCW;
mutable_path().rArcTo(radiusX, radiusY, xAxisRotation, arcSize, direction,
arcEndDeltaX, arcEndDeltaY);
resetVolatility();
}
void CanvasPath::addRect(float left, float top, float right, float bottom) {
mutable_path().addRect(SkRect::MakeLTRB(left, top, right, bottom));
resetVolatility();
}
void CanvasPath::addOval(float left, float top, float right, float bottom) {
mutable_path().addOval(SkRect::MakeLTRB(left, top, right, bottom));
resetVolatility();
}
void CanvasPath::addArc(float left,
float top,
float right,
float bottom,
float startAngle,
float sweepAngle) {
mutable_path().addArc(SkRect::MakeLTRB(left, top, right, bottom),
startAngle * 180.0 / M_PI, sweepAngle * 180.0 / M_PI);
resetVolatility();
}
void CanvasPath::addPolygon(const tonic::Float32List& points, bool close) {
mutable_path().addPoly(reinterpret_cast<const SkPoint*>(points.data()),
points.num_elements() / 2, close);
resetVolatility();
}
void CanvasPath::addRRect(const RRect& rrect) {
mutable_path().addRRect(rrect.sk_rrect);
resetVolatility();
}
void CanvasPath::addPath(CanvasPath* path, double dx, double dy) {
if (!path) {
Dart_ThrowException(ToDart("Path.addPath called with non-genuine Path."));
return;
}
mutable_path().addPath(path->path(), dx, dy, SkPath::kAppend_AddPathMode);
resetVolatility();
}
void CanvasPath::addPathWithMatrix(CanvasPath* path,
double dx,
double dy,
tonic::Float64List& matrix4) {
if (!path) {
Dart_ThrowException(
ToDart("Path.addPathWithMatrix called with non-genuine Path."));
return;
}
SkMatrix matrix = ToSkMatrix(matrix4);
matrix.setTranslateX(matrix.getTranslateX() + dx);
matrix.setTranslateY(matrix.getTranslateY() + dy);
mutable_path().addPath(path->path(), matrix, SkPath::kAppend_AddPathMode);
matrix4.Release();
resetVolatility();
}
void CanvasPath::extendWithPath(CanvasPath* path, double dx, double dy) {
if (!path) {
Dart_ThrowException(
ToDart("Path.extendWithPath called with non-genuine Path."));
return;
}
mutable_path().addPath(path->path(), dx, dy, SkPath::kExtend_AddPathMode);
resetVolatility();
}
void CanvasPath::extendWithPathAndMatrix(CanvasPath* path,
double dx,
double dy,
tonic::Float64List& matrix4) {
if (!path) {
Dart_ThrowException(
ToDart("Path.addPathWithMatrix called with non-genuine Path."));
return;
}
SkMatrix matrix = ToSkMatrix(matrix4);
matrix.setTranslateX(matrix.getTranslateX() + dx);
matrix.setTranslateY(matrix.getTranslateY() + dy);
mutable_path().addPath(path->path(), matrix, SkPath::kExtend_AddPathMode);
matrix4.Release();
resetVolatility();
}
void CanvasPath::close() {
mutable_path().close();
resetVolatility();
}
void CanvasPath::reset() {
mutable_path().reset();
resetVolatility();
}
bool CanvasPath::contains(double x, double y) {
return path().contains(x, y);
}
void CanvasPath::shift(Dart_Handle path_handle, double dx, double dy) {
fml::RefPtr<CanvasPath> path = CanvasPath::Create(path_handle);
auto& other_mutable_path = path->mutable_path();
mutable_path().offset(dx, dy, &other_mutable_path);
resetVolatility();
}
void CanvasPath::transform(Dart_Handle path_handle,
tonic::Float64List& matrix4) {
fml::RefPtr<CanvasPath> path = CanvasPath::Create(path_handle);
auto& other_mutable_path = path->mutable_path();
mutable_path().transform(ToSkMatrix(matrix4), &other_mutable_path);
matrix4.Release();
}
tonic::Float32List CanvasPath::getBounds() {
tonic::Float32List rect(Dart_NewTypedData(Dart_TypedData_kFloat32, 4));
const SkRect& bounds = path().getBounds();
rect[0] = bounds.left();
rect[1] = bounds.top();
rect[2] = bounds.right();
rect[3] = bounds.bottom();
return rect;
}
bool CanvasPath::op(CanvasPath* path1, CanvasPath* path2, int operation) {
return Op(path1->path(), path2->path(), static_cast<SkPathOp>(operation),
&tracked_path_->path);
resetVolatility();
}
void CanvasPath::clone(Dart_Handle path_handle) {
fml::RefPtr<CanvasPath> path = CanvasPath::Create(path_handle);
// per Skia docs, this will create a fast copy
// data is shared until the source path or dest path are mutated
path->mutable_path() = this->path();
}
// This is doomed to be called too early, since Paths are mutable.
// However, it can help for some of the clone/shift/transform type methods
// where the resultant path will initially have a meaningful size.
size_t CanvasPath::GetAllocationSize() const {
return sizeof(CanvasPath) + path().approximateBytesUsed();
}
} // namespace flutter