blob: 1e88d49253d1f5b237cb0f13c7696f599d45233a [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.
// ignore_for_file: camel_case_types
import 'dart:ffi' as ffi;
import 'dart:io';
import 'dart:typed_data';
/// Determines the winding rule that decides how the interior of a Path is
/// calculated.
///
/// This enum is used by the [VerticesBuilder.tessellate] method.
// must match ordering in geometry/path.h
enum FillType {
/// The interior is defined by a non-zero sum of signed edge crossings.
nonZero,
/// The interior is defined by an odd number of edge crossings.
evenOdd,
}
/// Information about how to approximate points on a curved path segment.
///
/// In particular, the values in this object control how many vertices to
/// generate when approximating curves, and what tolerances to use when
/// calculating the sharpness of curves.
///
/// Used by [VerticesBuilder.tessellate].
class SmoothingApproximation {
/// Creates a new smoothing approximation instance with default values.
const SmoothingApproximation({
this.scale = 1.0,
this.angleTolerance = 0.0,
this.cuspLimit = 0.0,
});
/// The scaling coefficient to use when translating to screen coordinates.
///
/// Values approaching 0.0 will generate smoother looking curves with a
/// greater number of vertices, and will be more expensive to calculate.
final double scale;
/// The tolerance value in radians for calculating sharp angles.
///
/// Values approaching 0.0 will provide more accurate approximation of sharp
/// turns. A 0.0 value means angle conditions are not considered at all.
final double angleTolerance;
/// An angle in radians at which to introduce bevel cuts.
///
/// Values greater than zero will restirct the sharpness of bevel cuts on
/// turns.
final double cuspLimit;
}
/// Creates vertices from path commands.
///
/// First, build up the path contours with the [moveTo], [lineTo], [cubicTo],
/// and [close] methods. All methods expect absolute coordinates.
///
/// Then, use the [tessellate] method to create a [Float32List] of vertex pairs.
///
/// Finally, use the [dispose] method to clean up native resources. After
/// [dispose] has been called, this class must not be used again.
class VerticesBuilder {
VerticesBuilder() : _builder = _createPathFn();
ffi.Pointer<_PathBuilder>? _builder;
final List<ffi.Pointer<_Vertices>> _vertices = <ffi.Pointer<_Vertices>>[];
/// Adds a move verb to the absolute coordinates x,y.
void moveTo(double x, double y) {
assert(_builder != null);
_moveToFn(_builder!, x, y);
}
/// Adds a line verb to the absolute coordinates x,y.
void lineTo(double x, double y) {
assert(_builder != null);
_lineToFn(_builder!, x, y);
}
/// Adds a cubic Bezier curve with x1,y1 as the first control point, x2,y2 as
/// the second control point, and end point x3,y3.
void cubicTo(
double x1,
double y1,
double x2,
double y2,
double x3,
double y3,
) {
assert(_builder != null);
_cubicToFn(_builder!, x1, y1, x2, y2, x3, y3);
}
/// Adds a close command to the start of the current contour.
void close() {
assert(_builder != null);
closeFn(_builder!, true);
}
/// Tessellates the path created by the previous method calls into a list of
/// vertices.
Float32List tessellate({
FillType fillType = FillType.nonZero,
SmoothingApproximation smoothing = const SmoothingApproximation(),
}) {
assert(_vertices.isEmpty);
assert(_builder != null);
final ffi.Pointer<_Vertices> vertices = _tessellateFn(
_builder!,
fillType.index,
smoothing.scale,
smoothing.angleTolerance,
smoothing.cuspLimit,
);
_vertices.add(vertices);
return vertices.ref.points.asTypedList(vertices.ref.size);
}
/// Releases native resources.
///
/// After calling dispose, this class must not be used again.
void dispose() {
assert(_builder != null);
_vertices.forEach(_destroyVerticesFn);
_destroyFn(_builder!);
_vertices.clear();
_builder = null;
}
}
// TODO(dnfield): Figure out where to put this.
// https://github.com/flutter/flutter/issues/99563
final ffi.DynamicLibrary _dylib = () {
if (Platform.isWindows) {
return ffi.DynamicLibrary.open('tessellator.dll');
} else if (Platform.isIOS || Platform.isMacOS) {
return ffi.DynamicLibrary.open('libtessellator.dylib');
} else if (Platform.isAndroid || Platform.isLinux) {
return ffi.DynamicLibrary.open('libtessellator.so');
}
throw UnsupportedError('Unknown platform: ${Platform.operatingSystem}');
}();
class _Vertices extends ffi.Struct {
external ffi.Pointer<ffi.Float> points;
@ffi.Uint32()
external int size;
}
class _PathBuilder extends ffi.Opaque {}
typedef _CreatePathBuilderType = ffi.Pointer<_PathBuilder> Function();
typedef _create_path_builder_type = ffi.Pointer<_PathBuilder> Function();
final _CreatePathBuilderType _createPathFn =
_dylib.lookupFunction<_create_path_builder_type, _CreatePathBuilderType>(
'CreatePathBuilder',
);
typedef _MoveToType = void Function(ffi.Pointer<_PathBuilder>, double, double);
typedef _move_to_type = ffi.Void Function(
ffi.Pointer<_PathBuilder>,
ffi.Float,
ffi.Float,
);
final _MoveToType _moveToFn = _dylib.lookupFunction<_move_to_type, _MoveToType>(
'MoveTo',
);
typedef _LineToType = void Function(ffi.Pointer<_PathBuilder>, double, double);
typedef _line_to_type = ffi.Void Function(
ffi.Pointer<_PathBuilder>,
ffi.Float,
ffi.Float,
);
final _LineToType _lineToFn = _dylib.lookupFunction<_line_to_type, _LineToType>(
'LineTo',
);
typedef _CubicToType = void Function(
ffi.Pointer<_PathBuilder>,
double,
double,
double,
double,
double,
double,
);
typedef _cubic_to_type = ffi.Void Function(
ffi.Pointer<_PathBuilder>,
ffi.Float,
ffi.Float,
ffi.Float,
ffi.Float,
ffi.Float,
ffi.Float,
);
final _CubicToType _cubicToFn =
_dylib.lookupFunction<_cubic_to_type, _CubicToType>('CubicTo');
typedef _CloseType = void Function(ffi.Pointer<_PathBuilder>, bool);
typedef _close_type = ffi.Void Function(ffi.Pointer<_PathBuilder>, ffi.Bool);
final _CloseType closeFn =
_dylib.lookupFunction<_close_type, _CloseType>('Close');
typedef _TessellateType = ffi.Pointer<_Vertices> Function(
ffi.Pointer<_PathBuilder>,
int,
double,
double,
double,
);
typedef _tessellate_type = ffi.Pointer<_Vertices> Function(
ffi.Pointer<_PathBuilder>,
ffi.Int,
ffi.Float,
ffi.Float,
ffi.Float,
);
final _TessellateType _tessellateFn =
_dylib.lookupFunction<_tessellate_type, _TessellateType>('Tessellate');
typedef _DestroyType = void Function(ffi.Pointer<_PathBuilder>);
typedef _destroy_type = ffi.Void Function(ffi.Pointer<_PathBuilder>);
final _DestroyType _destroyFn =
_dylib.lookupFunction<_destroy_type, _DestroyType>(
'DestroyPathBuilder',
);
typedef _DestroyVerticesType = void Function(ffi.Pointer<_Vertices>);
typedef _destroy_vertices_type = ffi.Void Function(ffi.Pointer<_Vertices>);
final _DestroyVerticesType _destroyVerticesFn =
_dylib.lookupFunction<_destroy_vertices_type, _DestroyVerticesType>(
'DestroyVertices',
);