can "render" counter
diff --git a/examples/counter.dart b/examples/counter.dart
new file mode 100644
index 0000000..d560b3d
--- /dev/null
+++ b/examples/counter.dart
@@ -0,0 +1,65 @@
+import 'package:flute/material.dart';
+
+void main() {
+ runApp(MyApp());
+}
+
+class MyApp extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ title: 'Flutter Demo',
+ theme: ThemeData(
+ primarySwatch: Colors.blue,
+ ),
+ home: MyHomePage('Flutter Demo Home Page'),
+ );
+ }
+}
+
+class MyHomePage extends StatefulWidget {
+ MyHomePage(this.title);
+
+ final String title;
+
+ @override
+ _MyHomePageState createState() => _MyHomePageState();
+}
+
+class _MyHomePageState extends State<MyHomePage> {
+ int _counter = 0;
+
+ void _incrementCounter() {
+ setState(() {
+ _counter++;
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: Text(widget.title),
+ ),
+ body: Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: <Widget>[
+ Text(
+ 'You have pushed the button this many times:',
+ ),
+ Text(
+ '$_counter',
+ style: Theme.of(context).textTheme.headline4,
+ ),
+ ],
+ ),
+ ),
+ floatingActionButton: FloatingActionButton(
+ onPressed: _incrementCounter,
+ tooltip: 'Increment',
+ child: Icon(Icons.add),
+ ),
+ );
+ }
+}
diff --git a/lib/src/ui/compositing.dart b/lib/src/ui/compositing.dart
index d3990b4..b49e111 100644
--- a/lib/src/ui/compositing.dart
+++ b/lib/src/ui/compositing.dart
@@ -11,7 +11,7 @@
///
/// Scene objects can be displayed on the screen using the [FlutterView.render]
/// method.
-class Scene extends NativeFieldWrapperClass2 {
+class Scene {
/// This class is created by the engine, and should not be instantiated
/// or extended directly.
///
@@ -45,15 +45,13 @@
// passed as `oldLayer` to `pushTransform`. This is achieved by having one
// concrete subclass of this class per push method.
abstract class _EngineLayerWrapper implements EngineLayer {
- _EngineLayerWrapper._(this._nativeLayer);
-
- EngineLayer _nativeLayer;
+ _EngineLayerWrapper._();
// Children of this layer.
//
// Null if this layer has no children. This field is populated only in debug
// mode.
- List<_EngineLayerWrapper>? _debugChildren;
+ List<_EngineLayerWrapper>? _children;
// Whether this layer was used as `oldLayer` in a past frame.
//
@@ -73,6 +71,15 @@
}
}
+class _PictureLayer extends _EngineLayerWrapper {
+ _PictureLayer(this.dx, this.dy, this.picture, this.hints) : super._();
+
+ final double dx;
+ final double dy;
+ final Picture picture;
+ final int hints;
+}
+
/// An opaque handle to a transform engine layer.
///
/// Instances of this class are created by [SceneBuilder.pushTransform].
@@ -83,7 +90,10 @@
/// a custom implementation of this class.
/// {@endtemplate}
class TransformEngineLayer extends _EngineLayerWrapper {
- TransformEngineLayer._(EngineLayer nativeLayer) : super._(nativeLayer);
+ TransformEngineLayer._(Float64List matrix4)
+ : this.matrix4 = Float64List.fromList(matrix4), super._();
+
+ final Float64List matrix4;
}
/// An opaque handle to an offset engine layer.
@@ -92,7 +102,10 @@
///
/// {@macro dart.ui.sceneBuilder.oldLayerCompatibility}
class OffsetEngineLayer extends _EngineLayerWrapper {
- OffsetEngineLayer._(EngineLayer nativeLayer) : super._(nativeLayer);
+ OffsetEngineLayer._(this.dx, this.dy) : super._();
+
+ final double dx;
+ final double dy;
}
/// An opaque handle to a clip rect engine layer.
@@ -101,7 +114,13 @@
///
/// {@macro dart.ui.sceneBuilder.oldLayerCompatibility}
class ClipRectEngineLayer extends _EngineLayerWrapper {
- ClipRectEngineLayer._(EngineLayer nativeLayer) : super._(nativeLayer);
+ ClipRectEngineLayer._(this.left, this.right, this.top, this.bottom, this.clipBehavior) : super._();
+
+ final double left;
+ final double top;
+ final double right;
+ final double bottom;
+ final int clipBehavior;
}
/// An opaque handle to a clip rounded rect engine layer.
@@ -110,7 +129,10 @@
///
/// {@macro dart.ui.sceneBuilder.oldLayerCompatibility}
class ClipRRectEngineLayer extends _EngineLayerWrapper {
- ClipRRectEngineLayer._(EngineLayer nativeLayer) : super._(nativeLayer);
+ ClipRRectEngineLayer._(this.rrect, this.clipBehavior) : super._();
+
+ final Float32List rrect;
+ final int clipBehavior;
}
/// An opaque handle to a clip path engine layer.
@@ -119,7 +141,9 @@
///
/// {@macro dart.ui.sceneBuilder.oldLayerCompatibility}
class ClipPathEngineLayer extends _EngineLayerWrapper {
- ClipPathEngineLayer._(EngineLayer nativeLayer) : super._(nativeLayer);
+ ClipPathEngineLayer._(this.path) : super._();
+
+ final Path path;
}
/// An opaque handle to an opacity engine layer.
@@ -128,7 +152,11 @@
///
/// {@macro dart.ui.sceneBuilder.oldLayerCompatibility}
class OpacityEngineLayer extends _EngineLayerWrapper {
- OpacityEngineLayer._(EngineLayer nativeLayer) : super._(nativeLayer);
+ OpacityEngineLayer._(this.alpha, this.dx, this.dy) : super._();
+
+ final int alpha;
+ final double dx;
+ final double dy;
}
/// An opaque handle to a color filter engine layer.
@@ -137,7 +165,9 @@
///
/// {@macro dart.ui.sceneBuilder.oldLayerCompatibility}
class ColorFilterEngineLayer extends _EngineLayerWrapper {
- ColorFilterEngineLayer._(EngineLayer nativeLayer) : super._(nativeLayer);
+ ColorFilterEngineLayer._(this.filter) : super._();
+
+ final _ColorFilter filter;
}
/// An opaque handle to an image filter engine layer.
@@ -146,7 +176,9 @@
///
/// {@macro dart.ui.sceneBuilder.oldLayerCompatibility}
class ImageFilterEngineLayer extends _EngineLayerWrapper {
- ImageFilterEngineLayer._(EngineLayer nativeLayer) : super._(nativeLayer);
+ ImageFilterEngineLayer._(this.filter) : super._();
+
+ final _ImageFilter filter;
}
/// An opaque handle to a backdrop filter engine layer.
@@ -155,7 +187,9 @@
///
/// {@macro dart.ui.sceneBuilder.oldLayerCompatibility}
class BackdropFilterEngineLayer extends _EngineLayerWrapper {
- BackdropFilterEngineLayer._(EngineLayer nativeLayer) : super._(nativeLayer);
+ BackdropFilterEngineLayer._(this.filter) : super._();
+
+ final _ImageFilter filter;
}
/// An opaque handle to a shader mask engine layer.
@@ -164,7 +198,21 @@
///
/// {@macro dart.ui.sceneBuilder.oldLayerCompatibility}
class ShaderMaskEngineLayer extends _EngineLayerWrapper {
- ShaderMaskEngineLayer._(EngineLayer nativeLayer) : super._(nativeLayer);
+ ShaderMaskEngineLayer._(
+ this.shader,
+ this.left,
+ this.right,
+ this.top,
+ this.bottom,
+ this.blendMode,
+ ) : super._();
+
+ final Shader shader;
+ final double left;
+ final double top;
+ final double right;
+ final double bottom;
+ final int blendMode;
}
/// An opaque handle to a physical shape engine layer.
@@ -173,7 +221,19 @@
///
/// {@macro dart.ui.sceneBuilder.oldLayerCompatibility}
class PhysicalShapeEngineLayer extends _EngineLayerWrapper {
- PhysicalShapeEngineLayer._(EngineLayer nativeLayer) : super._(nativeLayer);
+ PhysicalShapeEngineLayer._(
+ this.path,
+ this.elevation,
+ this.colorValue,
+ this.shadowColorValue,
+ this.clipBehavior,
+ ) : super._();
+
+ final Path path;
+ final double elevation;
+ final int colorValue;
+ final int shadowColorValue;
+ final int clipBehavior;
}
/// Builds a [Scene] containing the given visuals.
@@ -183,9 +243,8 @@
/// To draw graphical operations onto a [Scene], first create a
/// [Picture] using a [PictureRecorder] and a [Canvas], and then add
/// it to the scene using [addPicture].
-class SceneBuilder extends NativeFieldWrapperClass2 {
+class SceneBuilder {
/// Creates an empty [SceneBuilder] object.
- @pragma('vm:entry-point')
SceneBuilder();
// Layers used in this scene.
@@ -223,21 +282,17 @@
return true;
}
+ _EngineLayerWrapper? _rootLayer;
final List<_EngineLayerWrapper> _layerStack = <_EngineLayerWrapper>[];
- // Pushes the `newLayer` onto the `_layerStack` and adds it to the
- // `_debugChildren` of the current layer in the stack, if any.
- bool _debugPushLayer(_EngineLayerWrapper newLayer) {
- assert(() {
- if (_layerStack.isNotEmpty) {
- final _EngineLayerWrapper currentLayer = _layerStack.last;
- currentLayer._debugChildren ??= <_EngineLayerWrapper>[];
- currentLayer._debugChildren!.add(newLayer);
- }
- _layerStack.add(newLayer);
- return true;
- }());
- return true;
+ void _pushLayer(_EngineLayerWrapper layer) {
+ _rootLayer ??= layer;
+ if (_layerStack.isNotEmpty) {
+ final _EngineLayerWrapper currentLayer = _layerStack.last;
+ currentLayer._children ??= <_EngineLayerWrapper>[];
+ currentLayer._children!.add(layer);
+ }
+ _layerStack.add(layer);
}
/// Pushes a transform operation onto the operation stack.
@@ -274,15 +329,11 @@
}) {
assert(_matrix4IsValid(matrix4));
assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushTransform'));
- final EngineLayer engineLayer = EngineLayer._();
- _pushTransform(engineLayer, matrix4);
- final TransformEngineLayer layer = TransformEngineLayer._(engineLayer);
- assert(_debugPushLayer(layer));
+ final TransformEngineLayer layer = TransformEngineLayer._(matrix4);
+ _pushLayer(layer);
return layer;
}
- void _pushTransform(EngineLayer layer, Float64List matrix4) { throw UnimplementedError(); }
-
/// Pushes an offset operation onto the operation stack.
///
/// This is equivalent to [pushTransform] with a matrix with only translation.
@@ -298,15 +349,11 @@
OffsetEngineLayer? oldLayer,
}) {
assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushOffset'));
- final EngineLayer engineLayer = EngineLayer._();
- _pushOffset(engineLayer, dx, dy);
- final OffsetEngineLayer layer = OffsetEngineLayer._(engineLayer);
- assert(_debugPushLayer(layer));
+ final OffsetEngineLayer layer = OffsetEngineLayer._(dx ,dy);
+ _pushLayer(layer);
return layer;
}
- void _pushOffset(EngineLayer layer, double dx, double dy) { throw UnimplementedError(); }
-
/// Pushes a rectangular clip operation onto the operation stack.
///
/// Rasterization outside the given rectangle is discarded.
@@ -325,16 +372,11 @@
assert(clipBehavior != null); // ignore: unnecessary_null_comparison
assert(clipBehavior != Clip.none);
assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushClipRect'));
- final EngineLayer engineLayer = EngineLayer._();
- _pushClipRect(engineLayer, rect.left, rect.right, rect.top, rect.bottom, clipBehavior.index);
- final ClipRectEngineLayer layer = ClipRectEngineLayer._(engineLayer);
- assert(_debugPushLayer(layer));
+ final ClipRectEngineLayer layer = ClipRectEngineLayer._(rect.left, rect.right, rect.top, rect.bottom, clipBehavior.index);
+ _pushLayer(layer);
return layer;
}
- void _pushClipRect(EngineLayer outEngineLayer, double left, double right, double top, double bottom, int clipBehavior)
- { throw UnimplementedError(); }
-
/// Pushes a rounded-rectangular clip operation onto the operation stack.
///
/// Rasterization outside the given rounded rectangle is discarded.
@@ -353,16 +395,11 @@
assert(clipBehavior != null); // ignore: unnecessary_null_comparison
assert(clipBehavior != Clip.none);
assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushClipRRect'));
- final EngineLayer engineLayer = EngineLayer._();
- _pushClipRRect(engineLayer, rrect._value32, clipBehavior.index);
- final ClipRRectEngineLayer layer = ClipRRectEngineLayer._(engineLayer);
- assert(_debugPushLayer(layer));
+ final ClipRRectEngineLayer layer = ClipRRectEngineLayer._(rrect._value32, clipBehavior.index);
+ _pushLayer(layer);
return layer;
}
- void _pushClipRRect(EngineLayer layer, Float32List rrect, int clipBehavior)
- { throw UnimplementedError(); }
-
/// Pushes a path clip operation onto the operation stack.
///
/// Rasterization outside the given path is discarded.
@@ -381,15 +418,11 @@
assert(clipBehavior != null); // ignore: unnecessary_null_comparison
assert(clipBehavior != Clip.none);
assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushClipPath'));
- final EngineLayer engineLayer = EngineLayer._();
- _pushClipPath(engineLayer, path, clipBehavior.index);
- final ClipPathEngineLayer layer = ClipPathEngineLayer._(engineLayer);
- assert(_debugPushLayer(layer));
+ final ClipPathEngineLayer layer = ClipPathEngineLayer._(path);
+ _pushLayer(layer);
return layer;
}
- void _pushClipPath(EngineLayer layer, Path path, int clipBehavior) { throw UnimplementedError(); }
-
/// Pushes an opacity operation onto the operation stack.
///
/// The given alpha value is blended into the alpha value of the objects'
@@ -408,15 +441,11 @@
OpacityEngineLayer? oldLayer,
}) {
assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushOpacity'));
- final EngineLayer engineLayer = EngineLayer._();
- _pushOpacity(engineLayer, alpha, offset!.dx, offset.dy);
- final OpacityEngineLayer layer = OpacityEngineLayer._(engineLayer);
- assert(_debugPushLayer(layer));
+ final OpacityEngineLayer layer = OpacityEngineLayer._(alpha, offset!.dx, offset.dy);
+ _pushLayer(layer);
return layer;
}
- void _pushOpacity(EngineLayer layer, int alpha, double dx, double dy) { throw UnimplementedError(); }
-
/// Pushes a color filter operation onto the operation stack.
///
/// The given color is applied to the objects' rasterization using the given
@@ -435,15 +464,11 @@
assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushColorFilter'));
final _ColorFilter nativeFilter = filter._toNativeColorFilter()!;
assert(nativeFilter != null); // ignore: unnecessary_null_comparison
- final EngineLayer engineLayer = EngineLayer._();
- _pushColorFilter(engineLayer, nativeFilter);
- final ColorFilterEngineLayer layer = ColorFilterEngineLayer._(engineLayer);
- assert(_debugPushLayer(layer));
+ final ColorFilterEngineLayer layer = ColorFilterEngineLayer._(nativeFilter);
+ _pushLayer(layer);
return layer;
}
- void _pushColorFilter(EngineLayer layer, _ColorFilter filter) { throw UnimplementedError(); }
-
/// Pushes an image filter operation onto the operation stack.
///
/// The given filter is applied to the children's rasterization before compositing them into
@@ -462,15 +487,11 @@
assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushImageFilter'));
final _ImageFilter nativeFilter = filter._toNativeImageFilter();
assert(nativeFilter != null); // ignore: unnecessary_null_comparison
- final EngineLayer engineLayer = EngineLayer._();
- _pushImageFilter(engineLayer, nativeFilter);
- final ImageFilterEngineLayer layer = ImageFilterEngineLayer._(engineLayer);
- assert(_debugPushLayer(layer));
+ final ImageFilterEngineLayer layer = ImageFilterEngineLayer._(nativeFilter);
+ _pushLayer(layer);
return layer;
}
- void _pushImageFilter(EngineLayer outEngineLayer, _ImageFilter filter) { throw UnimplementedError(); }
-
/// Pushes a backdrop filter operation onto the operation stack.
///
/// The given filter is applied to the current contents of the scene prior to
@@ -486,15 +507,11 @@
BackdropFilterEngineLayer? oldLayer,
}) {
assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushBackdropFilter'));
- final EngineLayer engineLayer = EngineLayer._();
- _pushBackdropFilter(engineLayer, filter._toNativeImageFilter());
- final BackdropFilterEngineLayer layer = BackdropFilterEngineLayer._(engineLayer);
- assert(_debugPushLayer(layer));
+ final BackdropFilterEngineLayer layer = BackdropFilterEngineLayer._(filter._toNativeImageFilter());
+ _pushLayer(layer);
return layer;
}
- void _pushBackdropFilter(EngineLayer outEngineLayer, _ImageFilter filter) { throw UnimplementedError(); }
-
/// Pushes a shader mask operation onto the operation stack.
///
/// The given shader is applied to the object's rasterization in the given
@@ -512,9 +529,7 @@
ShaderMaskEngineLayer? oldLayer,
}) {
assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushShaderMask'));
- final EngineLayer engineLayer = EngineLayer._();
- _pushShaderMask(
- engineLayer,
+ final ShaderMaskEngineLayer layer = ShaderMaskEngineLayer._(
shader,
maskRect.left,
maskRect.right,
@@ -522,20 +537,10 @@
maskRect.bottom,
blendMode.index,
);
- final ShaderMaskEngineLayer layer = ShaderMaskEngineLayer._(engineLayer);
- assert(_debugPushLayer(layer));
+ _pushLayer(layer);
return layer;
}
- void _pushShaderMask(
- EngineLayer engineLayer,
- Shader shader,
- double maskRectLeft,
- double maskRectRight,
- double maskRectTop,
- double maskRectBottom,
- int blendMode) { throw UnimplementedError(); }
-
/// Pushes a physical layer operation for an arbitrary shape onto the
/// operation stack.
///
@@ -562,23 +567,17 @@
PhysicalShapeEngineLayer? oldLayer,
}) {
assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushPhysicalShape'));
- final EngineLayer engineLayer = EngineLayer._();
- _pushPhysicalShape(
- engineLayer,
+ final PhysicalShapeEngineLayer layer = PhysicalShapeEngineLayer._(
path,
elevation,
color.value,
shadowColor?.value ?? 0xFF000000,
clipBehavior.index,
);
- final PhysicalShapeEngineLayer layer = PhysicalShapeEngineLayer._(engineLayer);
- assert(_debugPushLayer(layer));
+ _pushLayer(layer);
return layer;
}
- void _pushPhysicalShape(EngineLayer outEngineLayer, Path path, double elevation, int color, int shadowColor,
- int clipBehavior) { throw UnimplementedError(); }
-
/// Ends the effect of the most recently pushed operation.
///
/// Internally the scene builder maintains a stack of operations. Each of the
@@ -589,11 +588,8 @@
if (_layerStack.isNotEmpty) {
_layerStack.removeLast();
}
- _pop();
}
- void _pop() { throw UnimplementedError(); }
-
/// Add a retained engine layer subtree from previous frames.
///
/// All the engine layers that are in the subtree of the retained layer will
@@ -613,7 +609,7 @@
_debugCheckUsedOnce(parentLayer, 'retained layer');
parentLayer._debugCheckNotUsedAsOldLayer();
- final List<_EngineLayerWrapper>? children = parentLayer._debugChildren;
+ final List<_EngineLayerWrapper>? children = parentLayer._children;
if (children == null || children.isEmpty) {
return;
}
@@ -626,11 +622,10 @@
}());
final _EngineLayerWrapper wrapper = retainedLayer as _EngineLayerWrapper;
- _addRetained(wrapper._nativeLayer);
+ _pushLayer(wrapper);
+ pop();
}
- void _addRetained(EngineLayer retainedLayer) { throw UnimplementedError(); }
-
/// Adds an object to the scene that displays performance statistics.
///
/// Useful during development to assess the performance of the application.
@@ -677,12 +672,10 @@
bool willChangeHint = false,
}) {
final int hints = (isComplexHint ? 1 : 0) | (willChangeHint ? 2 : 0);
- _addPicture(offset.dx, offset.dy, picture, hints);
+ final _PictureLayer layer = _PictureLayer(offset.dx, offset.dy, picture, hints);
+ _pushLayer(layer);
}
- void _addPicture(double dx, double dy, Picture picture, int hints)
- { throw UnimplementedError(); }
-
/// Adds a backend texture to the scene.
///
/// The texture is scaled to the given size and rasterized at the given offset.
@@ -797,15 +790,12 @@
/// cannot be used further.
Scene build() {
final Scene scene = Scene._();
- _build(scene);
return scene;
}
-
- void _build(Scene outScene) { throw UnimplementedError(); }
}
/// (Fuchsia-only) Hosts content provided by another application.
-class SceneHost extends NativeFieldWrapperClass2 {
+class SceneHost {
/// Creates a host for a child scene's content.
///
/// The ViewHolder token is bound to a ViewHolder scene graph node which acts
diff --git a/lib/src/ui/geometry.dart b/lib/src/ui/geometry.dart
index 92545ec..81f5b7f 100644
--- a/lib/src/ui/geometry.dart
+++ b/lib/src/ui/geometry.dart
@@ -630,8 +630,7 @@
/// ```
class Rect {
/// Construct a rectangle from its left, top, right, and bottom edges.
- @pragma('vm:entry-point')
- const Rect.fromLTRB(this.left, this.top, this.right, this.bottom)
+ const Rect.fromLTRB(this.left, this.top, this.right, this.bottom)
: assert(left != null), // ignore: unnecessary_null_comparison
assert(top != null), // ignore: unnecessary_null_comparison
assert(right != null), // ignore: unnecessary_null_comparison
diff --git a/lib/src/ui/hooks.dart b/lib/src/ui/hooks.dart
index 0056145..eb7d29b 100644
--- a/lib/src/ui/hooks.dart
+++ b/lib/src/ui/hooks.dart
@@ -8,7 +8,6 @@
part of dart.ui;
-@pragma('vm:entry-point')
// ignore: unused_element
void _updateWindowMetrics(
Object id,
@@ -50,71 +49,59 @@
typedef _LocaleClosure = String Function();
-@pragma('vm:entry-point')
// ignore: unused_element
_LocaleClosure? _getLocaleClosure() => PlatformDispatcher.instance._localeClosure;
-@pragma('vm:entry-point')
// ignore: unused_element
void _updateLocales(List<String> locales) {
PlatformDispatcher.instance._updateLocales(locales);
}
-@pragma('vm:entry-point')
// ignore: unused_element
void _updateUserSettingsData(String jsonData) {
PlatformDispatcher.instance._updateUserSettingsData(jsonData);
}
-@pragma('vm:entry-point')
// ignore: unused_element
void _updateLifecycleState(String state) {
PlatformDispatcher.instance._updateLifecycleState(state);
}
-@pragma('vm:entry-point')
// ignore: unused_element
void _updateSemanticsEnabled(bool enabled) {
PlatformDispatcher.instance._updateSemanticsEnabled(enabled);
}
-@pragma('vm:entry-point')
// ignore: unused_element
void _updateAccessibilityFeatures(int values) {
PlatformDispatcher.instance._updateAccessibilityFeatures(values);
}
-@pragma('vm:entry-point')
// ignore: unused_element
void _dispatchPlatformMessage(String name, ByteData? data, int responseId) {
PlatformDispatcher.instance._dispatchPlatformMessage(name, data, responseId);
}
-@pragma('vm:entry-point')
// ignore: unused_element
void _dispatchPointerDataPacket(ByteData packet) {
PlatformDispatcher.instance._dispatchPointerDataPacket(packet);
}
-@pragma('vm:entry-point')
// ignore: unused_element
void _dispatchSemanticsAction(int id, int action, ByteData? args) {
PlatformDispatcher.instance._dispatchSemanticsAction(id, action, args);
}
-@pragma('vm:entry-point')
// ignore: unused_element
void _beginFrame(int microseconds) {
PlatformDispatcher.instance._beginFrame(microseconds);
}
-@pragma('vm:entry-point')
// ignore: unused_element
void _reportTimings(List<int> timings) {
PlatformDispatcher.instance._reportTimings(timings);
}
-@pragma('vm:entry-point')
// ignore: unused_element
void _drawFrame() {
PlatformDispatcher.instance._drawFrame();
@@ -123,7 +110,6 @@
// ignore: always_declare_return_types, prefer_generic_function_type_aliases
typedef _ListStringArgFunction(List<String> args);
-@pragma('vm:entry-point')
// ignore: unused_element
void _runMainZoned(Function startMainIsolateFunction,
Function userMainFunction,
diff --git a/lib/src/ui/natives.dart b/lib/src/ui/natives.dart
index 65e02fe..f291cf9 100644
--- a/lib/src/ui/natives.dart
+++ b/lib/src/ui/natives.dart
@@ -41,7 +41,6 @@
}));
}
-@pragma('vm:entry-point')
void _setupHooks() { // ignore: unused_element
assert(() {
// In debug mode, register the schedule frame extension.
@@ -86,7 +85,5 @@
// Required for gen_snapshot to work correctly.
int? _isolateId; // ignore: unused_element
-@pragma('vm:entry-point')
Function _getPrintClosure() => _print; // ignore: unused_element
-@pragma('vm:entry-point')
Function _getScheduleMicrotaskClosure() => _scheduleMicrotask; // ignore: unused_element
diff --git a/lib/src/ui/painting.dart b/lib/src/ui/painting.dart
index 3230a97..45d15a8 100644
--- a/lib/src/ui/painting.dart
+++ b/lib/src/ui/painting.dart
@@ -6,35 +6,6 @@
part of dart.ui;
-// Some methods in this file assert that their arguments are not null. These
-// asserts are just to improve the error messages; they should only cover
-// arguments that are either dereferenced _in Dart_, before being passed to the
-// engine, or that the engine explicitly null-checks itself (after attempting to
-// convert the argument to a native type). It should not be possible for a null
-// or invalid value to be used by the engine even in release mode, since that
-// would cause a crash. It is, however, acceptable for error messages to be much
-// less useful or correct in release mode than in debug mode.
-//
-// Painting APIs will also warn about arguments representing NaN coordinates,
-// which can not be rendered by Skia.
-
-// Update this list when changing the list of supported codecs.
-/// {@template dart.ui.imageFormats}
-/// JPEG, PNG, GIF, Animated GIF, WebP, Animated WebP, BMP, and WBMP. Additional
-/// formats may be supported by the underlying platform. Flutter will
-/// attempt to call platform API to decode unrecognized formats, and if the
-/// platform API supports decoding the image Flutter will be able to render it.
-/// {@endtemplate}
-
-// TODO(gspencergoog): remove this template block once the framework templates
-// are renamed to not reference it.
-/// {@template flutter.dart:ui.imageFormats}
-/// JPEG, PNG, GIF, Animated GIF, WebP, Animated WebP, BMP, and WBMP. Additional
-/// formats may be supported by the underlying platform. Flutter will
-/// attempt to call platform API to decode unrecognized formats, and if the
-/// platform API supports decoding the image Flutter will be able to render it.
-/// {@endtemplate}
-
bool _rectIsValid(Rect rect) {
assert(rect != null, 'Rect argument was null.'); // ignore: unnecessary_null_comparison
assert(!rect.hasNaN, 'Rect argument contained a NaN value.');
@@ -69,160 +40,37 @@
Color _scaleAlpha(Color a, double factor) {
return a.withAlpha((a.alpha * factor).round().clamp(0, 255));
}
-
-/// An immutable 32 bit color value in ARGB format.
-///
-/// Consider the light teal of the Flutter logo. It is fully opaque, with a red
-/// channel value of 0x42 (66), a green channel value of 0xA5 (165), and a blue
-/// channel value of 0xF5 (245). In the common "hash syntax" for color values,
-/// it would be described as `#42A5F5`.
-///
-/// Here are some ways it could be constructed:
-///
-/// ```dart
-/// Color c = const Color(0xFF42A5F5);
-/// Color c = const Color.fromARGB(0xFF, 0x42, 0xA5, 0xF5);
-/// Color c = const Color.fromARGB(255, 66, 165, 245);
-/// Color c = const Color.fromRGBO(66, 165, 245, 1.0);
-/// ```
-///
-/// If you are having a problem with `Color` wherein it seems your color is just
-/// not painting, check to make sure you are specifying the full 8 hexadecimal
-/// digits. If you only specify six, then the leading two digits are assumed to
-/// be zero, which means fully-transparent:
-///
-/// ```dart
-/// Color c1 = const Color(0xFFFFFF); // fully transparent white (invisible)
-/// Color c2 = const Color(0xFFFFFFFF); // fully opaque white (visible)
-/// ```
-///
-/// See also:
-///
-/// * [Colors](https://docs.flutter.io/flutter/material/Colors-class.html), which
-/// defines the colors found in the Material Design specification.
class Color {
- /// Construct a color from the lower 32 bits of an [int].
- ///
- /// The bits are interpreted as follows:
- ///
- /// * Bits 24-31 are the alpha value.
- /// * Bits 16-23 are the red value.
- /// * Bits 8-15 are the green value.
- /// * Bits 0-7 are the blue value.
- ///
- /// In other words, if AA is the alpha value in hex, RR the red value in hex,
- /// GG the green value in hex, and BB the blue value in hex, a color can be
- /// expressed as `const Color(0xAARRGGBB)`.
- ///
- /// For example, to get a fully opaque orange, you would use `const
- /// Color(0xFFFF9000)` (`FF` for the alpha, `FF` for the red, `90` for the
- /// green, and `00` for the blue).
- @pragma('vm:entry-point')
- const Color(int value) : value = value & 0xFFFFFFFF;
-
- /// Construct a color from the lower 8 bits of four integers.
- ///
- /// * `a` is the alpha value, with 0 being transparent and 255 being fully
- /// opaque.
- /// * `r` is [red], from 0 to 255.
- /// * `g` is [green], from 0 to 255.
- /// * `b` is [blue], from 0 to 255.
- ///
- /// Out of range values are brought into range using modulo 255.
- ///
- /// See also [fromRGBO], which takes the alpha value as a floating point
- /// value.
+ const Color(int value) : value = value & 0xFFFFFFFF;
const Color.fromARGB(int a, int r, int g, int b) :
value = (((a & 0xff) << 24) |
((r & 0xff) << 16) |
((g & 0xff) << 8) |
((b & 0xff) << 0)) & 0xFFFFFFFF;
-
- /// Create a color from red, green, blue, and opacity, similar to `rgba()` in CSS.
- ///
- /// * `r` is [red], from 0 to 255.
- /// * `g` is [green], from 0 to 255.
- /// * `b` is [blue], from 0 to 255.
- /// * `opacity` is alpha channel of this color as a double, with 0.0 being
- /// transparent and 1.0 being fully opaque.
- ///
- /// Out of range values are brought into range using modulo 255.
- ///
- /// See also [fromARGB], which takes the opacity as an integer value.
const Color.fromRGBO(int r, int g, int b, double opacity) :
value = ((((opacity * 0xff ~/ 1) & 0xff) << 24) |
((r & 0xff) << 16) |
((g & 0xff) << 8) |
((b & 0xff) << 0)) & 0xFFFFFFFF;
-
- /// A 32 bit value representing this color.
- ///
- /// The bits are assigned as follows:
- ///
- /// * Bits 24-31 are the alpha value.
- /// * Bits 16-23 are the red value.
- /// * Bits 8-15 are the green value.
- /// * Bits 0-7 are the blue value.
final int value;
-
- /// The alpha channel of this color in an 8 bit value.
- ///
- /// A value of 0 means this color is fully transparent. A value of 255 means
- /// this color is fully opaque.
int get alpha => (0xff000000 & value) >> 24;
-
- /// The alpha channel of this color as a double.
- ///
- /// A value of 0.0 means this color is fully transparent. A value of 1.0 means
- /// this color is fully opaque.
double get opacity => alpha / 0xFF;
-
- /// The red channel of this color in an 8 bit value.
int get red => (0x00ff0000 & value) >> 16;
-
- /// The green channel of this color in an 8 bit value.
int get green => (0x0000ff00 & value) >> 8;
-
- /// The blue channel of this color in an 8 bit value.
int get blue => (0x000000ff & value) >> 0;
-
- /// Returns a new color that matches this color with the alpha channel
- /// replaced with `a` (which ranges from 0 to 255).
- ///
- /// Out of range values will have unexpected effects.
Color withAlpha(int a) {
return Color.fromARGB(a, red, green, blue);
}
-
- /// Returns a new color that matches this color with the alpha channel
- /// replaced with the given `opacity` (which ranges from 0.0 to 1.0).
- ///
- /// Out of range values will have unexpected effects.
Color withOpacity(double opacity) {
assert(opacity >= 0.0 && opacity <= 1.0);
return withAlpha((255.0 * opacity).round());
}
-
- /// Returns a new color that matches this color with the red channel replaced
- /// with `r` (which ranges from 0 to 255).
- ///
- /// Out of range values will have unexpected effects.
Color withRed(int r) {
return Color.fromARGB(alpha, r, green, blue);
}
-
- /// Returns a new color that matches this color with the green channel
- /// replaced with `g` (which ranges from 0 to 255).
- ///
- /// Out of range values will have unexpected effects.
Color withGreen(int g) {
return Color.fromARGB(alpha, red, g, blue);
}
-
- /// Returns a new color that matches this color with the blue channel replaced
- /// with `b` (which ranges from 0 to 255).
- ///
- /// Out of range values will have unexpected effects.
Color withBlue(int b) {
return Color.fromARGB(alpha, red, green, b);
}
@@ -233,13 +81,6 @@
return component / 12.92;
return math.pow((component + 0.055) / 1.055, 2.4) as double;
}
-
- /// Returns a brightness value between 0 for darkest and 1 for lightest.
- ///
- /// Represents the relative luminance of the color. This value is computationally
- /// expensive to calculate.
- ///
- /// See <https://en.wikipedia.org/wiki/Relative_luminance>.
double computeLuminance() {
// See <https://www.w3.org/TR/WCAG20/#relativeluminancedef>
final double R = _linearizeColorComponent(red / 0xFF);
@@ -247,29 +88,6 @@
final double B = _linearizeColorComponent(blue / 0xFF);
return 0.2126 * R + 0.7152 * G + 0.0722 * B;
}
-
- /// Linearly interpolate between two colors.
- ///
- /// This is intended to be fast but as a result may be ugly. Consider
- /// [HSVColor] or writing custom logic for interpolating colors.
- ///
- /// If either color is null, this function linearly interpolates from a
- /// transparent instance of the other color. This is usually preferable to
- /// interpolating from [material.Colors.transparent] (`const
- /// Color(0x00000000)`), which is specifically transparent _black_.
- ///
- /// The `t` argument represents position on the timeline, with 0.0 meaning
- /// that the interpolation has not started, returning `a` (or something
- /// equivalent to `a`), 1.0 meaning that the interpolation has finished,
- /// returning `b` (or something equivalent to `b`), and values in between
- /// meaning that the interpolation is at the relevant point on the timeline
- /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
- /// 1.0, so negative values and values greater than 1.0 are valid (and can
- /// easily be generated by curves such as [Curves.elasticInOut]). Each channel
- /// will be clamped to the range 0 to 255.
- ///
- /// Values for `t` are usually obtained from an [Animation<double>], such as
- /// an [AnimationController].
static Color? lerp(Color? a, Color? b, double t) {
assert(t != null); // ignore: unnecessary_null_comparison
if (b == null) {
@@ -291,15 +109,6 @@
}
}
}
-
- /// Combine the foreground color as a transparent color over top
- /// of a background color, and return the resulting combined color.
- ///
- /// This uses standard alpha blending ("SRC over DST") rules to produce a
- /// blended color from two colors. This can be used as a performance
- /// enhancement when trying to avoid needless alpha blending compositing
- /// operations for two things that are solid colors with the same shape, but
- /// overlay each other: instead, just paint one with the combined color.
static Color alphaBlend(Color foreground, Color background) {
final int alpha = foreground.alpha;
if (alpha == 0x00) { // Foreground completely transparent.
@@ -326,10 +135,6 @@
);
}
}
-
- /// Returns an alpha value representative of the provided [opacity] value.
- ///
- /// The [opacity] value may not be null.
static int getAlphaFromOpacity(double opacity) {
assert(opacity != null); // ignore: unnecessary_null_comparison
return (opacity.clamp(0.0, 1.0) * 255).round();
@@ -351,706 +156,75 @@
@override
String toString() => 'Color(0x${value.toRadixString(16).padLeft(8, '0')})';
}
-
-/// Algorithms to use when painting on the canvas.
-///
-/// When drawing a shape or image onto a canvas, different algorithms can be
-/// used to blend the pixels. The different values of [BlendMode] specify
-/// different such algorithms.
-///
-/// Each algorithm has two inputs, the _source_, which is the image being drawn,
-/// and the _destination_, which is the image into which the source image is
-/// being composited. The destination is often thought of as the _background_.
-/// The source and destination both have four color channels, the red, green,
-/// blue, and alpha channels. These are typically represented as numbers in the
-/// range 0.0 to 1.0. The output of the algorithm also has these same four
-/// channels, with values computed from the source and destination.
-///
-/// The documentation of each value below describes how the algorithm works. In
-/// each case, an image shows the output of blending a source image with a
-/// destination image. In the images below, the destination is represented by an
-/// image with horizontal lines and an opaque landscape photograph, and the
-/// source is represented by an image with vertical lines (the same lines but
-/// rotated) and a bird clip-art image. The [src] mode shows only the source
-/// image, and the [dst] mode shows only the destination image. In the
-/// documentation below, the transparency is illustrated by a checkerboard
-/// pattern. The [clear] mode drops both the source and destination, resulting
-/// in an output that is entirely transparent (illustrated by a solid
-/// checkerboard pattern).
-///
-/// The horizontal and vertical bars in these images show the red, green, and
-/// blue channels with varying opacity levels, then all three color channels
-/// together with those same varying opacity levels, then all three color
-/// channels set to zero with those varying opacity levels, then two bars showing
-/// a red/green/blue repeating gradient, the first with full opacity and the
-/// second with partial opacity, and finally a bar with the three color channels
-/// set to zero but the opacity varying in a repeating gradient.
-///
-/// ## Application to the [Canvas] API
-///
-/// When using [Canvas.saveLayer] and [Canvas.restore], the blend mode of the
-/// [Paint] given to the [Canvas.saveLayer] will be applied when
-/// [Canvas.restore] is called. Each call to [Canvas.saveLayer] introduces a new
-/// layer onto which shapes and images are painted; when [Canvas.restore] is
-/// called, that layer is then composited onto the parent layer, with the source
-/// being the most-recently-drawn shapes and images, and the destination being
-/// the parent layer. (For the first [Canvas.saveLayer] call, the parent layer
-/// is the canvas itself.)
-///
-/// See also:
-///
-/// * [Paint.blendMode], which uses [BlendMode] to define the compositing
-/// strategy.
enum BlendMode {
// This list comes from Skia's SkXfermode.h and the values (order) should be
// kept in sync.
// See: https://skia.org/user/api/skpaint#SkXfermode
-
- /// Drop both the source and destination images, leaving nothing.
- ///
- /// This corresponds to the "clear" Porter-Duff operator.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_clear.png)
clear,
-
- /// Drop the destination image, only paint the source image.
- ///
- /// Conceptually, the destination is first cleared, then the source image is
- /// painted.
- ///
- /// This corresponds to the "Copy" Porter-Duff operator.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_src.png)
src,
-
- /// Drop the source image, only paint the destination image.
- ///
- /// Conceptually, the source image is discarded, leaving the destination
- /// untouched.
- ///
- /// This corresponds to the "Destination" Porter-Duff operator.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_dst.png)
dst,
-
- /// Composite the source image over the destination image.
- ///
- /// This is the default value. It represents the most intuitive case, where
- /// shapes are painted on top of what is below, with transparent areas showing
- /// the destination layer.
- ///
- /// This corresponds to the "Source over Destination" Porter-Duff operator,
- /// also known as the Painter's Algorithm.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_srcOver.png)
srcOver,
-
- /// Composite the source image under the destination image.
- ///
- /// This is the opposite of [srcOver].
- ///
- /// This corresponds to the "Destination over Source" Porter-Duff operator.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_dstOver.png)
- ///
- /// This is useful when the source image should have been painted before the
- /// destination image, but could not be.
dstOver,
-
- /// Show the source image, but only where the two images overlap. The
- /// destination image is not rendered, it is treated merely as a mask. The
- /// color channels of the destination are ignored, only the opacity has an
- /// effect.
- ///
- /// To show the destination image instead, consider [dstIn].
- ///
- /// To reverse the semantic of the mask (only showing the source where the
- /// destination is absent, rather than where it is present), consider
- /// [srcOut].
- ///
- /// This corresponds to the "Source in Destination" Porter-Duff operator.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_srcIn.png)
srcIn,
-
- /// Show the destination image, but only where the two images overlap. The
- /// source image is not rendered, it is treated merely as a mask. The color
- /// channels of the source are ignored, only the opacity has an effect.
- ///
- /// To show the source image instead, consider [srcIn].
- ///
- /// To reverse the semantic of the mask (only showing the source where the
- /// destination is present, rather than where it is absent), consider [dstOut].
- ///
- /// This corresponds to the "Destination in Source" Porter-Duff operator.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_dstIn.png)
dstIn,
-
- /// Show the source image, but only where the two images do not overlap. The
- /// destination image is not rendered, it is treated merely as a mask. The color
- /// channels of the destination are ignored, only the opacity has an effect.
- ///
- /// To show the destination image instead, consider [dstOut].
- ///
- /// To reverse the semantic of the mask (only showing the source where the
- /// destination is present, rather than where it is absent), consider [srcIn].
- ///
- /// This corresponds to the "Source out Destination" Porter-Duff operator.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_srcOut.png)
srcOut,
-
- /// Show the destination image, but only where the two images do not overlap. The
- /// source image is not rendered, it is treated merely as a mask. The color
- /// channels of the source are ignored, only the opacity has an effect.
- ///
- /// To show the source image instead, consider [srcOut].
- ///
- /// To reverse the semantic of the mask (only showing the destination where the
- /// source is present, rather than where it is absent), consider [dstIn].
- ///
- /// This corresponds to the "Destination out Source" Porter-Duff operator.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_dstOut.png)
dstOut,
-
- /// Composite the source image over the destination image, but only where it
- /// overlaps the destination.
- ///
- /// This corresponds to the "Source atop Destination" Porter-Duff operator.
- ///
- /// This is essentially the [srcOver] operator, but with the output's opacity
- /// channel being set to that of the destination image instead of being a
- /// combination of both image's opacity channels.
- ///
- /// For a variant with the destination on top instead of the source, see
- /// [dstATop].
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_srcATop.png)
srcATop,
-
- /// Composite the destination image over the source image, but only where it
- /// overlaps the source.
- ///
- /// This corresponds to the "Destination atop Source" Porter-Duff operator.
- ///
- /// This is essentially the [dstOver] operator, but with the output's opacity
- /// channel being set to that of the source image instead of being a
- /// combination of both image's opacity channels.
- ///
- /// For a variant with the source on top instead of the destination, see
- /// [srcATop].
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_dstATop.png)
dstATop,
-
- /// Apply a bitwise `xor` operator to the source and destination images. This
- /// leaves transparency where they would overlap.
- ///
- /// This corresponds to the "Source xor Destination" Porter-Duff operator.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_xor.png)
xor,
-
- /// Sum the components of the source and destination images.
- ///
- /// Transparency in a pixel of one of the images reduces the contribution of
- /// that image to the corresponding output pixel, as if the color of that
- /// pixel in that image was darker.
- ///
- /// This corresponds to the "Source plus Destination" Porter-Duff operator.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_plus.png)
plus,
-
- /// Multiply the color components of the source and destination images.
- ///
- /// This can only result in the same or darker colors (multiplying by white,
- /// 1.0, results in no change; multiplying by black, 0.0, results in black).
- ///
- /// When compositing two opaque images, this has similar effect to overlapping
- /// two transparencies on a projector.
- ///
- /// For a variant that also multiplies the alpha channel, consider [multiply].
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_modulate.png)
- ///
- /// See also:
- ///
- /// * [screen], which does a similar computation but inverted.
- /// * [overlay], which combines [modulate] and [screen] to favor the
- /// destination image.
- /// * [hardLight], which combines [modulate] and [screen] to favor the
- /// source image.
modulate,
// Following blend modes are defined in the CSS Compositing standard.
-
- /// Multiply the inverse of the components of the source and destination
- /// images, and inverse the result.
- ///
- /// Inverting the components means that a fully saturated channel (opaque
- /// white) is treated as the value 0.0, and values normally treated as 0.0
- /// (black, transparent) are treated as 1.0.
- ///
- /// This is essentially the same as [modulate] blend mode, but with the values
- /// of the colors inverted before the multiplication and the result being
- /// inverted back before rendering.
- ///
- /// This can only result in the same or lighter colors (multiplying by black,
- /// 1.0, results in no change; multiplying by white, 0.0, results in white).
- /// Similarly, in the alpha channel, it can only result in more opaque colors.
- ///
- /// This has similar effect to two projectors displaying their images on the
- /// same screen simultaneously.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_screen.png)
- ///
- /// See also:
- ///
- /// * [modulate], which does a similar computation but without inverting the
- /// values.
- /// * [overlay], which combines [modulate] and [screen] to favor the
- /// destination image.
- /// * [hardLight], which combines [modulate] and [screen] to favor the
- /// source image.
screen, // The last coeff mode.
-
- /// Multiply the components of the source and destination images after
- /// adjusting them to favor the destination.
- ///
- /// Specifically, if the destination value is smaller, this multiplies it with
- /// the source value, whereas is the source value is smaller, it multiplies
- /// the inverse of the source value with the inverse of the destination value,
- /// then inverts the result.
- ///
- /// Inverting the components means that a fully saturated channel (opaque
- /// white) is treated as the value 0.0, and values normally treated as 0.0
- /// (black, transparent) are treated as 1.0.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_overlay.png)
- ///
- /// See also:
- ///
- /// * [modulate], which always multiplies the values.
- /// * [screen], which always multiplies the inverses of the values.
- /// * [hardLight], which is similar to [overlay] but favors the source image
- /// instead of the destination image.
overlay,
-
- /// Composite the source and destination image by choosing the lowest value
- /// from each color channel.
- ///
- /// The opacity of the output image is computed in the same way as for
- /// [srcOver].
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_darken.png)
darken,
-
- /// Composite the source and destination image by choosing the highest value
- /// from each color channel.
- ///
- /// The opacity of the output image is computed in the same way as for
- /// [srcOver].
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_lighten.png)
lighten,
-
- /// Divide the destination by the inverse of the source.
- ///
- /// Inverting the components means that a fully saturated channel (opaque
- /// white) is treated as the value 0.0, and values normally treated as 0.0
- /// (black, transparent) are treated as 1.0.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_colorDodge.png)
colorDodge,
-
- /// Divide the inverse of the destination by the source, and inverse the result.
- ///
- /// Inverting the components means that a fully saturated channel (opaque
- /// white) is treated as the value 0.0, and values normally treated as 0.0
- /// (black, transparent) are treated as 1.0.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_colorBurn.png)
colorBurn,
-
- /// Multiply the components of the source and destination images after
- /// adjusting them to favor the source.
- ///
- /// Specifically, if the source value is smaller, this multiplies it with the
- /// destination value, whereas is the destination value is smaller, it
- /// multiplies the inverse of the destination value with the inverse of the
- /// source value, then inverts the result.
- ///
- /// Inverting the components means that a fully saturated channel (opaque
- /// white) is treated as the value 0.0, and values normally treated as 0.0
- /// (black, transparent) are treated as 1.0.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_hardLight.png)
- ///
- /// See also:
- ///
- /// * [modulate], which always multiplies the values.
- /// * [screen], which always multiplies the inverses of the values.
- /// * [overlay], which is similar to [hardLight] but favors the destination
- /// image instead of the source image.
hardLight,
-
- /// Use [colorDodge] for source values below 0.5 and [colorBurn] for source
- /// values above 0.5.
- ///
- /// This results in a similar but softer effect than [overlay].
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_softLight.png)
- ///
- /// See also:
- ///
- /// * [color], which is a more subtle tinting effect.
softLight,
-
- /// Subtract the smaller value from the bigger value for each channel.
- ///
- /// Compositing black has no effect; compositing white inverts the colors of
- /// the other image.
- ///
- /// The opacity of the output image is computed in the same way as for
- /// [srcOver].
- ///
- /// The effect is similar to [exclusion] but harsher.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_difference.png)
difference,
-
- /// Subtract double the product of the two images from the sum of the two
- /// images.
- ///
- /// Compositing black has no effect; compositing white inverts the colors of
- /// the other image.
- ///
- /// The opacity of the output image is computed in the same way as for
- /// [srcOver].
- ///
- /// The effect is similar to [difference] but softer.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_exclusion.png)
exclusion,
-
- /// Multiply the components of the source and destination images, including
- /// the alpha channel.
- ///
- /// This can only result in the same or darker colors (multiplying by white,
- /// 1.0, results in no change; multiplying by black, 0.0, results in black).
- ///
- /// Since the alpha channel is also multiplied, a fully-transparent pixel
- /// (opacity 0.0) in one image results in a fully transparent pixel in the
- /// output. This is similar to [dstIn], but with the colors combined.
- ///
- /// For a variant that multiplies the colors but does not multiply the alpha
- /// channel, consider [modulate].
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_multiply.png)
multiply, // The last separable mode.
-
- /// Take the hue of the source image, and the saturation and luminosity of the
- /// destination image.
- ///
- /// The effect is to tint the destination image with the source image.
- ///
- /// The opacity of the output image is computed in the same way as for
- /// [srcOver]. Regions that are entirely transparent in the source image take
- /// their hue from the destination.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_hue.png)
- ///
- /// See also:
- ///
- /// * [color], which is a similar but stronger effect as it also applies the
- /// saturation of the source image.
- /// * [HSVColor], which allows colors to be expressed using Hue rather than
- /// the red/green/blue channels of [Color].
hue,
-
- /// Take the saturation of the source image, and the hue and luminosity of the
- /// destination image.
- ///
- /// The opacity of the output image is computed in the same way as for
- /// [srcOver]. Regions that are entirely transparent in the source image take
- /// their saturation from the destination.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_hue.png)
- ///
- /// See also:
- ///
- /// * [color], which also applies the hue of the source image.
- /// * [luminosity], which applies the luminosity of the source image to the
- /// destination.
saturation,
-
- /// Take the hue and saturation of the source image, and the luminosity of the
- /// destination image.
- ///
- /// The effect is to tint the destination image with the source image.
- ///
- /// The opacity of the output image is computed in the same way as for
- /// [srcOver]. Regions that are entirely transparent in the source image take
- /// their hue and saturation from the destination.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_color.png)
- ///
- /// See also:
- ///
- /// * [hue], which is a similar but weaker effect.
- /// * [softLight], which is a similar tinting effect but also tints white.
- /// * [saturation], which only applies the saturation of the source image.
color,
-
- /// Take the luminosity of the source image, and the hue and saturation of the
- /// destination image.
- ///
- /// The opacity of the output image is computed in the same way as for
- /// [srcOver]. Regions that are entirely transparent in the source image take
- /// their luminosity from the destination.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_luminosity.png)
- ///
- /// See also:
- ///
- /// * [saturation], which applies the saturation of the source image to the
- /// destination.
- /// * [ImageFilter.blur], which can be used with [BackdropFilter] for a
- /// related effect.
luminosity,
}
-
-/// Quality levels for image filters.
-///
-/// See [Paint.filterQuality].
enum FilterQuality {
// This list comes from Skia's SkFilterQuality.h and the values (order) should
// be kept in sync.
-
- /// Fastest possible filtering, albeit also the lowest quality.
- ///
- /// Typically this implies nearest-neighbor filtering.
none,
-
- /// Better quality than [none], faster than [medium].
- ///
- /// Typically this implies bilinear interpolation.
low,
-
- /// Better quality than [low], faster than [high].
- ///
- /// Typically this implies a combination of bilinear interpolation and
- /// pyramidal parametric pre-filtering (mipmaps).
medium,
-
- /// Best possible quality filtering, albeit also the slowest.
- ///
- /// Typically this implies bicubic interpolation or better.
high,
}
-
-/// Styles to use for line endings.
-///
-/// See also:
-///
-/// * [Paint.strokeCap] for how this value is used.
-/// * [StrokeJoin] for the different kinds of line segment joins.
// These enum values must be kept in sync with SkPaint::Cap.
enum StrokeCap {
- /// Begin and end contours with a flat edge and no extension.
- ///
- /// ![A butt cap ends line segments with a square end that stops at the end of
- /// the line segment.](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/butt_cap.png)
- ///
- /// Compare to the [square] cap, which has the same shape, but extends past
- /// the end of the line by half a stroke width.
butt,
-
- /// Begin and end contours with a semi-circle extension.
- ///
- /// ![A round cap adds a rounded end to the line segment that protrudes
- /// by one half of the thickness of the line (which is the radius of the cap)
- /// past the end of the segment.](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/round_cap.png)
- ///
- /// The cap is colored in the diagram above to highlight it: in normal use it
- /// is the same color as the line.
round,
-
- /// Begin and end contours with a half square extension. This is
- /// similar to extending each contour by half the stroke width (as
- /// given by [Paint.strokeWidth]).
- ///
- /// ![A square cap has a square end that effectively extends the line length
- /// by half of the stroke width.](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/square_cap.png)
- ///
- /// The cap is colored in the diagram above to highlight it: in normal use it
- /// is the same color as the line.
- ///
- /// Compare to the [butt] cap, which has the same shape, but doesn't extend
- /// past the end of the line.
square,
}
-
-/// Styles to use for line segment joins.
-///
-/// This only affects line joins for polygons drawn by [Canvas.drawPath] and
-/// rectangles, not points drawn as lines with [Canvas.drawPoints].
-///
-/// See also:
-///
-/// * [Paint.strokeJoin] and [Paint.strokeMiterLimit] for how this value is
-/// used.
-/// * [StrokeCap] for the different kinds of line endings.
// These enum values must be kept in sync with SkPaint::Join.
enum StrokeJoin {
- /// Joins between line segments form sharp corners.
- ///
- /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_4_join.mp4}
- ///
- /// The center of the line segment is colored in the diagram above to
- /// highlight the join, but in normal usage the join is the same color as the
- /// line.
- ///
- /// See also:
- ///
- /// * [Paint.strokeJoin], used to set the line segment join style to this
- /// value.
- /// * [Paint.strokeMiterLimit], used to define when a miter is drawn instead
- /// of a bevel when the join is set to this value.
miter,
-
- /// Joins between line segments are semi-circular.
- ///
- /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/round_join.mp4}
- ///
- /// The center of the line segment is colored in the diagram above to
- /// highlight the join, but in normal usage the join is the same color as the
- /// line.
- ///
- /// See also:
- ///
- /// * [Paint.strokeJoin], used to set the line segment join style to this
- /// value.
round,
-
- /// Joins between line segments connect the corners of the butt ends of the
- /// line segments to give a beveled appearance.
- ///
- /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/bevel_join.mp4}
- ///
- /// The center of the line segment is colored in the diagram above to
- /// highlight the join, but in normal usage the join is the same color as the
- /// line.
- ///
- /// See also:
- ///
- /// * [Paint.strokeJoin], used to set the line segment join style to this
- /// value.
bevel,
}
-
-/// Strategies for painting shapes and paths on a canvas.
-///
-/// See [Paint.style].
// These enum values must be kept in sync with SkPaint::Style.
enum PaintingStyle {
// This list comes from Skia's SkPaint.h and the values (order) should be kept
// in sync.
-
- /// Apply the [Paint] to the inside of the shape. For example, when
- /// applied to the [Canvas.drawCircle] call, this results in a disc
- /// of the given size being painted.
fill,
-
- /// Apply the [Paint] to the edge of the shape. For example, when
- /// applied to the [Canvas.drawCircle] call, this results is a hoop
- /// of the given size being painted. The line drawn on the edge will
- /// be the width given by the [Paint.strokeWidth] property.
stroke,
}
-
-
-/// Different ways to clip a widget's content.
enum Clip {
- /// No clip at all.
- ///
- /// This is the default option for most widgets: if the content does not
- /// overflow the widget boundary, don't pay any performance cost for clipping.
- ///
- /// If the content does overflow, please explicitly specify the following
- /// [Clip] options:
- /// * [hardEdge], which is the fastest clipping, but with lower fidelity.
- /// * [antiAlias], which is a little slower than [hardEdge], but with smoothed edges.
- /// * [antiAliasWithSaveLayer], which is much slower than [antiAlias], and should
- /// rarely be used.
none,
-
- /// Clip, but do not apply anti-aliasing.
- ///
- /// This mode enables clipping, but curves and non-axis-aligned straight lines will be
- /// jagged as no effort is made to anti-alias.
- ///
- /// Faster than other clipping modes, but slower than [none].
- ///
- /// This is a reasonable choice when clipping is needed, if the container is an axis-
- /// aligned rectangle or an axis-aligned rounded rectangle with very small corner radii.
- ///
- /// See also:
- ///
- /// * [antiAlias], which is more reasonable when clipping is needed and the shape is not
- /// an axis-aligned rectangle.
hardEdge,
-
- /// Clip with anti-aliasing.
- ///
- /// This mode has anti-aliased clipping edges to achieve a smoother look.
- ///
- /// It' s much faster than [antiAliasWithSaveLayer], but slower than [hardEdge].
- ///
- /// This will be the common case when dealing with circles and arcs.
- ///
- /// Different from [hardEdge] and [antiAliasWithSaveLayer], this clipping may have
- /// bleeding edge artifacts.
- /// (See https://fiddle.skia.org/c/21cb4c2b2515996b537f36e7819288ae for an example.)
- ///
- /// See also:
- ///
- /// * [hardEdge], which is a little faster, but with lower fidelity.
- /// * [antiAliasWithSaveLayer], which is much slower, but can avoid the
- /// bleeding edges if there's no other way.
- /// * [Paint.isAntiAlias], which is the anti-aliasing switch for general draw operations.
antiAlias,
-
- /// Clip with anti-aliasing and saveLayer immediately following the clip.
- ///
- /// This mode not only clips with anti-aliasing, but also allocates an offscreen
- /// buffer. All subsequent paints are carried out on that buffer before finally
- /// being clipped and composited back.
- ///
- /// This is very slow. It has no bleeding edge artifacts (that [antiAlias] has)
- /// but it changes the semantics as an offscreen buffer is now introduced.
- /// (See https://github.com/flutter/flutter/issues/18057#issuecomment-394197336
- /// for a difference between paint without saveLayer and paint with saveLayer.)
- ///
- /// This will be only rarely needed. One case where you might need this is if
- /// you have an image overlaid on a very different background color. In these
- /// cases, consider whether you can avoid overlaying multiple colors in one
- /// spot (e.g. by having the background color only present where the image is
- /// absent). If you can, [antiAlias] would be fine and much faster.
- ///
- /// See also:
- ///
- /// * [antiAlias], which is much faster, and has similar clipping results.
antiAliasWithSaveLayer,
}
-
-/// A description of the style to use when drawing on a [Canvas].
-///
-/// Most APIs on [Canvas] take a [Paint] object to describe the style
-/// to use for that operation.
class Paint {
// Paint objects are encoded in two buffers:
//
@@ -1110,19 +284,11 @@
static const int _kColorFilterIndex = 1;
static const int _kImageFilterIndex = 2;
static const int _kObjectCount = 3; // Must be one larger than the largest index.
-
- /// Constructs an empty [Paint] object with all fields initialized to
- /// their defaults.
Paint() {
if (enableDithering) {
_dither = true;
}
}
-
- /// Whether to apply anti-aliasing to lines and images drawn on the
- /// canvas.
- ///
- /// Defaults to true.
bool get isAntiAlias {
return _data.getInt32(_kIsAntiAliasOffset, _kFakeHostEndian) == 0;
}
@@ -1135,19 +301,6 @@
// Must be kept in sync with the default in paint.cc.
static const int _kColorDefault = 0xFF000000;
-
- /// The color to use when stroking or filling a shape.
- ///
- /// Defaults to opaque black.
- ///
- /// See also:
- ///
- /// * [style], which controls whether to stroke or fill (or both).
- /// * [colorFilter], which overrides [color].
- /// * [shader], which overrides [color] with more elaborate effects.
- ///
- /// This color is not used when compositing. To colorize a layer, use
- /// [colorFilter].
Color get color {
final int encoded = _data.getInt32(_kColorOffset, _kFakeHostEndian);
return Color(encoded ^ _kColorDefault);
@@ -1160,25 +313,6 @@
// Must be kept in sync with the default in paint.cc.
static final int _kBlendModeDefault = BlendMode.srcOver.index;
-
- /// A blend mode to apply when a shape is drawn or a layer is composited.
- ///
- /// The source colors are from the shape being drawn (e.g. from
- /// [Canvas.drawPath]) or layer being composited (the graphics that were drawn
- /// between the [Canvas.saveLayer] and [Canvas.restore] calls), after applying
- /// the [colorFilter], if any.
- ///
- /// The destination colors are from the background onto which the shape or
- /// layer is being composited.
- ///
- /// Defaults to [BlendMode.srcOver].
- ///
- /// See also:
- ///
- /// * [Canvas.saveLayer], which uses its [Paint]'s [blendMode] to composite
- /// the layer when [Canvas.restore] is called.
- /// * [BlendMode], which discusses the user of [Canvas.saveLayer] with
- /// [blendMode].
BlendMode get blendMode {
final int encoded = _data.getInt32(_kBlendModeOffset, _kFakeHostEndian);
return BlendMode.values[encoded ^ _kBlendModeDefault];
@@ -1188,10 +322,6 @@
final int encoded = value.index ^ _kBlendModeDefault;
_data.setInt32(_kBlendModeOffset, encoded, _kFakeHostEndian);
}
-
- /// Whether to paint inside shapes, the edges of shapes, or both.
- ///
- /// Defaults to [PaintingStyle.fill].
PaintingStyle get style {
return PaintingStyle.values[_data.getInt32(_kStyleOffset, _kFakeHostEndian)];
}
@@ -1200,12 +330,6 @@
final int encoded = value.index;
_data.setInt32(_kStyleOffset, encoded, _kFakeHostEndian);
}
-
- /// How wide to make edges drawn when [style] is set to
- /// [PaintingStyle.stroke]. The width is given in logical pixels measured in
- /// the direction orthogonal to the direction of the path.
- ///
- /// Defaults to 0.0, which correspond to a hairline width.
double get strokeWidth {
return _data.getFloat32(_kStrokeWidthOffset, _kFakeHostEndian);
}
@@ -1214,11 +338,6 @@
final double encoded = value;
_data.setFloat32(_kStrokeWidthOffset, encoded, _kFakeHostEndian);
}
-
- /// The kind of finish to place on the end of lines drawn when
- /// [style] is set to [PaintingStyle.stroke].
- ///
- /// Defaults to [StrokeCap.butt], i.e. no caps.
StrokeCap get strokeCap {
return StrokeCap.values[_data.getInt32(_kStrokeCapOffset, _kFakeHostEndian)];
}
@@ -1227,32 +346,6 @@
final int encoded = value.index;
_data.setInt32(_kStrokeCapOffset, encoded, _kFakeHostEndian);
}
-
- /// The kind of finish to place on the joins between segments.
- ///
- /// This applies to paths drawn when [style] is set to [PaintingStyle.stroke],
- /// It does not apply to points drawn as lines with [Canvas.drawPoints].
- ///
- /// Defaults to [StrokeJoin.miter], i.e. sharp corners.
- ///
- /// Some examples of joins:
- ///
- /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_4_join.mp4}
- ///
- /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/round_join.mp4}
- ///
- /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/bevel_join.mp4}
- ///
- /// The centers of the line segments are colored in the diagrams above to
- /// highlight the joins, but in normal usage the join is the same color as the
- /// line.
- ///
- /// See also:
- ///
- /// * [strokeMiterLimit] to control when miters are replaced by bevels when
- /// this is set to [StrokeJoin.miter].
- /// * [strokeCap] to control what is drawn at the ends of the stroke.
- /// * [StrokeJoin] for the definitive list of stroke joins.
StrokeJoin get strokeJoin {
return StrokeJoin.values[_data.getInt32(_kStrokeJoinOffset, _kFakeHostEndian)];
}
@@ -1264,33 +357,6 @@
// Must be kept in sync with the default in paint.cc.
static const double _kStrokeMiterLimitDefault = 4.0;
-
- /// The limit for miters to be drawn on segments when the join is set to
- /// [StrokeJoin.miter] and the [style] is set to [PaintingStyle.stroke]. If
- /// this limit is exceeded, then a [StrokeJoin.bevel] join will be drawn
- /// instead. This may cause some 'popping' of the corners of a path if the
- /// angle between line segments is animated, as seen in the diagrams below.
- ///
- /// This limit is expressed as a limit on the length of the miter.
- ///
- /// Defaults to 4.0. Using zero as a limit will cause a [StrokeJoin.bevel]
- /// join to be used all the time.
- ///
- /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_0_join.mp4}
- ///
- /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_4_join.mp4}
- ///
- /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_6_join.mp4}
- ///
- /// The centers of the line segments are colored in the diagrams above to
- /// highlight the joins, but in normal usage the join is the same color as the
- /// line.
- ///
- /// See also:
- ///
- /// * [strokeJoin] to control the kind of finish to place on the joins
- /// between segments.
- /// * [strokeCap] to control what is drawn at the ends of the stroke.
double get strokeMiterLimit {
return _data.getFloat32(_kStrokeMiterLimitOffset, _kFakeHostEndian);
}
@@ -1299,11 +365,6 @@
final double encoded = value - _kStrokeMiterLimitDefault;
_data.setFloat32(_kStrokeMiterLimitOffset, encoded, _kFakeHostEndian);
}
-
- /// A mask filter (for example, a blur) to apply to a shape after it has been
- /// drawn but before it has been composited into the image.
- ///
- /// See [MaskFilter] for details.
MaskFilter? get maskFilter {
switch (_data.getInt32(_kMaskFilterOffset, _kFakeHostEndian)) {
case MaskFilter._TypeNone:
@@ -1329,12 +390,6 @@
_data.setFloat32(_kMaskFilterSigmaOffset, value._sigma, _kFakeHostEndian);
}
}
-
- /// Controls the performance vs quality trade-off to use when applying
- /// filters, such as [maskFilter], or when drawing images, as with
- /// [Canvas.drawImageRect] or [Canvas.drawImageNine].
- ///
- /// Defaults to [FilterQuality.none].
// TODO(ianh): verify that the image drawing methods actually respect this
FilterQuality get filterQuality {
return FilterQuality.values[_data.getInt32(_kFilterQualityOffset, _kFakeHostEndian)];
@@ -1344,30 +399,12 @@
final int encoded = value.index;
_data.setInt32(_kFilterQualityOffset, encoded, _kFakeHostEndian);
}
-
- /// The shader to use when stroking or filling a shape.
- ///
- /// When this is null, the [color] is used instead.
- ///
- /// See also:
- ///
- /// * [Gradient], a shader that paints a color gradient.
- /// * [ImageShader], a shader that tiles an [Image].
- /// * [colorFilter], which overrides [shader].
- /// * [color], which is used if [shader] and [colorFilter] are null.
Shader? get shader {
return _objects?[_kShaderIndex] as Shader?;
}
set shader(Shader? value) {
_ensureObjectsInitialized()[_kShaderIndex] = value;
}
-
- /// A color filter to apply when a shape is drawn or when a layer is
- /// composited.
- ///
- /// See [ColorFilter] for details.
- ///
- /// When a shape is being drawn, [colorFilter] overrides [color] and [shader].
ColorFilter? get colorFilter {
return _objects?[_kColorFilterIndex]?.creator as ColorFilter?;
}
@@ -1382,29 +419,6 @@
_ensureObjectsInitialized()[_kColorFilterIndex] = nativeFilter;
}
}
-
- /// The [ImageFilter] to use when drawing raster images.
- ///
- /// For example, to blur an image using [Canvas.drawImage], apply an
- /// [ImageFilter.blur]:
- ///
- /// ```dart
- /// import 'dart:ui' as ui;
- ///
- /// ui.Image image;
- ///
- /// void paint(Canvas canvas, Size size) {
- /// canvas.drawImage(
- /// image,
- /// Offset.zero,
- /// Paint()..imageFilter = ui.ImageFilter.blur(sigmaX: .5, sigmaY: .5),
- /// );
- /// }
- /// ```
- ///
- /// See also:
- ///
- /// * [MaskFilter], which is used for drawing geometry.
ImageFilter? get imageFilter {
return _objects?[_kImageFilterIndex]?.creator as ImageFilter?;
}
@@ -1421,12 +435,6 @@
}
}
}
-
- /// Whether the colors of the image are inverted when drawn.
- ///
- /// Inverting the colors of an image applies a new color filter that will
- /// be composed with any user provided color filters. This is primarily
- /// used for implementing smart invert on iOS.
bool get invertColors {
return _data.getInt32(_kInvertColorOffset, _kFakeHostEndian) == 1;
}
@@ -1440,22 +448,6 @@
set _dither(bool value) {
_data.setInt32(_kDitherOffset, value ? 1 : 0, _kFakeHostEndian);
}
-
- /// Whether to dither the output when drawing images.
- ///
- /// If false, the default value, dithering will be enabled when the input
- /// color depth is higher than the output color depth. For example,
- /// drawing an RGB8 image onto an RGB565 canvas.
- ///
- /// This value also controls dithering of [shader]s, which can make
- /// gradients appear smoother.
- ///
- /// Whether or not dithering affects the output is implementation defined.
- /// Some implementations may choose to ignore this completely, if they're
- /// unable to control dithering.
- ///
- /// To ensure that dithering is consistently enabled for your entire
- /// application, set this to true before invoking any drawing related code.
static bool enableDithering = false;
@override
@@ -1522,76 +514,15 @@
return result.toString();
}
}
-
-/// The format in which image bytes should be returned when using
-/// [Image.toByteData].
enum ImageByteFormat {
- /// Raw RGBA format.
- ///
- /// Unencoded bytes, in RGBA row-primary form, 8 bits per channel.
rawRgba,
-
- /// Raw unmodified format.
- ///
- /// Unencoded bytes, in the image's existing format. For example, a grayscale
- /// image may use a single 8-bit channel for each pixel.
rawUnmodified,
-
- /// PNG format.
- ///
- /// A loss-less compression format for images. This format is well suited for
- /// images with hard edges, such as screenshots or sprites, and images with
- /// text. Transparency is supported. The PNG format supports images up to
- /// 2,147,483,647 pixels in either dimension, though in practice available
- /// memory provides a more immediate limitation on maximum image size.
- ///
- /// PNG images normally use the `.png` file extension and the `image/png` MIME
- /// type.
- ///
- /// See also:
- ///
- /// * <https://en.wikipedia.org/wiki/Portable_Network_Graphics>, the Wikipedia page on PNG.
- /// * <https://tools.ietf.org/rfc/rfc2083.txt>, the PNG standard.
png,
}
-
-/// The format of pixel data given to [decodeImageFromPixels].
enum PixelFormat {
- /// Each pixel is 32 bits, with the highest 8 bits encoding red, the next 8
- /// bits encoding green, the next 8 bits encoding blue, and the lowest 8 bits
- /// encoding alpha.
rgba8888,
-
- /// Each pixel is 32 bits, with the highest 8 bits encoding blue, the next 8
- /// bits encoding green, the next 8 bits encoding red, and the lowest 8 bits
- /// encoding alpha.
bgra8888,
}
-
-/// Opaque handle to raw decoded image data (pixels).
-///
-/// To obtain an [Image] object, use the [ImageDescriptor] API.
-///
-/// To draw an [Image], use one of the methods on the [Canvas] class, such as
-/// [Canvas.drawImage].
-///
-/// A class or method that receives an image object must call [dispose] on the
-/// handle when it is no longer needed. To create a shareable reference to the
-/// underlying image, call [clone]. The method or object that recieves
-/// the new instance will then be responsible for disposing it, and the
-/// underlying image itself will be disposed when all outstanding handles are
-/// disposed.
-///
-/// If `dart:ui` passes an `Image` object and the recipient wishes to share
-/// that handle with other callers, [clone] must be called _before_ [dispose].
-/// A handle that has been disposed cannot create new handles anymore.
-///
-/// See also:
-///
-/// * [Image](https://api.flutter.dev/flutter/widgets/Image-class.html), the class in the [widgets] library.
-/// * [ImageDescriptor], which allows reading information about the image and
-/// creating a codec to decode it.
-/// * [instantiateImageCodec], a utility method that wraps [ImageDescriptor].
class Image {
Image._(this._image) {
assert(() {
@@ -1602,34 +533,19 @@
}
// C++ unit tests access this.
- @pragma('vm:entry-point')
- final _Image _image;
+ final _Image _image;
StackTrace? _debugStack;
-
- /// The number of image pixels along the image's horizontal axis.
int get width {
assert(!_disposed && !_image._disposed);
return _image.width;
}
-
- /// The number of image pixels along the image's vertical axis.
int get height {
assert(!_disposed && !_image._disposed);
return _image.height;
}
bool _disposed = false;
- /// Release this handle's claim on the underlying Image. This handle is no
- /// longer usable after this method is called.
- ///
- /// Once all outstanding handles have been disposed, the underlying image will
- /// be disposed as well.
- ///
- /// In debug mode, [debugGetOpenHandleStackTraces] will return a list of
- /// [StackTrace] objects from all open handles' creation points. This is
- /// useful when trying to determine what parts of the program are keeping an
- /// image resident in memory.
void dispose() {
assert(!_disposed && !_image._disposed);
assert(_image._handles.contains(this));
@@ -1640,11 +556,6 @@
_image.dispose();
}
}
-
- /// Whether this reference to the underlying image is [dispose]d.
- ///
- /// This only returns a valid value if asserts are enabled, and must not be
- /// used otherwise.
bool get debugDisposed {
bool? disposed;
assert(() {
@@ -1653,23 +564,10 @@
}());
return disposed ?? (throw StateError('Image.debugDisposed is only available when asserts are enabled.'));
}
-
- /// Converts the [Image] object into a byte array.
- ///
- /// The [format] argument specifies the format in which the bytes will be
- /// returned.
- ///
- /// Returns a future that completes with the binary image data or an error
- /// if encoding fails.
Future<ByteData?> toByteData({ImageByteFormat format = ImageByteFormat.rawRgba}) {
assert(!_disposed && !_image._disposed);
return _image.toByteData(format: format);
}
-
- /// If asserts are enabled, returns the [StackTrace]s of each open handle from
- /// [clone], in creation order.
- ///
- /// If asserts are disabled, this method always returns null.
List<StackTrace>? debugGetOpenHandleStackTraces() {
List<StackTrace>? stacks;
assert(() {
@@ -1678,81 +576,6 @@
}());
return stacks;
}
-
- /// Creates a disposable handle to this image.
- ///
- /// Holders of an [Image] must dispose of the image when they no longer need
- /// to access it or draw it. However, once the underlying image is disposed,
- /// it is no longer possible to use it. If a holder of an image needs to share
- /// access to that image with another object or method, [clone] creates a
- /// duplicate handle. The underlying image will only be disposed once all
- /// outstanding handles are disposed. This allows for safe sharing of image
- /// references while still disposing of the underlying resources when all
- /// consumers are finished.
- ///
- /// It is safe to pass an [Image] handle to another object or method if the
- /// current holder no longer needs it.
- ///
- /// To check whether two [Image] references are refering to the same
- /// underlying image memory, use [isCloneOf] rather than the equality operator
- /// or [identical].
- ///
- /// The following example demonstrates valid usage.
- ///
- /// ```dart
- /// import 'dart:async';
- ///
- /// Future<Image> _loadImage(int width, int height) {
- /// final Completer<Image> completer = Completer<Image>();
- /// decodeImageFromPixels(
- /// Uint8List.fromList(List<int>.filled(width * height * 4, 0xFF)),
- /// width,
- /// height,
- /// PixelFormat.rgba8888,
- /// // Don't worry about disposing or cloning this image - responsibility
- /// // is transferred to the caller, and that is safe since this method
- /// // will not touch it again.
- /// (Image image) => completer.complete(image),
- /// );
- /// return completer.future;
- /// }
- ///
- /// Future<void> main() async {
- /// final Image image = await _loadImage(5, 5);
- /// // Make sure to clone the image, because MyHolder might dispose it
- /// // and we need to access it again.
- /// final MyImageHolder holder = MyImageHolder(image.clone());
- /// final MyImageHolder holder2 = MyImageHolder(image.clone());
- /// // Now we dispose it because we won't need it again.
- /// image.dispose();
- ///
- /// final PictureRecorder recorder = PictureRecorder();
- /// final Canvas canvas = Canvas(recorder);
- ///
- /// holder.draw(canvas);
- /// holder.dispose();
- ///
- /// canvas.translate(50, 50);
- /// holder2.draw(canvas);
- /// holder2.dispose();
- /// }
- ///
- /// class MyImageHolder {
- /// MyImageLoader(this.image);
- ///
- /// final Image image;
- ///
- /// void draw(Canvas canvas) {
- /// canvas.drawImage(image, Offset.zero, Paint());
- /// }
- ///
- /// void dispose() => image.dispose();
- /// }
- /// ```
- ///
- /// The returned object behaves identically to this image. Calling
- /// [dispose] on it will only dispose the underlying native resources if it
- /// is the last remaining handle.
Image clone() {
if (_disposed) {
throw StateError(
@@ -1765,35 +588,23 @@
assert(!_image._disposed);
return Image._(_image);
}
-
- /// Returns true if `other` is a [clone] of this and thus shares the same
- /// underlying image memory, even if this or `other` is [dispose]d.
- ///
- /// This method may return false for two images that were decoded from the
- /// same underlying asset, if they are not sharing the same memory. For
- /// example, if the same file is decoded using [instantiateImageCodec] twice,
- /// or the same bytes are decoded using [decodeImageFromPixels] twice, there
- /// will be two distinct [Image]s that render the same but do not share
- /// underlying memory, and so will not be treated as clones of each other.
bool isCloneOf(Image other) => other._image == _image;
@override
String toString() => _image.toString();
}
-@pragma('vm:entry-point')
-class _Image extends NativeFieldWrapperClass2 {
+class _Image {
// This class is created by the engine, and should not be instantiated
// or extended directly.
//
// _Images are always handed out wrapped in [Image]s. To create an [Image],
// use the ImageDescriptor API.
- @pragma('vm:entry-point')
- _Image._();
+ _Image._(this.width, this.height);
- int get width { throw UnimplementedError(); }
+ final int width;
- int get height { throw UnimplementedError(); }
+ final int height;
Future<ByteData?> toByteData({ImageByteFormat format = ImageByteFormat.rawRgba}) {
return _futurize((_Callback<ByteData> callback) {
@@ -1802,8 +613,6 @@
});
});
}
-
- /// Returns an error message on failure, null on success.
String? _toByteData(int format, _Callback<Uint8List?> callback) { throw UnimplementedError(); }
bool _disposed = false;
@@ -1827,114 +636,28 @@
@override
String toString() => '[$width\u00D7$height]';
}
-
-/// Callback signature for [decodeImageFromList].
typedef ImageDecoderCallback = void Function(Image result);
-
-/// Information for a single frame of an animation.
-///
-/// To obtain an instance of the [FrameInfo] interface, see
-/// [Codec.getNextFrame].
-///
-/// The recipient of an instance of this class is responsible for calling
-/// [Image.dispose] on [image]. To share the image with other interested
-/// parties, use [Image.clone]. If the [FrameInfo] object itself is passed to
-/// another method or object, that method or object must assume it is
-/// responsible for disposing the image when done, and the passer must not
-/// access the [image] after that point.
-///
-/// For example, the following code sample is incorrect:
-///
-/// ```dart
-/// /// BAD
-/// Future<void> nextFrameRoutine(Codec codec) async {
-/// final FrameInfo frameInfo = await codec.getNextFrame();
-/// _cacheImage(frameInfo);
-/// // ERROR - _cacheImage is now responsible for disposing the image, and
-/// // the image may not be available any more for this drawing routine.
-/// _drawImage(frameInfo);
-/// // ERROR again - the previous methods might or might not have created
-/// // handles to the image.
-/// frameInfo.image.dispose();
-/// }
-/// ```
-///
-/// Correct usage is:
-///
-/// ```dart
-/// /// GOOD
-/// Future<void> nextFrameRoutine(Codec codec) async {
-/// final FrameInfo frameInfo = await codec.getNextFrame();
-/// _cacheImage(frameInfo.image.clone(), frameInfo.duration);
-/// _drawImage(frameInfo.image.clone(), frameInfo.duration);
-/// // This method is done with its handle, and has passed handles to its
-/// // clients already.
-/// // The image will live until those clients dispose of their handles, and
-/// // this one must not be disposed since it will not be used again.
-/// frameInfo.image.dispose();
-/// }
-/// ```
class FrameInfo {
- /// This class is created by the engine, and should not be instantiated
- /// or extended directly.
- ///
- /// To obtain an instance of the [FrameInfo] interface, see
- /// [Codec.getNextFrame].
FrameInfo._({required this.duration, required this.image});
-
- /// The duration this frame should be shown.
- ///
- /// A zero duration indicates that the frame should be shown indefinitely.
final Duration duration;
-
-
- /// The [Image] object for this frame.
- ///
- /// This object must be disposed by the recipient of this frame info.
- ///
- /// To share this image with other interested parties, use [Image.clone].
final Image image;
}
-
-/// A handle to an image codec.
-///
-/// This class is created by the engine, and should not be instantiated
-/// or extended directly.
-///
-/// To obtain an instance of the [Codec] interface, see
-/// [instantiateImageCodec].
-@pragma('vm:entry-point')
-class Codec extends NativeFieldWrapperClass2 {
+class Codec {
//
// This class is created by the engine, and should not be instantiated
// or extended directly.
//
// To obtain an instance of the [Codec] interface, see
// [instantiateImageCodec].
- @pragma('vm:entry-point')
- Codec._();
+ Codec._();
int? _cachedFrameCount;
- /// Number of frames in this image.
int get frameCount => _cachedFrameCount ??= _frameCount;
int get _frameCount { throw UnimplementedError(); }
int? _cachedRepetitionCount;
- /// Number of times to repeat the animation.
- ///
- /// * 0 when the animation should be played once.
- /// * -1 for infinity repetitions.
int get repetitionCount => _cachedRepetitionCount ??= _repetitionCount;
int get _repetitionCount { throw UnimplementedError(); }
-
- /// Fetches the next animation frame.
- ///
- /// Wraps back to the first frame after returning the last frame.
- ///
- /// The returned future can complete with an error if the decoding has failed.
- ///
- /// The caller of this method is responsible for disposing the
- /// [FrameInfo.image] on the returned object.
Future<FrameInfo> getNextFrame() async {
final Completer<FrameInfo> completer = Completer<FrameInfo>.sync();
final String? error = _getNextFrame((_Image? image, int durationMilliseconds) {
@@ -1951,43 +674,9 @@
}
return await completer.future;
}
-
- /// Returns an error message on failure, null on success.
String? _getNextFrame(void Function(_Image?, int) callback) { throw UnimplementedError(); }
-
- /// Release the resources used by this object. The object is no longer usable
- /// after this method is called.
void dispose() { throw UnimplementedError(); }
}
-
-/// Instantiates an image [Codec].
-///
-/// This method is a convenience wrapper around the [ImageDescriptor] API, and
-/// using [ImageDescriptor] directly is preferred since it allows the caller to
-/// make better determinations about how and whether to use the `targetWidth`
-/// and `targetHeight` parameters.
-///
-/// The `list` parameter is the binary image data (e.g a PNG or GIF binary data).
-/// The data can be for either static or animated images. The following image
-/// formats are supported: {@macro dart.ui.imageFormats}
-///
-/// The `targetWidth` and `targetHeight` arguments specify the size of the
-/// output image, in image pixels. If they are not equal to the intrinsic
-/// dimensions of the image, then the image will be scaled after being decoded.
-/// If the `allowUpscaling` parameter is not set to true, both dimensions will
-/// be capped at the intrinsic dimensions of the image, even if only one of
-/// them would have exceeded those intrinsic dimensions. If exactly one of these
-/// two arguments is specified, then the aspect ratio will be maintained while
-/// forcing the image to match the other given dimension. If neither is
-/// specified, then the image maintains its intrinsic size.
-///
-/// Scaling the image to larger than its intrinsic size should usually be
-/// avoided, since it causes the image to use more memory than necessary.
-/// Instead, prefer scaling the [Canvas] transform. If the image must be scaled
-/// up, the `allowUpscaling` parameter must be set to true.
-///
-/// The returned future can complete with an error if the image decoding has
-/// failed.
Future<Codec> instantiateImageCodec(
Uint8List list, {
int? targetWidth,
@@ -2009,12 +698,6 @@
targetHeight: targetHeight,
);
}
-
-/// Loads a single image frame from a byte array into an [Image] object.
-///
-/// This is a convenience wrapper around [instantiateImageCodec]. Prefer using
-/// [instantiateImageCodec] which also supports multi frame images and offers
-/// better error handling. This function swallows asynchronous errors.
void decodeImageFromList(Uint8List list, ImageDecoderCallback callback) {
_decodeImageFromListAsync(list, callback);
}
@@ -2025,30 +708,6 @@
final FrameInfo frameInfo = await codec.getNextFrame();
callback(frameInfo.image);
}
-
-/// Convert an array of pixel values into an [Image] object.
-///
-/// The `pixels` parameter is the pixel data in the encoding described by
-/// `format`.
-///
-/// The `rowBytes` parameter is the number of bytes consumed by each row of
-/// pixels in the data buffer. If unspecified, it defaults to `width` multiplied
-/// by the number of bytes per pixel in the provided `format`.
-///
-/// The `targetWidth` and `targetHeight` arguments specify the size of the
-/// output image, in image pixels. If they are not equal to the intrinsic
-/// dimensions of the image, then the image will be scaled after being decoded.
-/// If the `allowUpscaling` parameter is not set to true, both dimensions will
-/// be capped at the intrinsic dimensions of the image, even if only one of
-/// them would have exceeded those intrinsic dimensions. If exactly one of these
-/// two arguments is specified, then the aspect ratio will be maintained while
-/// forcing the image to match the other given dimension. If neither is
-/// specified, then the image maintains its intrinsic size.
-///
-/// Scaling the image to larger than its intrinsic size should usually be
-/// avoided, since it causes the image to use more memory than necessary.
-/// Instead, prefer scaling the [Canvas] transform. If the image must be scaled
-/// up, the `allowUpscaling` parameter must be set to true.
void decodeImageFromPixels(
Uint8List pixels,
int width,
@@ -2095,230 +754,289 @@
.then((FrameInfo frameInfo) => callback(frameInfo.image));
});
}
-
-/// Determines the winding rule that decides how the interior of a [Path] is
-/// calculated.
-///
-/// This enum is used by the [Path.fillType] property.
enum PathFillType {
- /// The interior is defined by a non-zero sum of signed edge crossings.
- ///
- /// For a given point, the point is considered to be on the inside of the path
- /// if a line drawn from the point to infinity crosses lines going clockwise
- /// around the point a different number of times than it crosses lines going
- /// counter-clockwise around that point.
- ///
- /// See: <https://en.wikipedia.org/wiki/Nonzero-rule>
nonZero,
-
- /// The interior is defined by an odd number of edge crossings.
- ///
- /// For a given point, the point is considered to be on the inside of the path
- /// if a line drawn from the point to infinity crosses an odd number of lines.
- ///
- /// See: <https://en.wikipedia.org/wiki/Even-odd_rule>
evenOdd,
}
-
-/// Strategies for combining paths.
-///
-/// See also:
-///
-/// * [Path.combine], which uses this enum to decide how to combine two paths.
// Must be kept in sync with SkPathOp
enum PathOperation {
- /// Subtract the second path from the first path.
- ///
- /// For example, if the two paths are overlapping circles of equal diameter
- /// but differing centers, the result would be a crescent portion of the
- /// first circle that was not overlapped by the second circle.
- ///
- /// See also:
- ///
- /// * [reverseDifference], which is the same but subtracting the first path
- /// from the second.
difference,
- /// Create a new path that is the intersection of the two paths, leaving the
- /// overlapping pieces of the path.
- ///
- /// For example, if the two paths are overlapping circles of equal diameter
- /// but differing centers, the result would be only the overlapping portion
- /// of the two circles.
- ///
- /// See also:
- /// * [xor], which is the inverse of this operation
intersect,
- /// Create a new path that is the union (inclusive-or) of the two paths.
- ///
- /// For example, if the two paths are overlapping circles of equal diameter
- /// but differing centers, the result would be a figure-eight like shape
- /// matching the outer boundaries of both circles.
union,
- /// Create a new path that is the exclusive-or of the two paths, leaving
- /// everything but the overlapping pieces of the path.
- ///
- /// For example, if the two paths are overlapping circles of equal diameter
- /// but differing centers, the figure-eight like shape less the overlapping parts
- ///
- /// See also:
- /// * [intersect], which is the inverse of this operation
xor,
- /// Subtract the first path from the second path.
- ///
- /// For example, if the two paths are overlapping circles of equal diameter
- /// but differing centers, the result would be a crescent portion of the
- /// second circle that was not overlapped by the first circle.
- ///
- /// See also:
- ///
- /// * [difference], which is the same but subtracting the second path
- /// from the first.
reverseDifference,
}
-/// A handle for the framework to hold and retain an engine layer across frames.
-@pragma('vm:entry-point')
-class EngineLayer extends NativeFieldWrapperClass2 {
- /// This class is created by the engine, and should not be instantiated
- /// or extended directly.
- @pragma('vm:entry-point')
- EngineLayer._();
+abstract class EngineLayer {
}
-/// A complex, one-dimensional subset of a plane.
-///
-/// A path consists of a number of sub-paths, and a _current point_.
-///
-/// Sub-paths consist of segments of various types, such as lines,
-/// arcs, or beziers. Sub-paths can be open or closed, and can
-/// self-intersect.
-///
-/// Closed sub-paths enclose a (possibly discontiguous) region of the
-/// plane based on the current [fillType].
-///
-/// The _current point_ is initially at the origin. After each
-/// operation adding a segment to a sub-path, the current point is
-/// updated to the end of that segment.
-///
-/// Paths can be drawn on canvases using [Canvas.drawPath], and can
-/// used to create clip regions using [Canvas.clipPath].
-@pragma('vm:entry-point')
-class Path extends NativeFieldWrapperClass2 {
- /// Create a new empty [Path] object.
- @pragma('vm:entry-point')
- Path() { _constructor(); }
- void _constructor() { throw UnimplementedError(); }
-
- /// Avoids creating a new native backing for the path for methods that will
- /// create it later, such as [Path.from], [shift] and [transform].
+class _PathMethods {
+ static const int moveTo = 0;
+ static const int relativeMoveTo = 1;
+ static const int lineTo = 2;
+ static const int relativeLineTo = 3;
+ static const int quadraticBezierTo = 4;
+ static const int relativeQuadraticBezierTo = 5;
+ static const int cubicTo = 6;
+ static const int relativeCubicTo = 7;
+ static const int conicTo = 8;
+ static const int relativeConicTo = 9;
+ static const int arcTo = 10;
+ static const int arcToPoint = 11;
+ static const int relativeArcToPoint = 12;
+ static const int addRect = 13;
+ static const int addOval = 14;
+ static const int addArc = 15;
+ static const int addPolygon = 16;
+ static const int addRRect = 17;
+ static const int addPath = 18;
+ static const int addPathWithMatrix = 19;
+ static const int extendWithPath = 20;
+ static const int extendWithPathAndMatrix = 21;
+ static const int close = 22;
+ static const int reset = 23;
+}
+class Path {
+ Path();
Path._();
- /// Creates a copy of another [Path].
- ///
- /// This copy is fast and does not require additional memory unless either
- /// the `source` path or the path returned by this constructor are modified.
- factory Path.from(Path source) {
- final Path clonedPath = Path._();
- source._clone(clonedPath);
- return clonedPath;
+ PathFillType fillType = PathFillType.nonZero;
+ double _currentX = 0;
+ double _currentY = 0;
+ Uint8List _methods = Uint8List(10);
+ int _methodsLength = 0;
+ Float32List _data = Float32List(30);
+ int _dataLength = 0;
+ List<Object> _objects = <Object>[];
+ bool _isEmpty = true;
+ double _left = 0;
+ double _top = 0;
+ double _right = 0;
+ double _bottom = 0;
+
+ void _updateBounds(double x, double y) {
+ if (_isEmpty) {
+ _left = _right = x;
+ _top = _bottom = y;
+ _isEmpty = false;
+ } else {
+ if (x < _left) {
+ _left = x;
+ }
+ if (x > _right) {
+ _right = x;
+ }
+ if (y < _top) {
+ _top = y;
+ }
+ if (y > _bottom) {
+ _bottom = y;
+ }
+ }
}
- void _clone(Path outPath) { throw UnimplementedError(); }
- /// Determines how the interior of this path is calculated.
- ///
- /// Defaults to the non-zero winding rule, [PathFillType.nonZero].
- PathFillType get fillType => PathFillType.values[_getFillType()];
- set fillType(PathFillType value) => _setFillType(value.index);
+ _updateBoundsFromCurrent() {
+ _updateBounds(_currentX, _currentY);
+ }
- int _getFillType() { throw UnimplementedError(); }
- void _setFillType(int fillType) { throw UnimplementedError(); }
+ void _addObject(Object object) {
+ _objects.add(object);
+ }
- /// Starts a new sub-path at the given coordinate.
- void moveTo(double x, double y) { throw UnimplementedError(); }
+ void _addMethod(int methodId) {
+ if (_methodsLength >= _methods.length) {
+ final Uint8List newList = Uint8List(_methods.length * 2);
+ for (int i = 0; i < _methodsLength; i++) {
+ newList[i] = _methods[i];
+ }
+ _methods = newList;
+ }
+ _methods[_methodsLength] = methodId;
+ _methodsLength += 1;
+ }
- /// Starts a new sub-path at the given offset from the current point.
- void relativeMoveTo(double dx, double dy) { throw UnimplementedError(); }
+ void _ensureDataLength(int newLength) {
+ if (_data.length >= newLength) {
+ return;
+ }
- /// Adds a straight line segment from the current point to the given
- /// point.
- void lineTo(double x, double y) { throw UnimplementedError(); }
+ final Float32List newList = Float32List(_data.length * 2);
+ for (int i = 0; i < _dataLength; i++) {
+ newList[i] = _data[i];
+ }
+ _data = newList;
+ }
- /// Adds a straight line segment from the current point to the point
- /// at the given offset from the current point.
- void relativeLineTo(double dx, double dy) { throw UnimplementedError(); }
+ void _addData2(double a, double b) {
+ _ensureDataLength(_dataLength + 2);
+ _data[_dataLength++] = a;
+ _data[_dataLength++] = b;
+ }
- /// Adds a quadratic bezier segment that curves from the current
- /// point to the given point (x2,y2), using the control point
- /// (x1,y1).
- void quadraticBezierTo(double x1, double y1, double x2, double y2) { throw UnimplementedError(); }
+ void _addData4(double a, double b, double c, double d) {
+ _ensureDataLength(_dataLength + 4);
+ _data[_dataLength++] = a;
+ _data[_dataLength++] = b;
+ _data[_dataLength++] = c;
+ _data[_dataLength++] = d;
+ }
- /// Adds a quadratic bezier segment that curves from the current
- /// point to the point at the offset (x2,y2) from the current point,
- /// using the control point at the offset (x1,y1) from the current
- /// point.
- void relativeQuadraticBezierTo(double x1, double y1, double x2, double y2) { throw UnimplementedError(); }
+ void _addData5(double a, double b, double c, double d, double e) {
+ _ensureDataLength(_dataLength + 5);
+ _data[_dataLength++] = a;
+ _data[_dataLength++] = b;
+ _data[_dataLength++] = c;
+ _data[_dataLength++] = d;
+ _data[_dataLength++] = e;
+ }
- /// Adds a cubic bezier segment that curves from the current point
- /// to the given point (x3,y3), using the control points (x1,y1) and
- /// (x2,y2).
- void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3) { throw UnimplementedError(); }
+ void _addData6(double a, double b, double c, double d, double e, double f) {
+ _ensureDataLength(_dataLength + 6);
+ _data[_dataLength++] = a;
+ _data[_dataLength++] = b;
+ _data[_dataLength++] = c;
+ _data[_dataLength++] = d;
+ _data[_dataLength++] = e;
+ _data[_dataLength++] = f;
+ }
- /// Adds a cubic bezier segment that curves from the current point
- /// to the point at the offset (x3,y3) from the current point, using
- /// the control points at the offsets (x1,y1) and (x2,y2) from the
- /// current point.
- void relativeCubicTo(double x1, double y1, double x2, double y2, double x3, double y3) { throw UnimplementedError(); }
+ void _addData7(double a, double b, double c, double d, double e, double f, double g) {
+ _ensureDataLength(_dataLength + 7);
+ _data[_dataLength++] = a;
+ _data[_dataLength++] = b;
+ _data[_dataLength++] = c;
+ _data[_dataLength++] = d;
+ _data[_dataLength++] = e;
+ _data[_dataLength++] = f;
+ _data[_dataLength++] = g;
+ }
- /// Adds a bezier segment that curves from the current point to the
- /// given point (x2,y2), using the control points (x1,y1) and the
- /// weight w. If the weight is greater than 1, then the curve is a
- /// hyperbola; if the weight equals 1, it's a parabola; and if it is
- /// less than 1, it is an ellipse.
- void conicTo(double x1, double y1, double x2, double y2, double w) { throw UnimplementedError(); }
-
- /// Adds a bezier segment that curves from the current point to the
- /// point at the offset (x2,y2) from the current point, using the
- /// control point at the offset (x1,y1) from the current point and
- /// the weight w. If the weight is greater than 1, then the curve is
- /// a hyperbola; if the weight equals 1, it's a parabola; and if it
- /// is less than 1, it is an ellipse.
- void relativeConicTo(double x1, double y1, double x2, double y2, double w) { throw UnimplementedError(); }
-
- /// If the `forceMoveTo` argument is false, adds a straight line
- /// segment and an arc segment.
- ///
- /// If the `forceMoveTo` argument is true, starts a new sub-path
- /// consisting of an arc segment.
- ///
- /// In either case, the arc segment consists of the arc that follows
- /// the edge of the oval bounded by the given rectangle, from
- /// startAngle radians around the oval up to startAngle + sweepAngle
- /// radians around the oval, with zero radians being the point on
- /// the right hand side of the oval that crosses the horizontal line
- /// that intersects the center of the rectangle and with positive
- /// angles going clockwise around the oval.
- ///
- /// The line segment added if `forceMoveTo` is false starts at the
- /// current point and ends at the start of the arc.
+ void _addData12(double a, double b, double c, double d, double e, double f,
+ double g, double h, double i, double j, double k, double l) {
+ _ensureDataLength(_dataLength + 12);
+ _data[_dataLength++] = a;
+ _data[_dataLength++] = b;
+ _data[_dataLength++] = c;
+ _data[_dataLength++] = d;
+ _data[_dataLength++] = e;
+ _data[_dataLength++] = f;
+ _data[_dataLength++] = g;
+ _data[_dataLength++] = h;
+ _data[_dataLength++] = i;
+ _data[_dataLength++] = j;
+ _data[_dataLength++] = k;
+ _data[_dataLength++] = l;
+ }
+ factory Path.from(Path source) {
+ return source.shift(Offset.zero);
+ }
+ void moveTo(double x, double y) {
+ _addMethod(_PathMethods.moveTo);
+ _addData2(x, y);
+ _currentX = x;
+ _currentY = y;
+ _updateBoundsFromCurrent();
+ }
+ void relativeMoveTo(double dx, double dy) {
+ _addMethod(_PathMethods.relativeMoveTo);
+ _addData2(dx, dy);
+ _currentX += dx;
+ _currentY += dy;
+ _updateBoundsFromCurrent();
+ }
+ void lineTo(double x, double y) {
+ _addMethod(_PathMethods.lineTo);
+ _addData2(x, y);
+ _updateBoundsFromCurrent();
+ _currentX = x;
+ _currentY = y;
+ _updateBoundsFromCurrent();
+ }
+ void relativeLineTo(double dx, double dy) {
+ _addMethod(_PathMethods.relativeLineTo);
+ _addData2(dx, dy);
+ _updateBoundsFromCurrent();
+ _currentX += dx;
+ _currentY += dy;
+ _updateBoundsFromCurrent();
+ }
+ void quadraticBezierTo(double x1, double y1, double x2, double y2) {
+ _addMethod(_PathMethods.quadraticBezierTo);
+ _addData4(x1, y1, x2, y2);
+ _currentX = x1;
+ _currentY = y1;
+ _updateBoundsFromCurrent();
+ _currentX = x2;
+ _currentY = y2;
+ _updateBoundsFromCurrent();
+ }
+ void relativeQuadraticBezierTo(double x1, double y1, double x2, double y2) {
+ _addMethod(_PathMethods.relativeQuadraticBezierTo);
+ _addData4(x1, y1, x2, y2);
+ _currentX += x1;
+ _currentY += y1;
+ _updateBoundsFromCurrent();
+ _currentX += x2;
+ _currentY += y2;
+ _updateBoundsFromCurrent();
+ }
+ void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3) {
+ _addMethod(_PathMethods.cubicTo);
+ _addData6(x1, y1, x2, y2, x3, y3);
+ _currentX = x1;
+ _currentY = y1;
+ _updateBoundsFromCurrent();
+ _currentX = x2;
+ _currentY = y2;
+ _updateBoundsFromCurrent();
+ _currentX = x3;
+ _currentY = y3;
+ _updateBoundsFromCurrent();
+ }
+ void relativeCubicTo(double x1, double y1, double x2, double y2, double x3, double y3) {
+ _addMethod(_PathMethods.relativeCubicTo);
+ _addData6(x1, y1, x2, y2, x3, y3);
+ _currentX += x1;
+ _currentY += y1;
+ _updateBoundsFromCurrent();
+ _currentX += x2;
+ _currentY += y2;
+ _updateBoundsFromCurrent();
+ _currentX += x3;
+ _currentY += y3;
+ _updateBoundsFromCurrent();
+ }
+ void conicTo(double x1, double y1, double x2, double y2, double w) {
+ _addMethod(_PathMethods.conicTo);
+ _addData5(x1, y1, x2, y2, w);
+ _currentX = x1;
+ _currentY = y1;
+ _updateBoundsFromCurrent();
+ _currentX = x2;
+ _currentY = y2;
+ _updateBoundsFromCurrent();
+ }
+ void relativeConicTo(double x1, double y1, double x2, double y2, double w) {
+ _addMethod(_PathMethods.relativeConicTo);
+ _addData5(x1, y1, x2, y2, w);
+ _currentX += x1;
+ _currentY += y1;
+ _updateBoundsFromCurrent();
+ _currentX += x2;
+ _currentY += y2;
+ _updateBoundsFromCurrent();
+ }
void arcTo(Rect rect, double startAngle, double sweepAngle, bool forceMoveTo) {
assert(_rectIsValid(rect));
- _arcTo(rect.left, rect.top, rect.right, rect.bottom, startAngle, sweepAngle, forceMoveTo);
+ _addMethod(_PathMethods.arcTo);
+ _addData7(rect.left, rect.top, rect.right, rect.bottom, startAngle, sweepAngle, forceMoveTo ? 1 : 0);
+ _currentX = rect.left;
+ _currentY = rect.top;
+ _updateBoundsFromCurrent();
+ _currentX = rect.right;
+ _currentY = rect.bottom;
+ _updateBoundsFromCurrent();
}
- void _arcTo(double left, double top, double right, double bottom,
- double startAngle, double sweepAngle, bool forceMoveTo) { throw UnimplementedError(); }
-
- /// Appends up to four conic curves weighted to describe an oval of `radius`
- /// and rotated by `rotation`.
- ///
- /// The first curve begins from the last point in the path and the last ends
- /// at `arcEnd`. The curves follow a path in a direction determined by
- /// `clockwise` and `largeArc` in such a way that the sweep angle
- /// is always less than 360 degrees.
- ///
- /// A simple line is appended if either either radii are zero or the last
- /// point in the path is `arcEnd`. The radii are scaled to fit the last path
- /// point if both are greater than zero but too small to describe an arc.
- ///
void arcToPoint(Offset arcEnd, {
Radius radius = Radius.zero,
double rotation = 0.0,
@@ -2327,28 +1045,13 @@
}) {
assert(_offsetIsValid(arcEnd));
assert(_radiusIsValid(radius));
- _arcToPoint(arcEnd.dx, arcEnd.dy, radius.x, radius.y, rotation,
- largeArc, clockwise);
+ _addMethod(_PathMethods.arcToPoint);
+ _addData7(arcEnd.dx, arcEnd.dy, radius.x, radius.y, rotation, largeArc ? 1 : 0, clockwise ? 1 : 0);
+ _updateBoundsFromCurrent();
+ _currentX = arcEnd.dx;
+ _currentY = arcEnd.dy;
+ _updateBoundsFromCurrent();
}
- void _arcToPoint(double arcEndX, double arcEndY, double radiusX,
- double radiusY, double rotation, bool largeArc,
- bool clockwise) { throw UnimplementedError(); }
-
-
- /// Appends up to four conic curves weighted to describe an oval of `radius`
- /// and rotated by `rotation`.
- ///
- /// The last path point is described by (px, py).
- ///
- /// The first curve begins from the last point in the path and the last ends
- /// at `arcEndDelta.dx + px` and `arcEndDelta.dy + py`. The curves follow a
- /// path in a direction determined by `clockwise` and `largeArc`
- /// in such a way that the sweep angle is always less than 360 degrees.
- ///
- /// A simple line is appended if either either radii are zero, or, both
- /// `arcEndDelta.dx` and `arcEndDelta.dy` are zero. The radii are scaled to
- /// fit the last path point if both are greater than zero but too small to
- /// describe an arc.
void relativeArcToPoint(Offset arcEndDelta, {
Radius radius = Radius.zero,
double rotation = 0.0,
@@ -2357,289 +1060,219 @@
}) {
assert(_offsetIsValid(arcEndDelta));
assert(_radiusIsValid(radius));
- _relativeArcToPoint(arcEndDelta.dx, arcEndDelta.dy, radius.x, radius.y,
- rotation, largeArc, clockwise);
+ _addMethod(_PathMethods.relativeArcToPoint);
+ _addData7(arcEndDelta.dx, arcEndDelta.dy, radius.x, radius.y, rotation, largeArc ? 1 : 0, clockwise ? 1 : 0);
+ _updateBoundsFromCurrent();
+ _currentX += arcEndDelta.dx;
+ _currentY += arcEndDelta.dy;
+ _updateBoundsFromCurrent();
}
- void _relativeArcToPoint(double arcEndX, double arcEndY, double radiusX,
- double radiusY, double rotation,
- bool largeArc, bool clockwise)
- { throw UnimplementedError(); }
-
- /// Adds a new sub-path that consists of four lines that outline the
- /// given rectangle.
void addRect(Rect rect) {
assert(_rectIsValid(rect));
- _addRect(rect.left, rect.top, rect.right, rect.bottom);
+ _addMethod(_PathMethods.addRect);
+ _addData4(rect.left, rect.top, rect.right, rect.bottom);
+ _currentX = rect.left;
+ _currentY = rect.top;
+ _updateBoundsFromCurrent();
+ _currentX = rect.right;
+ _currentY = rect.bottom;
+ _updateBoundsFromCurrent();
}
- void _addRect(double left, double top, double right, double bottom) { throw UnimplementedError(); }
-
- /// Adds a new sub-path that consists of a curve that forms the
- /// ellipse that fills the given rectangle.
- ///
- /// To add a circle, pass an appropriate rectangle as `oval`. [Rect.fromCircle]
- /// can be used to easily describe the circle's center [Offset] and radius.
void addOval(Rect oval) {
assert(_rectIsValid(oval));
- _addOval(oval.left, oval.top, oval.right, oval.bottom);
+ _addMethod(_PathMethods.addOval);
+ _addData4(oval.left, oval.top, oval.right, oval.bottom);
+ _currentX = oval.left;
+ _currentY = oval.top;
+ _updateBoundsFromCurrent();
+ _currentX = oval.right;
+ _currentY = oval.bottom;
+ _updateBoundsFromCurrent();
}
- void _addOval(double left, double top, double right, double bottom) { throw UnimplementedError(); }
-
- /// Adds a new sub-path with one arc segment that consists of the arc
- /// that follows the edge of the oval bounded by the given
- /// rectangle, from startAngle radians around the oval up to
- /// startAngle + sweepAngle radians around the oval, with zero
- /// radians being the point on the right hand side of the oval that
- /// crosses the horizontal line that intersects the center of the
- /// rectangle and with positive angles going clockwise around the
- /// oval.
void addArc(Rect oval, double startAngle, double sweepAngle) {
assert(_rectIsValid(oval));
- _addArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle);
+ _addMethod(_PathMethods.addArc);
+ _addData6(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle);
+ _currentX = oval.left;
+ _currentY = oval.top;
+ _updateBoundsFromCurrent();
+ _currentX = oval.right;
+ _currentY = oval.bottom;
+ _updateBoundsFromCurrent();
}
- void _addArc(double left, double top, double right, double bottom,
- double startAngle, double sweepAngle) { throw UnimplementedError(); }
-
- /// Adds a new sub-path with a sequence of line segments that connect the given
- /// points.
- ///
- /// If `close` is true, a final line segment will be added that connects the
- /// last point to the first point.
- ///
- /// The `points` argument is interpreted as offsets from the origin.
void addPolygon(List<Offset> points, bool close) {
assert(points != null); // ignore: unnecessary_null_comparison
- _addPolygon(_encodePointList(points), close);
+ _addMethod(_PathMethods.addPolygon);
+ _ensureDataLength(_dataLength + points.length * 2);
+ for (final Offset point in points) {
+ _data[_dataLength++] = point.dx;
+ _data[_dataLength++] = point.dy;
+ _currentX = point.dx;
+ _currentY = point.dy;
+ _updateBoundsFromCurrent();
+ }
}
- void _addPolygon(Float32List points, bool close) { throw UnimplementedError(); }
-
- /// Adds a new sub-path that consists of the straight lines and
- /// curves needed to form the rounded rectangle described by the
- /// argument.
void addRRect(RRect rrect) {
assert(_rrectIsValid(rrect));
- _addRRect(rrect._value32);
+ _addMethod(_PathMethods.addRRect);
+ _addData12(
+ rrect.left,
+ rrect.top,
+ rrect.right,
+ rrect.bottom,
+ rrect.tlRadiusX,
+ rrect.tlRadiusY,
+ rrect.trRadiusX,
+ rrect.trRadiusY,
+ rrect.brRadiusX,
+ rrect.brRadiusY,
+ rrect.blRadiusX,
+ rrect.blRadiusY,
+ );
+ _currentX = rrect.left;
+ _currentY = rrect.top;
+ _updateBoundsFromCurrent();
+ _currentX = rrect.right;
+ _currentY = rrect.bottom;
+ _updateBoundsFromCurrent();
}
- void _addRRect(Float32List rrect) { throw UnimplementedError(); }
-
- /// Adds a new sub-path that consists of the given `path` offset by the given
- /// `offset`.
- ///
- /// If `matrix4` is specified, the path will be transformed by this matrix
- /// after the matrix is translated by the given offset. The matrix is a 4x4
- /// matrix stored in column major order.
void addPath(Path path, Offset offset, {Float64List? matrix4}) {
// ignore: unnecessary_null_comparison
assert(path != null); // path is checked on the engine side
assert(_offsetIsValid(offset));
if (matrix4 != null) {
assert(_matrix4IsValid(matrix4));
- _addPathWithMatrix(path, offset.dx, offset.dy, matrix4);
+ _addMethod(_PathMethods.addPathWithMatrix);
+ _addData2(offset.dx, offset.dy);
+ _addObject(path);
+ _addObject(matrix4);
} else {
- _addPath(path, offset.dx, offset.dy);
+ _addMethod(_PathMethods.addPath);
+ _addData2(offset.dx, offset.dy);
+ _addObject(path);
}
+ final Rect otherBounds = path.getBounds();
+ _updateBounds(otherBounds.left, otherBounds.top);
+ _updateBounds(otherBounds.right, otherBounds.bottom);
}
- void _addPath(Path path, double dx, double dy) { throw UnimplementedError(); }
- void _addPathWithMatrix(Path path, double dx, double dy, Float64List matrix) { throw UnimplementedError(); }
-
- /// Adds the given path to this path by extending the current segment of this
- /// path with the first segment of the given path.
- ///
- /// If `matrix4` is specified, the path will be transformed by this matrix
- /// after the matrix is translated by the given `offset`. The matrix is a 4x4
- /// matrix stored in column major order.
void extendWithPath(Path path, Offset offset, {Float64List? matrix4}) {
// ignore: unnecessary_null_comparison
assert(path != null); // path is checked on the engine side
assert(_offsetIsValid(offset));
if (matrix4 != null) {
assert(_matrix4IsValid(matrix4));
- _extendWithPathAndMatrix(path, offset.dx, offset.dy, matrix4);
+ _addMethod(_PathMethods.extendWithPathAndMatrix);
+ _addData2(offset.dx, offset.dy);
+ _addObject(path);
+ _addObject(matrix4);
} else {
- _extendWithPath(path, offset.dx, offset.dy);
+ _addMethod(_PathMethods.extendWithPath);
+ _addData2(offset.dx, offset.dy);
+ _addObject(path);
}
+ final Rect otherBounds = path.getBounds();
+ _updateBounds(otherBounds.left, otherBounds.top);
+ _updateBounds(otherBounds.right, otherBounds.bottom);
}
- void _extendWithPath(Path path, double dx, double dy) { throw UnimplementedError(); }
- void _extendWithPathAndMatrix(Path path, double dx, double dy, Float64List matrix) { throw UnimplementedError(); }
-
- /// Closes the last sub-path, as if a straight line had been drawn
- /// from the current point to the first point of the sub-path.
- void close() { throw UnimplementedError(); }
-
- /// Clears the [Path] object of all sub-paths, returning it to the
- /// same state it had when it was created. The _current point_ is
- /// reset to the origin.
- void reset() { throw UnimplementedError(); }
-
- /// Tests to see if the given point is within the path. (That is, whether the
- /// point would be in the visible portion of the path if the path was used
- /// with [Canvas.clipPath].)
- ///
- /// The `point` argument is interpreted as an offset from the origin.
- ///
- /// Returns true if the point is in the path, and false otherwise.
+ void close() {
+ _addMethod(_PathMethods.close);
+ }
+ void reset() {
+ _addMethod(_PathMethods.reset);
+ }
bool contains(Offset point) {
assert(_offsetIsValid(point));
- return _contains(point.dx, point.dy);
+ return getBounds().contains(point);
}
- bool _contains(double x, double y) { throw UnimplementedError(); }
-
- /// Returns a copy of the path with all the segments of every
- /// sub-path translated by the given offset.
Path shift(Offset offset) {
assert(_offsetIsValid(offset));
- final Path path = Path._();
- _shift(path, offset.dx, offset.dy);
- return path;
+ // This is a dummy implementation.
+ final Path shifted = Path._();
+ shifted._methods = Uint8List.fromList(this._methods);
+ shifted._methodsLength = _methodsLength;
+ shifted._data = Float32List.fromList(this._data);
+ shifted._dataLength = _dataLength;
+ shifted._objects = this._objects.toList();
+ shifted._isEmpty = _isEmpty;
+ shifted._left = _left + offset.dx;
+ shifted._top = _top + offset.dy;
+ shifted._right = _right + offset.dx;
+ shifted._bottom = _bottom + offset.dy;
+ shifted._currentX = _currentX + offset.dx;
+ shifted._currentY = _currentY + offset.dy;
+ shifted.fillType = fillType;
+ return shifted;
}
- void _shift(Path outPath, double dx, double dy) { throw UnimplementedError(); }
-
- /// Returns a copy of the path with all the segments of every
- /// sub-path transformed by the given matrix.
Path transform(Float64List matrix4) {
assert(_matrix4IsValid(matrix4));
- final Path path = Path._();
- _transform(path, matrix4);
- return path;
+ // This is a dummy implementation.
+ final double dx = matrix4[12];
+ final double dy = matrix4[13];
+ final Path transformed = Path._();
+ transformed._methods = Uint8List.fromList(this._methods);
+ transformed._methodsLength = _methodsLength;
+ transformed._data = Float32List.fromList(this._data);
+ transformed._dataLength = _dataLength;
+ transformed._objects = this._objects.toList();
+ transformed._isEmpty = _isEmpty;
+ transformed._left = _left + dx;
+ transformed._top = _top + dy;
+ transformed._right = _right + dx;
+ transformed._bottom = _bottom + dy;
+ transformed._currentX = _currentX + dx;
+ transformed._currentY = _currentY + dy;
+ transformed.fillType = fillType;
+ return transformed;
}
- void _transform(Path outPath, Float64List matrix4) { throw UnimplementedError(); }
-
- /// Computes the bounding rectangle for this path.
- ///
- /// A path containing only axis-aligned points on the same straight line will
- /// have no area, and therefore `Rect.isEmpty` will return true for such a
- /// path. Consider checking `rect.width + rect.height > 0.0` instead, or
- /// using the [computeMetrics] API to check the path length.
- ///
- /// For many more elaborate paths, the bounds may be inaccurate. For example,
- /// when a path contains a circle, the points used to compute the bounds are
- /// the circle's implied control points, which form a square around the circle;
- /// if the circle has a transformation applied using [transform] then that
- /// square is rotated, and the (axis-aligned, non-rotated) bounding box
- /// therefore ends up grossly overestimating the actual area covered by the
- /// circle.
// see https://skia.org/user/api/SkPath_Reference#SkPath_getBounds
Rect getBounds() {
- final Float32List rect = _getBounds();
- return Rect.fromLTRB(rect[0], rect[1], rect[2], rect[3]);
+ return Rect.fromLTRB(_left, _top, _right, _bottom);
}
- Float32List _getBounds() { throw UnimplementedError(); }
-
- /// Combines the two paths according to the manner specified by the given
- /// `operation`.
- ///
- /// The resulting path will be constructed from non-overlapping contours. The
- /// curve order is reduced where possible so that cubics may be turned into
- /// quadratics, and quadratics maybe turned into lines.
static Path combine(PathOperation operation, Path path1, Path path2) {
assert(path1 != null); // ignore: unnecessary_null_comparison
assert(path2 != null); // ignore: unnecessary_null_comparison
- final Path path = Path();
- if (path._op(path1, path2, operation.index)) {
- return path;
- }
- throw StateError('Path.combine() failed. This may be due an invalid path; in particular, check for NaN values.');
+ // This is a dummy implementation
+ final Path combined = Path._();
+ combined._methods = Uint8List.fromList([
+ ...path1._methods,
+ ...path2._methods,
+ ]);
+ combined._methodsLength = path1._methodsLength + path2._methodsLength;
+ combined._data = Float32List.fromList([
+ ...path1._data,
+ ...path2._data,
+ ]);
+ combined._dataLength = path1._dataLength + path2._dataLength;
+ combined._objects = <Object>[
+ ...path1._objects,
+ ...path2._objects,
+ ];
+ combined._isEmpty = path1._isEmpty && path2._isEmpty;
+ combined._left = math.min(path1._left, path2._left);
+ combined._top = math.min(path1._top, path2._top);
+ combined._right = math.max(path1._right, path2._right);
+ combined._bottom = math.max(path1._bottom, path2._bottom);
+ combined._currentX = path2._currentX;
+ combined._currentY = path2._currentY;
+ combined.fillType = path1.fillType;
+ return combined;
}
- bool _op(Path path1, Path path2, int operation) { throw UnimplementedError(); }
-
- /// Creates a [PathMetrics] object for this path, which can describe various
- /// properties about the contours of the path.
- ///
- /// A [Path] is made up of zero or more contours. A contour is made up of
- /// connected curves and segments, created via methods like [lineTo],
- /// [cubicTo], [arcTo], [quadraticBezierTo], their relative counterparts, as
- /// well as the add* methods such as [addRect]. Creating a new [Path] starts
- /// a new contour once it has any drawing instructions, and another new
- /// contour is started for each [moveTo] instruction.
- ///
- /// A [PathMetric] object describes properties of an individual contour,
- /// such as its length, whether it is closed, what the tangent vector of a
- /// particular offset along the path is. It also provides a method for
- /// creating sub-paths: [PathMetric.extractPath].
- ///
- /// Calculating [PathMetric] objects is not trivial. The [PathMetrics] object
- /// returned by this method is a lazy [Iterable], meaning it only performs
- /// calculations when the iterator is moved to the next [PathMetric]. Callers
- /// that wish to memoize this iterable can easily do so by using
- /// [Iterable.toList] on the result of this method. In particular, callers
- /// looking for information about how many contours are in the path should
- /// either store the result of `path.computeMetrics().length`, or should use
- /// `path.computeMetrics().toList()` so they can repeatedly check the length,
- /// since calling `Iterable.length` causes traversal of the entire iterable.
- ///
- /// In particular, callers should be aware that [PathMetrics.length] is the
- /// number of contours, **not the length of the path**. To get the length of
- /// a contour in a path, use [PathMetric.length].
- ///
- /// If `forceClosed` is set to true, the contours of the path will be measured
- /// as if they had been closed, even if they were not explicitly closed.
PathMetrics computeMetrics({bool forceClosed = false}) {
return PathMetrics._(this, forceClosed);
}
}
-
-/// The geometric description of a tangent: the angle at a point.
-///
-/// See also:
-/// * [PathMetric.getTangentForOffset], which returns the tangent of an offset along a path.
class Tangent {
- /// Creates a [Tangent] with the given values.
- ///
- /// The arguments must not be null.
const Tangent(this.position, this.vector)
: assert(position != null), // ignore: unnecessary_null_comparison
assert(vector != null); // ignore: unnecessary_null_comparison
-
- /// Creates a [Tangent] based on the angle rather than the vector.
- ///
- /// The [vector] is computed to be the unit vector at the given angle, interpreted
- /// as clockwise radians from the x axis.
factory Tangent.fromAngle(Offset position, double angle) {
return Tangent(position, Offset(math.cos(angle), math.sin(angle)));
}
-
- /// Position of the tangent.
- ///
- /// When used with [PathMetric.getTangentForOffset], this represents the precise
- /// position that the given offset along the path corresponds to.
final Offset position;
-
- /// The vector of the curve at [position].
- ///
- /// When used with [PathMetric.getTangentForOffset], this is the vector of the
- /// curve that is at the given offset along the path (i.e. the direction of the
- /// curve at [position]).
final Offset vector;
-
- /// The direction of the curve at [position].
- ///
- /// When used with [PathMetric.getTangentForOffset], this is the angle of the
- /// curve that is the given offset along the path (i.e. the direction of the
- /// curve at [position]).
- ///
- /// This value is in radians, with 0.0 meaning pointing along the x axis in
- /// the positive x-axis direction, positive numbers pointing downward toward
- /// the negative y-axis, i.e. in a clockwise direction, and negative numbers
- /// pointing upward toward the positive y-axis, i.e. in a counter-clockwise
- /// direction.
// flip the sign to be consistent with [Path.arcTo]'s `sweepAngle`
double get angle => -math.atan2(vector.dy, vector.dx);
}
-
-/// An iterable collection of [PathMetric] objects describing a [Path].
-///
-/// A [PathMetrics] object is created by using the [Path.computeMetrics] method,
-/// and represents the path as it stood at the time of the call. Subsequent
-/// modifications of the path do not affect the [PathMetrics] object.
-///
-/// Each path metric corresponds to a segment, or contour, of a path.
-///
-/// For example, a path consisting of a [Path.lineTo], a [Path.moveTo], and
-/// another [Path.lineTo] will contain two contours and thus be represented by
-/// two [PathMetric] objects.
-///
-/// This iterable does not memoize. Callers who need to traverse the list
-/// multiple times, or who need to randomly access elements of the list, should
-/// use [toList] on this object.
class PathMetrics extends collection.IterableBase<PathMetric> {
PathMetrics._(Path path, bool forceClosed) :
_iterator = PathMetricIterator._(_PathMeasure(path, forceClosed));
@@ -2649,9 +1282,6 @@
@override
Iterator<PathMetric> get iterator => _iterator;
}
-
-/// Used by [PathMetrics] to track iteration from one segment of a path to the
-/// next for measurement.
class PathMetricIterator implements Iterator<PathMetric> {
PathMetricIterator._(this._pathMeasure) : assert(_pathMeasure != null); // ignore: unnecessary_null_comparison
@@ -2681,73 +1311,20 @@
return false;
}
}
-
-/// Utilities for measuring a [Path] and extracting sub-paths.
-///
-/// Iterate over the object returned by [Path.computeMetrics] to obtain
-/// [PathMetric] objects. Callers that want to randomly access elements or
-/// iterate multiple times should use `path.computeMetrics().toList()`, since
-/// [PathMetrics] does not memoize.
-///
-/// Once created, the metrics are only valid for the path as it was specified
-/// when [Path.computeMetrics] was called. If additional contours are added or
-/// any contours are updated, the metrics need to be recomputed. Previously
-/// created metrics will still refer to a snapshot of the path at the time they
-/// were computed, rather than to the actual metrics for the new mutations to
-/// the path.
class PathMetric {
PathMetric._(this._measure)
: assert(_measure != null), // ignore: unnecessary_null_comparison
length = _measure.length(_measure.currentContourIndex),
isClosed = _measure.isClosed(_measure.currentContourIndex),
contourIndex = _measure.currentContourIndex;
-
- /// Return the total length of the current contour.
final double length;
-
- /// Whether the contour is closed.
- ///
- /// Returns true if the contour ends with a call to [Path.close] (which may
- /// have been implied when using methods like [Path.addRect]) or if
- /// `forceClosed` was specified as true in the call to [Path.computeMetrics].
- /// Returns false otherwise.
final bool isClosed;
-
- /// The zero-based index of the contour.
- ///
- /// [Path] objects are made up of zero or more contours. The first contour is
- /// created once a drawing command (e.g. [Path.lineTo]) is issued. A
- /// [Path.moveTo] command after a drawing command may create a new contour,
- /// although it may not if optimizations are applied that determine the move
- /// command did not actually result in moving the pen.
- ///
- /// This property is only valid with reference to its original iterator and
- /// the contours of the path at the time the path's metrics were computed. If
- /// additional contours were added or existing contours updated, this metric
- /// will be invalid for the current state of the path.
final int contourIndex;
final _PathMeasure _measure;
-
-
- /// Computes the position of the current contour at the given offset, and the
- /// angle of the path at that point.
- ///
- /// For example, calling this method with a distance of 1.41 for a line from
- /// 0.0,0.0 to 2.0,2.0 would give a point 1.0,1.0 and the angle 45 degrees
- /// (but in radians).
- ///
- /// Returns null if the contour has zero [length].
- ///
- /// The distance is clamped to the [length] of the current contour.
Tangent? getTangentForOffset(double distance) {
return _measure.getTangentForOffset(contourIndex, distance);
}
-
- /// Given a start and end distance, return the intervening segment(s).
- ///
- /// `start` and `end` are clamped to legal values (0..[length])
- /// Begin the segment with a moveTo if `startWithMoveTo` is true.
Path extractPath(double start, double end, {bool startWithMoveTo = true}) {
return _measure.extractPath(contourIndex, start, end, startWithMoveTo: startWithMoveTo);
}
@@ -2756,7 +1333,7 @@
String toString() => '$runtimeType{length: $length, isClosed: $isClosed, contourIndex:$contourIndex}';
}
-class _PathMeasure extends NativeFieldWrapperClass2 {
+class _PathMeasure {
_PathMeasure(Path path, bool forceClosed) {
_constructor(path, forceClosed);
}
@@ -2809,61 +1386,17 @@
return next;
}
bool _nativeNextContour() { throw UnimplementedError(); }
-
- /// The index of the current contour in the list of contours in the path.
- ///
- /// [nextContour] will increment this to the zero based index.
int currentContourIndex = -1;
}
-
-/// Styles to use for blurs in [MaskFilter] objects.
// These enum values must be kept in sync with SkBlurStyle.
enum BlurStyle {
// These mirror SkBlurStyle and must be kept in sync.
-
- /// Fuzzy inside and outside. This is useful for painting shadows that are
- /// offset from the shape that ostensibly is casting the shadow.
normal,
-
- /// Solid inside, fuzzy outside. This corresponds to drawing the shape, and
- /// additionally drawing the blur. This can make objects appear brighter,
- /// maybe even as if they were fluorescent.
solid,
-
- /// Nothing inside, fuzzy outside. This is useful for painting shadows for
- /// partially transparent shapes, when they are painted separately but without
- /// an offset, so that the shadow doesn't paint below the shape.
outer,
-
- /// Fuzzy inside, nothing outside. This can make shapes appear to be lit from
- /// within.
inner,
}
-
-/// A mask filter to apply to shapes as they are painted. A mask filter is a
-/// function that takes a bitmap of color pixels, and returns another bitmap of
-/// color pixels.
-///
-/// Instances of this class are used with [Paint.maskFilter] on [Paint] objects.
class MaskFilter {
- /// Creates a mask filter that takes the shape being drawn and blurs it.
- ///
- /// This is commonly used to approximate shadows.
- ///
- /// The `style` argument controls the kind of effect to draw; see [BlurStyle].
- ///
- /// The `sigma` argument controls the size of the effect. It is the standard
- /// deviation of the Gaussian blur to apply. The value must be greater than
- /// zero. The sigma corresponds to very roughly half the radius of the effect
- /// in pixels.
- ///
- /// A blur is an expensive operation and should therefore be used sparingly.
- ///
- /// The arguments must not be null.
- ///
- /// See also:
- ///
- /// * [Canvas.drawShadow], which is a more efficient way to draw shadows.
const MaskFilter.blur(
this._style,
this._sigma,
@@ -2891,105 +1424,22 @@
@override
String toString() => 'MaskFilter.blur($_style, ${_sigma.toStringAsFixed(1)})';
}
-
-/// A description of a color filter to apply when drawing a shape or compositing
-/// a layer with a particular [Paint]. A color filter is a function that takes
-/// two colors, and outputs one color. When applied during compositing, it is
-/// independently applied to each pixel of the layer being drawn before the
-/// entire layer is merged with the destination.
-///
-/// Instances of this class are used with [Paint.colorFilter] on [Paint]
-/// objects.
class ColorFilter implements ImageFilter {
- /// Creates a color filter that applies the blend mode given as the second
- /// argument. The source color is the one given as the first argument, and the
- /// destination color is the one from the layer being composited.
- ///
- /// The output of this filter is then composited into the background according
- /// to the [Paint.blendMode], using the output of this filter as the source
- /// and the background as the destination.
const ColorFilter.mode(Color color, BlendMode blendMode)
: _color = color,
_blendMode = blendMode,
_matrix = null,
_type = _kTypeMode;
-
- /// Construct a color filter that transforms a color by a 5x5 matrix, where
- /// the fifth row is implicitly added in an identity configuration.
- ///
- /// Every pixel's color value, repsented as an `[R, G, B, A]`, is matrix
- /// multiplied to create a new color:
- ///
- /// ```text
- /// | R' | | a00 a01 a02 a03 a04 | | R |
- /// | G' | | a10 a11 a22 a33 a44 | | G |
- /// | B' | = | a20 a21 a22 a33 a44 | * | B |
- /// | A' | | a30 a31 a22 a33 a44 | | A |
- /// | 1 | | 0 0 0 0 1 | | 1 |
- /// ```
- ///
- /// The matrix is in row-major order and the translation column is specified
- /// in unnormalized, 0...255, space. For example, the identity matrix is:
- ///
- /// ```
- /// const ColorFilter identity = ColorFilter.matrix(<double>[
- /// 1, 0, 0, 0, 0,
- /// 0, 1, 0, 0, 0,
- /// 0, 0, 1, 0, 0,
- /// 0, 0, 0, 1, 0,
- /// ]);
- /// ```
- ///
- /// ## Examples
- ///
- /// An inversion color matrix:
- ///
- /// ```
- /// const ColorFilter invert = ColorFilter.matrix(<double>[
- /// -1, 0, 0, 0, 255,
- /// 0, -1, 0, 0, 255,
- /// 0, 0, -1, 0, 255,
- /// 0, 0, 0, 1, 0,
- /// ]);
- /// ```
- ///
- /// A sepia-toned color matrix (values based on the [Filter Effects Spec](https://www.w3.org/TR/filter-effects-1/#sepiaEquivalent)):
- ///
- /// ```
- /// const ColorFilter sepia = ColorFilter.matrix(<double>[
- /// 0.393, 0.769, 0.189, 0, 0,
- /// 0.349, 0.686, 0.168, 0, 0,
- /// 0.272, 0.534, 0.131, 0, 0,
- /// 0, 0, 0, 1, 0,
- /// ]);
- /// ```
- ///
- /// A greyscale color filter (values based on the [Filter Effects Spec](https://www.w3.org/TR/filter-effects-1/#grayscaleEquivalent)):
- ///
- /// ```
- /// const ColorFilter greyscale = ColorFilter.matrix(<double>[
- /// 0.2126, 0.7152, 0.0722, 0, 0,
- /// 0.2126, 0.7152, 0.0722, 0, 0,
- /// 0.2126, 0.7152, 0.0722, 0, 0,
- /// 0, 0, 0, 1, 0,
- /// ]);
- /// ```
const ColorFilter.matrix(List<double> matrix)
: _color = null,
_blendMode = null,
_matrix = matrix,
_type = _kTypeMatrix;
-
- /// Construct a color filter that applies the sRGB gamma curve to the RGB
- /// channels.
const ColorFilter.linearToSrgbGamma()
: _color = null,
_blendMode = null,
_matrix = null,
_type = _kTypeLinearToSrgbGamma;
-
- /// Creates a color filter that applies the inverse of the sRGB gamma curve
- /// to the RGB channels.
const ColorFilter.srgbToLinearGamma()
: _color = null,
_blendMode = null,
@@ -3079,14 +1529,7 @@
}
}
}
-
-/// A [ColorFilter] that is backed by a native SkColorFilter.
-///
-/// This is a private class, rather than being the implementation of the public
-/// ColorFilter, because we want ColorFilter to be const constructible and
-/// efficiently comparable, so that widgets can check for ColorFilter equality to
-/// avoid repainting.
-class _ColorFilter extends NativeFieldWrapperClass2 {
+class _ColorFilter {
_ColorFilter.mode(this.creator)
: assert(creator != null), // ignore: unnecessary_null_comparison
assert(creator._type == ColorFilter._kTypeMode) {
@@ -3113,9 +1556,6 @@
_constructor();
_initSrgbToLinearGamma();
}
-
- /// The original Dart object that created the native wrapper, which retains
- /// the values used for the filter.
final ColorFilter creator;
void _constructor() { throw UnimplementedError(); }
@@ -3124,30 +1564,13 @@
void _initLinearToSrgbGamma() { throw UnimplementedError(); }
void _initSrgbToLinearGamma() { throw UnimplementedError(); }
}
-
-/// A filter operation to apply to a raster image.
-///
-/// See also:
-///
-/// * [BackdropFilter], a widget that applies [ImageFilter] to its rendering.
-/// * [ImageFiltered], a widget that applies [ImageFilter] to its children.
-/// * [SceneBuilder.pushBackdropFilter], which is the low-level API for using
-/// this class as a backdrop filter.
-/// * [SceneBuilder.pushImageFilter], which is the low-level API for using
-/// this class as a child layer filter.
abstract class ImageFilter {
- /// Creates an image filter that applies a Gaussian blur.
factory ImageFilter.blur({ double sigmaX = 0.0, double sigmaY = 0.0, TileMode tileMode = TileMode.clamp }) {
assert(sigmaX != null); // ignore: unnecessary_null_comparison
assert(sigmaY != null); // ignore: unnecessary_null_comparison
assert(tileMode != null); // ignore: unnecessary_null_comparison
return _GaussianBlurImageFilter(sigmaX: sigmaX, sigmaY: sigmaY, tileMode: tileMode);
}
-
- /// Creates an image filter that applies a matrix transformation.
- ///
- /// For example, applying a positive scale matrix (see [Matrix4.diagonal3])
- /// when used with [BackdropFilter] would magnify the background image.
factory ImageFilter.matrix(Float64List matrix4,
{ FilterQuality filterQuality = FilterQuality.low }) {
assert(matrix4 != null); // ignore: unnecessary_null_comparison
@@ -3156,12 +1579,6 @@
throw ArgumentError('"matrix4" must have 16 entries.');
return _MatrixImageFilter(data: Float64List.fromList(matrix4), filterQuality: filterQuality);
}
-
- /// Composes the `inner` filter with `outer`, to combine their effects.
- ///
- /// Creates a single [ImageFilter] that when applied, has the same effect as
- /// subsequently applying `inner` and `outer`, i.e.,
- /// result = outer(inner(source)).
factory ImageFilter.compose({ required ImageFilter outer, required ImageFilter inner }) {
assert (inner != null && outer != null); // ignore: unnecessary_null_comparison
return _ComposeImageFilter(innerFilter: inner, outerFilter: outer);
@@ -3276,16 +1693,8 @@
@override
int get hashCode => hashValues(innerFilter, outerFilter);
}
-
-/// An [ImageFilter] that is backed by a native SkImageFilter.
-///
-/// This is a private class, rather than being the implementation of the public
-/// ImageFilter, because we want ImageFilter to be efficiently comparable, so that
-/// widgets can check for ImageFilter equality to avoid repainting.
-class _ImageFilter extends NativeFieldWrapperClass2 {
+class _ImageFilter {
void _constructor() { throw UnimplementedError(); }
-
- /// Creates an image filter that applies a Gaussian blur.
_ImageFilter.blur(_GaussianBlurImageFilter filter)
: assert(filter != null), // ignore: unnecessary_null_comparison
creator = filter { // ignore: prefer_initializing_formals
@@ -3293,11 +1702,6 @@
_initBlur(filter.sigmaX, filter.sigmaY, filter.tileMode.index);
}
void _initBlur(double sigmaX, double sigmaY, int tileMode) { throw UnimplementedError(); }
-
- /// Creates an image filter that applies a matrix transformation.
- ///
- /// For example, applying a positive scale matrix (see [Matrix4.diagonal3])
- /// when used with [BackdropFilter] would magnify the background image.
_ImageFilter.matrix(_MatrixImageFilter filter)
: assert(filter != null), // ignore: unnecessary_null_comparison
creator = filter { // ignore: prefer_initializing_formals
@@ -3307,8 +1711,6 @@
_initMatrix(filter.data, filter.filterQuality.index);
}
void _initMatrix(Float64List matrix4, int filterQuality) { throw UnimplementedError(); }
-
- /// Converts a color filter to an image filter.
_ImageFilter.fromColorFilter(ColorFilter filter)
: assert(filter != null), // ignore: unnecessary_null_comparison
creator = filter { // ignore: prefer_initializing_formals
@@ -3317,8 +1719,6 @@
_initColorFilter(nativeFilter);
}
void _initColorFilter(_ColorFilter? colorFilter) { throw UnimplementedError(); }
-
- /// Composes `_innerFilter` with `_outerFilter`.
_ImageFilter.composed(_ComposeImageFilter filter)
: assert(filter != null), // ignore: unnecessary_null_comparison
creator = filter { // ignore: prefer_initializing_formals
@@ -3328,100 +1728,16 @@
_initComposed(nativeFilterOuter, nativeFilterInner);
}
void _initComposed(_ImageFilter outerFilter, _ImageFilter innerFilter) { throw UnimplementedError(); }
- /// The original Dart object that created the native wrapper, which retains
- /// the values used for the filter.
final ImageFilter creator;
}
-
-/// Base class for objects such as [Gradient] and [ImageShader] which
-/// correspond to shaders as used by [Paint.shader].
-class Shader extends NativeFieldWrapperClass2 {
- /// This class is created by the engine, and should not be instantiated
- /// or extended directly.
- @pragma('vm:entry-point')
- Shader._();
+class Shader {
+ Shader._();
}
-
-/// Defines what happens at the edge of a gradient or the sampling of a source image
-/// in an [ImageFilter].
-///
-/// A gradient is defined along a finite inner area. In the case of a linear
-/// gradient, it's between the parallel lines that are orthogonal to the line
-/// drawn between two points. In the case of radial gradients, it's the disc
-/// that covers the circle centered on a particular point up to a given radius.
-///
-/// An image filter reads source samples from a source image and performs operations
-/// on those samples to produce a result image. An image defines color samples only
-/// for pixels within the bounds of the image but some filter operations, such as a blur
-/// filter, read samples over a wide area to compute the output for a given pixel. Such
-/// a filter would need to combine samples from inside the image with hypothetical
-/// color values from outside the image.
-///
-/// This enum is used to define how the gradient or image filter should treat the regions
-/// outside that defined inner area.
-///
-/// See also:
-///
-/// * [painting.Gradient], the superclass for [LinearGradient] and
-/// [RadialGradient], as used by [BoxDecoration] et al, which works in
-/// relative coordinates and can create a [Shader] representing the gradient
-/// for a particular [Rect] on demand.
-/// * [dart:ui.Gradient], the low-level class used when dealing with the
-/// [Paint.shader] property directly, with its [Gradient.linear] and
-/// [Gradient.radial] constructors.
-/// * [dart:ui.ImageFilter.blur], an ImageFilter that may sometimes need to
-/// read samples from outside an image to combine with the pixels near the
-/// edge of the image.
// These enum values must be kept in sync with SkTileMode.
enum TileMode {
- /// Samples beyond the edge are clamped to the nearest color in the defined inner area.
- ///
- /// A gradient will paint all the regions outside the inner area with the
- /// color at the end of the color stop list closest to that region.
- ///
- /// An image filter will substitute the nearest edge pixel for any samples taken from
- /// outside its source image.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_clamp_linear.png)
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_clamp_radial.png)
clamp,
-
- /// Samples beyond the edge are repeated from the far end of the defined area.
- ///
- /// For a gradient, this technique is as if the stop points from 0.0 to 1.0 were then
- /// repeated from 1.0 to 2.0, 2.0 to 3.0, and so forth (and for linear gradients, similarly
- /// from -1.0 to 0.0, -2.0 to -1.0, etc).
- ///
- /// An image filter will treat its source image as if it were tiled across the enlarged
- /// sample space from which it reads, each tile in the same orientation as the base image.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_repeated_linear.png)
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_repeated_radial.png)
repeated,
-
- /// Samples beyond the edge are mirrored back and forth across the defined area.
- ///
- /// For a gradient, this technique is as if the stop points from 0.0 to 1.0 were then
- /// repeated backwards from 2.0 to 1.0, then forwards from 2.0 to 3.0, then backwards
- /// again from 4.0 to 3.0, and so forth (and for linear gradients, similarly in the
- /// negative direction).
- ///
- /// An image filter will treat its source image as tiled in an alternating forwards and
- /// backwards or upwards and downwards direction across the sample space from which
- /// it is reading.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_mirror_linear.png)
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_mirror_radial.png)
mirror,
-
- /// Samples beyond the edge are treated as transparent black.
- ///
- /// A gradient will render transparency over any region that is outside the circle of a
- /// radial gradient or outside the parallel lines that define the inner area of a linear
- /// gradient.
- ///
- /// An image filter will substitute transparent black for any sample it must read from
- /// outside its source image.
decal,
}
@@ -3458,41 +1774,9 @@
result[3] = pointB.dy;
return result;
}
-
-/// A shader (as used by [Paint.shader]) that renders a color gradient.
-///
-/// There are several types of gradients, represented by the various constructors
-/// on this class.
-///
-/// See also:
-///
-/// * [Gradient](https://api.flutter.dev/flutter/painting/Gradient-class.html), the class in the [painting] library.
-///
class Gradient extends Shader {
void _constructor() { throw UnimplementedError(); }
-
- /// Creates a linear gradient from `from` to `to`.
- ///
- /// If `colorStops` is provided, `colorStops[i]` is a number from 0.0 to 1.0
- /// that specifies where `color[i]` begins in the gradient. If `colorStops` is
- /// not provided, then only two stops, at 0.0 and 1.0, are implied (and
- /// `color` must therefore only have two entries).
- ///
- /// The behavior before `from` and after `to` is described by the `tileMode`
- /// argument. For details, see the [TileMode] enum.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_clamp_linear.png)
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_mirror_linear.png)
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_repeated_linear.png)
- ///
- /// If `from`, `to`, `colors`, or `tileMode` are null, or if `colors` or
- /// `colorStops` contain null values, this constructor will throw a
- /// [NoSuchMethodError].
- ///
- /// If `matrix4` is provided, the gradient fill will be transformed by the
- /// specified 4x4 matrix relative to the local coordinate system. `matrix4` must
- /// be a column-major matrix packed into a list of 16 values.
Gradient.linear(
Offset from,
Offset to,
@@ -3514,36 +1798,6 @@
_initLinear(endPointsBuffer, colorsBuffer, colorStopsBuffer, tileMode.index, matrix4);
}
void _initLinear(Float32List endPoints, Int32List colors, Float32List? colorStops, int tileMode, Float64List? matrix4) { throw UnimplementedError(); }
-
- /// Creates a radial gradient centered at `center` that ends at `radius`
- /// distance from the center.
- ///
- /// If `colorStops` is provided, `colorStops[i]` is a number from 0.0 to 1.0
- /// that specifies where `color[i]` begins in the gradient. If `colorStops` is
- /// not provided, then only two stops, at 0.0 and 1.0, are implied (and
- /// `color` must therefore only have two entries).
- ///
- /// The behavior before and after the radius is described by the `tileMode`
- /// argument. For details, see the [TileMode] enum.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_clamp_radial.png)
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_mirror_radial.png)
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_repeated_radial.png)
- ///
- /// If `center`, `radius`, `colors`, or `tileMode` are null, or if `colors` or
- /// `colorStops` contain null values, this constructor will throw a
- /// [NoSuchMethodError].
- ///
- /// If `matrix4` is provided, the gradient fill will be transformed by the
- /// specified 4x4 matrix relative to the local coordinate system. `matrix4` must
- /// be a column-major matrix packed into a list of 16 values.
- ///
- /// If `focal` is provided and not equal to `center` and `focalRadius` is
- /// provided and not equal to 0.0, the generated shader will be a two point
- /// conical radial gradient, with `focal` being the center of the focal
- /// circle and `focalRadius` being the radius of that circle. If `focal` is
- /// provided and not equal to `center`, at least one of the two offsets must
- /// not be equal to [Offset.zero].
Gradient.radial(
Offset center,
double radius,
@@ -3575,33 +1829,6 @@
}
void _initRadial(double centerX, double centerY, double radius, Int32List colors, Float32List? colorStops, int tileMode, Float64List? matrix4) { throw UnimplementedError(); }
void _initConical(double startX, double startY, double startRadius, double endX, double endY, double endRadius, Int32List colors, Float32List? colorStops, int tileMode, Float64List? matrix4) { throw UnimplementedError(); }
-
- /// Creates a sweep gradient centered at `center` that starts at `startAngle`
- /// and ends at `endAngle`.
- ///
- /// `startAngle` and `endAngle` should be provided in radians, with zero
- /// radians being the horizontal line to the right of the `center` and with
- /// positive angles going clockwise around the `center`.
- ///
- /// If `colorStops` is provided, `colorStops[i]` is a number from 0.0 to 1.0
- /// that specifies where `color[i]` begins in the gradient. If `colorStops` is
- /// not provided, then only two stops, at 0.0 and 1.0, are implied (and
- /// `color` must therefore only have two entries).
- ///
- /// The behavior before `startAngle` and after `endAngle` is described by the
- /// `tileMode` argument. For details, see the [TileMode] enum.
- ///
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_clamp_sweep.png)
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_mirror_sweep.png)
- /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_repeated_sweep.png)
- ///
- /// If `center`, `colors`, `tileMode`, `startAngle`, or `endAngle` are null,
- /// or if `colors` or `colorStops` contain null values, this constructor will
- /// throw a [NoSuchMethodError].
- ///
- /// If `matrix4` is provided, the gradient fill will be transformed by the
- /// specified 4x4 matrix relative to the local coordinate system. `matrix4` must
- /// be a column-major matrix packed into a list of 16 values.
Gradient.sweep(
Offset center,
List<Color> colors, [
@@ -3636,16 +1863,8 @@
}
}
}
-
-/// A shader (as used by [Paint.shader]) that tiles an image.
class ImageShader extends Shader {
- /// Creates an image-tiling shader. The first argument specifies the image to
- /// tile. The second and third arguments specify the [TileMode] for the x
- /// direction and y direction respectively. The fourth argument gives the
- /// matrix to apply to the effect. All the arguments are required and must not
- /// be null.
- @pragma('vm:entry-point')
- ImageShader(Image image, TileMode tmx, TileMode tmy, Float64List matrix4) :
+ ImageShader(Image image, TileMode tmx, TileMode tmy, Float64List matrix4) :
// ignore: unnecessary_null_comparison
assert(image != null), // image is checked on the engine side
assert(tmx != null), // ignore: unnecessary_null_comparison
@@ -3660,33 +1879,13 @@
void _constructor() { throw UnimplementedError(); }
void _initWithImage(_Image image, int tmx, int tmy, Float64List matrix4) { throw UnimplementedError(); }
}
-
-/// Defines how a list of points is interpreted when drawing a set of triangles.
-///
-/// Used by [Canvas.drawVertices].
// These enum values must be kept in sync with SkVertices::VertexMode.
enum VertexMode {
- /// Draw each sequence of three points as the vertices of a triangle.
triangles,
-
- /// Draw each sliding window of three points as the vertices of a triangle.
triangleStrip,
-
- /// Draw the first point and each sliding window of two points as the vertices of a triangle.
triangleFan,
}
-
-/// A set of vertex data used by [Canvas.drawVertices].
-class Vertices extends NativeFieldWrapperClass2 {
- /// Creates a set of vertex data for use with [Canvas.drawVertices].
- ///
- /// The [mode] and [positions] parameters must not be null.
- ///
- /// If the [textureCoordinates] or [colors] parameters are provided, they must
- /// be the same length as [positions].
- ///
- /// If the [indices] parameter is provided, all values in the list must be
- /// valid index values for [positions].
+class Vertices {
Vertices(
VertexMode mode,
List<Offset> positions, {
@@ -3716,25 +1915,6 @@
if (!_init(this, mode.index, encodedPositions, encodedTextureCoordinates, encodedColors, encodedIndices))
throw ArgumentError('Invalid configuration for vertices.');
}
-
- /// Creates a set of vertex data for use with [Canvas.drawVertices], directly
- /// using the encoding methods of [new Vertices].
- ///
- /// The [mode] parameter must not be null.
- ///
- /// The [positions] list is interpreted as a list of repeated pairs of x,y
- /// coordinates. It must not be null.
- ///
- /// The [textureCoordinates] list is interpreted as a list of repeated pairs
- /// of x,y coordinates, and must be the same length of [positions] if it
- /// is not null.
- ///
- /// The [colors] list is interpreted as a list of RGBA encoded colors, similar
- /// to [Color.value]. It must be half length of [positions] if it is not
- /// null.
- ///
- /// If the [indices] list is provided, all values in the list must be
- /// valid index values for [positions].
Vertices.raw(
VertexMode mode,
Float32List positions, {
@@ -3761,814 +1941,517 @@
Int32List? colors,
Uint16List? indices) { throw UnimplementedError(); }
}
-
-/// Defines how a list of points is interpreted when drawing a set of points.
-///
// ignore: deprecated_member_use
-/// Used by [Canvas.drawPoints].
// These enum values must be kept in sync with SkCanvas::PointMode.
enum PointMode {
- /// Draw each point separately.
- ///
- /// If the [Paint.strokeCap] is [StrokeCap.round], then each point is drawn
- /// as a circle with the diameter of the [Paint.strokeWidth], filled as
- /// described by the [Paint] (ignoring [Paint.style]).
- ///
- /// Otherwise, each point is drawn as an axis-aligned square with sides of
- /// length [Paint.strokeWidth], filled as described by the [Paint] (ignoring
- /// [Paint.style]).
points,
-
- /// Draw each sequence of two points as a line segment.
- ///
- /// If the number of points is odd, then the last point is ignored.
- ///
- /// The lines are stroked as described by the [Paint] (ignoring
- /// [Paint.style]).
lines,
-
- /// Draw the entire sequence of point as one line.
- ///
- /// The lines are stroked as described by the [Paint] (ignoring
- /// [Paint.style]).
polygon,
}
-
-/// Defines how a new clip region should be merged with the existing clip
-/// region.
-///
-/// Used by [Canvas.clipRect].
enum ClipOp {
- /// Subtract the new region from the existing region.
difference,
-
- /// Intersect the new region from the existing region.
intersect,
}
-/// An interface for recording graphical operations.
-///
-/// [Canvas] objects are used in creating [Picture] objects, which can
-/// themselves be used with a [SceneBuilder] to build a [Scene]. In
-/// normal usage, however, this is all handled by the framework.
-///
-/// A canvas has a current transformation matrix which is applied to all
-/// operations. Initially, the transformation matrix is the identity transform.
-/// It can be modified using the [translate], [scale], [rotate], [skew],
-/// and [transform] methods.
-///
-/// A canvas also has a current clip region which is applied to all operations.
-/// Initially, the clip region is infinite. It can be modified using the
-/// [clipRect], [clipRRect], and [clipPath] methods.
-///
-/// The current transform and clip can be saved and restored using the stack
-/// managed by the [save], [saveLayer], and [restore] methods.
-class Canvas extends NativeFieldWrapperClass2 {
- /// Creates a canvas for recording graphical operations into the
- /// given picture recorder.
- ///
- /// Graphical operations that affect pixels entirely outside the given
- /// `cullRect` might be discarded by the implementation. However, the
- /// implementation might draw outside these bounds if, for example, a command
- /// draws partially inside and outside the `cullRect`. To ensure that pixels
- /// outside a given region are discarded, consider using a [clipRect]. The
- /// `cullRect` is optional; by default, all operations are kept.
- ///
- /// To end the recording, call [PictureRecorder.endRecording] on the
- /// given recorder.
- @pragma('vm:entry-point')
- Canvas(PictureRecorder recorder, [ Rect? cullRect ]) : assert(recorder != null) { // ignore: unnecessary_null_comparison
- if (recorder.isRecording)
+class _CanvasMethods {
+ static const int save = 0;
+ static const int saveLayer = 1;
+ static const int restore = 2;
+ static const int translate = 3;
+ static const int scale = 4;
+ static const int rotate = 5;
+ static const int skew = 6;
+ static const int transform = 7;
+ static const int clipRect = 8;
+ static const int clipRRect = 9;
+ static const int clipPath = 10;
+ static const int drawColor = 11;
+ static const int drawLine = 12;
+ static const int drawPaint = 13;
+ static const int drawRect = 14;
+ static const int drawRRect = 15;
+ static const int drawDRRect = 16;
+ static const int drawOval = 17;
+ static const int drawCircle = 18;
+ static const int drawArc = 19;
+ static const int drawPath = 20;
+ static const int drawImage = 21;
+ static const int drawImageRect = 22;
+ static const int drawImageNine = 23;
+ static const int drawPicture = 24;
+ static const int drawParagraph = 25;
+ static const int drawPoints = 26;
+ static const int drawVertices = 27;
+ static const int drawAtlas = 28;
+ static const int drawShadow = 29;
+}
+class Canvas {
+ Canvas(this._recorder, [ Rect? cullRect ]) : _cullRect = cullRect ?? Rect.largest, assert(_recorder != null) { // ignore: unnecessary_null_comparison
+ if (_recorder!.isRecording)
throw ArgumentError('"recorder" must not already be associated with another Canvas.');
- _recorder = recorder;
_recorder!._canvas = this;
- cullRect ??= Rect.largest;
- _constructor(recorder, cullRect.left, cullRect.top, cullRect.right, cullRect.bottom);
}
- void _constructor(PictureRecorder recorder,
- double left,
- double top,
- double right,
- double bottom) { throw UnimplementedError(); }
// The underlying Skia SkCanvas is owned by the PictureRecorder used to create this Canvas.
// The Canvas holds a reference to the PictureRecorder to prevent the recorder from being
// garbage collected until PictureRecorder.endRecording is called.
PictureRecorder? _recorder;
+ final Rect _cullRect;
- /// Saves a copy of the current transform and clip on the save stack.
- ///
- /// Call [restore] to pop the save stack.
- ///
- /// See also:
- ///
- /// * [saveLayer], which does the same thing but additionally also groups the
- /// commands done until the matching [restore].
- void save() { throw UnimplementedError(); }
+ double _currentX = 0;
+ double _currentY = 0;
+ Uint8List _methods = Uint8List(10);
+ int _methodsLength = 0;
+ Float32List _data = Float32List(30);
+ int _dataLength = 0;
+ List<Object> _objects = <Object>[];
+ bool _isEmpty = true;
+ double _left = 0;
+ double _top = 0;
+ double _right = 0;
+ double _bottom = 0;
+ int _saveCount = 0;
- /// Saves a copy of the current transform and clip on the save stack, and then
- /// creates a new group which subsequent calls will become a part of. When the
- /// save stack is later popped, the group will be flattened into a layer and
- /// have the given `paint`'s [Paint.colorFilter] and [Paint.blendMode]
- /// applied.
- ///
- /// This lets you create composite effects, for example making a group of
- /// drawing commands semi-transparent. Without using [saveLayer], each part of
- /// the group would be painted individually, so where they overlap would be
- /// darker than where they do not. By using [saveLayer] to group them
- /// together, they can be drawn with an opaque color at first, and then the
- /// entire group can be made transparent using the [saveLayer]'s paint.
- ///
- /// Call [restore] to pop the save stack and apply the paint to the group.
- ///
- /// ## Using saveLayer with clips
- ///
- /// When a rectangular clip operation (from [clipRect]) is not axis-aligned
- /// with the raster buffer, or when the clip operation is not rectilinear
- /// (e.g. because it is a rounded rectangle clip created by [clipRRect] or an
- /// arbitrarily complicated path clip created by [clipPath]), the edge of the
- /// clip needs to be anti-aliased.
- ///
- /// If two draw calls overlap at the edge of such a clipped region, without
- /// using [saveLayer], the first drawing will be anti-aliased with the
- /// background first, and then the second will be anti-aliased with the result
- /// of blending the first drawing and the background. On the other hand, if
- /// [saveLayer] is used immediately after establishing the clip, the second
- /// drawing will cover the first in the layer, and thus the second alone will
- /// be anti-aliased with the background when the layer is clipped and
- /// composited (when [restore] is called).
- ///
- /// For example, this [CustomPainter.paint] method paints a clean white
- /// rounded rectangle:
- ///
- /// ```dart
- /// void paint(Canvas canvas, Size size) {
- /// Rect rect = Offset.zero & size;
- /// canvas.save();
- /// canvas.clipRRect(new RRect.fromRectXY(rect, 100.0, 100.0));
- /// canvas.saveLayer(rect, Paint());
- /// canvas.drawPaint(new Paint()..color = Colors.red);
- /// canvas.drawPaint(new Paint()..color = Colors.white);
- /// canvas.restore();
- /// canvas.restore();
- /// }
- /// ```
- ///
- /// On the other hand, this one renders a red outline, the result of the red
- /// paint being anti-aliased with the background at the clip edge, then the
- /// white paint being similarly anti-aliased with the background _including
- /// the clipped red paint_:
- ///
- /// ```dart
- /// void paint(Canvas canvas, Size size) {
- /// // (this example renders poorly, prefer the example above)
- /// Rect rect = Offset.zero & size;
- /// canvas.save();
- /// canvas.clipRRect(new RRect.fromRectXY(rect, 100.0, 100.0));
- /// canvas.drawPaint(new Paint()..color = Colors.red);
- /// canvas.drawPaint(new Paint()..color = Colors.white);
- /// canvas.restore();
- /// }
- /// ```
- ///
- /// This point is moot if the clip only clips one draw operation. For example,
- /// the following paint method paints a pair of clean white rounded
- /// rectangles, even though the clips are not done on a separate layer:
- ///
- /// ```dart
- /// void paint(Canvas canvas, Size size) {
- /// canvas.save();
- /// canvas.clipRRect(new RRect.fromRectXY(Offset.zero & (size / 2.0), 50.0, 50.0));
- /// canvas.drawPaint(new Paint()..color = Colors.white);
- /// canvas.restore();
- /// canvas.save();
- /// canvas.clipRRect(new RRect.fromRectXY(size.center(Offset.zero) & (size / 2.0), 50.0, 50.0));
- /// canvas.drawPaint(new Paint()..color = Colors.white);
- /// canvas.restore();
- /// }
- /// ```
- ///
- /// (Incidentally, rather than using [clipRRect] and [drawPaint] to draw
- /// rounded rectangles like this, prefer the [drawRRect] method. These
- /// examples are using [drawPaint] as a proxy for "complicated draw operations
- /// that will get clipped", to illustrate the point.)
- ///
- /// ## Performance considerations
- ///
- /// Generally speaking, [saveLayer] is relatively expensive.
- ///
- /// There are a several different hardware architectures for GPUs (graphics
- /// processing units, the hardware that handles graphics), but most of them
- /// involve batching commands and reordering them for performance. When layers
- /// are used, they cause the rendering pipeline to have to switch render
- /// target (from one layer to another). Render target switches can flush the
- /// GPU's command buffer, which typically means that optimizations that one
- /// could get with larger batching are lost. Render target switches also
- /// generate a lot of memory churn because the GPU needs to copy out the
- /// current frame buffer contents from the part of memory that's optimized for
- /// writing, and then needs to copy it back in once the previous render target
- /// (layer) is restored.
- ///
- /// See also:
- ///
- /// * [save], which saves the current state, but does not create a new layer
- /// for subsequent commands.
- /// * [BlendMode], which discusses the use of [Paint.blendMode] with
- /// [saveLayer].
- void saveLayer(Rect? bounds, Paint paint) {
- assert(paint != null); // ignore: unnecessary_null_comparison
- if (bounds == null) {
- _saveLayerWithoutBounds(paint._objects, paint._data);
+ void _updateBounds(double x, double y) {
+ if (_isEmpty) {
+ _left = _right = x;
+ _top = _bottom = y;
+ _isEmpty = false;
} else {
- assert(_rectIsValid(bounds));
- _saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom,
- paint._objects, paint._data);
+ if (x < _left) {
+ _left = x;
+ }
+ if (x > _right) {
+ _right = x;
+ }
+ if (y < _top) {
+ _top = y;
+ }
+ if (y > _bottom) {
+ _bottom = y;
+ }
}
}
- void _saveLayerWithoutBounds(List<dynamic>? paintObjects, ByteData paintData)
- { throw UnimplementedError(); }
- void _saveLayer(double left,
- double top,
- double right,
- double bottom,
- List<dynamic>? paintObjects,
- ByteData paintData) { throw UnimplementedError(); }
- /// Pops the current save stack, if there is anything to pop.
- /// Otherwise, does nothing.
- ///
- /// Use [save] and [saveLayer] to push state onto the stack.
- ///
- /// If the state was pushed with with [saveLayer], then this call will also
- /// cause the new layer to be composited into the previous layer.
- void restore() { throw UnimplementedError(); }
+ _updateBoundsFromCurrent() {
+ _updateBounds(_currentX, _currentY);
+ }
- /// Returns the number of items on the save stack, including the
- /// initial state. This means it returns 1 for a clean canvas, and
- /// that each call to [save] and [saveLayer] increments it, and that
- /// each matching call to [restore] decrements it.
- ///
- /// This number cannot go below 1.
- int getSaveCount() { throw UnimplementedError(); }
+ void _addObject(Object object) {
+ _objects.add(object);
+ }
- /// Add a translation to the current transform, shifting the coordinate space
- /// horizontally by the first argument and vertically by the second argument.
- void translate(double dx, double dy) { throw UnimplementedError(); }
+ void _addMethod(int methodId) {
+ if (_methodsLength >= _methods.length) {
+ final Uint8List newList = Uint8List(_methods.length * 2);
+ for (int i = 0; i < _methodsLength; i++) {
+ newList[i] = _methods[i];
+ }
+ _methods = newList;
+ }
+ _methods[_methodsLength] = methodId;
+ _methodsLength += 1;
+ }
- /// Add an axis-aligned scale to the current transform, scaling by the first
- /// argument in the horizontal direction and the second in the vertical
- /// direction.
- ///
- /// If [sy] is unspecified, [sx] will be used for the scale in both
- /// directions.
- void scale(double sx, [double? sy]) => _scale(sx, sy ?? sx);
+ void _ensureDataLength(int newLength) {
+ if (_data.length >= newLength) {
+ return;
+ }
- void _scale(double sx, double sy) { throw UnimplementedError(); }
+ final Float32List newList = Float32List(_data.length * 2);
+ for (int i = 0; i < _dataLength; i++) {
+ newList[i] = _data[i];
+ }
+ _data = newList;
+ }
- /// Add a rotation to the current transform. The argument is in radians clockwise.
- void rotate(double radians) { throw UnimplementedError(); }
+ void _addData1(double a) {
+ _ensureDataLength(_dataLength + 1);
+ _data[_dataLength++] = a;
+ }
- /// Add an axis-aligned skew to the current transform, with the first argument
- /// being the horizontal skew in rise over run units clockwise around the
- /// origin, and the second argument being the vertical skew in rise over run
- /// units clockwise around the origin.
- void skew(double sx, double sy) { throw UnimplementedError(); }
+ void _addData2(double a, double b) {
+ _ensureDataLength(_dataLength + 2);
+ _data[_dataLength++] = a;
+ _data[_dataLength++] = b;
+ }
- /// Multiply the current transform by the specified 4⨉4 transformation matrix
- /// specified as a list of values in column-major order.
+ void _addData4(double a, double b, double c, double d) {
+ _ensureDataLength(_dataLength + 4);
+ _data[_dataLength++] = a;
+ _data[_dataLength++] = b;
+ _data[_dataLength++] = c;
+ _data[_dataLength++] = d;
+ }
+
+ void _addData5(double a, double b, double c, double d, double e) {
+ _ensureDataLength(_dataLength + 5);
+ _data[_dataLength++] = a;
+ _data[_dataLength++] = b;
+ _data[_dataLength++] = c;
+ _data[_dataLength++] = d;
+ _data[_dataLength++] = e;
+ }
+
+ void _addData6(double a, double b, double c, double d, double e, double f) {
+ _ensureDataLength(_dataLength + 6);
+ _data[_dataLength++] = a;
+ _data[_dataLength++] = b;
+ _data[_dataLength++] = c;
+ _data[_dataLength++] = d;
+ _data[_dataLength++] = e;
+ _data[_dataLength++] = f;
+ }
+
+ void _addData7(double a, double b, double c, double d, double e, double f, double g) {
+ _ensureDataLength(_dataLength + 7);
+ _data[_dataLength++] = a;
+ _data[_dataLength++] = b;
+ _data[_dataLength++] = c;
+ _data[_dataLength++] = d;
+ _data[_dataLength++] = e;
+ _data[_dataLength++] = f;
+ _data[_dataLength++] = g;
+ }
+
+ void _addData12(double a, double b, double c, double d, double e, double f,
+ double g, double h, double i, double j, double k, double l) {
+ _ensureDataLength(_dataLength + 12);
+ _data[_dataLength++] = a;
+ _data[_dataLength++] = b;
+ _data[_dataLength++] = c;
+ _data[_dataLength++] = d;
+ _data[_dataLength++] = e;
+ _data[_dataLength++] = f;
+ _data[_dataLength++] = g;
+ _data[_dataLength++] = h;
+ _data[_dataLength++] = i;
+ _data[_dataLength++] = j;
+ _data[_dataLength++] = k;
+ _data[_dataLength++] = l;
+ }
+
+ void save() {
+ _addMethod(_CanvasMethods.save);
+ _saveCount += 1;
+ }
+ void saveLayer(Rect? bounds, Paint paint) {
+ assert(paint != null); // ignore: unnecessary_null_comparison
+ _addMethod(_CanvasMethods.saveLayer);
+ _addObject(paint);
+ if (bounds != null) {
+ assert(_rectIsValid(bounds));
+ _addData4(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ }
+ }
+
+ void restore() {
+ _addMethod(_CanvasMethods.restore);
+ _saveCount -= 1;
+ }
+
+ int getSaveCount() => _saveCount;
+
+ void translate(double dx, double dy) {
+ _addMethod(_CanvasMethods.translate);
+ _addData2(dx, dy);
+ }
+
+ void scale(double sx, [double? sy]) {
+ _addMethod(_CanvasMethods.scale);
+ _addData2(sx, sy ?? 1.0);
+ }
+
+ void rotate(double radians) {
+ _addMethod(_CanvasMethods.rotate);
+ _addData1(radians);
+ }
+
+ void skew(double sx, double sy) {
+ _addMethod(_CanvasMethods.skew);
+ _addData2(sx, sy);
+ }
+
void transform(Float64List matrix4) {
assert(matrix4 != null); // ignore: unnecessary_null_comparison
if (matrix4.length != 16)
throw ArgumentError('"matrix4" must have 16 entries.');
- _transform(matrix4);
- }
- void _transform(Float64List matrix4) { throw UnimplementedError(); }
- /// Reduces the clip region to the intersection of the current clip and the
- /// given rectangle.
- ///
- /// If [doAntiAlias] is true, then the clip will be anti-aliased.
- ///
- /// If multiple draw commands intersect with the clip boundary, this can result
- /// in incorrect blending at the clip boundary. See [saveLayer] for a
- /// discussion of how to address that.
- ///
- /// Use [ClipOp.difference] to subtract the provided rectangle from the
- /// current clip.
+ _addMethod(_CanvasMethods.transform);
+ _addObject(matrix4);
+ }
+
void clipRect(Rect rect, { ClipOp clipOp = ClipOp.intersect, bool doAntiAlias = true }) {
assert(_rectIsValid(rect));
assert(clipOp != null); // ignore: unnecessary_null_comparison
assert(doAntiAlias != null); // ignore: unnecessary_null_comparison
- _clipRect(rect.left, rect.top, rect.right, rect.bottom, clipOp.index, doAntiAlias);
+ _addMethod(_CanvasMethods.clipRect);
+ _addData6(rect.left, rect.top, rect.right, rect.bottom, clipOp.index.toDouble(), doAntiAlias ? 1 : 0);
}
- void _clipRect(double left,
- double top,
- double right,
- double bottom,
- int clipOp,
- bool doAntiAlias) { throw UnimplementedError(); }
- /// Reduces the clip region to the intersection of the current clip and the
- /// given rounded rectangle.
- ///
- /// If [doAntiAlias] is true, then the clip will be anti-aliased.
- ///
- /// If multiple draw commands intersect with the clip boundary, this can result
- /// in incorrect blending at the clip boundary. See [saveLayer] for a
- /// discussion of how to address that and some examples of using [clipRRect].
void clipRRect(RRect rrect, {bool doAntiAlias = true}) {
assert(_rrectIsValid(rrect));
assert(doAntiAlias != null); // ignore: unnecessary_null_comparison
- _clipRRect(rrect._value32, doAntiAlias);
+ _addMethod(_CanvasMethods.clipRRect);
+ _addData12(
+ rrect.left,
+ rrect.top,
+ rrect.right,
+ rrect.bottom,
+ rrect.tlRadiusX,
+ rrect.tlRadiusY,
+ rrect.trRadiusX,
+ rrect.trRadiusY,
+ rrect.brRadiusX,
+ rrect.brRadiusY,
+ rrect.blRadiusX,
+ rrect.blRadiusY,
+ );
+ _addData1(doAntiAlias ? 1 : 0);
}
- void _clipRRect(Float32List rrect, bool doAntiAlias) { throw UnimplementedError(); }
- /// Reduces the clip region to the intersection of the current clip and the
- /// given [Path].
- ///
- /// If [doAntiAlias] is true, then the clip will be anti-aliased.
- ///
- /// If multiple draw commands intersect with the clip boundary, this can result
- /// multiple draw commands intersect with the clip boundary, this can result
- /// in incorrect blending at the clip boundary. See [saveLayer] for a
- /// discussion of how to address that.
void clipPath(Path path, {bool doAntiAlias = true}) {
// ignore: unnecessary_null_comparison
assert(path != null); // path is checked on the engine side
assert(doAntiAlias != null); // ignore: unnecessary_null_comparison
- _clipPath(path, doAntiAlias);
+ _addMethod(_CanvasMethods.clipPath);
+ _addObject(path);
+ _addData1(doAntiAlias ? 1 : 0);
}
- void _clipPath(Path path, bool doAntiAlias) { throw UnimplementedError(); }
- /// Paints the given [Color] onto the canvas, applying the given
- /// [BlendMode], with the given color being the source and the background
- /// being the destination.
void drawColor(Color color, BlendMode blendMode) {
assert(color != null); // ignore: unnecessary_null_comparison
assert(blendMode != null); // ignore: unnecessary_null_comparison
- _drawColor(color.value, blendMode.index);
+ _addMethod(_CanvasMethods.drawColor);
+ _addData2(
+ color.value.toDouble(),
+ blendMode.index.toDouble(),
+ );
}
- void _drawColor(int color, int blendMode) { throw UnimplementedError(); }
- /// Draws a line between the given points using the given paint. The line is
- /// stroked, the value of the [Paint.style] is ignored for this call.
- ///
- /// The `p1` and `p2` arguments are interpreted as offsets from the origin.
void drawLine(Offset p1, Offset p2, Paint paint) {
assert(_offsetIsValid(p1));
assert(_offsetIsValid(p2));
assert(paint != null); // ignore: unnecessary_null_comparison
- _drawLine(p1.dx, p1.dy, p2.dx, p2.dy, paint._objects, paint._data);
+ _addMethod(_CanvasMethods.drawLine);
+ _addObject(paint);
+ _addData4(p1.dx, p1.dy, p2.dx, p2.dy);
}
- void _drawLine(double x1,
- double y1,
- double x2,
- double y2,
- List<dynamic>? paintObjects,
- ByteData paintData) { throw UnimplementedError(); }
- /// Fills the canvas with the given [Paint].
- ///
- /// To fill the canvas with a solid color and blend mode, consider
- /// [drawColor] instead.
void drawPaint(Paint paint) {
assert(paint != null); // ignore: unnecessary_null_comparison
- _drawPaint(paint._objects, paint._data);
+ _addMethod(_CanvasMethods.drawPaint);
+ _addObject(paint);
}
- void _drawPaint(List<dynamic>? paintObjects, ByteData paintData) { throw UnimplementedError(); }
- /// Draws a rectangle with the given [Paint]. Whether the rectangle is filled
- /// or stroked (or both) is controlled by [Paint.style].
void drawRect(Rect rect, Paint paint) {
assert(_rectIsValid(rect));
assert(paint != null); // ignore: unnecessary_null_comparison
- _drawRect(rect.left, rect.top, rect.right, rect.bottom,
- paint._objects, paint._data);
+ _addMethod(_CanvasMethods.drawRect);
+ _addObject(paint);
+ _addData4(rect.left, rect.top, rect.right, rect.bottom);
}
- void _drawRect(double left,
- double top,
- double right,
- double bottom,
- List<dynamic>? paintObjects,
- ByteData paintData) { throw UnimplementedError(); }
- /// Draws a rounded rectangle with the given [Paint]. Whether the rectangle is
- /// filled or stroked (or both) is controlled by [Paint.style].
void drawRRect(RRect rrect, Paint paint) {
assert(_rrectIsValid(rrect));
assert(paint != null); // ignore: unnecessary_null_comparison
- _drawRRect(rrect._value32, paint._objects, paint._data);
+ _addMethod(_CanvasMethods.drawRRect);
+ _addObject(paint);
+ _addData12(
+ rrect.left,
+ rrect.top,
+ rrect.right,
+ rrect.bottom,
+ rrect.tlRadiusX,
+ rrect.tlRadiusY,
+ rrect.trRadiusX,
+ rrect.trRadiusY,
+ rrect.brRadiusX,
+ rrect.brRadiusY,
+ rrect.blRadiusX,
+ rrect.blRadiusY,
+ );
}
- void _drawRRect(Float32List rrect,
- List<dynamic>? paintObjects,
- ByteData paintData) { throw UnimplementedError(); }
- /// Draws a shape consisting of the difference between two rounded rectangles
- /// with the given [Paint]. Whether this shape is filled or stroked (or both)
- /// is controlled by [Paint.style].
- ///
- /// This shape is almost but not quite entirely unlike an annulus.
void drawDRRect(RRect outer, RRect inner, Paint paint) {
assert(_rrectIsValid(outer));
assert(_rrectIsValid(inner));
assert(paint != null); // ignore: unnecessary_null_comparison
- _drawDRRect(outer._value32, inner._value32, paint._objects, paint._data);
+ _addMethod(_CanvasMethods.drawDRRect);
+ _addObject(paint);
+ _addData12(
+ outer.left,
+ outer.top,
+ outer.right,
+ outer.bottom,
+ outer.tlRadiusX,
+ outer.tlRadiusY,
+ outer.trRadiusX,
+ outer.trRadiusY,
+ outer.brRadiusX,
+ outer.brRadiusY,
+ outer.blRadiusX,
+ outer.blRadiusY,
+ );
+ _addData12(
+ inner.left,
+ inner.top,
+ inner.right,
+ inner.bottom,
+ inner.tlRadiusX,
+ inner.tlRadiusY,
+ inner.trRadiusX,
+ inner.trRadiusY,
+ inner.brRadiusX,
+ inner.brRadiusY,
+ inner.blRadiusX,
+ inner.blRadiusY,
+ );
}
- void _drawDRRect(Float32List outer,
- Float32List inner,
- List<dynamic>? paintObjects,
- ByteData paintData) { throw UnimplementedError(); }
- /// Draws an axis-aligned oval that fills the given axis-aligned rectangle
- /// with the given [Paint]. Whether the oval is filled or stroked (or both) is
- /// controlled by [Paint.style].
void drawOval(Rect rect, Paint paint) {
assert(_rectIsValid(rect));
assert(paint != null); // ignore: unnecessary_null_comparison
- _drawOval(rect.left, rect.top, rect.right, rect.bottom,
- paint._objects, paint._data);
+ _addMethod(_CanvasMethods.drawOval);
+ _addObject(paint);
+ _addData4(rect.left, rect.top, rect.right, rect.bottom);
}
- void _drawOval(double left,
- double top,
- double right,
- double bottom,
- List<dynamic>? paintObjects,
- ByteData paintData) { throw UnimplementedError(); }
- /// Draws a circle centered at the point given by the first argument and
- /// that has the radius given by the second argument, with the [Paint] given in
- /// the third argument. Whether the circle is filled or stroked (or both) is
- /// controlled by [Paint.style].
void drawCircle(Offset c, double radius, Paint paint) {
assert(_offsetIsValid(c));
assert(paint != null); // ignore: unnecessary_null_comparison
- _drawCircle(c.dx, c.dy, radius, paint._objects, paint._data);
+ _addMethod(_CanvasMethods.drawCircle);
+ _addObject(paint);
+ _addData2(c.dx, c.dy);
+ _addData1(radius);
}
- void _drawCircle(double x,
- double y,
- double radius,
- List<dynamic>? paintObjects,
- ByteData paintData) { throw UnimplementedError(); }
- /// Draw an arc scaled to fit inside the given rectangle.
- ///
- /// It starts from `startAngle` radians around the oval up to
- /// `startAngle` + `sweepAngle` radians around the oval, with zero radians
- /// being the point on the right hand side of the oval that crosses the
- /// horizontal line that intersects the center of the rectangle and with positive
- /// angles going clockwise around the oval. If `useCenter` is true, the arc is
- /// closed back to the center, forming a circle sector. Otherwise, the arc is
- /// not closed, forming a circle segment.
- ///
- /// This method is optimized for drawing arcs and should be faster than [Path.arcTo].
void drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint) {
assert(_rectIsValid(rect));
assert(paint != null); // ignore: unnecessary_null_comparison
- _drawArc(rect.left, rect.top, rect.right, rect.bottom, startAngle,
- sweepAngle, useCenter, paint._objects, paint._data);
+ _addMethod(_CanvasMethods.drawArc);
+ _addObject(paint);
+ _addData7(rect.left, rect.top, rect.right, rect.bottom, startAngle,
+ sweepAngle, useCenter ? 1 : 0);
}
- void _drawArc(double left,
- double top,
- double right,
- double bottom,
- double startAngle,
- double sweepAngle,
- bool useCenter,
- List<dynamic>? paintObjects,
- ByteData paintData) { throw UnimplementedError(); }
- /// Draws the given [Path] with the given [Paint].
- ///
- /// Whether this shape is filled or stroked (or both) is controlled by
- /// [Paint.style]. If the path is filled, then sub-paths within it are
- /// implicitly closed (see [Path.close]).
void drawPath(Path path, Paint paint) {
// ignore: unnecessary_null_comparison
assert(path != null); // path is checked on the engine side
assert(paint != null); // ignore: unnecessary_null_comparison
- _drawPath(path, paint._objects, paint._data);
+ _addMethod(_CanvasMethods.drawPath);
+ _addObject(paint);
+ _addObject(path);
}
- void _drawPath(Path path,
- List<dynamic>? paintObjects,
- ByteData paintData) { throw UnimplementedError(); }
- /// Draws the given [Image] into the canvas with its top-left corner at the
- /// given [Offset]. The image is composited into the canvas using the given [Paint].
void drawImage(Image image, Offset offset, Paint paint) {
// ignore: unnecessary_null_comparison
assert(image != null); // image is checked on the engine side
assert(_offsetIsValid(offset));
assert(paint != null); // ignore: unnecessary_null_comparison
- _drawImage(image._image, offset.dx, offset.dy, paint._objects, paint._data);
+ _addMethod(_CanvasMethods.drawImage);
+ _addObject(paint);
+ _addObject(image);
}
- void _drawImage(_Image image,
- double x,
- double y,
- List<dynamic>? paintObjects,
- ByteData paintData) { throw UnimplementedError(); }
- /// Draws the subset of the given image described by the `src` argument into
- /// the canvas in the axis-aligned rectangle given by the `dst` argument.
- ///
- /// This might sample from outside the `src` rect by up to half the width of
- /// an applied filter.
- ///
- /// Multiple calls to this method with different arguments (from the same
- /// image) can be batched into a single call to [drawAtlas] to improve
- /// performance.
void drawImageRect(Image image, Rect src, Rect dst, Paint paint) {
// ignore: unnecessary_null_comparison
assert(image != null); // image is checked on the engine side
assert(_rectIsValid(src));
assert(_rectIsValid(dst));
assert(paint != null); // ignore: unnecessary_null_comparison
- _drawImageRect(image._image,
- src.left,
- src.top,
- src.right,
- src.bottom,
- dst.left,
- dst.top,
- dst.right,
- dst.bottom,
- paint._objects,
- paint._data);
+ _addMethod(_CanvasMethods.drawImageRect);
+ _addObject(paint);
+ _addObject(image);
+ _addData4(
+ src.left,
+ src.top,
+ src.right,
+ src.bottom,
+ );
+ _addData4(
+ dst.left,
+ dst.top,
+ dst.right,
+ dst.bottom,
+ );
}
- void _drawImageRect(_Image image,
- double srcLeft,
- double srcTop,
- double srcRight,
- double srcBottom,
- double dstLeft,
- double dstTop,
- double dstRight,
- double dstBottom,
- List<dynamic>? paintObjects,
- ByteData paintData) { throw UnimplementedError(); }
- /// Draws the given [Image] into the canvas using the given [Paint].
- ///
- /// The image is drawn in nine portions described by splitting the image by
- /// drawing two horizontal lines and two vertical lines, where the `center`
- /// argument describes the rectangle formed by the four points where these
- /// four lines intersect each other. (This forms a 3-by-3 grid of regions,
- /// the center region being described by the `center` argument.)
- ///
- /// The four regions in the corners are drawn, without scaling, in the four
- /// corners of the destination rectangle described by `dst`. The remaining
- /// five regions are drawn by stretching them to fit such that they exactly
- /// cover the destination rectangle while maintaining their relative
- /// positions.
void drawImageNine(Image image, Rect center, Rect dst, Paint paint) {
// ignore: unnecessary_null_comparison
assert(image != null); // image is checked on the engine side
assert(_rectIsValid(center));
assert(_rectIsValid(dst));
assert(paint != null); // ignore: unnecessary_null_comparison
- _drawImageNine(image._image,
- center.left,
- center.top,
- center.right,
- center.bottom,
- dst.left,
- dst.top,
- dst.right,
- dst.bottom,
- paint._objects,
- paint._data);
+ _addMethod(_CanvasMethods.drawImageNine);
+ _addObject(paint);
+ _addObject(image);
+ _addData4(
+ center.left,
+ center.top,
+ center.right,
+ center.bottom,
+ );
+ _addData4(
+ dst.left,
+ dst.top,
+ dst.right,
+ dst.bottom,
+ );
}
- void _drawImageNine(_Image image,
- double centerLeft,
- double centerTop,
- double centerRight,
- double centerBottom,
- double dstLeft,
- double dstTop,
- double dstRight,
- double dstBottom,
- List<dynamic>? paintObjects,
- ByteData paintData) { throw UnimplementedError(); }
- /// Draw the given picture onto the canvas. To create a picture, see
- /// [PictureRecorder].
void drawPicture(Picture picture) {
// ignore: unnecessary_null_comparison
assert(picture != null); // picture is checked on the engine side
- _drawPicture(picture);
+ _addMethod(_CanvasMethods.drawPicture);
+ _addObject(picture);
}
- void _drawPicture(Picture picture) { throw UnimplementedError(); }
- /// Draws the text in the given [Paragraph] into this canvas at the given
- /// [Offset].
- ///
- /// The [Paragraph] object must have had [Paragraph.layout] called on it
- /// first.
- ///
- /// To align the text, set the `textAlign` on the [ParagraphStyle] object
- /// passed to the [new ParagraphBuilder] constructor. For more details see
- /// [TextAlign] and the discussion at [new ParagraphStyle].
- ///
- /// If the text is left aligned or justified, the left margin will be at the
- /// position specified by the `offset` argument's [Offset.dx] coordinate.
- ///
- /// If the text is right aligned or justified, the right margin will be at the
- /// position described by adding the [ParagraphConstraints.width] given to
- /// [Paragraph.layout], to the `offset` argument's [Offset.dx] coordinate.
- ///
- /// If the text is centered, the centering axis will be at the position
- /// described by adding half of the [ParagraphConstraints.width] given to
- /// [Paragraph.layout], to the `offset` argument's [Offset.dx] coordinate.
void drawParagraph(Paragraph paragraph, Offset offset) {
assert(paragraph != null); // ignore: unnecessary_null_comparison
assert(_offsetIsValid(offset));
- paragraph._paint(this, offset.dx, offset.dy);
+ _addMethod(_CanvasMethods.drawParagraph);
+ _addObject(paragraph);
}
- /// Draws a sequence of points according to the given [PointMode].
- ///
- /// The `points` argument is interpreted as offsets from the origin.
- ///
- /// See also:
- ///
- /// * [drawRawPoints], which takes `points` as a [Float32List] rather than a
- /// [List<Offset>].
void drawPoints(PointMode pointMode, List<Offset> points, Paint paint) {
assert(pointMode != null); // ignore: unnecessary_null_comparison
assert(points != null); // ignore: unnecessary_null_comparison
assert(paint != null); // ignore: unnecessary_null_comparison
- _drawPoints(paint._objects, paint._data, pointMode.index, _encodePointList(points));
+ _drawPoints(paint, pointMode.index, _encodePointList(points));
}
- /// Draws a sequence of points according to the given [PointMode].
- ///
- /// The `points` argument is interpreted as a list of pairs of floating point
- /// numbers, where each pair represents an x and y offset from the origin.
- ///
- /// See also:
- ///
- /// * [drawPoints], which takes `points` as a [List<Offset>] rather than a
- /// [List<Float32List>].
void drawRawPoints(PointMode pointMode, Float32List points, Paint paint) {
assert(pointMode != null); // ignore: unnecessary_null_comparison
assert(points != null); // ignore: unnecessary_null_comparison
assert(paint != null); // ignore: unnecessary_null_comparison
if (points.length % 2 != 0)
throw ArgumentError('"points" must have an even number of values.');
- _drawPoints(paint._objects, paint._data, pointMode.index, points);
+ _drawPoints(paint, pointMode.index, points);
}
- void _drawPoints(List<dynamic>? paintObjects,
- ByteData paintData,
- int pointMode,
- Float32List points) { throw UnimplementedError(); }
+ void _drawPoints(
+ Paint paint,
+ int pointMode,
+ Float32List points,
+ ) {
+ _addMethod(_CanvasMethods.drawPoints);
+ _addObject(paint);
+ _addObject(points);
+ _addData1(pointMode.toDouble());
+ }
- /// Draws the set of [Vertices] onto the canvas.
- ///
- /// All parameters must not be null.
- ///
- /// See also:
- /// * [new Vertices], which creates a set of vertices to draw on the canvas.
- /// * [Vertices.raw], which creates the vertices using typed data lists
- /// rather than unencoded lists.
void drawVertices(Vertices vertices, BlendMode blendMode, Paint paint) {
// ignore: unnecessary_null_comparison
assert(vertices != null); // vertices is checked on the engine side
assert(paint != null); // ignore: unnecessary_null_comparison
assert(blendMode != null); // ignore: unnecessary_null_comparison
- _drawVertices(vertices, blendMode.index, paint._objects, paint._data);
+ _addMethod(_CanvasMethods.drawVertices);
+ _addObject(paint);
+ _addObject(vertices);
+ _addData1(blendMode.index.toDouble());
}
- void _drawVertices(Vertices vertices,
- int blendMode,
- List<dynamic>? paintObjects,
- ByteData paintData) { throw UnimplementedError(); }
- /// Draws many parts of an image - the [atlas] - onto the canvas.
- ///
- /// This method allows for optimization when you want to draw many parts of an
- /// image onto the canvas, such as when using sprites or zooming. It is more efficient
- /// than using multiple calls to [drawImageRect] and provides more functionality
- /// to individually transform each image part by a separate rotation or scale and
- /// blend or modulate those parts with a solid color.
- ///
- /// The method takes a list of [Rect] objects that each define a piece of the
- /// [atlas] image to be drawn independently. Each [Rect] is associated with an
- /// [RSTransform] entry in the [transforms] list which defines the location,
- /// rotation, and (uniform) scale with which to draw that portion of the image.
- /// Each [Rect] can also be associated with an optional [Color] which will be
- /// composed with the associated image part using the [blendMode] before blending
- /// the result onto the canvas. The full operation can be broken down as:
- ///
- /// - Blend each rectangular portion of the image specified by an entry in the
- /// [rects] argument with its associated entry in the [colors] list using the
- /// [blendMode] argument (if a color is specified). In this part of the operation,
- /// the image part will be considered the source of the operation and the associated
- /// color will be considered the destination.
- /// - Blend the result from the first step onto the canvas using the translation,
- /// rotation, and scale properties expressed in the associated entry in the
- /// [transforms] list using the properties of the [Paint] object.
- ///
- /// If the first stage of the operation which blends each part of the image with
- /// a color is needed, then both the [colors] and [blendMode] arguments must
- /// not be null and there must be an entry in the [colors] list for each
- /// image part. If that stage is not needed, then the [colors] argument can
- /// be either null or an empty list and the [blendMode] argument may also be null.
- ///
- /// The optional [cullRect] argument can provide an estimate of the bounds of the
- /// coordinates rendered by all components of the atlas to be compared against
- /// the clip to quickly reject the operation if it does not intersect.
- ///
- /// An example usage to render many sprites from a single sprite atlas with no
- /// rotations or scales:
- ///
- /// ```dart
- /// class Sprite {
- /// int index;
- /// double centerX;
- /// double centerY;
- /// }
- ///
- /// class MyPainter extends CustomPainter {
- /// // assume spriteAtlas contains N 10x10 sprites side by side in a (N*10)x10 image
- /// ui.Image spriteAtlas;
- /// List<Sprite> allSprites;
- ///
- /// @override
- /// void paint(Canvas canvas, Size size) {
- /// Paint paint = Paint();
- /// canvas.drawAtlas(spriteAtlas, <RSTransform>[
- /// for (Sprite sprite in allSprites)
- /// RSTransform.fromComponents(
- /// rotation: 0.0,
- /// scale: 1.0,
- /// // Center of the sprite relative to its rect
- /// anchorX: 5.0,
- /// anchorY: 5.0,
- /// // Location at which to draw the center of the sprite
- /// translateX: sprite.centerX,
- /// translateY: sprite.centerY,
- /// ),
- /// ], <Rect>[
- /// for (Sprite sprite in allSprites)
- /// Rect.fromLTWH(sprite.index * 10.0, 0.0, 10.0, 10.0),
- /// ], null, null, null, paint);
- /// }
- ///
- /// ...
- /// }
- /// ```
- ///
- /// Another example usage which renders sprites with an optional opacity and rotation:
- ///
- /// ```dart
- /// class Sprite {
- /// int index;
- /// double centerX;
- /// double centerY;
- /// int alpha;
- /// double rotation;
- /// }
- ///
- /// class MyPainter extends CustomPainter {
- /// // assume spriteAtlas contains N 10x10 sprites side by side in a (N*10)x10 image
- /// ui.Image spriteAtlas;
- /// List<Sprite> allSprites;
- ///
- /// @override
- /// void paint(Canvas canvas, Size size) {
- /// Paint paint = Paint();
- /// canvas.drawAtlas(spriteAtlas, <RSTransform>[
- /// for (Sprite sprite in allSprites)
- /// RSTransform.fromComponents(
- /// rotation: sprite.rotation,
- /// scale: 1.0,
- /// // Center of the sprite relative to its rect
- /// anchorX: 5.0,
- /// anchorY: 5.0,
- /// // Location at which to draw the center of the sprite
- /// translateX: sprite.centerX,
- /// translateY: sprite.centerY,
- /// ),
- /// ], <Rect>[
- /// for (Sprite sprite in allSprites)
- /// Rect.fromLTWH(sprite.index * 10.0, 0.0, 10.0, 10.0),
- /// ], <Color>[
- /// for (Sprite sprite in allSprites)
- /// Colors.white.withAlpha(sprite.alpha),
- /// ], BlendMode.srcIn, null, paint);
- /// }
- ///
- /// ...
- /// }
- /// ```
- ///
- /// The length of the [transforms] and [rects] lists must be equal and
- /// if the [colors] argument is not null then it must either be empty or
- /// have the same length as the other two lists.
- ///
- /// See also:
- ///
- /// * [drawRawAtlas], which takes its arguments as typed data lists rather
- /// than objects.
void drawAtlas(Image atlas,
List<RSTransform> transforms,
List<Rect> rects,
@@ -4611,155 +2494,12 @@
}
final Int32List? colorBuffer = (colors == null || colors.isEmpty) ? null : _encodeColorList(colors);
- final Float32List? cullRectBuffer = cullRect?._value32;
_drawAtlas(
- paint._objects, paint._data, atlas._image, rstTransformBuffer, rectBuffer,
- colorBuffer, (blendMode ?? BlendMode.src).index, cullRectBuffer
+ paint, atlas._image, rstTransformBuffer, rectBuffer,
+ colorBuffer, (blendMode ?? BlendMode.src).index, cullRect ?? Rect.largest,
);
}
-
- /// Draws many parts of an image - the [atlas] - onto the canvas.
- ///
- /// This method allows for optimization when you want to draw many parts of an
- /// image onto the canvas, such as when using sprites or zooming. It is more efficient
- /// than using multiple calls to [drawImageRect] and provides more functionality
- /// to individually transform each image part by a separate rotation or scale and
- /// blend or modulate those parts with a solid color. It is also more efficient
- /// than [drawAtlas] as the data in the arguments is already packed in a format
- /// that can be directly used by the rendering code.
- ///
- /// A full description of how this method uses its arguments to draw onto the
- /// canvas can be found in the description of the [drawAtlas] method.
- ///
- /// The [rstTransforms] argument is interpreted as a list of four-tuples, with
- /// each tuple being ([RSTransform.scos], [RSTransform.ssin],
- /// [RSTransform.tx], [RSTransform.ty]).
- ///
- /// The [rects] argument is interpreted as a list of four-tuples, with each
- /// tuple being ([Rect.left], [Rect.top], [Rect.right], [Rect.bottom]).
- ///
- /// The [colors] argument, which can be null, is interpreted as a list of
- /// 32-bit colors, with the same packing as [Color.value]. If the [colors]
- /// argument is not null then the [blendMode] argument must also not be null.
- ///
- /// An example usage to render many sprites from a single sprite atlas with no rotations
- /// or scales:
- ///
- /// ```dart
- /// class Sprite {
- /// int index;
- /// double centerX;
- /// double centerY;
- /// }
- ///
- /// class MyPainter extends CustomPainter {
- /// // assume spriteAtlas contains N 10x10 sprites side by side in a (N*10)x10 image
- /// ui.Image spriteAtlas;
- /// List<Sprite> allSprites;
- ///
- /// @override
- /// void paint(Canvas canvas, Size size) {
- /// // For best advantage, these lists should be cached and only specific
- /// // entries updated when the sprite information changes. This code is
- /// // illustrative of how to set up the data and not a recommendation for
- /// // optimal usage.
- /// Float32List rectList = Float32List(allSprites.length * 4);
- /// Float32List transformList = Float32List(allSprites.length * 4);
- /// for (int i = 0; i < allSprites.length; i++) {
- /// final double rectX = sprite.spriteIndex * 10.0;
- /// rectList[i * 4 + 0] = rectX;
- /// rectList[i * 4 + 1] = 0.0;
- /// rectList[i * 4 + 2] = rectX + 10.0;
- /// rectList[i * 4 + 3] = 10.0;
- ///
- /// // This example sets the RSTransform values directly for a common case of no
- /// // rotations or scales and just a translation to position the atlas entry. For
- /// // more complicated transforms one could use the RSTransform class to compute
- /// // the necessary values or do the same math directly.
- /// transformList[i * 4 + 0] = 1.0;
- /// transformList[i * 4 + 1] = 0.0;
- /// transformList[i * 4 + 2] = sprite.centerX - 5.0;
- /// transformList[i * 4 + 3] = sprite.centerY - 5.0;
- /// }
- /// Paint paint = Paint();
- /// canvas.drawAtlas(spriteAtlas, transformList, rectList, null, null, null, paint);
- /// }
- ///
- /// ...
- /// }
- /// ```
- ///
- /// Another example usage which renders sprites with an optional opacity and rotation:
- ///
- /// ```dart
- /// class Sprite {
- /// int index;
- /// double centerX;
- /// double centerY;
- /// int alpha;
- /// double rotation;
- /// }
- ///
- /// class MyPainter extends CustomPainter {
- /// // assume spriteAtlas contains N 10x10 sprites side by side in a (N*10)x10 image
- /// ui.Image spriteAtlas;
- /// List<Sprite> allSprites;
- ///
- /// @override
- /// void paint(Canvas canvas, Size size) {
- /// // For best advantage, these lists should be cached and only specific
- /// // entries updated when the sprite information changes. This code is
- /// // illustrative of how to set up the data and not a recommendation for
- /// // optimal usage.
- /// Float32List rectList = Float32List(allSprites.length * 4);
- /// Float32List transformList = Float32List(allSprites.length * 4);
- /// Int32List colorList = Int32List(allSprites.length);
- /// for (int i = 0; i < allSprites.length; i++) {
- /// final double rectX = sprite.spriteIndex * 10.0;
- /// rectList[i * 4 + 0] = rectX;
- /// rectList[i * 4 + 1] = 0.0;
- /// rectList[i * 4 + 2] = rectX + 10.0;
- /// rectList[i * 4 + 3] = 10.0;
- ///
- /// // This example uses an RSTransform object to compute the necessary values for
- /// // the transform using a factory helper method because the sprites contain
- /// // rotation values which are not trivial to work with. But if the math for the
- /// // values falls out from other calculations on the sprites then the values could
- /// // possibly be generated directly from the sprite update code.
- /// final RSTransform transform = RSTransform.fromComponents(
- /// rotation: sprite.rotation,
- /// scale: 1.0,
- /// // Center of the sprite relative to its rect
- /// anchorX: 5.0,
- /// anchorY: 5.0,
- /// // Location at which to draw the center of the sprite
- /// translateX: sprite.centerX,
- /// translateY: sprite.centerY,
- /// );
- /// transformList[i * 4 + 0] = transform.scos;
- /// transformList[i * 4 + 1] = transform.ssin;
- /// transformList[i * 4 + 2] = transform.tx;
- /// transformList[i * 4 + 3] = transform.ty;
- ///
- /// // This example computes the color value directly, but one could also compute
- /// // an actual Color object and use its Color.value getter for the same result.
- /// // Since we are using BlendMode.srcIn, only the alpha component matters for
- /// // these colors which makes this a simple shift operation.
- /// colorList[i] = sprite.alpha << 24;
- /// }
- /// Paint paint = Paint();
- /// canvas.drawAtlas(spriteAtlas, transformList, rectList, colorList, BlendMode.srcIn, null, paint);
- /// }
- ///
- /// ...
- /// }
- /// ```
- ///
- /// See also:
- ///
- /// * [drawAtlas], which takes its arguments as objects rather than typed
- /// data lists.
void drawRawAtlas(Image atlas,
Float32List rstTransforms,
Float32List rects,
@@ -4783,141 +2523,73 @@
throw ArgumentError('If non-null, "colors" length must be one fourth the length of "rstTransforms" and "rects".');
_drawAtlas(
- paint._objects, paint._data, atlas._image, rstTransforms, rects,
- colors, (blendMode ?? BlendMode.src).index, cullRect?._value32
+ paint, atlas._image, rstTransforms, rects,
+ colors, (blendMode ?? BlendMode.src).index, cullRect ?? Rect.largest,
);
}
- void _drawAtlas(List<dynamic>? paintObjects,
- ByteData paintData,
- _Image atlas,
- Float32List rstTransforms,
- Float32List rects,
- Int32List? colors,
- int blendMode,
- Float32List? cullRect) { throw UnimplementedError(); }
+ void _drawAtlas(
+ Paint paint,
+ _Image atlas,
+ Float32List rstTransforms,
+ Float32List rects,
+ Int32List? colors,
+ int blendMode,
+ Rect cullRect,
+ ) {
+ _addMethod(_CanvasMethods.drawAtlas);
+ _addObject(paint);
+ _addObject(rstTransforms);
+ _addObject(rects);
+ _addObject(colors ?? Int32List(0));
+ _addData1(blendMode.toDouble());
+ _addData4(
+ cullRect.left,
+ cullRect.top,
+ cullRect.right,
+ cullRect.bottom,
+ );
+ }
- /// Draws a shadow for a [Path] representing the given material elevation.
- ///
- /// The `transparentOccluder` argument should be true if the occluding object
- /// is not opaque.
- ///
- /// The arguments must not be null.
void drawShadow(Path path, Color color, double elevation, bool transparentOccluder) {
// ignore: unnecessary_null_comparison
assert(path != null); // path is checked on the engine side
assert(color != null); // ignore: unnecessary_null_comparison
assert(transparentOccluder != null); // ignore: unnecessary_null_comparison
- _drawShadow(path, color.value, elevation, transparentOccluder);
+ _addMethod(_CanvasMethods.drawShadow);
+ _addObject(path);
+ _addData2(color.value.toDouble(), elevation);
+ _addData1(transparentOccluder ? 1 : 0);
}
- void _drawShadow(Path path,
- int color,
- double elevation,
- bool transparentOccluder) { throw UnimplementedError(); }
}
-/// An object representing a sequence of recorded graphical operations.
-///
-/// To create a [Picture], use a [PictureRecorder].
-///
-/// A [Picture] can be placed in a [Scene] using a [SceneBuilder], via
-/// the [SceneBuilder.addPicture] method. A [Picture] can also be
-/// drawn into a [Canvas], using the [Canvas.drawPicture] method.
-@pragma('vm:entry-point')
-class Picture extends NativeFieldWrapperClass2 {
- /// This class is created by the engine, and should not be instantiated
- /// or extended directly.
- ///
- /// To create a [Picture], use a [PictureRecorder].
- @pragma('vm:entry-point')
- Picture._();
-
- /// Creates an image from this picture.
- ///
- /// The returned image will be `width` pixels wide and `height` pixels high.
- /// The picture is rasterized within the 0 (left), 0 (top), `width` (right),
- /// `height` (bottom) bounds. Content outside these bounds is clipped.
- ///
- /// Although the image is returned synchronously, the picture is actually
- /// rasterized the first time the image is drawn and then cached.
- Future<Image> toImage(int width, int height) {
+class Picture {
+ Picture._();
+ Future<Image> toImage(int width, int height) async {
if (width <= 0 || height <= 0)
throw Exception('Invalid image dimensions.');
- return _futurize(
- (_Callback<Image> callback) => _toImage(width, height, (_Image image) {
- callback(Image._(image));
- }),
- );
+ return Image._(_Image._(width, height));
}
- String? _toImage(int width, int height, _Callback<_Image> callback) { throw UnimplementedError(); }
-
- /// Release the resources used by this object. The object is no longer usable
- /// after this method is called.
void dispose() { throw UnimplementedError(); }
-
- /// Returns the approximate number of bytes allocated for this object.
- ///
- /// The actual size of this picture may be larger, particularly if it contains
- /// references to image or other large objects.
int get approximateBytesUsed { throw UnimplementedError(); }
}
-/// Records a [Picture] containing a sequence of graphical operations.
-///
-/// To begin recording, construct a [Canvas] to record the commands.
-/// To end recording, use the [PictureRecorder.endRecording] method.
-class PictureRecorder extends NativeFieldWrapperClass2 {
- /// Creates a new idle PictureRecorder. To associate it with a
- /// [Canvas] and begin recording, pass this [PictureRecorder] to the
- /// [Canvas] constructor.
- @pragma('vm:entry-point')
- PictureRecorder() { _constructor(); }
- void _constructor() { throw UnimplementedError(); }
-
- /// Whether this object is currently recording commands.
- ///
- /// Specifically, this returns true if a [Canvas] object has been
- /// created to record commands and recording has not yet ended via a
- /// call to [endRecording], and false if either this
- /// [PictureRecorder] has not yet been associated with a [Canvas],
- /// or the [endRecording] method has already been called.
+class PictureRecorder {
+ PictureRecorder();
bool get isRecording => _canvas != null;
-
- /// Finishes recording graphical operations.
- ///
- /// Returns a picture containing the graphical operations that have been
- /// recorded thus far. After calling this function, both the picture recorder
- /// and the canvas objects are invalid and cannot be used further.
Picture endRecording() {
if (_canvas == null)
throw StateError('PictureRecorder did not start recording.');
final Picture picture = Picture._();
- _endRecording(picture);
_canvas!._recorder = null;
_canvas = null;
return picture;
}
- void _endRecording(Picture outPicture) { throw UnimplementedError(); }
-
Canvas? _canvas;
}
-
-/// A single shadow.
-///
-/// Multiple shadows are stacked together in a [TextStyle].
class Shadow {
- /// Construct a shadow.
- ///
- /// The default shadow is a black shadow with zero offset and zero blur.
- /// Default shadows should be completely covered by the casting element,
- /// and not be visible.
- ///
- /// Transparency should be adjusted through the [color] alpha.
- ///
- /// Shadow order matters due to compositing multiple translucent objects not
- /// being commutative.
const Shadow({
this.color = const Color(_kColorDefault),
this.offset = Offset.zero,
@@ -4933,56 +2605,20 @@
static const int _kXOffset = 1 << 2;
static const int _kYOffset = 2 << 2;
static const int _kBlurOffset = 3 << 2;
-
- /// Color that the shadow will be drawn with.
- ///
- /// The shadows are shapes composited directly over the base canvas, and do not
- /// represent optical occlusion.
final Color color;
-
- /// The displacement of the shadow from the casting element.
- ///
- /// Positive x/y offsets will shift the shadow to the right and down, while
- /// negative offsets shift the shadow to the left and up. The offsets are
- /// relative to the position of the element that is casting it.
final Offset offset;
-
- /// The standard deviation of the Gaussian to convolve with the shadow's shape.
final double blurRadius;
-
- /// Converts a blur radius in pixels to sigmas.
- ///
- /// See the sigma argument to [MaskFilter.blur].
- ///
// See SkBlurMask::ConvertRadiusToSigma().
// <https://github.com/google/skia/blob/bb5b77db51d2e149ee66db284903572a5aac09be/src/effects/SkBlurMask.cpp#L23>
static double convertRadiusToSigma(double radius) {
return radius * 0.57735 + 0.5;
}
-
- /// The [blurRadius] in sigmas instead of logical pixels.
- ///
- /// See the sigma argument to [MaskFilter.blur].
double get blurSigma => convertRadiusToSigma(blurRadius);
-
- /// Create the [Paint] object that corresponds to this shadow description.
- ///
- /// The [offset] is not represented in the [Paint] object.
- /// To honor this as well, the shape should be translated by [offset] before
- /// being filled using this [Paint].
- ///
- /// This class does not provide a way to disable shadows to avoid
- /// inconsistencies in shadow blur rendering, primarily as a method of
- /// reducing test flakiness. [toPaint] should be overridden in subclasses to
- /// provide this functionality.
Paint toPaint() {
return Paint()
..color = color
..maskFilter = MaskFilter.blur(BlurStyle.normal, blurSigma);
}
-
- /// Returns a new shadow with its [offset] and [blurRadius] scaled by the given
- /// factor.
Shadow scale(double factor) {
return Shadow(
color: color,
@@ -4990,26 +2626,6 @@
blurRadius: blurRadius * factor,
);
}
-
- /// Linearly interpolate between two shadows.
- ///
- /// If either shadow is null, this function linearly interpolates from a
- /// a shadow that matches the other shadow in color but has a zero
- /// offset and a zero blurRadius.
- ///
- /// {@template dart.ui.shadow.lerp}
- /// The `t` argument represents position on the timeline, with 0.0 meaning
- /// that the interpolation has not started, returning `a` (or something
- /// equivalent to `a`), 1.0 meaning that the interpolation has finished,
- /// returning `b` (or something equivalent to `b`), and values in between
- /// meaning that the interpolation is at the relevant point on the timeline
- /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
- /// 1.0, so negative values and values greater than 1.0 are valid (and can
- /// easily be generated by curves such as [Curves.elasticInOut]).
- ///
- /// Values for `t` are usually obtained from an [Animation<double>], such as
- /// an [AnimationController].
- /// {@endtemplate}
static Shadow? lerp(Shadow? a, Shadow? b, double t) {
assert(t != null); // ignore: unnecessary_null_comparison
if (b == null) {
@@ -5030,12 +2646,6 @@
}
}
}
-
- /// Linearly interpolate between two lists of shadows.
- ///
- /// If the lists differ in length, excess items are lerped with null.
- ///
- /// {@macro dart.ui.shadow.lerp}
static List<Shadow>? lerpList(List<Shadow>? a, List<Shadow>? b, double t) {
assert(t != null); // ignore: unnecessary_null_comparison
if (a == null && b == null)
@@ -5106,13 +2716,8 @@
@override
String toString() => 'TextShadow($color, $offset, $blurRadius)';
}
-
-/// A handle to a read-only byte buffer that is managed by the engine.
-class ImmutableBuffer extends NativeFieldWrapperClass2 {
+class ImmutableBuffer {
ImmutableBuffer._(this.length);
-
- /// Creates a copy of the data from a [Uint8List] suitable for internal use
- /// in the engine.
static Future<ImmutableBuffer> fromUint8List(Uint8List list) {
final ImmutableBuffer instance = ImmutableBuffer._(list.length);
return _futurize((_Callback<void> callback) {
@@ -5120,23 +2725,11 @@
}).then((_) => instance);
}
void _init(Uint8List list, _Callback<void> callback) { throw UnimplementedError(); }
-
- /// The length, in bytes, of the underlying data.
final int length;
-
- /// Release the resources used by this object. The object is no longer usable
- /// after this method is called.
void dispose() { throw UnimplementedError(); }
}
-
-/// A descriptor of data that can be turned into an [Image] via a [Codec].
-///
-/// Use this class to determine the height, width, and byte size of image data
-/// before decoding it.
-class ImageDescriptor extends NativeFieldWrapperClass2 {
+class ImageDescriptor {
ImageDescriptor._();
-
- /// Creates an image descriptor from encoded data in a supported format.
static Future<ImageDescriptor> encoded(ImmutableBuffer buffer) {
final ImageDescriptor descriptor = ImageDescriptor._();
return _futurize((_Callback<void> callback) {
@@ -5144,15 +2737,6 @@
}).then((_) => descriptor);
}
String? _initEncoded(ImmutableBuffer buffer, _Callback<void> callback) { throw UnimplementedError(); }
-
- /// Creates an image descriptor from raw image pixels.
- ///
- /// The `pixels` parameter is the pixel data in the encoding described by
- /// `format`.
- ///
- /// The `rowBytes` parameter is the number of bytes consumed by each row of
- /// pixels in the data buffer. If unspecified, it defaults to `width` multiplied
- /// by the number of bytes per pixel in the provided `format`.
// Not async because there's no expensive work to do here.
ImageDescriptor.raw(
ImmutableBuffer buffer, {
@@ -5171,38 +2755,16 @@
int? _width;
int _getWidth() { throw UnimplementedError(); }
- /// The width, in pixels, of the image.
- ///
- /// On the Web, this is only supported for [raw] images.
int get width => _width ??= _getWidth();
int? _height;
int _getHeight() { throw UnimplementedError(); }
- /// The height, in pixels, of the image.
- ///
- /// On the Web, this is only supported for [raw] images.
int get height => _height ??= _getHeight();
int? _bytesPerPixel;
int _getBytesPerPixel() { throw UnimplementedError(); }
- /// The number of bytes per pixel in the image.
- ///
- /// On web, this is only supported for [raw] images.
int get bytesPerPixel => _bytesPerPixel ??= _getBytesPerPixel();
-
- /// Release the resources used by this object. The object is no longer usable
- /// after this method is called.
void dispose() { throw UnimplementedError(); }
-
- /// Creates a [Codec] object which is suitable for decoding the data in the
- /// buffer to an [Image].
- ///
- /// If only one of targetWidth or targetHeight are specified, the other
- /// dimension will be scaled according to the aspect ratio of the supplied
- /// dimension.
- ///
- /// If either targetWidth or targetHeight is less than or equal to zero, it
- /// will be treated as if it is null.
Future<Codec> instantiateCodec({int? targetWidth, int? targetHeight}) async {
if (targetWidth != null && targetWidth <= 0) {
targetWidth = null;
@@ -5230,37 +2792,8 @@
}
void _instantiateCodec(Codec outCodec, int targetWidth, int targetHeight) { throw UnimplementedError(); }
}
-
-/// Generic callback signature, used by [_futurize].
typedef _Callback<T> = void Function(T result);
-
-/// Signature for a method that receives a [_Callback].
-///
-/// Return value should be null on success, and a string error message on
-/// failure.
typedef _Callbacker<T> = String? Function(_Callback<T> callback);
-
-/// Converts a method that receives a value-returning callback to a method that
-/// returns a Future.
-///
-/// Return a [String] to cause an [Exception] to be synchronously thrown with
-/// that string as a message.
-///
-/// If the callback is called with null, the future completes with an error.
-///
-/// Example usage:
-///
-/// ```dart
-/// typedef IntCallback = void Function(int result);
-///
-/// String _doSomethingAndCallback(IntCallback callback) {
-/// Timer(new Duration(seconds: 1), () { callback(1); });
-/// }
-///
-/// Future<int> doSomething() {
-/// return _futurize(_doSomethingAndCallback);
-/// }
-/// ```
Future<T> _futurize<T>(_Callbacker<T> callbacker) {
final Completer<T> completer = Completer<T>.sync();
final String? error = callbacker((T t) {
diff --git a/lib/src/ui/platform_dispatcher.dart b/lib/src/ui/platform_dispatcher.dart
index 8c832ed..6e4ab0a 100644
--- a/lib/src/ui/platform_dispatcher.dart
+++ b/lib/src/ui/platform_dispatcher.dart
@@ -365,8 +365,8 @@
}
late _SetNeedsReportTimingsFunc _setNeedsReportTimings;
- void _nativeSetNeedsReportTimings(bool value)
- { throw UnimplementedError(); }
+
+ void _nativeSetNeedsReportTimings(bool value) {}
// Called from the engine, via hooks.dart
void _reportTimings(List<int> timings) {
@@ -394,8 +394,9 @@
throw Exception(error);
}
- String? _sendPlatformMessage(String name, PlatformMessageResponseCallback? callback, ByteData? data)
- { throw UnimplementedError(); }
+ String? _sendPlatformMessage(String name, PlatformMessageResponseCallback? callback, ByteData? data) {
+ return null;
+ }
/// Called whenever this platform dispatcher receives a message from a
/// platform-specific plugin.
@@ -492,6 +493,9 @@
/// platform channel may be used.
ByteData? getPersistentIsolateData() { throw UnimplementedError(); }
+ Timer? _frameTimer;
+ Duration _frameTime = Duration.zero;
+
/// Requests that, at the next appropriate opportunity, the [onBeginFrame] and
/// [onDrawFrame] callbacks be invoked.
///
@@ -499,7 +503,22 @@
///
/// * [SchedulerBinding], the Flutter framework class which manages the
/// scheduling of frames.
- void scheduleFrame() { throw UnimplementedError(); }
+ void scheduleFrame() {
+ _frameTimer ??= new Timer(
+ const Duration(milliseconds: 16),
+ () {
+ _frameTimer = null;
+ int microseconds = _frameTime.inMicroseconds;
+ _frameTime += const Duration(milliseconds: 16);
+ Timer.run(() {
+ _beginFrame(microseconds);
+ });
+ Timer.run(() {
+ _drawFrame();
+ });
+ },
+ );
+ }
/// Additional accessibility features that may be enabled by the platform.
AccessibilityFeatures get accessibilityFeatures => configuration.accessibilityFeatures;
@@ -855,8 +874,7 @@
/// * [Navigator], a widget that handles routing.
/// * [SystemChannels.navigation], which handles subsequent navigation
/// requests from the embedder.
- String get defaultRouteName => _defaultRouteName();
- String _defaultRouteName() { throw UnimplementedError(); }
+ String get defaultRouteName => '/';
}
/// Configuration of the platform.
diff --git a/lib/src/ui/semantics.dart b/lib/src/ui/semantics.dart
index 5a08f50..3b78720 100644
--- a/lib/src/ui/semantics.dart
+++ b/lib/src/ui/semantics.dart
@@ -627,11 +627,9 @@
/// Once created, the [SemanticsUpdate] objects can be passed to
/// [PlatformDispatcher.updateSemantics] to update the semantics conveyed to the
/// user.
-@pragma('vm:entry-point')
-class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 {
+class SemanticsUpdateBuilder {
/// Creates an empty [SemanticsUpdateBuilder] object.
- @pragma('vm:entry-point')
- SemanticsUpdateBuilder() { _constructor(); }
+ SemanticsUpdateBuilder() { _constructor(); }
void _constructor() { throw UnimplementedError(); }
/// Update the information associated with the node with the given `id`.
@@ -849,14 +847,12 @@
///
/// Semantics updates can be applied to the system's retained semantics tree
/// using the [PlatformDispatcher.updateSemantics] method.
-@pragma('vm:entry-point')
-class SemanticsUpdate extends NativeFieldWrapperClass2 {
+class SemanticsUpdate {
/// This class is created by the engine, and should not be instantiated
/// or extended directly.
///
/// To create a SemanticsUpdate object, use a [SemanticsUpdateBuilder].
- @pragma('vm:entry-point')
- SemanticsUpdate._();
+ SemanticsUpdate._();
/// Releases the resources used by this semantics update.
///
diff --git a/lib/src/ui/text.dart b/lib/src/ui/text.dart
index cf76fe7..5f9f0df 100644
--- a/lib/src/ui/text.dart
+++ b/lib/src/ui/text.dart
@@ -739,13 +739,20 @@
shadows,
fontFeatures,
),
- _fontFamily = fontFamily ?? '',
+ _color = color,
+ _decoration = decoration,
+ _decorationColor = decorationColor,
+ _decorationStyle = decorationStyle,
+ _decorationThickness = decorationThickness,
+ _fontFamily = fontFamily,
_fontFamilyFallback = fontFamilyFallback,
_fontSize = fontSize,
+ _fontWeight = fontWeight,
+ _fontStyle = fontStyle,
+ _textBaseline = textBaseline,
_letterSpacing = letterSpacing,
_wordSpacing = wordSpacing,
_height = height,
- _decorationThickness = decorationThickness,
_locale = locale,
_background = background,
_foreground = foreground,
@@ -753,7 +760,7 @@
_fontFeatures = fontFeatures;
final Int32List _encoded;
- final String _fontFamily;
+ final String? _fontFamily;
final List<String>? _fontFamilyFallback;
final double? _fontSize;
final double? _letterSpacing;
@@ -765,6 +772,50 @@
final Paint? _foreground;
final List<Shadow>? _shadows;
final List<FontFeature>? _fontFeatures;
+ final Color? _color;
+ final TextDecoration? _decoration;
+ final Color? _decorationColor;
+ final TextDecorationStyle? _decorationStyle;
+ final FontWeight? _fontWeight;
+ final FontStyle? _fontStyle;
+ final TextBaseline? _textBaseline;
+
+
+ TextStyle _mergeInto(TextStyle other) {
+ return TextStyle(
+ color: _color ?? other._color,
+ decoration: _decoration ?? other._decoration,
+ decorationColor: _decorationColor ?? other._decorationColor,
+ decorationStyle: _decorationStyle ?? other._decorationStyle,
+ decorationThickness: _decorationThickness ?? other._decorationThickness,
+ fontWeight: _fontWeight ?? other._fontWeight,
+ fontStyle: _fontStyle ?? other._fontStyle,
+ textBaseline: _textBaseline ?? other._textBaseline,
+ fontFamily: _fontFamily ?? other._fontFamily,
+ fontFamilyFallback: _fontFamilyFallback ?? other._fontFamilyFallback,
+ fontSize: _fontSize ?? other._fontSize,
+ letterSpacing: _letterSpacing ?? other._letterSpacing,
+ wordSpacing: _wordSpacing ?? other._wordSpacing,
+ height: _height ?? other._height,
+ locale: _locale ?? other._locale,
+ background: _background ?? other._background,
+ foreground: _foreground ?? other._foreground,
+ shadows: _shadows == null
+ ? other._shadows == null
+ ? null
+ : other._shadows
+ : other._shadows == null
+ ? _shadows
+ : <Shadow>[..._shadows!, ...other._shadows!],
+ fontFeatures: _fontFeatures == null
+ ? other._fontFeatures == null
+ ? null
+ : other._fontFeatures
+ : other._fontFeatures == null
+ ? _fontFeatures
+ : <FontFeature>[..._fontFeatures!, ...other._fontFeatures!],
+ );
+ }
@override
bool operator ==(Object other) {
@@ -991,21 +1042,43 @@
ellipsis,
locale,
),
+ _textAlign = textAlign ?? TextAlign.start,
+ _textDirection = textDirection ?? TextDirection.ltr,
+ _maxLines = maxLines,
_fontFamily = fontFamily,
_fontSize = fontSize,
_height = height,
+ _textHeightBehavior = textHeightBehavior ?? const TextHeightBehavior(),
+ _fontWeight = fontWeight ?? FontWeight.normal,
+ _fontStyle = fontStyle ?? FontStyle.normal,
_strutStyle = strutStyle,
_ellipsis = ellipsis,
_locale = locale;
final Int32List _encoded;
+ final TextAlign _textAlign;
+ final TextDirection _textDirection;
+ final int? _maxLines;
final String? _fontFamily;
final double? _fontSize;
final double? _height;
+ final TextHeightBehavior _textHeightBehavior;
+ final FontWeight _fontWeight;
+ final FontStyle _fontStyle;
final StrutStyle? _strutStyle;
final String? _ellipsis;
final Locale? _locale;
+ TextStyle _toTextStyle() {
+ return TextStyle(
+ fontFamily: _fontFamily,
+ fontSize: _fontSize,
+ fontStyle: _fontStyle,
+ fontWeight: _fontWeight,
+ height: _height,
+ );
+ }
+
@override
bool operator ==(Object other) {
if (identical(this, other))
@@ -1298,7 +1371,6 @@
/// A rectangle enclosing a run of text.
///
/// This is similar to [Rect] but includes an inherent [TextDirection].
-@pragma('vm:entry-point')
class TextBox {
/// Creates an object that describes a box containing text.
const TextBox.fromLTRBD(
@@ -1866,50 +1938,59 @@
///
/// Paragraphs can be displayed on a [Canvas] using the [Canvas.drawParagraph]
/// method.
-@pragma('vm:entry-point')
-class Paragraph extends NativeFieldWrapperClass2 {
+class Paragraph {
/// This class is created by the engine, and should not be instantiated
/// or extended directly.
///
/// To create a [Paragraph] object, use a [ParagraphBuilder].
- @pragma('vm:entry-point')
- Paragraph._();
+ Paragraph._(this._paragraphStyle, this._spans);
+
+ final ParagraphStyle _paragraphStyle;
+ final List<_Span> _spans;
+ late List<List<_SpanBox>> _boxes;
/// The amount of horizontal space this paragraph occupies.
///
/// Valid only after [layout] has been called.
- double get width { throw UnimplementedError(); }
+ double get width => _width;
+ double _width = 0;
/// The amount of vertical space this paragraph occupies.
///
/// Valid only after [layout] has been called.
- double get height { throw UnimplementedError(); }
+ double get height => _height;
+ double _height = 0;
/// The distance from the left edge of the leftmost glyph to the right edge of
/// the rightmost glyph in the paragraph.
///
/// Valid only after [layout] has been called.
- double get longestLine { throw UnimplementedError(); }
+ double get longestLine => _longestLine;
+ double _longestLine = 0;
/// The minimum width that this paragraph could be without failing to paint
/// its contents within itself.
///
/// Valid only after [layout] has been called.
- double get minIntrinsicWidth { throw UnimplementedError(); }
+ double get minIntrinsicWidth => _minIntrinsicWidth;
+ double _minIntrinsicWidth = 0;
/// Returns the smallest width beyond which increasing the width never
/// decreases the height.
///
/// Valid only after [layout] has been called.
- double get maxIntrinsicWidth { throw UnimplementedError(); }
+ double get maxIntrinsicWidth => _maxIntrinsicWidth;
+ double _maxIntrinsicWidth = 0;
/// The distance from the top of the paragraph to the alphabetic
/// baseline of the first line, in logical pixels.
- double get alphabeticBaseline { throw UnimplementedError(); }
+ double get alphabeticBaseline => _alphabeticBaseline;
+ double _alphabeticBaseline = 0;
/// The distance from the top of the paragraph to the ideographic
/// baseline of the first line, in logical pixels.
- double get ideographicBaseline { throw UnimplementedError(); }
+ double get ideographicBaseline => _ideographicBaseline;
+ double _ideographicBaseline = 0;
/// True if there is more vertical content, but the text was truncated, either
/// because we reached `maxLines` lines of text or because the `maxLines` was
@@ -1918,28 +1999,59 @@
///
/// See the discussion of the `maxLines` and `ellipsis` arguments at
/// [new ParagraphStyle].
- bool get didExceedMaxLines { throw UnimplementedError(); }
+ bool get didExceedMaxLines => _didExceedMaxLines;
+ bool _didExceedMaxLines = false;
/// Computes the size and position of each glyph in the paragraph.
///
/// The [ParagraphConstraints] control how wide the text is allowed to be.
- void layout(ParagraphConstraints constraints) => _layout(constraints.width);
- void _layout(double width) { throw UnimplementedError(); }
+ void layout(ParagraphConstraints constraints) {
+ _boxes = <List<_SpanBox>>[];
+ double paragraphRight = 0;
+ double minIntrinsicWidth = 0;
+ double maxIntrinsicWidth = 0;
- List<TextBox> _decodeTextBoxes(Float32List encoded) {
- final int count = encoded.length ~/ 5;
- final List<TextBox> boxes = <TextBox>[];
- int position = 0;
- for (int index = 0; index < count; index += 1) {
- boxes.add(TextBox.fromLTRBD(
- encoded[position++],
- encoded[position++],
- encoded[position++],
- encoded[position++],
- TextDirection.values[encoded[position++].toInt()],
- ));
+ double currentLineTop = 0;
+ double currentLineWidth = 0;
+ double currentLineHeight = 0;
+ List<_SpanBox>? currentLine;
+
+ for (_Span span in _spans) {
+ final double spanWidth = span.width;
+ minIntrinsicWidth = math.max(minIntrinsicWidth, spanWidth);
+ maxIntrinsicWidth += spanWidth;
+ final double availableWidth = constraints.width - currentLineWidth;
+
+ // First span in paragraph, or span doesn't fit? Start a new line.
+ if (currentLine == null || spanWidth > availableWidth) {
+ _boxes.add(currentLine = <_SpanBox>[]);
+ currentLineTop += currentLineHeight;
+ currentLineWidth = 0;
+ currentLineHeight = 0;
+ }
+
+ final _SpanBox _spanBox = _SpanBox.fromLTRBS(
+ currentLineWidth,
+ currentLineTop,
+ currentLineWidth + spanWidth,
+ currentLineTop + span.height,
+ span,
+ );
+ currentLine.add(_spanBox);
+
+ currentLineWidth += spanWidth;
+ currentLineHeight = math.max(currentLineHeight, span.height);
+ paragraphRight = math.max(paragraphRight, currentLineWidth);
}
- return boxes;
+
+ _minIntrinsicWidth = math.min(minIntrinsicWidth, maxIntrinsicWidth);
+ _maxIntrinsicWidth = maxIntrinsicWidth;
+ _width = paragraphRight;
+ _height = currentLineTop + currentLineHeight;
+ final int? maxLines = _paragraphStyle._maxLines;
+ _didExceedMaxLines = maxLines == null
+ ? false
+ : _boxes.length > maxLines;
}
/// Returns a list of text boxes that enclose the given text range.
@@ -1958,10 +2070,8 @@
List<TextBox> getBoxesForRange(int start, int end, {BoxHeightStyle boxHeightStyle = BoxHeightStyle.tight, BoxWidthStyle boxWidthStyle = BoxWidthStyle.tight}) {
assert(boxHeightStyle != null); // ignore: unnecessary_null_comparison
assert(boxWidthStyle != null); // ignore: unnecessary_null_comparison
- return _decodeTextBoxes(_getBoxesForRange(start, end, boxHeightStyle.index, boxWidthStyle.index));
+ throw UnimplementedError();
}
- // See paragraph.cc for the layout of this return value.
- Float32List _getBoxesForRange(int start, int end, int boxHeightStyle, int boxWidthStyle) { throw UnimplementedError(); }
/// Returns a list of text boxes that enclose all placeholders in the paragraph.
///
@@ -1971,16 +2081,27 @@
/// Coordinates of the [TextBox] are relative to the upper-left corner of the paragraph,
/// where positive y values indicate down.
List<TextBox> getBoxesForPlaceholders() {
- return _decodeTextBoxes(_getBoxesForPlaceholders());
+ final List<TextBox> result = <TextBox>[];
+ for (List<_SpanBox> line in _boxes) {
+ for (_SpanBox box in line) {
+ if (box.span is _Placeholder) {
+ result.add(TextBox.fromLTRBD(
+ box.left,
+ box.top,
+ box.right,
+ box.bottom,
+ _paragraphStyle._textDirection,
+ ));
+ }
+ }
+ }
+ return result;
}
- Float32List _getBoxesForPlaceholders() { throw UnimplementedError(); }
/// Returns the text position closest to the given offset.
TextPosition getPositionForOffset(Offset offset) {
- final List<int> encoded = _getPositionForOffset(offset.dx, offset.dy);
- return TextPosition(offset: encoded[0], affinity: TextAffinity.values[encoded[1]]);
+ throw UnimplementedError();
}
- List<int> _getPositionForOffset(double dx, double dy) { throw UnimplementedError(); }
/// Returns the [TextRange] of the word at the given [TextPosition].
///
@@ -1989,10 +2110,8 @@
/// [offset, offset+1]. Word boundaries are defined more precisely in Unicode
/// Standard Annex #29 http://www.unicode.org/reports/tr29/#Word_Boundaries
TextRange getWordBoundary(TextPosition position) {
- final List<int> boundary = _getWordBoundary(position.offset);
- return TextRange(start: boundary[0], end: boundary[1]);
+ throw UnimplementedError();
}
- List<int> _getWordBoundary(int offset) { throw UnimplementedError(); }
/// Returns the [TextRange] of the line at the given [TextPosition].
///
@@ -2003,15 +2122,8 @@
/// This can potentially be expensive, since it needs to compute the line
/// metrics, so use it sparingly.
TextRange getLineBoundary(TextPosition position) {
- final List<int> boundary = _getLineBoundary(position.offset);
- return TextRange(start: boundary[0], end: boundary[1]);
+ throw UnimplementedError();
}
- List<int> _getLineBoundary(int offset) { throw UnimplementedError(); }
-
- // Redirecting the paint function in this way solves some dependency problems
- // in the C++ code. If we straighten out the C++ dependencies, we can remove
- // this indirection.
- void _paint(Canvas canvas, double x, double y) { throw UnimplementedError(); }
/// Returns the full list of [LineMetrics] that describe in detail the various
/// metrics of each laid out line.
@@ -2043,6 +2155,48 @@
Float64List _computeLineMetrics() { throw UnimplementedError(); }
}
+abstract class _Span {
+ double get width;
+ double get height;
+}
+
+class _SpanBox {
+ _SpanBox.fromLTRBS(this.left, this.top, this.right, this.bottom, this.span);
+ final double left;
+ final double top;
+ final double right;
+ final double bottom;
+ final _Span span;
+}
+
+class _TextSpan extends _Span {
+ _TextSpan(this._style, this._text);
+ final TextStyle _style;
+ final String _text;
+
+ @override
+ double get width => _text.length * (_style._fontSize ?? 10.0);
+
+ @override
+ double get height => _style._height ?? _style._fontSize ?? 10.0;
+}
+
+class _Placeholder extends _Span {
+ _Placeholder(this._width, this._height, this._alignment, this._baselineOffset, this._baseline);
+
+ final double _width;
+ final double _height;
+ final int _alignment;
+ final double _baselineOffset;
+ final int? _baseline;
+
+ @override
+ double get width => _width;
+
+ @override
+ double get height => _height;
+}
+
/// Builds a [Paragraph] containing text with the given styling information.
///
/// To set the paragraph's alignment, truncation, and ellipsizing behavior, pass
@@ -2057,12 +2211,11 @@
///
/// After constructing a [Paragraph], call [Paragraph.layout] on it and then
/// paint it with [Canvas.drawParagraph].
-class ParagraphBuilder extends NativeFieldWrapperClass2 {
+class ParagraphBuilder {
/// Creates a new [ParagraphBuilder] object, which is used to create a
/// [Paragraph].
- @pragma('vm:entry-point')
- ParagraphBuilder(ParagraphStyle style) {
+ factory ParagraphBuilder(ParagraphStyle style) {
List<String>? strutFontFamilies;
final StrutStyle? strutStyle = style._strutStyle;
if (strutStyle != null) {
@@ -2073,28 +2226,21 @@
if (strutStyle._fontFamilyFallback != null)
strutFontFamilies.addAll(strutStyle._fontFamilyFallback!);
}
- _constructor(
- style._encoded,
- strutStyle?._encoded,
- style._fontFamily,
- strutFontFamilies,
- style._fontSize,
- style._height,
- style._ellipsis,
- _encodeLocale(style._locale)
- );
+ return ParagraphBuilder._(style, style._toTextStyle(), strutFontFamilies);
}
- void _constructor(
- Int32List encoded,
- ByteData? strutData,
- String? fontFamily,
- List<dynamic>? strutFontFamily,
- double? fontSize,
- double? height,
- String? ellipsis,
- String locale
- ) { throw UnimplementedError(); }
+ ParagraphBuilder._(this._style, TextStyle textStyle, this._strutFontFamilies)
+ : _currentStyle = textStyle {
+ _styleStack.add(_currentStyle);
+ }
+
+ final ParagraphStyle _style;
+ final List<String>? _strutFontFamilies;
+
+ final List<TextStyle> _styleStack = <TextStyle>[];
+ TextStyle _currentStyle;
+
+ List<_Span> _spans = <_Span>[];
/// The number of placeholders currently in the paragraph.
int get placeholderCount => _placeholderCount;
@@ -2108,8 +2254,11 @@
///
/// See [pop] for details.
void pushStyle(TextStyle style) {
+ _currentStyle = style._mergeInto(_currentStyle);
+ _styleStack.add(_currentStyle);
+
final List<String> fullFontFamilies = <String>[];
- fullFontFamilies.add(style._fontFamily);
+ fullFontFamilies.add(style._fontFamily ?? '');
if (style._fontFamilyFallback != null)
fullFontFamilies.addAll(style._fontFamilyFallback!);
@@ -2123,109 +2272,29 @@
byteOffset += FontFeature._kEncodedSize;
}
}
-
- _pushStyle(
- style._encoded,
- fullFontFamilies,
- style._fontSize,
- style._letterSpacing,
- style._wordSpacing,
- style._height,
- style._decorationThickness,
- _encodeLocale(style._locale),
- style._background?._objects,
- style._background?._data,
- style._foreground?._objects,
- style._foreground?._data,
- Shadow._encodeShadows(style._shadows),
- encodedFontFeatures,
- );
}
- void _pushStyle(
- Int32List encoded,
- List<dynamic> fontFamilies,
- double? fontSize,
- double? letterSpacing,
- double? wordSpacing,
- double? height,
- double? decorationThickness,
- String locale,
- List<dynamic>? backgroundObjects,
- ByteData? backgroundData,
- List<dynamic>? foregroundObjects,
- ByteData? foregroundData,
- ByteData shadowsData,
- ByteData? fontFeaturesData,
- ) { throw UnimplementedError(); }
-
- static String _encodeLocale(Locale? locale) => locale?.toString() ?? '';
-
/// Ends the effect of the most recent call to [pushStyle].
///
/// Internally, the paragraph builder maintains a stack of text styles. Text
/// added to the paragraph is affected by all the styles in the stack. Calling
/// [pop] removes the topmost style in the stack, leaving the remaining styles
/// in effect.
- void pop() { throw UnimplementedError(); }
+ void pop() {
+ _currentStyle = _styleStack.removeLast();
+ }
+
+ static final RegExp _whitespace = RegExp(r'\s+');
/// Adds the given text to the paragraph.
///
/// The text will be styled according to the current stack of text styles.
void addText(String text) {
- final String? error = _addText(text);
- if (error != null)
- throw ArgumentError(error);
+ for (String span in text.split(_whitespace)) {
+ _spans.add(_TextSpan(_currentStyle, span));
+ }
}
- String? _addText(String text) { throw UnimplementedError(); }
- /// Adds an inline placeholder space to the paragraph.
- ///
- /// The paragraph will contain a rectangular space with no text of the dimensions
- /// specified.
- ///
- /// The `width` and `height` parameters specify the size of the placeholder rectangle.
- ///
- /// The `alignment` parameter specifies how the placeholder rectangle will be vertically
- /// aligned with the surrounding text. When [PlaceholderAlignment.baseline],
- /// [PlaceholderAlignment.aboveBaseline], and [PlaceholderAlignment.belowBaseline]
- /// alignment modes are used, the baseline needs to be set with the `baseline`.
- /// When using [PlaceholderAlignment.baseline], `baselineOffset` indicates the distance
- /// of the baseline down from the top of of the rectangle. The default `baselineOffset`
- /// is the `height`.
- ///
- /// Examples:
- ///
- /// * For a 30x50 placeholder with the bottom edge aligned with the bottom of the text, use:
- /// `addPlaceholder(30, 50, PlaceholderAlignment.bottom);`
- /// * For a 30x50 placeholder that is vertically centered around the text, use:
- /// `addPlaceholder(30, 50, PlaceholderAlignment.middle);`.
- /// * For a 30x50 placeholder that sits completely on top of the alphabetic baseline, use:
- /// `addPlaceholder(30, 50, PlaceholderAlignment.aboveBaseline, baseline: TextBaseline.alphabetic)`.
- /// * For a 30x50 placeholder with 40 pixels above and 10 pixels below the alphabetic baseline, use:
- /// `addPlaceholder(30, 50, PlaceholderAlignment.baseline, baseline: TextBaseline.alphabetic, baselineOffset: 40)`.
- ///
- /// Lines are permitted to break around each placeholder.
- ///
- /// Decorations will be drawn based on the font defined in the most recently
- /// pushed [TextStyle]. The decorations are drawn as if unicode text were present
- /// in the placeholder space, and will draw the same regardless of the height and
- /// alignment of the placeholder. To hide or manually adjust decorations to fit,
- /// a text style with the desired decoration behavior should be pushed before
- /// adding a placeholder.
- ///
- /// Any decorations drawn through a placeholder will exist on the same canvas/layer
- /// as the text. This means any content drawn on top of the space reserved by
- /// the placeholder will be drawn over the decoration, possibly obscuring the
- /// decoration.
- ///
- /// Placeholders are represented by a unicode 0xFFFC "object replacement character"
- /// in the text buffer. For each placeholder, one object replacement character is
- /// added on to the text buffer.
- ///
- /// The `scale` parameter will scale the `width` and `height` by the specified amount,
- /// and keep track of the scale. The scales of placeholders added can be accessed
- /// through [placeholderScales]. This is primarily used for accessibility scaling.
void addPlaceholder(double width, double height, PlaceholderAlignment alignment, {
double scale = 1.0,
double? baselineOffset,
@@ -2238,23 +2307,22 @@
// Default the baselineOffset to height if null. This will place the placeholder
// fully above the baseline, similar to [PlaceholderAlignment.aboveBaseline].
baselineOffset = baselineOffset ?? height;
- _addPlaceholder(width * scale, height * scale, alignment.index, baselineOffset * scale, baseline == null ? null : baseline.index);
+ _spans.add(_Placeholder(
+ width * scale,
+ height * scale,
+ alignment.index,
+ baselineOffset * scale,
+ baseline?.index,
+ ));
_placeholderCount++;
_placeholderScales.add(scale);
}
- String? _addPlaceholder(double width, double height, int alignment, double baselineOffset, int? baseline) { throw UnimplementedError(); }
- /// Applies the given paragraph style and returns a [Paragraph] containing the
- /// added text and associated styling.
- ///
- /// After calling this function, the paragraph builder object is invalid and
- /// cannot be used further.
Paragraph build() {
- final Paragraph paragraph = Paragraph._();
- _build(paragraph);
+ final Paragraph paragraph = Paragraph._(_style, _spans);
+ _spans = <_Span>[];
return paragraph;
}
- void _build(Paragraph outParagraph) { throw UnimplementedError(); }
}
/// Loads a font from a buffer and makes it available for rendering text.
diff --git a/lib/src/ui/window.dart b/lib/src/ui/window.dart
index 3654a17..ac3c855 100644
--- a/lib/src/ui/window.dart
+++ b/lib/src/ui/window.dart
@@ -253,8 +253,9 @@
/// scheduling of frames.
/// * [RendererBinding], the Flutter framework class which manages layout and
/// painting.
- void render(Scene scene) => _render(scene, this);
- void _render(Scene scene, FlutterView view) { throw UnimplementedError(); }
+ void render(Scene scene) {
+ // TODO(yjbanov): implement a basic preroll for better benchmark realism.
+ }
}
/// A top-level platform window displaying a Flutter layer tree drawn from a
@@ -306,7 +307,27 @@
/// [PlatformDispatcher.instance] for more details about this recommendation.
class SingletonFlutterWindow extends FlutterWindow {
SingletonFlutterWindow._(Object windowId, PlatformDispatcher platformDispatcher)
- : super._(windowId, platformDispatcher);
+ : super._(windowId, platformDispatcher) {
+ platformDispatcher._updateLifecycleState('resumed');
+ _updateWindowMetrics(
+ 0, // id
+ 1.0, // devicePixelRatio
+ 1024, // width
+ 768, // height
+ 0, // viewPaddingTop
+ 0, // viewPaddingRight
+ 0, // viewPaddingBottom
+ 0, // viewPaddingLeft
+ 0, // viewInsetTop
+ 0, // viewInsetRight
+ 0, // viewInsetBottom
+ 0, // viewInsetLeft
+ 0, // systemGestureInsetTop
+ 0, // systemGestureInsetRight
+ 0, // systemGestureInsetBottom
+ 0, // systemGestureInsetLeft
+ );
+ }
/// A callback that is invoked whenever the [devicePixelRatio],
/// [physicalSize], [padding], [viewInsets], [PlatformDispatcher.views], or