diff --git a/lib/ui/compositing.dart b/lib/ui/compositing.dart index e9700d49bda04..fac0ac8a5f121 100644 --- a/lib/ui/compositing.dart +++ b/lib/ui/compositing.dart @@ -9,18 +9,40 @@ part of dart.ui; /// /// Scene objects can be displayed on the screen using the [FlutterView.render] /// method. +abstract class Scene { + /// Synchronously creates a handle to an image from this scene. + /// + /// {@macro dart.ui.painting.Picture.toImageSync} + Image toImageSync(int width, int height); + + /// Creates a raster image representation of the current state of the scene. + /// + /// This is a slow operation that is performed on a background thread. + /// + /// Callers must dispose the [Image] when they are done with it. If the result + /// will be shared with other methods or classes, [Image.clone] should be used + /// and each handle created must be disposed. + Future toImage(int width, int height); + + /// Releases the resources used by this scene. + /// + /// After calling this function, the scene is cannot be used further. + /// + /// This can't be a leaf call because the native function calls Dart API + /// (Dart_SetNativeInstanceField). + void dispose(); +} + @pragma('vm:entry-point') -class Scene extends NativeFieldWrapperClass1 { +base class _NativeScene extends NativeFieldWrapperClass1 implements Scene { /// This class is created by the engine, and should not be instantiated /// or extended directly. /// /// To create a Scene object, use a [SceneBuilder]. @pragma('vm:entry-point') - Scene._(); + _NativeScene._(); - /// Synchronously creates a handle to an image from this scene. - /// - /// {@macro dart.ui.painting.Picture.toImageSync} + @override Image toImageSync(int width, int height) { if (width <= 0 || height <= 0) { throw Exception('Invalid image dimensions.'); @@ -37,13 +59,7 @@ class Scene extends NativeFieldWrapperClass1 { @Native, Uint32, Uint32, Handle)>(symbol: 'Scene::toImageSync') external String? _toImageSync(int width, int height, _Image outImage); - /// Creates a raster image representation of the current state of the scene. - /// - /// This is a slow operation that is performed on a background thread. - /// - /// Callers must dispose the [Image] when they are done with it. If the result - /// will be shared with other methods or classes, [Image.clone] should be used - /// and each handle created must be disposed. + @override Future toImage(int width, int height) { if (width <= 0 || height <= 0) { throw Exception('Invalid image dimensions.'); @@ -61,12 +77,7 @@ class Scene extends NativeFieldWrapperClass1 { @Native, Uint32, Uint32, Handle)>(symbol: 'Scene::toImage') external String? _toImage(int width, int height, _Callback<_Image?> callback); - /// Releases the resources used by this scene. - /// - /// After calling this function, the scene is cannot be used further. - /// - /// This can't be a leaf call because the native function calls Dart API - /// (Dart_SetNativeInstanceField). + @override @Native)>(symbol: 'Scene::dispose') external void dispose(); } @@ -219,68 +230,8 @@ class ShaderMaskEngineLayer extends _EngineLayerWrapper { /// 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 NativeFieldWrapperClass1 { - /// Creates an empty [SceneBuilder] object. - @pragma('vm:entry-point') - SceneBuilder() { - _constructor(); - } - - @Native(symbol: 'SceneBuilder::Create') - external void _constructor(); - - // Layers used in this scene. - // - // The key is the layer used. The value is the description of what the layer - // is used for, e.g. "pushOpacity" or "addRetained". - final Map _usedLayers = {}; - - // In debug mode checks that the `layer` is only used once in a given scene. - bool _debugCheckUsedOnce(EngineLayer layer, String usage) { - assert(() { - assert( - !_usedLayers.containsKey(layer), - 'Layer ${layer.runtimeType} already used.\n' - 'The layer is already being used as ${_usedLayers[layer]} in this scene.\n' - 'A layer may only be used once in a given scene.'); - - _usedLayers[layer] = usage; - return true; - }()); - - return true; - } - - bool _debugCheckCanBeUsedAsOldLayer(_EngineLayerWrapper? layer, String methodName) { - assert(() { - if (layer == null) { - return true; - } - assert(layer._nativeLayer != null, 'Object disposed'); - layer._debugCheckNotUsedAsOldLayer(); - assert(_debugCheckUsedOnce(layer, 'oldLayer in $methodName')); - layer._debugWasUsedAsOldLayer = true; - return true; - }()); - return true; - } - - 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; - } +abstract class SceneBuilder { + factory SceneBuilder() = _NativeSceneBuilder; /// Pushes a transform operation onto the operation stack. /// @@ -313,18 +264,7 @@ class SceneBuilder extends NativeFieldWrapperClass1 { TransformEngineLayer pushTransform( Float64List matrix4, { TransformEngineLayer? oldLayer, - }) { - assert(_matrix4IsValid(matrix4)); - assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushTransform')); - final EngineLayer engineLayer = EngineLayer._(); - _pushTransform(engineLayer, matrix4, oldLayer?._nativeLayer); - final TransformEngineLayer layer = TransformEngineLayer._(engineLayer); - assert(_debugPushLayer(layer)); - return layer; - } - - @Native, Handle, Handle, Handle)>(symbol: 'SceneBuilder::pushTransformHandle') - external void _pushTransform(EngineLayer layer, Float64List matrix4, EngineLayer? oldLayer); + }); /// Pushes an offset operation onto the operation stack. /// @@ -339,17 +279,7 @@ class SceneBuilder extends NativeFieldWrapperClass1 { double dx, double dy, { OffsetEngineLayer? oldLayer, - }) { - assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushOffset')); - final EngineLayer engineLayer = EngineLayer._(); - _pushOffset(engineLayer, dx, dy, oldLayer?._nativeLayer); - final OffsetEngineLayer layer = OffsetEngineLayer._(engineLayer); - assert(_debugPushLayer(layer)); - return layer; - } - - @Native, Handle, Double, Double, Handle)>(symbol: 'SceneBuilder::pushOffset') - external void _pushOffset(EngineLayer layer, double dx, double dy, EngineLayer? oldLayer); + }); /// Pushes a rectangular clip operation onto the operation stack. /// @@ -365,26 +295,7 @@ class SceneBuilder extends NativeFieldWrapperClass1 { Rect rect, { Clip clipBehavior = Clip.antiAlias, ClipRectEngineLayer? oldLayer, - }) { - assert(clipBehavior != Clip.none); - assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushClipRect')); - final EngineLayer engineLayer = EngineLayer._(); - _pushClipRect(engineLayer, rect.left, rect.right, rect.top, rect.bottom, clipBehavior.index, - oldLayer?._nativeLayer); - final ClipRectEngineLayer layer = ClipRectEngineLayer._(engineLayer); - assert(_debugPushLayer(layer)); - return layer; - } - - @Native, Handle, Double, Double, Double, Double, Int32, Handle)>(symbol: 'SceneBuilder::pushClipRect') - external void _pushClipRect( - EngineLayer outEngineLayer, - double left, - double right, - double top, - double bottom, - int clipBehavior, - EngineLayer? oldLayer); + }); /// Pushes a rounded-rectangular clip operation onto the operation stack. /// @@ -400,18 +311,7 @@ class SceneBuilder extends NativeFieldWrapperClass1 { RRect rrect, { Clip clipBehavior = Clip.antiAlias, ClipRRectEngineLayer? oldLayer, - }) { - assert(clipBehavior != Clip.none); - assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushClipRRect')); - final EngineLayer engineLayer = EngineLayer._(); - _pushClipRRect(engineLayer, rrect._getValue32(), clipBehavior.index, oldLayer?._nativeLayer); - final ClipRRectEngineLayer layer = ClipRRectEngineLayer._(engineLayer); - assert(_debugPushLayer(layer)); - return layer; - } - - @Native, Handle, Handle, Int32, Handle)>(symbol: 'SceneBuilder::pushClipRRect') - external void _pushClipRRect(EngineLayer layer, Float32List rrect, int clipBehavior, EngineLayer? oldLayer); + }); /// Pushes a path clip operation onto the operation stack. /// @@ -427,18 +327,7 @@ class SceneBuilder extends NativeFieldWrapperClass1 { Path path, { Clip clipBehavior = Clip.antiAlias, ClipPathEngineLayer? oldLayer, - }) { - assert(clipBehavior != Clip.none); - assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushClipPath')); - final EngineLayer engineLayer = EngineLayer._(); - _pushClipPath(engineLayer, path, clipBehavior.index, oldLayer?._nativeLayer); - final ClipPathEngineLayer layer = ClipPathEngineLayer._(engineLayer); - assert(_debugPushLayer(layer)); - return layer; - } - - @Native, Handle, Pointer, Int32, Handle)>(symbol: 'SceneBuilder::pushClipPath') - external void _pushClipPath(EngineLayer layer, Path path, int clipBehavior, EngineLayer? oldLayer); + }); /// Pushes an opacity operation onto the operation stack. /// @@ -456,17 +345,7 @@ class SceneBuilder extends NativeFieldWrapperClass1 { int alpha, { Offset? offset = Offset.zero, OpacityEngineLayer? oldLayer, - }) { - assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushOpacity')); - final EngineLayer engineLayer = EngineLayer._(); - _pushOpacity(engineLayer, alpha, offset!.dx, offset.dy, oldLayer?._nativeLayer); - final OpacityEngineLayer layer = OpacityEngineLayer._(engineLayer); - assert(_debugPushLayer(layer)); - return layer; - } - - @Native, Handle, Int32, Double, Double, Handle)>(symbol: 'SceneBuilder::pushOpacity') - external void _pushOpacity(EngineLayer layer, int alpha, double dx, double dy, EngineLayer? oldLayer); + }); /// Pushes a color filter operation onto the operation stack. /// @@ -481,18 +360,7 @@ class SceneBuilder extends NativeFieldWrapperClass1 { ColorFilterEngineLayer pushColorFilter( ColorFilter filter, { ColorFilterEngineLayer? oldLayer, - }) { - assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushColorFilter')); - final _ColorFilter nativeFilter = filter._toNativeColorFilter()!; - final EngineLayer engineLayer = EngineLayer._(); - _pushColorFilter(engineLayer, nativeFilter, oldLayer?._nativeLayer); - final ColorFilterEngineLayer layer = ColorFilterEngineLayer._(engineLayer); - assert(_debugPushLayer(layer)); - return layer; - } - - @Native, Handle, Pointer, Handle)>(symbol: 'SceneBuilder::pushColorFilter') - external void _pushColorFilter(EngineLayer layer, _ColorFilter filter, EngineLayer? oldLayer); + }); /// Pushes an image filter operation onto the operation stack. /// @@ -508,18 +376,7 @@ class SceneBuilder extends NativeFieldWrapperClass1 { ImageFilter filter, { Offset offset = Offset.zero, ImageFilterEngineLayer? oldLayer, - }) { - assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushImageFilter')); - final _ImageFilter nativeFilter = filter._toNativeImageFilter(); - final EngineLayer engineLayer = EngineLayer._(); - _pushImageFilter(engineLayer, nativeFilter, offset.dx, offset.dy, oldLayer?._nativeLayer); - final ImageFilterEngineLayer layer = ImageFilterEngineLayer._(engineLayer); - assert(_debugPushLayer(layer)); - return layer; - } - - @Native, Handle, Pointer, Double, Double, Handle)>(symbol: 'SceneBuilder::pushImageFilter') - external void _pushImageFilter(EngineLayer outEngineLayer, _ImageFilter filter, double dx, double dy, EngineLayer? oldLayer); + }); /// Pushes a backdrop filter operation onto the operation stack. /// @@ -536,17 +393,7 @@ class SceneBuilder extends NativeFieldWrapperClass1 { ImageFilter filter, { BlendMode blendMode = BlendMode.srcOver, BackdropFilterEngineLayer? oldLayer, - }) { - assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushBackdropFilter')); - final EngineLayer engineLayer = EngineLayer._(); - _pushBackdropFilter(engineLayer, filter._toNativeImageFilter(), blendMode.index, oldLayer?._nativeLayer); - final BackdropFilterEngineLayer layer = BackdropFilterEngineLayer._(engineLayer); - assert(_debugPushLayer(layer)); - return layer; - } - - @Native, Handle, Pointer, Int32, Handle)>(symbol: 'SceneBuilder::pushBackdropFilter') - external void _pushBackdropFilter(EngineLayer outEngineLayer, _ImageFilter filter, int blendMode, EngineLayer? oldLayer); + }); /// Pushes a shader mask operation onto the operation stack. /// @@ -564,36 +411,7 @@ class SceneBuilder extends NativeFieldWrapperClass1 { BlendMode blendMode, { ShaderMaskEngineLayer? oldLayer, FilterQuality filterQuality = FilterQuality.low, - }) { - assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushShaderMask')); - final EngineLayer engineLayer = EngineLayer._(); - _pushShaderMask( - engineLayer, - shader, - maskRect.left, - maskRect.right, - maskRect.top, - maskRect.bottom, - blendMode.index, - filterQuality.index, - oldLayer?._nativeLayer, - ); - final ShaderMaskEngineLayer layer = ShaderMaskEngineLayer._(engineLayer); - assert(_debugPushLayer(layer)); - return layer; - } - - @Native, Handle, Pointer, Double, Double, Double, Double, Int32, Int32, Handle)>(symbol: 'SceneBuilder::pushShaderMask') - external void _pushShaderMask( - EngineLayer engineLayer, - Shader shader, - double maskRectLeft, - double maskRectRight, - double maskRectTop, - double maskRectBottom, - int blendMode, - int filterQualityIndex, - EngineLayer? oldLayer); + }); /// Ends the effect of the most recently pushed operation. /// @@ -601,15 +419,7 @@ class SceneBuilder extends NativeFieldWrapperClass1 { /// operations in the stack applies to each of the objects added to the scene. /// Calling this function removes the most recently added operation from the /// stack. - void pop() { - if (_layerStack.isNotEmpty) { - _layerStack.removeLast(); - } - _pop(); - } - - @Native)>(symbol: 'SceneBuilder::pop', isLeaf: true) - external void _pop(); + void pop(); /// Add a retained engine layer subtree from previous frames. /// @@ -621,35 +431,7 @@ class SceneBuilder extends NativeFieldWrapperClass1 { /// no need to call [Layer.addToScene] for its children layers. /// /// {@macro dart.ui.sceneBuilder.oldLayerVsRetained} - void addRetained(EngineLayer retainedLayer) { - assert(retainedLayer is _EngineLayerWrapper); - assert(() { - final _EngineLayerWrapper layer = retainedLayer as _EngineLayerWrapper; - - assert(layer._nativeLayer != null); - - void recursivelyCheckChildrenUsedOnce(_EngineLayerWrapper parentLayer) { - _debugCheckUsedOnce(parentLayer, 'retained layer'); - parentLayer._debugCheckNotUsedAsOldLayer(); - - final List<_EngineLayerWrapper>? children = parentLayer._debugChildren; - if (children == null || children.isEmpty) { - return; - } - children.forEach(recursivelyCheckChildrenUsedOnce); - } - - recursivelyCheckChildrenUsedOnce(layer); - - return true; - }()); - - final _EngineLayerWrapper wrapper = retainedLayer as _EngineLayerWrapper; - _addRetained(wrapper._nativeLayer!); - } - - @Native, Handle)>(symbol: 'SceneBuilder::addRetained') - external void _addRetained(EngineLayer retainedLayer); + void addRetained(EngineLayer retainedLayer); /// Adds an object to the scene that displays performance statistics. /// @@ -675,12 +457,7 @@ class SceneBuilder extends NativeFieldWrapperClass1 { /// See also the [PerformanceOverlayOption] enum in the rendering library. /// for more details. // Values above must match constants in //engine/src/sky/compositor/performance_overlay_layer.h - void addPerformanceOverlay(int enabledOptions, Rect bounds) { - _addPerformanceOverlay(enabledOptions, bounds.left, bounds.right, bounds.top, bounds.bottom); - } - - @Native, Uint64, Double, Double, Double, Double)>(symbol: 'SceneBuilder::addPerformanceOverlay', isLeaf: true) - external void _addPerformanceOverlay(int enabledOptions, double left, double right, double top, double bottom); + void addPerformanceOverlay(int enabledOptions, Rect bounds); /// Adds a [Picture] to the scene. /// @@ -708,14 +485,7 @@ class SceneBuilder extends NativeFieldWrapperClass1 { Picture picture, { bool isComplexHint = false, bool willChangeHint = false, - }) { - assert(!picture.debugDisposed); - final int hints = (isComplexHint ? 1 : 0) | (willChangeHint ? 2 : 0); - _addPicture(offset.dx, offset.dy, picture, hints); - } - - @Native, Double, Double, Pointer, Int32)>(symbol: 'SceneBuilder::addPicture') - external void _addPicture(double dx, double dy, Picture picture, int hints); + }); /// Adds a backend texture to the scene. /// @@ -735,12 +505,7 @@ class SceneBuilder extends NativeFieldWrapperClass1 { double height = 0.0, bool freeze = false, FilterQuality filterQuality = FilterQuality.low, - }) { - _addTexture(offset.dx, offset.dy, width, height, textureId, freeze, filterQuality.index); - } - - @Native, Double, Double, Double, Double, Int64, Bool, Int32)>(symbol: 'SceneBuilder::addTexture', isLeaf: true) - external void _addTexture(double dx, double dy, double width, double height, int textureId, bool freeze, int filterQuality); + }); /// Adds a platform view (e.g an iOS UIView) to the scene. /// @@ -763,12 +528,7 @@ class SceneBuilder extends NativeFieldWrapperClass1 { Offset offset = Offset.zero, double width = 0.0, double height = 0.0, - }) { - _addPlatformView(offset.dx, offset.dy, width, height, viewId); - } - - @Native, Double, Double, Double, Double, Int64)>(symbol: 'SceneBuilder::addPlatformView', isLeaf: true) - external void _addPlatformView(double dx, double dy, double width, double height, int viewId); + }); /// Sets a threshold after which additional debugging information should be recorded. /// @@ -776,8 +536,7 @@ class SceneBuilder extends NativeFieldWrapperClass1 { /// interested in using this feature, please contact [flutter-dev](https://groups.google.com/forum/#!forum/flutter-dev). /// We'll hopefully be able to figure out how to make this feature more useful /// to you. - @Native, Uint32)>(symbol: 'SceneBuilder::setRasterizerTracingThreshold', isLeaf: true) - external void setRasterizerTracingThreshold(int frameInterval); + void setRasterizerTracingThreshold(int frameInterval); /// Sets whether the raster cache should checkerboard cached entries. This is /// only useful for debugging purposes. @@ -794,15 +553,13 @@ class SceneBuilder extends NativeFieldWrapperClass1 { /// /// Currently this interface is difficult to use by end-developers. If you're /// interested in using this feature, please contact [flutter-dev](https://groups.google.com/forum/#!forum/flutter-dev). - @Native, Bool)>(symbol: 'SceneBuilder::setCheckerboardRasterCacheImages', isLeaf: true) - external void setCheckerboardRasterCacheImages(bool checkerboard); + void setCheckerboardRasterCacheImages(bool checkerboard); /// Sets whether the compositor should checkerboard layers that are rendered /// to offscreen bitmaps. /// /// This is only useful for debugging purposes. - @Native, Bool)>(symbol: 'SceneBuilder::setCheckerboardOffscreenLayers', isLeaf: true) - external void setCheckerboardOffscreenLayers(bool checkerboard); + void setCheckerboardOffscreenLayers(bool checkerboard); /// Finishes building the scene. /// @@ -812,8 +569,383 @@ class SceneBuilder extends NativeFieldWrapperClass1 { /// /// After calling this function, the scene builder object is invalid and /// cannot be used further. + Scene build(); +} + +base class _NativeSceneBuilder extends NativeFieldWrapperClass1 implements SceneBuilder { + /// Creates an empty [SceneBuilder] object. + @pragma('vm:entry-point') + _NativeSceneBuilder() { + _constructor(); + } + + @Native(symbol: 'SceneBuilder::Create') + external void _constructor(); + + // Layers used in this scene. + // + // The key is the layer used. The value is the description of what the layer + // is used for, e.g. "pushOpacity" or "addRetained". + final Map _usedLayers = {}; + + // In debug mode checks that the `layer` is only used once in a given scene. + bool _debugCheckUsedOnce(EngineLayer layer, String usage) { + assert(() { + assert( + !_usedLayers.containsKey(layer), + 'Layer ${layer.runtimeType} already used.\n' + 'The layer is already being used as ${_usedLayers[layer]} in this scene.\n' + 'A layer may only be used once in a given scene.'); + + _usedLayers[layer] = usage; + return true; + }()); + + return true; + } + + bool _debugCheckCanBeUsedAsOldLayer(_EngineLayerWrapper? layer, String methodName) { + assert(() { + if (layer == null) { + return true; + } + assert(layer._nativeLayer != null, 'Object disposed'); + layer._debugCheckNotUsedAsOldLayer(); + assert(_debugCheckUsedOnce(layer, 'oldLayer in $methodName')); + layer._debugWasUsedAsOldLayer = true; + return true; + }()); + return true; + } + + 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; + } + + @override + TransformEngineLayer pushTransform( + Float64List matrix4, { + TransformEngineLayer? oldLayer, + }) { + assert(_matrix4IsValid(matrix4)); + assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushTransform')); + final EngineLayer engineLayer = _NativeEngineLayer._(); + _pushTransform(engineLayer, matrix4, oldLayer?._nativeLayer); + final TransformEngineLayer layer = TransformEngineLayer._(engineLayer); + assert(_debugPushLayer(layer)); + return layer; + } + + @Native, Handle, Handle, Handle)>(symbol: 'SceneBuilder::pushTransformHandle') + external void _pushTransform(EngineLayer layer, Float64List matrix4, EngineLayer? oldLayer); + + @override + OffsetEngineLayer pushOffset( + double dx, + double dy, { + OffsetEngineLayer? oldLayer, + }) { + assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushOffset')); + final EngineLayer engineLayer = _NativeEngineLayer._(); + _pushOffset(engineLayer, dx, dy, oldLayer?._nativeLayer); + final OffsetEngineLayer layer = OffsetEngineLayer._(engineLayer); + assert(_debugPushLayer(layer)); + return layer; + } + + @Native, Handle, Double, Double, Handle)>(symbol: 'SceneBuilder::pushOffset') + external void _pushOffset(EngineLayer layer, double dx, double dy, EngineLayer? oldLayer); + + @override + ClipRectEngineLayer pushClipRect( + Rect rect, { + Clip clipBehavior = Clip.antiAlias, + ClipRectEngineLayer? oldLayer, + }) { + assert(clipBehavior != Clip.none); + assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushClipRect')); + final EngineLayer engineLayer = _NativeEngineLayer._(); + _pushClipRect(engineLayer, rect.left, rect.right, rect.top, rect.bottom, clipBehavior.index, + oldLayer?._nativeLayer); + final ClipRectEngineLayer layer = ClipRectEngineLayer._(engineLayer); + assert(_debugPushLayer(layer)); + return layer; + } + + @Native, Handle, Double, Double, Double, Double, Int32, Handle)>(symbol: 'SceneBuilder::pushClipRect') + external void _pushClipRect( + EngineLayer outEngineLayer, + double left, + double right, + double top, + double bottom, + int clipBehavior, + EngineLayer? oldLayer); + + @override + ClipRRectEngineLayer pushClipRRect( + RRect rrect, { + Clip clipBehavior = Clip.antiAlias, + ClipRRectEngineLayer? oldLayer, + }) { + assert(clipBehavior != Clip.none); + assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushClipRRect')); + final EngineLayer engineLayer = _NativeEngineLayer._(); + _pushClipRRect(engineLayer, rrect._getValue32(), clipBehavior.index, oldLayer?._nativeLayer); + final ClipRRectEngineLayer layer = ClipRRectEngineLayer._(engineLayer); + assert(_debugPushLayer(layer)); + return layer; + } + + @Native, Handle, Handle, Int32, Handle)>(symbol: 'SceneBuilder::pushClipRRect') + external void _pushClipRRect(EngineLayer layer, Float32List rrect, int clipBehavior, EngineLayer? oldLayer); + + @override + ClipPathEngineLayer pushClipPath( + Path path, { + Clip clipBehavior = Clip.antiAlias, + ClipPathEngineLayer? oldLayer, + }) { + assert(clipBehavior != Clip.none); + assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushClipPath')); + final EngineLayer engineLayer = _NativeEngineLayer._(); + _pushClipPath(engineLayer, path as _NativePath, clipBehavior.index, oldLayer?._nativeLayer); + final ClipPathEngineLayer layer = ClipPathEngineLayer._(engineLayer); + assert(_debugPushLayer(layer)); + return layer; + } + + @Native, Handle, Pointer, Int32, Handle)>(symbol: 'SceneBuilder::pushClipPath') + external void _pushClipPath(EngineLayer layer, _NativePath path, int clipBehavior, EngineLayer? oldLayer); + + @override + OpacityEngineLayer pushOpacity( + int alpha, { + Offset? offset = Offset.zero, + OpacityEngineLayer? oldLayer, + }) { + assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushOpacity')); + final EngineLayer engineLayer = _NativeEngineLayer._(); + _pushOpacity(engineLayer, alpha, offset!.dx, offset.dy, oldLayer?._nativeLayer); + final OpacityEngineLayer layer = OpacityEngineLayer._(engineLayer); + assert(_debugPushLayer(layer)); + return layer; + } + + @Native, Handle, Int32, Double, Double, Handle)>(symbol: 'SceneBuilder::pushOpacity') + external void _pushOpacity(EngineLayer layer, int alpha, double dx, double dy, EngineLayer? oldLayer); + + @override + ColorFilterEngineLayer pushColorFilter( + ColorFilter filter, { + ColorFilterEngineLayer? oldLayer, + }) { + assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushColorFilter')); + final _ColorFilter nativeFilter = filter._toNativeColorFilter()!; + final EngineLayer engineLayer = _NativeEngineLayer._(); + _pushColorFilter(engineLayer, nativeFilter, oldLayer?._nativeLayer); + final ColorFilterEngineLayer layer = ColorFilterEngineLayer._(engineLayer); + assert(_debugPushLayer(layer)); + return layer; + } + + @Native, Handle, Pointer, Handle)>(symbol: 'SceneBuilder::pushColorFilter') + external void _pushColorFilter(EngineLayer layer, _ColorFilter filter, EngineLayer? oldLayer); + + @override + ImageFilterEngineLayer pushImageFilter( + ImageFilter filter, { + Offset offset = Offset.zero, + ImageFilterEngineLayer? oldLayer, + }) { + assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushImageFilter')); + final _ImageFilter nativeFilter = filter._toNativeImageFilter(); + final EngineLayer engineLayer = _NativeEngineLayer._(); + _pushImageFilter(engineLayer, nativeFilter, offset.dx, offset.dy, oldLayer?._nativeLayer); + final ImageFilterEngineLayer layer = ImageFilterEngineLayer._(engineLayer); + assert(_debugPushLayer(layer)); + return layer; + } + + @Native, Handle, Pointer, Double, Double, Handle)>(symbol: 'SceneBuilder::pushImageFilter') + external void _pushImageFilter(EngineLayer outEngineLayer, _ImageFilter filter, double dx, double dy, EngineLayer? oldLayer); + + @override + BackdropFilterEngineLayer pushBackdropFilter( + ImageFilter filter, { + BlendMode blendMode = BlendMode.srcOver, + BackdropFilterEngineLayer? oldLayer, + }) { + assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushBackdropFilter')); + final EngineLayer engineLayer = _NativeEngineLayer._(); + _pushBackdropFilter(engineLayer, filter._toNativeImageFilter(), blendMode.index, oldLayer?._nativeLayer); + final BackdropFilterEngineLayer layer = BackdropFilterEngineLayer._(engineLayer); + assert(_debugPushLayer(layer)); + return layer; + } + + @Native, Handle, Pointer, Int32, Handle)>(symbol: 'SceneBuilder::pushBackdropFilter') + external void _pushBackdropFilter(EngineLayer outEngineLayer, _ImageFilter filter, int blendMode, EngineLayer? oldLayer); + + @override + ShaderMaskEngineLayer pushShaderMask( + Shader shader, + Rect maskRect, + BlendMode blendMode, { + ShaderMaskEngineLayer? oldLayer, + FilterQuality filterQuality = FilterQuality.low, + }) { + assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushShaderMask')); + final EngineLayer engineLayer = _NativeEngineLayer._(); + _pushShaderMask( + engineLayer, + shader, + maskRect.left, + maskRect.right, + maskRect.top, + maskRect.bottom, + blendMode.index, + filterQuality.index, + oldLayer?._nativeLayer, + ); + final ShaderMaskEngineLayer layer = ShaderMaskEngineLayer._(engineLayer); + assert(_debugPushLayer(layer)); + return layer; + } + + @Native, Handle, Pointer, Double, Double, Double, Double, Int32, Int32, Handle)>(symbol: 'SceneBuilder::pushShaderMask') + external void _pushShaderMask( + EngineLayer engineLayer, + Shader shader, + double maskRectLeft, + double maskRectRight, + double maskRectTop, + double maskRectBottom, + int blendMode, + int filterQualityIndex, + EngineLayer? oldLayer); + + @override + void pop() { + if (_layerStack.isNotEmpty) { + _layerStack.removeLast(); + } + _pop(); + } + + @Native)>(symbol: 'SceneBuilder::pop', isLeaf: true) + external void _pop(); + + @override + void addRetained(EngineLayer retainedLayer) { + assert(retainedLayer is _EngineLayerWrapper); + assert(() { + final _EngineLayerWrapper layer = retainedLayer as _EngineLayerWrapper; + + assert(layer._nativeLayer != null); + + void recursivelyCheckChildrenUsedOnce(_EngineLayerWrapper parentLayer) { + _debugCheckUsedOnce(parentLayer, 'retained layer'); + parentLayer._debugCheckNotUsedAsOldLayer(); + + final List<_EngineLayerWrapper>? children = parentLayer._debugChildren; + if (children == null || children.isEmpty) { + return; + } + children.forEach(recursivelyCheckChildrenUsedOnce); + } + + recursivelyCheckChildrenUsedOnce(layer); + + return true; + }()); + + final _EngineLayerWrapper wrapper = retainedLayer as _EngineLayerWrapper; + _addRetained(wrapper._nativeLayer!); + } + + @Native, Handle)>(symbol: 'SceneBuilder::addRetained') + external void _addRetained(EngineLayer retainedLayer); + + @override + void addPerformanceOverlay(int enabledOptions, Rect bounds) { + _addPerformanceOverlay(enabledOptions, bounds.left, bounds.right, bounds.top, bounds.bottom); + } + + @Native, Uint64, Double, Double, Double, Double)>(symbol: 'SceneBuilder::addPerformanceOverlay', isLeaf: true) + external void _addPerformanceOverlay(int enabledOptions, double left, double right, double top, double bottom); + + @override + void addPicture( + Offset offset, + Picture picture, { + bool isComplexHint = false, + bool willChangeHint = false, + }) { + assert(!picture.debugDisposed); + final int hints = (isComplexHint ? 1 : 0) | (willChangeHint ? 2 : 0); + _addPicture(offset.dx, offset.dy, picture as _NativePicture, hints); + } + + @Native, Double, Double, Pointer, Int32)>(symbol: 'SceneBuilder::addPicture') + external void _addPicture(double dx, double dy, _NativePicture picture, int hints); + + @override + void addTexture( + int textureId, { + Offset offset = Offset.zero, + double width = 0.0, + double height = 0.0, + bool freeze = false, + FilterQuality filterQuality = FilterQuality.low, + }) { + _addTexture(offset.dx, offset.dy, width, height, textureId, freeze, filterQuality.index); + } + + @Native, Double, Double, Double, Double, Int64, Bool, Int32)>(symbol: 'SceneBuilder::addTexture', isLeaf: true) + external void _addTexture(double dx, double dy, double width, double height, int textureId, bool freeze, int filterQuality); + + @override + void addPlatformView( + int viewId, { + Offset offset = Offset.zero, + double width = 0.0, + double height = 0.0, + }) { + _addPlatformView(offset.dx, offset.dy, width, height, viewId); + } + + @Native, Double, Double, Double, Double, Int64)>(symbol: 'SceneBuilder::addPlatformView', isLeaf: true) + external void _addPlatformView(double dx, double dy, double width, double height, int viewId); + + @override + @Native, Uint32)>(symbol: 'SceneBuilder::setRasterizerTracingThreshold', isLeaf: true) + external void setRasterizerTracingThreshold(int frameInterval); + + @override + @Native, Bool)>(symbol: 'SceneBuilder::setCheckerboardRasterCacheImages', isLeaf: true) + external void setCheckerboardRasterCacheImages(bool checkerboard); + + @override + @Native, Bool)>(symbol: 'SceneBuilder::setCheckerboardOffscreenLayers', isLeaf: true) + external void setCheckerboardOffscreenLayers(bool checkerboard); + + @override Scene build() { - final Scene scene = Scene._(); + final Scene scene = _NativeScene._(); _build(scene); return scene; } diff --git a/lib/ui/experiments/scene.dart b/lib/ui/experiments/scene.dart index c452cf46d0266..34fbe6358560e 100644 --- a/lib/ui/experiments/scene.dart +++ b/lib/ui/experiments/scene.dart @@ -4,7 +4,7 @@ part of dart.ui; /// A composable [SceneNode]. -class SceneNode extends NativeFieldWrapperClass1 { +base class SceneNode extends NativeFieldWrapperClass1 { @pragma('vm:entry-point') SceneNode._create() { _constructor(); @@ -178,7 +178,7 @@ class SceneNodeValue { /// /// Instances of this class can be obtained from the /// [SceneNode.sceneShader] method. -class SceneShader extends Shader { +base class SceneShader extends Shader { SceneShader._(SceneNode node, { String? debugName }) : _debugName = debugName, super._() { _constructor(node); } diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index 8cbacfc328565..c23bf58167718 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -1938,7 +1938,7 @@ class Image { } @pragma('vm:entry-point') -class _Image extends NativeFieldWrapperClass1 { +base class _Image extends NativeFieldWrapperClass1 { // This class is created by the engine, and should not be instantiated // or extended directly. // @@ -2067,8 +2067,36 @@ class FrameInfo { /// /// To obtain an instance of the [Codec] interface, see /// [instantiateImageCodec]. +abstract class Codec { + /// Number of frames in this image. + int get frameCount; + + /// Number of times to repeat the animation. + /// + /// * 0 when the animation should be played once. + /// * -1 for infinity repetitions. + int get repetitionCount; + + /// 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 getNextFrame(); + + /// Release the resources used by this object. The object is no longer usable + /// after this method is called. + /// + /// This can't be a leaf call because the native function calls Dart API + /// (Dart_SetNativeInstanceField). + void dispose(); +} + @pragma('vm:entry-point') -class Codec extends NativeFieldWrapperClass1 { +base class _NativeCodec extends NativeFieldWrapperClass1 implements Codec { // // This class is created by the engine, and should not be instantiated // or extended directly. @@ -2076,33 +2104,25 @@ class Codec extends NativeFieldWrapperClass1 { // To obtain an instance of the [Codec] interface, see // [instantiateImageCodec]. @pragma('vm:entry-point') - Codec._(); + _NativeCodec._(); int? _cachedFrameCount; - /// Number of frames in this image. + + @override int get frameCount => _cachedFrameCount ??= _frameCount; @Native)>(symbol: 'Codec::frameCount', isLeaf: true) external int get _frameCount; int? _cachedRepetitionCount; - /// Number of times to repeat the animation. - /// - /// * 0 when the animation should be played once. - /// * -1 for infinity repetitions. + + @override int get repetitionCount => _cachedRepetitionCount ??= _repetitionCount; @Native)>(symbol: 'Codec::repetitionCount', isLeaf: true) external int get _repetitionCount; - /// 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. + @override Future getNextFrame() async { final Completer completer = Completer.sync(); final String? error = _getNextFrame((_Image? image, int durationMilliseconds) { @@ -2125,11 +2145,7 @@ class Codec extends NativeFieldWrapperClass1 { @Native, Handle)>(symbol: 'Codec::getNextFrame') external String? _getNextFrame(void Function(_Image?, int) callback); - /// Release the resources used by this object. The object is no longer usable - /// after this method is called. - /// - /// This can't be a leaf call because the native function calls Dart API - /// (Dart_SetNativeInstanceField). + @override @Native)>(symbol: 'Codec::dispose') external void dispose(); } @@ -2533,13 +2549,7 @@ enum PathOperation { } /// A handle for the framework to hold and retain an engine layer across frames. -@pragma('vm:entry-point') -class EngineLayer extends NativeFieldWrapperClass1 { - /// This class is created by the engine, and should not be instantiated - /// or extended directly. - @pragma('vm:entry-point') - EngineLayer._(); - +abstract class EngineLayer { /// Release the resources used by this object. The object is no longer usable /// after this method is called. /// @@ -2555,6 +2565,17 @@ class EngineLayer extends NativeFieldWrapperClass1 { /// /// This can't be a leaf call because the native function calls Dart API /// (Dart_SetNativeInstanceField). + void dispose(); +} + +@pragma('vm:entry-point') +base class _NativeEngineLayer extends NativeFieldWrapperClass1 implements EngineLayer { + /// This class is created by the engine, and should not be instantiated + /// or extended directly. + @pragma('vm:entry-point') + _NativeEngineLayer._(); + + @override @Native)>(symbol: 'EngineLayer::dispose') external void dispose(); } @@ -2576,61 +2597,38 @@ class EngineLayer extends NativeFieldWrapperClass1 { /// /// 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 NativeFieldWrapperClass1 { - /// Create a new empty [Path] object. - @pragma('vm:entry-point') - Path() { _constructor(); } - - /// Avoids creating a new native backing for the path for methods that will - /// create it later, such as [Path.from], [shift] and [transform]. - Path._(); +abstract class Path { + factory Path() = _NativePath; /// 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); + final _NativePath clonedPath = _NativePath._(); + (source as _NativePath)._clone(clonedPath); return clonedPath; } - @Native(symbol: 'Path::Create') - external void _constructor(); - - @Native, Handle)>(symbol: 'Path::clone') - external void _clone(Path outPath); - /// 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); - - @Native)>(symbol: 'Path::getFillType', isLeaf: true) - external int _getFillType(); - - @Native, Int32)>(symbol: 'Path::setFillType', isLeaf: true) - external void _setFillType(int fillType); + PathFillType get fillType; + set fillType(PathFillType value); /// Starts a new sub-path at the given coordinate. - @Native, Double, Double)>(symbol: 'Path::moveTo', isLeaf: true) - external void moveTo(double x, double y); + void moveTo(double x, double y); /// Starts a new sub-path at the given offset from the current point. - @Native, Double, Double)>(symbol: 'Path::relativeMoveTo', isLeaf: true) - external void relativeMoveTo(double dx, double dy); + void relativeMoveTo(double dx, double dy); /// Adds a straight line segment from the current point to the given /// point. - @Native, Double, Double)>(symbol: 'Path::lineTo', isLeaf: true) - external void lineTo(double x, double y); + void lineTo(double x, double y); /// Adds a straight line segment from the current point to the point /// at the given offset from the current point. - @Native, Double, Double)>(symbol: 'Path::relativeLineTo', isLeaf: true) - external void relativeLineTo(double dx, double dy); + void relativeLineTo(double dx, double dy); /// Adds a quadratic bezier segment that curves from the current /// point to the given point (x2,y2), using the control point @@ -2638,16 +2636,13 @@ class Path extends NativeFieldWrapperClass1 { /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/path_quadratic_to.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/path_quadratic_to_dark.png#gh-dark-mode-only) - @Native, Double, Double, Double, Double)>(symbol: 'Path::quadraticBezierTo', isLeaf: true) - external void quadraticBezierTo(double x1, double y1, double x2, double y2); + void quadraticBezierTo(double x1, double y1, double x2, double y2); /// 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. - @Native, Double, Double, Double, Double)>(symbol: 'Path::relativeQuadraticBezierTo', isLeaf: true) - external void relativeQuadraticBezierTo( - double x1, double y1, double x2, double y2); + void relativeQuadraticBezierTo(double x1, double y1, double x2, double y2); /// Adds a cubic bezier segment that curves from the current point /// to the given point (x3,y3), using the control points (x1,y1) and @@ -2655,15 +2650,13 @@ class Path extends NativeFieldWrapperClass1 { /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/path_cubic_to.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/path_cubic_to_dark.png#gh-dark-mode-only) - @Native, Double, Double, Double, Double, Double, Double)>(symbol: 'Path::cubicTo', isLeaf: true) - external void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3); + void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3); /// 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. - @Native, Double, Double, Double, Double, Double, Double)>(symbol: 'Path::relativeCubicTo', isLeaf: true) - external void relativeCubicTo(double x1, double y1, double x2, double y2, double x3, double y3); + void relativeCubicTo(double x1, double y1, double x2, double y2, double x3, double y3); /// Adds a bezier segment that curves from the current point to the /// given point (x2,y2), using the control points (x1,y1) and the @@ -2673,8 +2666,7 @@ class Path extends NativeFieldWrapperClass1 { /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/path_conic_to.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/path_conic_to_dark.png#gh-dark-mode-only) - @Native, Double, Double, Double, Double, Double)>(symbol: 'Path::conicTo', isLeaf: true) - external void conicTo(double x1, double y1, double x2, double y2, double w); + void conicTo(double x1, double y1, double x2, double y2, double w); /// Adds a bezier segment that curves from the current point to the /// point at the offset (x2,y2) from the current point, using the @@ -2682,8 +2674,7 @@ class Path extends NativeFieldWrapperClass1 { /// 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. - @Native, Double, Double, Double, Double, Double)>(symbol: 'Path::relativeConicTo', isLeaf: true) - external void relativeConicTo(double x1, double y1, double x2, double y2, double w); + void relativeConicTo(double x1, double y1, double x2, double y2, double w); /// If the `forceMoveTo` argument is false, adds a straight line /// segment and an arc segment. @@ -2701,13 +2692,7 @@ class Path extends NativeFieldWrapperClass1 { /// /// The line segment added if `forceMoveTo` is false starts at the /// current point and ends at the start of the arc. - 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); - } - - @Native, Double, Double, Double, Double, Double, Double, Bool)>(symbol: 'Path::arcTo', isLeaf: true) - external void _arcTo(double left, double top, double right, double bottom, double startAngle, double sweepAngle, bool forceMoveTo); + void arcTo(Rect rect, double startAngle, double sweepAngle, bool forceMoveTo); /// Appends up to four conic curves weighted to describe an oval of `radius` /// and rotated by `rotation` (measured in degrees and clockwise). @@ -2726,14 +2711,7 @@ class Path extends NativeFieldWrapperClass1 { double rotation = 0.0, bool largeArc = false, bool clockwise = true, - }) { - assert(_offsetIsValid(arcEnd)); - assert(_radiusIsValid(radius)); - _arcToPoint(arcEnd.dx, arcEnd.dy, radius.x, radius.y, rotation, largeArc, clockwise); - } - - @Native, Double, Double, Double, Double, Double, Bool, Bool)>(symbol: 'Path::arcToPoint', isLeaf: true) - external void _arcToPoint(double arcEndX, double arcEndY, double radiusX, double radiusY, double rotation, bool largeArc, bool clockwise); + }); /// Appends up to four conic curves weighted to describe an oval of `radius` /// and rotated by `rotation` (measured in degrees and clockwise). @@ -2755,44 +2733,18 @@ class Path extends NativeFieldWrapperClass1 { double rotation = 0.0, bool largeArc = false, bool clockwise = true, - }) { - assert(_offsetIsValid(arcEndDelta)); - assert(_radiusIsValid(radius)); - _relativeArcToPoint(arcEndDelta.dx, arcEndDelta.dy, radius.x, radius.y, rotation, largeArc, clockwise); - } - - @Native, Double, Double, Double, Double, Double, Bool, Bool)>(symbol: 'Path::relativeArcToPoint', isLeaf: true) - external void _relativeArcToPoint( - double arcEndX, - double arcEndY, - double radiusX, - double radiusY, - double rotation, - bool largeArc, - bool clockwise); + }); /// 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); - } - - @Native, Double, Double, Double, Double)>(symbol: 'Path::addRect', isLeaf: true) - external void _addRect(double left, double top, double right, double bottom); + void addRect(Rect rect); /// 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); - } - - @Native, Double, Double, Double, Double)>(symbol: 'Path::addOval', isLeaf: true) - external void _addOval(double left, double top, double right, double bottom); + void addOval(Rect oval); /// 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 @@ -2808,13 +2760,7 @@ class Path extends NativeFieldWrapperClass1 { /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/path_add_arc_ccw.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/path_add_arc_ccw_dark.png#gh-dark-mode-only) - void addArc(Rect oval, double startAngle, double sweepAngle) { - assert(_rectIsValid(oval)); - _addArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle); - } - - @Native, Double, Double, Double, Double, Double, Double)>(symbol: 'Path::addArc', isLeaf: true) - external void _addArc(double left, double top, double right, double bottom, double startAngle, double sweepAngle); + void addArc(Rect oval, double startAngle, double sweepAngle); /// Adds a new sub-path with a sequence of line segments that connect the given /// points. @@ -2823,44 +2769,19 @@ class Path extends NativeFieldWrapperClass1 { /// last point to the first point. /// /// The `points` argument is interpreted as offsets from the origin. - void addPolygon(List points, bool close) { - _addPolygon(_encodePointList(points), close); - } - - @Native, Handle, Bool)>(symbol: 'Path::addPolygon') - external void _addPolygon(Float32List points, bool close); + void addPolygon(List points, bool close); /// 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._getValue32()); - } - - @Native, Handle)>(symbol: 'Path::addRRect') - external void _addRRect(Float32List rrect); + void addRRect(RRect rrect); /// Adds the sub-paths of `path`, offset by `offset`, to this 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 addPath(Path path, Offset offset, {Float64List? matrix4}) { - assert(_offsetIsValid(offset)); - if (matrix4 != null) { - assert(_matrix4IsValid(matrix4)); - _addPathWithMatrix(path, offset.dx, offset.dy, matrix4); - } else { - _addPath(path, offset.dx, offset.dy); - } - } - - @Native, Pointer, Double, Double)>(symbol: 'Path::addPath') - external void _addPath(Path path, double dx, double dy); - - @Native, Pointer, Double, Double, Handle)>(symbol: 'Path::addPathWithMatrix') - external void _addPathWithMatrix(Path path, double dx, double dy, Float64List matrix); + void addPath(Path path, Offset offset, {Float64List? matrix4}); /// Adds the sub-paths of `path`, offset by `offset`, to this path. /// The current sub-path is extended with the first sub-path @@ -2869,32 +2790,16 @@ class Path extends NativeFieldWrapperClass1 { /// 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}) { - assert(_offsetIsValid(offset)); - if (matrix4 != null) { - assert(_matrix4IsValid(matrix4)); - _extendWithPathAndMatrix(path, offset.dx, offset.dy, matrix4); - } else { - _extendWithPath(path, offset.dx, offset.dy); - } - } - - @Native, Pointer, Double, Double)>(symbol: 'Path::extendWithPath') - external void _extendWithPath(Path path, double dx, double dy); - - @Native, Pointer, Double, Double, Handle)>(symbol: 'Path::extendWithPathAndMatrix') - external void _extendWithPathAndMatrix(Path path, double dx, double dy, Float64List matrix); + void extendWithPath(Path path, Offset offset, {Float64List? matrix4}); /// 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. - @Native)>(symbol: 'Path::close', isLeaf: true) - external void close(); + void close(); /// 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. - @Native)>(symbol: 'Path::reset', isLeaf: true) - external void reset(); + void reset(); /// 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 @@ -2903,37 +2808,15 @@ class Path extends NativeFieldWrapperClass1 { /// The `point` argument is interpreted as an offset from the origin. /// /// Returns true if the point is in the path, and false otherwise. - bool contains(Offset point) { - assert(_offsetIsValid(point)); - return _contains(point.dx, point.dy); - } - - @Native, Double, Double)>(symbol: 'Path::contains', isLeaf: true) - external bool _contains(double x, double y); + bool contains(Offset point); /// 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; - } - - @Native, Handle, Double, Double)>(symbol: 'Path::shift') - external void _shift(Path outPath, double dx, double dy); + Path shift(Offset offset); /// 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; - } - - @Native, Handle, Handle)>(symbol: 'Path::transform') - external void _transform(Path outPath, Float64List matrix4); + Path transform(Float64List matrix4); /// Computes the bounding rectangle for this path. /// @@ -2950,13 +2833,7 @@ class Path extends NativeFieldWrapperClass1 { /// 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]); - } - - @Native)>(symbol: 'Path::getBounds') - external Float32List _getBounds(); + Rect getBounds(); /// Combines the two paths according to the manner specified by the given /// `operation`. @@ -2965,16 +2842,13 @@ class Path extends NativeFieldWrapperClass1 { /// 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) { - final Path path = Path(); - if (path._op(path1, path2, operation.index)) { + final _NativePath path = _NativePath(); + if (path._op(path1 as _NativePath, path2 as _NativePath, operation.index)) { return path; } throw StateError('Path.combine() failed. This may be due an invalid path; in particular, check for NaN values.'); } - @Native, Pointer, Pointer, Int32)>(symbol: 'Path::op') - external bool _op(Path path1, Path path2, int operation); - /// Creates a [PathMetrics] object for this path, which can describe various /// properties about the contours of the path. /// @@ -3006,34 +2880,282 @@ class Path extends NativeFieldWrapperClass1 { /// /// 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); - } + PathMetrics computeMetrics({bool forceClosed = false}); } -/// 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); +@pragma('vm:entry-point') +base class _NativePath extends NativeFieldWrapperClass1 implements Path { + /// Create a new empty [Path] object. + @pragma('vm:entry-point') + _NativePath() { _constructor(); } - /// 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))); - } + /// Avoids creating a new native backing for the path for methods that will + /// create it later, such as [Path.from], [shift] and [transform]. + _NativePath._(); - /// 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; + @Native(symbol: 'Path::Create') + external void _constructor(); + + @Native, Handle)>(symbol: 'Path::clone') + external void _clone(Path outPath); + + @override + PathFillType get fillType => PathFillType.values[_getFillType()]; + @override + set fillType(PathFillType value) => _setFillType(value.index); + + @Native)>(symbol: 'Path::getFillType', isLeaf: true) + external int _getFillType(); + + @Native, Int32)>(symbol: 'Path::setFillType', isLeaf: true) + external void _setFillType(int fillType); + + @override + @Native, Double, Double)>(symbol: 'Path::moveTo', isLeaf: true) + external void moveTo(double x, double y); + + @override + @Native, Double, Double)>(symbol: 'Path::relativeMoveTo', isLeaf: true) + external void relativeMoveTo(double dx, double dy); + + @override + @Native, Double, Double)>(symbol: 'Path::lineTo', isLeaf: true) + external void lineTo(double x, double y); + + @override + @Native, Double, Double)>(symbol: 'Path::relativeLineTo', isLeaf: true) + external void relativeLineTo(double dx, double dy); + + @override + @Native, Double, Double, Double, Double)>(symbol: 'Path::quadraticBezierTo', isLeaf: true) + external void quadraticBezierTo(double x1, double y1, double x2, double y2); + + @override + @Native, Double, Double, Double, Double)>(symbol: 'Path::relativeQuadraticBezierTo', isLeaf: true) + external void relativeQuadraticBezierTo( + double x1, double y1, double x2, double y2); + + @override + @Native, Double, Double, Double, Double, Double, Double)>(symbol: 'Path::cubicTo', isLeaf: true) + external void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3); + + @override + @Native, Double, Double, Double, Double, Double, Double)>(symbol: 'Path::relativeCubicTo', isLeaf: true) + external void relativeCubicTo(double x1, double y1, double x2, double y2, double x3, double y3); + + @override + @Native, Double, Double, Double, Double, Double)>(symbol: 'Path::conicTo', isLeaf: true) + external void conicTo(double x1, double y1, double x2, double y2, double w); + + @override + @Native, Double, Double, Double, Double, Double)>(symbol: 'Path::relativeConicTo', isLeaf: true) + external void relativeConicTo(double x1, double y1, double x2, double y2, double w); + + @override + 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); + } + + @Native, Double, Double, Double, Double, Double, Double, Bool)>(symbol: 'Path::arcTo', isLeaf: true) + external void _arcTo(double left, double top, double right, double bottom, double startAngle, double sweepAngle, bool forceMoveTo); + + @override + void arcToPoint(Offset arcEnd, { + Radius radius = Radius.zero, + double rotation = 0.0, + bool largeArc = false, + bool clockwise = true, + }) { + assert(_offsetIsValid(arcEnd)); + assert(_radiusIsValid(radius)); + _arcToPoint(arcEnd.dx, arcEnd.dy, radius.x, radius.y, rotation, largeArc, clockwise); + } + + @Native, Double, Double, Double, Double, Double, Bool, Bool)>(symbol: 'Path::arcToPoint', isLeaf: true) + external void _arcToPoint(double arcEndX, double arcEndY, double radiusX, double radiusY, double rotation, bool largeArc, bool clockwise); + + @override + void relativeArcToPoint( + Offset arcEndDelta, { + Radius radius = Radius.zero, + double rotation = 0.0, + bool largeArc = false, + bool clockwise = true, + }) { + assert(_offsetIsValid(arcEndDelta)); + assert(_radiusIsValid(radius)); + _relativeArcToPoint(arcEndDelta.dx, arcEndDelta.dy, radius.x, radius.y, rotation, largeArc, clockwise); + } + + @Native, Double, Double, Double, Double, Double, Bool, Bool)>(symbol: 'Path::relativeArcToPoint', isLeaf: true) + external void _relativeArcToPoint( + double arcEndX, + double arcEndY, + double radiusX, + double radiusY, + double rotation, + bool largeArc, + bool clockwise); + + @override + void addRect(Rect rect) { + assert(_rectIsValid(rect)); + _addRect(rect.left, rect.top, rect.right, rect.bottom); + } + + @Native, Double, Double, Double, Double)>(symbol: 'Path::addRect', isLeaf: true) + external void _addRect(double left, double top, double right, double bottom); + + @override + void addOval(Rect oval) { + assert(_rectIsValid(oval)); + _addOval(oval.left, oval.top, oval.right, oval.bottom); + } + + @Native, Double, Double, Double, Double)>(symbol: 'Path::addOval', isLeaf: true) + external void _addOval(double left, double top, double right, double bottom); + + @override + void addArc(Rect oval, double startAngle, double sweepAngle) { + assert(_rectIsValid(oval)); + _addArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle); + } + + @Native, Double, Double, Double, Double, Double, Double)>(symbol: 'Path::addArc', isLeaf: true) + external void _addArc(double left, double top, double right, double bottom, double startAngle, double sweepAngle); + + @override + void addPolygon(List points, bool close) { + _addPolygon(_encodePointList(points), close); + } + + @Native, Handle, Bool)>(symbol: 'Path::addPolygon') + external void _addPolygon(Float32List points, bool close); + + @override + void addRRect(RRect rrect) { + assert(_rrectIsValid(rrect)); + _addRRect(rrect._getValue32()); + } + + @Native, Handle)>(symbol: 'Path::addRRect') + external void _addRRect(Float32List rrect); + + @override + void addPath(Path path, Offset offset, {Float64List? matrix4}) { + assert(_offsetIsValid(offset)); + if (matrix4 != null) { + assert(_matrix4IsValid(matrix4)); + _addPathWithMatrix(path as _NativePath, offset.dx, offset.dy, matrix4); + } else { + _addPath(path as _NativePath, offset.dx, offset.dy); + } + } + + @Native, Pointer, Double, Double)>(symbol: 'Path::addPath') + external void _addPath(_NativePath path, double dx, double dy); + + @Native, Pointer, Double, Double, Handle)>(symbol: 'Path::addPathWithMatrix') + external void _addPathWithMatrix(_NativePath path, double dx, double dy, Float64List matrix); + + @override + void extendWithPath(Path path, Offset offset, {Float64List? matrix4}) { + assert(_offsetIsValid(offset)); + if (matrix4 != null) { + assert(_matrix4IsValid(matrix4)); + _extendWithPathAndMatrix(path as _NativePath, offset.dx, offset.dy, matrix4); + } else { + _extendWithPath(path as _NativePath, offset.dx, offset.dy); + } + } + + @Native, Pointer, Double, Double)>(symbol: 'Path::extendWithPath') + external void _extendWithPath(_NativePath path, double dx, double dy); + + @Native, Pointer, Double, Double, Handle)>(symbol: 'Path::extendWithPathAndMatrix') + external void _extendWithPathAndMatrix(_NativePath path, double dx, double dy, Float64List matrix); + + @override + @Native)>(symbol: 'Path::close', isLeaf: true) + external void close(); + + @override + @Native)>(symbol: 'Path::reset', isLeaf: true) + external void reset(); + + @override + bool contains(Offset point) { + assert(_offsetIsValid(point)); + return _contains(point.dx, point.dy); + } + + @Native, Double, Double)>(symbol: 'Path::contains', isLeaf: true) + external bool _contains(double x, double y); + + @override + Path shift(Offset offset) { + assert(_offsetIsValid(offset)); + final _NativePath path = _NativePath._(); + _shift(path, offset.dx, offset.dy); + return path; + } + + @Native, Handle, Double, Double)>(symbol: 'Path::shift') + external void _shift(Path outPath, double dx, double dy); + + @override + Path transform(Float64List matrix4) { + assert(_matrix4IsValid(matrix4)); + final _NativePath path = _NativePath._(); + _transform(path, matrix4); + return path; + } + + @Native, Handle, Handle)>(symbol: 'Path::transform') + external void _transform(Path outPath, Float64List matrix4); + + @override + Rect getBounds() { + final Float32List rect = _getBounds(); + return Rect.fromLTRB(rect[0], rect[1], rect[2], rect[3]); + } + + @Native)>(symbol: 'Path::getBounds') + external Float32List _getBounds(); + + @Native, Pointer, Pointer, Int32)>(symbol: 'Path::op') + external bool _op(_NativePath path1, _NativePath path2, int operation); + + @override + 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); + + /// 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]. /// @@ -3074,7 +3196,7 @@ class Tangent { /// use [toList] on this object. class PathMetrics extends collection.IterableBase { PathMetrics._(Path path, bool forceClosed) : - _iterator = PathMetricIterator._(_PathMeasure(path, forceClosed)); + _iterator = PathMetricIterator._(_PathMeasure(path as _NativePath, forceClosed)); final Iterator _iterator; @@ -3190,13 +3312,13 @@ class PathMetric { String toString() => 'PathMetric(length: $length, isClosed: $isClosed, contourIndex: $contourIndex)'; } -class _PathMeasure extends NativeFieldWrapperClass1 { - _PathMeasure(Path path, bool forceClosed) { +base class _PathMeasure extends NativeFieldWrapperClass1 { + _PathMeasure(_NativePath path, bool forceClosed) { _constructor(path, forceClosed); } @Native, Bool)>(symbol: 'PathMeasure::Create') - external void _constructor(Path path, bool forceClosed); + external void _constructor(_NativePath path, bool forceClosed); double length(int contourIndex) { assert(contourIndex <= currentContourIndex, 'Iterator must be advanced before index $contourIndex can be used.'); @@ -3227,7 +3349,7 @@ class _PathMeasure extends NativeFieldWrapperClass1 { Path extractPath(int contourIndex, double start, double end, {bool startWithMoveTo = true}) { assert(contourIndex <= currentContourIndex, 'Iterator must be advanced before index $contourIndex can be used.'); - final Path path = Path._(); + final _NativePath path = _NativePath._(); _extractPath(path, contourIndex, start, end, startWithMoveTo); return path; } @@ -3532,7 +3654,7 @@ class ColorFilter implements ImageFilter { /// 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 NativeFieldWrapperClass1 { +base class _ColorFilter extends NativeFieldWrapperClass1 { _ColorFilter.mode(this.creator) : assert(creator._type == ColorFilter._kTypeMode) { _constructor(); @@ -3806,7 +3928,7 @@ class _ComposeImageFilter implements ImageFilter { /// 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 NativeFieldWrapperClass1 { +base class _ImageFilter extends NativeFieldWrapperClass1 { /// Creates an image filter that applies a Gaussian blur. _ImageFilter.blur(_GaussianBlurImageFilter filter) : creator = filter { @@ -3888,7 +4010,7 @@ class _ImageFilter extends NativeFieldWrapperClass1 { /// Base class for objects such as [Gradient] and [ImageShader] which /// correspond to shaders as used by [Paint.shader]. -class Shader extends NativeFieldWrapperClass1 { +base class Shader extends NativeFieldWrapperClass1 { /// This class is created by the engine, and should not be instantiated /// or extended directly. @pragma('vm:entry-point') @@ -4059,7 +4181,7 @@ Float32List _encodeTwoPoints(Offset pointA, Offset pointB) { /// /// * [Gradient](https://api.flutter.dev/flutter/painting/Gradient-class.html), the class in the [painting] library. /// -class Gradient extends Shader { +base class Gradient extends Shader { /// Creates a linear gradient from `from` to `to`. /// /// If `colorStops` is provided, `colorStops[i]` is a number from 0.0 to 1.0 @@ -4260,7 +4382,7 @@ class Gradient extends Shader { } /// A shader (as used by [Paint.shader]) that tiles an image. -class ImageShader extends Shader { +base class ImageShader extends Shader { /// Creates an image-tiling shader. /// /// The first argument specifies the image to render. The @@ -4319,7 +4441,7 @@ class ImageShader extends Shader { /// /// For more information, see the website /// [documentation]( https://docs.flutter.dev/development/ui/advanced/shaders). -class FragmentProgram extends NativeFieldWrapperClass1 { +base class FragmentProgram extends NativeFieldWrapperClass1 { @pragma('vm:entry-point') FragmentProgram._fromAsset(String assetKey) { _constructor(); @@ -4415,7 +4537,7 @@ class FragmentProgram extends NativeFieldWrapperClass1 { /// if two [FragmentShader] objects with different float uniforms or samplers /// are required to exist simultaneously, they must be obtained from two /// different calls to [FragmentProgram.fragmentShader]. -class FragmentShader extends Shader { +base class FragmentShader extends Shader { FragmentShader._(FragmentProgram program, { String? debugName }) : _debugName = debugName, super._() { _floats = _constructor( program, @@ -4570,7 +4692,7 @@ enum VertexMode { /// the conversion overhead. The raw constructor is useful if the data is coming /// from another source (e.g. a file) and can therefore be parsed directly into /// the underlying representation. -class Vertices extends NativeFieldWrapperClass1 { +base class Vertices extends NativeFieldWrapperClass1 { /// Creates a set of vertex data for use with [Canvas.drawVertices]. /// /// The `mode` parameter describes how the points should be interpreted: as @@ -4828,7 +4950,7 @@ enum ClipOp { /// /// The current transform and clip can be saved and restored using the stack /// managed by the [save], [saveLayer], and [restore] methods. -class Canvas extends NativeFieldWrapperClass1 { +abstract class Canvas { /// Creates a canvas for recording graphical operations into the /// given picture recorder. /// @@ -4841,24 +4963,7 @@ class Canvas extends NativeFieldWrapperClass1 { /// /// To end the recording, call [PictureRecorder.endRecording] on the /// given recorder. - @pragma('vm:entry-point') - Canvas(PictureRecorder recorder, [ Rect? cullRect ]) { - 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); - } - - @Native, Double, Double, Double, Double)>(symbol: 'Canvas::Create') - external void _constructor(PictureRecorder recorder, double left, double top, double right, double bottom); - - // The underlying DlCanvas is owned by the DisplayListBuilder 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; + factory Canvas(PictureRecorder recorder, [ Rect? cullRect ]) = _NativeCanvas; /// Saves a copy of the current transform and clip on the save stack. /// @@ -4868,8 +4973,7 @@ class Canvas extends NativeFieldWrapperClass1 { /// /// * [saveLayer], which does the same thing but additionally also groups the /// commands done until the matching [restore]. - @Native)>(symbol: 'Canvas::save', isLeaf: true) - external void save(); + void save(); /// 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 @@ -4980,20 +5084,7 @@ class Canvas extends NativeFieldWrapperClass1 { /// for subsequent commands. /// * [BlendMode], which discusses the use of [Paint.blendMode] with /// [saveLayer]. - void saveLayer(Rect? bounds, Paint paint) { - if (bounds == null) { - _saveLayerWithoutBounds(paint._objects, paint._data); - } else { - assert(_rectIsValid(bounds)); - _saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, paint._objects, paint._data); - } - } - - @Native, Handle, Handle)>(symbol: 'Canvas::saveLayerWithoutBounds') - external void _saveLayerWithoutBounds(List? paintObjects, ByteData paintData); - - @Native, Double, Double, Double, Double, Handle, Handle)>(symbol: 'Canvas::saveLayer') - external void _saveLayer(double left, double top, double right, double bottom, List? paintObjects, ByteData paintData); + void saveLayer(Rect? bounds, Paint paint); /// Pops the current save stack, if there is anything to pop. /// Otherwise, does nothing. @@ -5002,8 +5093,7 @@ class Canvas extends NativeFieldWrapperClass1 { /// /// If the state was pushed with [saveLayer], then this call will also /// cause the new layer to be composited into the previous layer. - @Native)>(symbol: 'Canvas::restore', isLeaf: true) - external void restore(); + void restore(); /// Restores the save stack to a previous level as might be obtained from [getSaveCount]. /// If [count] is less than 1, the stack is restored to its initial state. @@ -5014,8 +5104,7 @@ class Canvas extends NativeFieldWrapperClass1 { /// If any of the state stack levels restored by this call were pushed with /// [saveLayer], then this call will also cause those layers to be composited /// into their previous layers. - @Native, Int32)>(symbol: 'Canvas::restoreToCount', isLeaf: true) - external void restoreToCount(int count); + void restoreToCount(int count); /// Returns the number of items on the save stack, including the /// initial state. This means it returns 1 for a clean canvas, and @@ -5023,13 +5112,11 @@ class Canvas extends NativeFieldWrapperClass1 { /// each matching call to [restore] decrements it. /// /// This number cannot go below 1. - @Native)>(symbol: 'Canvas::getSaveCount', isLeaf: true) - external int getSaveCount(); + int getSaveCount(); /// Add a translation to the current transform, shifting the coordinate space /// horizontally by the first argument and vertically by the second argument. - @Native, Double, Double)>(symbol: 'Canvas::translate', isLeaf: true) - external void translate(double dx, double dy); + void translate(double dx, double dy); /// Add an axis-aligned scale to the current transform, scaling by the first /// argument in the horizontal direction and the second in the vertical @@ -5037,33 +5124,20 @@ class Canvas extends NativeFieldWrapperClass1 { /// /// If [sy] is unspecified, [sx] will be used for the scale in both /// directions. - void scale(double sx, [double? sy]) => _scale(sx, sy ?? sx); - - @Native, Double, Double)>(symbol: 'Canvas::scale', isLeaf: true) - external void _scale(double sx, double sy); + void scale(double sx, [double? sy]); /// Add a rotation to the current transform. The argument is in radians clockwise. - @Native, Double)>(symbol: 'Canvas::rotate', isLeaf: true) - external void rotate(double radians); + void rotate(double radians); /// 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. - @Native, Double, Double)>(symbol: 'Canvas::skew', isLeaf: true) - external void skew(double sx, double sy); + void skew(double sx, double sy); /// Multiply the current transform by the specified 4⨉4 transformation matrix /// specified as a list of values in column-major order. - void transform(Float64List matrix4) { - if (matrix4.length != 16) { - throw ArgumentError('"matrix4" must have 16 entries.'); - } - _transform(matrix4); - } - - @Native, Handle)>(symbol: 'Canvas::transform') - external void _transform(Float64List matrix4); + void transform(Float64List matrix4); /// Returns the current transform including the combined result of all transform /// methods executed since the creation of this [Canvas] object, and respecting the @@ -5073,14 +5147,7 @@ class Canvas extends NativeFieldWrapperClass1 { /// [rotate], [skew], and [transform]. The [restore] method can also modify /// the current transform by restoring it to the same value it had before its /// associated [save] or [saveLayer] call. - Float64List getTransform() { - final Float64List matrix4 = Float64List(16); - _getTransform(matrix4); - return matrix4; - } - - @Native, Handle)>(symbol: 'Canvas::getTransform') - external void _getTransform(Float64List matrix4); + Float64List getTransform(); /// Reduces the clip region to the intersection of the current clip and the /// given rectangle. @@ -5095,13 +5162,7 @@ class Canvas extends NativeFieldWrapperClass1 { /// /// Use [ClipOp.difference] to subtract the provided rectangle from the /// current clip. - void clipRect(Rect rect, { ClipOp clipOp = ClipOp.intersect, bool doAntiAlias = true }) { - assert(_rectIsValid(rect)); - _clipRect(rect.left, rect.top, rect.right, rect.bottom, clipOp.index, doAntiAlias); - } - - @Native, Double, Double, Double, Double, Int32, Bool)>(symbol: 'Canvas::clipRect', isLeaf: true) - external void _clipRect(double left, double top, double right, double bottom, int clipOp, bool doAntiAlias); + void clipRect(Rect rect, { ClipOp clipOp = ClipOp.intersect, bool doAntiAlias = true }); /// Reduces the clip region to the intersection of the current clip and the /// given rounded rectangle. @@ -5113,13 +5174,7 @@ class Canvas extends NativeFieldWrapperClass1 { /// 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)); - _clipRRect(rrect._getValue32(), doAntiAlias); - } - - @Native, Handle, Bool)>(symbol: 'Canvas::clipRRect') - external void _clipRRect(Float32List rrect, bool doAntiAlias); + void clipRRect(RRect rrect, {bool doAntiAlias = true}); /// Reduces the clip region to the intersection of the current clip and the /// given [Path]. @@ -5131,12 +5186,7 @@ class Canvas extends NativeFieldWrapperClass1 { /// 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. - void clipPath(Path path, {bool doAntiAlias = true}) { - _clipPath(path, doAntiAlias); - } - - @Native, Pointer, Bool)>(symbol: 'Canvas::clipPath') - external void _clipPath(Path path, bool doAntiAlias); + void clipPath(Path path, {bool doAntiAlias = true}); /// Returns the conservative bounds of the combined result of all clip methods /// executed within the current save stack of this [Canvas] object, as measured @@ -5190,14 +5240,7 @@ class Canvas extends NativeFieldWrapperClass1 { /// restoring it to the same value it had before its associated [save] or /// [saveLayer] call. /// {@endtemplate} - Rect getLocalClipBounds() { - final Float64List bounds = Float64List(4); - _getLocalClipBounds(bounds); - return Rect.fromLTRB(bounds[0], bounds[1], bounds[2], bounds[3]); - } - - @Native, Handle)>(symbol: 'Canvas::getLocalClipBounds') - external void _getLocalClipBounds(Float64List bounds); + Rect getLocalClipBounds(); /// Returns the conservative bounds of the combined result of all clip methods /// executed within the current save stack of this [Canvas] object, as measured @@ -5213,24 +5256,12 @@ class Canvas extends NativeFieldWrapperClass1 { /// Ratio. /// /// {@macro dart.ui.canvas.conservativeClipBounds} - Rect getDestinationClipBounds() { - final Float64List bounds = Float64List(4); - _getDestinationClipBounds(bounds); - return Rect.fromLTRB(bounds[0], bounds[1], bounds[2], bounds[3]); - } - - @Native, Handle)>(symbol: 'Canvas::getDestinationClipBounds') - external void _getDestinationClipBounds(Float64List bounds); + Rect getDestinationClipBounds(); /// 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) { - _drawColor(color.value, blendMode.index); - } - - @Native, Uint32, Int32)>(symbol: 'Canvas::drawColor', isLeaf: true) - external void _drawColor(int color, int blendMode); + void drawColor(Color color, BlendMode blendMode); /// 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. @@ -5239,65 +5270,34 @@ class Canvas extends NativeFieldWrapperClass1 { /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/canvas_line.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/canvas_line_dark.png#gh-dark-mode-only) - void drawLine(Offset p1, Offset p2, Paint paint) { - assert(_offsetIsValid(p1)); - assert(_offsetIsValid(p2)); - _drawLine(p1.dx, p1.dy, p2.dx, p2.dy, paint._objects, paint._data); - } - - @Native, Double, Double, Double, Double, Handle, Handle)>(symbol: 'Canvas::drawLine') - external void _drawLine(double x1, double y1, double x2, double y2, List? paintObjects, ByteData paintData); + void drawLine(Offset p1, Offset p2, Paint paint); /// 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) { - _drawPaint(paint._objects, paint._data); - } - - @Native, Handle, Handle)>(symbol: 'Canvas::drawPaint') - external void _drawPaint(List? paintObjects, ByteData paintData); + void drawPaint(Paint paint); /// Draws a rectangle with the given [Paint]. Whether the rectangle is filled /// or stroked (or both) is controlled by [Paint.style]. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/canvas_rect.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/canvas_rect_dark.png#gh-dark-mode-only) - void drawRect(Rect rect, Paint paint) { - assert(_rectIsValid(rect)); - _drawRect(rect.left, rect.top, rect.right, rect.bottom, paint._objects, paint._data); - } - - @Native, Double, Double, Double, Double, Handle, Handle)>(symbol: 'Canvas::drawRect') - external void _drawRect(double left, double top, double right, double bottom, List? paintObjects, ByteData paintData); + void drawRect(Rect rect, Paint paint); /// Draws a rounded rectangle with the given [Paint]. Whether the rectangle is /// filled or stroked (or both) is controlled by [Paint.style]. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/canvas_rrect.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/canvas_rrect_dark.png#gh-dark-mode-only) - void drawRRect(RRect rrect, Paint paint) { - assert(_rrectIsValid(rrect)); - _drawRRect(rrect._getValue32(), paint._objects, paint._data); - } - - @Native, Handle, Handle, Handle)>(symbol: 'Canvas::drawRRect') - external void _drawRRect(Float32List rrect, List? paintObjects, ByteData paintData); + void drawRRect(RRect rrect, Paint paint); /// 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)); - _drawDRRect(outer._getValue32(), inner._getValue32(), paint._objects, paint._data); - } - - @Native, Handle, Handle, Handle, Handle)>(symbol: 'Canvas::drawDRRect') - external void _drawDRRect(Float32List outer, Float32List inner, List? paintObjects, ByteData paintData); + void drawDRRect(RRect outer, RRect inner, Paint paint); /// 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 @@ -5305,13 +5305,7 @@ class Canvas extends NativeFieldWrapperClass1 { /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/canvas_oval.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/canvas_oval_dark.png#gh-dark-mode-only) - void drawOval(Rect rect, Paint paint) { - assert(_rectIsValid(rect)); - _drawOval(rect.left, rect.top, rect.right, rect.bottom, paint._objects, paint._data); - } - - @Native, Double, Double, Double, Double, Handle, Handle)>(symbol: 'Canvas::drawOval') - external void _drawOval(double left, double top, double right, double bottom, List? paintObjects, ByteData paintData); + void drawOval(Rect rect, Paint paint); /// 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 @@ -5320,13 +5314,7 @@ class Canvas extends NativeFieldWrapperClass1 { /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/canvas_circle.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/canvas_circle_dark.png#gh-dark-mode-only) - void drawCircle(Offset c, double radius, Paint paint) { - assert(_offsetIsValid(c)); - _drawCircle(c.dx, c.dy, radius, paint._objects, paint._data); - } - - @Native, Double, Double, Double, Handle, Handle)>(symbol: 'Canvas::drawCircle') - external void _drawCircle(double x, double y, double radius, List? paintObjects, ByteData paintData); + void drawCircle(Offset c, double radius, Paint paint); /// Draw an arc scaled to fit inside the given rectangle. /// @@ -5342,48 +5330,18 @@ class Canvas extends NativeFieldWrapperClass1 { /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/canvas_draw_arc_dark.png#gh-dark-mode-only) /// /// 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)); - _drawArc(rect.left, rect.top, rect.right, rect.bottom, startAngle, sweepAngle, useCenter, paint._objects, paint._data); - } - - @Native, Double, Double, Double, Double, Double, Double, Bool, Handle, Handle)>(symbol: 'Canvas::drawArc') - external void _drawArc( - double left, - double top, - double right, - double bottom, - double startAngle, - double sweepAngle, - bool useCenter, - List? paintObjects, - ByteData paintData); + void drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint); /// 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) { - _drawPath(path, paint._objects, paint._data); - } - - @Native, Pointer, Handle, Handle)>(symbol: 'Canvas::drawPath') - external void _drawPath(Path path, List? paintObjects, ByteData paintData); + void drawPath(Path path, Paint paint); /// 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) { - assert(!image.debugDisposed); - assert(_offsetIsValid(offset)); - final String? error = _drawImage(image._image, offset.dx, offset.dy, paint._objects, paint._data, paint.filterQuality.index); - if (error != null) { - throw PictureRasterizationException._(error, stack: image._debugStack); - } - } - - @Native, Pointer, Double, Double, Handle, Handle, Int32)>(symbol: 'Canvas::drawImage') - external String? _drawImage(_Image image, double x, double y, List? paintObjects, ByteData paintData, int filterQualityIndex); + void drawImage(Image image, Offset offset, Paint paint); /// 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. @@ -5394,41 +5352,7 @@ class Canvas extends NativeFieldWrapperClass1 { /// 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) { - assert(!image.debugDisposed); - assert(_rectIsValid(src)); - assert(_rectIsValid(dst)); - final String? error = _drawImageRect(image._image, - src.left, - src.top, - src.right, - src.bottom, - dst.left, - dst.top, - dst.right, - dst.bottom, - paint._objects, - paint._data, - paint.filterQuality.index); - if (error != null) { - throw PictureRasterizationException._(error, stack: image._debugStack); - } - } - - @Native, Pointer, Double, Double, Double, Double, Double, Double, Double, Double, Handle, Handle, Int32)>(symbol: 'Canvas::drawImageRect') - external String? _drawImageRect( - _Image image, - double srcLeft, - double srcTop, - double srcRight, - double srcBottom, - double dstLeft, - double dstTop, - double dstRight, - double dstBottom, - List? paintObjects, - ByteData paintData, - int filterQualityIndex); + void drawImageRect(Image image, Rect src, Rect dst, Paint paint); /// Draws the given [Image] into the canvas using the given [Paint]. /// @@ -5443,51 +5367,11 @@ class Canvas extends NativeFieldWrapperClass1 { /// 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) { - assert(!image.debugDisposed); - assert(_rectIsValid(center)); - assert(_rectIsValid(dst)); - final String? error = _drawImageNine(image._image, - center.left, - center.top, - center.right, - center.bottom, - dst.left, - dst.top, - dst.right, - dst.bottom, - paint._objects, - paint._data, - paint.filterQuality.index); - if (error != null) { - throw PictureRasterizationException._(error, stack: image._debugStack); - } - } - - @Native, Pointer, Double, Double, Double, Double, Double, Double, Double, Double, Handle, Handle, Int32)>(symbol: 'Canvas::drawImageNine') - external String? _drawImageNine( - _Image image, - double centerLeft, - double centerTop, - double centerRight, - double centerBottom, - double dstLeft, - double dstTop, - double dstRight, - double dstBottom, - List? paintObjects, - ByteData paintData, - int filterQualityIndex); + void drawImageNine(Image image, Rect center, Rect dst, Paint paint); /// Draw the given picture onto the canvas. To create a picture, see /// [PictureRecorder]. - void drawPicture(Picture picture) { - assert(!picture.debugDisposed); - _drawPicture(picture); - } - - @Native, Pointer)>(symbol: 'Canvas::drawPicture') - external void _drawPicture(Picture picture); + void drawPicture(Picture picture); /// Draws the text in the given [Paragraph] into this canvas at the given /// [Offset]. @@ -5509,12 +5393,7 @@ class Canvas extends NativeFieldWrapperClass1 { /// 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.debugDisposed); - assert(_offsetIsValid(offset)); - assert(!paragraph._needsLayout); - paragraph._paint(this, offset.dx, offset.dy); - } + void drawParagraph(Paragraph paragraph, Offset offset); /// Draws a sequence of points according to the given [PointMode]. /// @@ -5527,9 +5406,7 @@ class Canvas extends NativeFieldWrapperClass1 { /// /// * [drawRawPoints], which takes `points` as a [Float32List] rather than a /// [List]. - void drawPoints(PointMode pointMode, List points, Paint paint) { - _drawPoints(paint._objects, paint._data, pointMode.index, _encodePointList(points)); - } + void drawPoints(PointMode pointMode, List points, Paint paint); /// Draws a sequence of points according to the given [PointMode]. /// @@ -5543,15 +5420,7 @@ class Canvas extends NativeFieldWrapperClass1 { /// /// * [drawPoints], which takes `points` as a [List] rather than a /// [List]. - void drawRawPoints(PointMode pointMode, Float32List points, Paint paint) { - if (points.length % 2 != 0) { - throw ArgumentError('"points" must have an even number of values.'); - } - _drawPoints(paint._objects, paint._data, pointMode.index, points); - } - - @Native, Handle, Handle, Int32, Handle)>(symbol: 'Canvas::drawPoints') - external void _drawPoints(List? paintObjects, ByteData paintData, int pointMode, Float32List points); + void drawRawPoints(PointMode pointMode, Float32List points, Paint paint); /// Draws a set of [Vertices] onto the canvas as one or more triangles. /// @@ -5581,13 +5450,7 @@ class Canvas extends NativeFieldWrapperClass1 { /// * [Vertices.raw], which creates the vertices using typed data lists /// rather than unencoded lists. /// * [paint], Image shaders can be used to draw images on a triangular mesh. - void drawVertices(Vertices vertices, BlendMode blendMode, Paint paint) { - assert(!vertices.debugDisposed); - _drawVertices(vertices, blendMode.index, paint._objects, paint._data); - } - - @Native, Pointer, Int32, Handle, Handle)>(symbol: 'Canvas::drawVertices') - external void _drawVertices(Vertices vertices, int blendMode, List? paintObjects, ByteData paintData); + void drawVertices(Vertices vertices, BlendMode blendMode, Paint paint); /// Draws many parts of an image - the [atlas] - onto the canvas. /// @@ -5726,52 +5589,7 @@ class Canvas extends NativeFieldWrapperClass1 { List? colors, BlendMode? blendMode, Rect? cullRect, - Paint paint) { - assert(!atlas.debugDisposed); - assert(colors == null || colors.isEmpty || blendMode != null); - - final int rectCount = rects.length; - if (transforms.length != rectCount) { - throw ArgumentError('"transforms" and "rects" lengths must match.'); - } - if (colors != null && colors.isNotEmpty && colors.length != rectCount) { - throw ArgumentError('If non-null, "colors" length must match that of "transforms" and "rects".'); - } - - final Float32List rstTransformBuffer = Float32List(rectCount * 4); - final Float32List rectBuffer = Float32List(rectCount * 4); - - for (int i = 0; i < rectCount; ++i) { - final int index0 = i * 4; - final int index1 = index0 + 1; - final int index2 = index0 + 2; - final int index3 = index0 + 3; - final RSTransform rstTransform = transforms[i]; - final Rect rect = rects[i]; - assert(_rectIsValid(rect)); - rstTransformBuffer[index0] = rstTransform.scos; - rstTransformBuffer[index1] = rstTransform.ssin; - rstTransformBuffer[index2] = rstTransform.tx; - rstTransformBuffer[index3] = rstTransform.ty; - rectBuffer[index0] = rect.left; - rectBuffer[index1] = rect.top; - rectBuffer[index2] = rect.right; - rectBuffer[index3] = rect.bottom; - } - - final Int32List? colorBuffer = (colors == null || colors.isEmpty) ? null : _encodeColorList(colors); - final Float32List? cullRectBuffer = cullRect?._getValue32(); - final int qualityIndex = paint.filterQuality.index; - - final String? error = _drawAtlas( - paint._objects, paint._data, qualityIndex, atlas._image, rstTransformBuffer, rectBuffer, - colorBuffer, (blendMode ?? BlendMode.src).index, cullRectBuffer - ); - - if (error != null) { - throw PictureRasterizationException._(error, stack: atlas._debugStack); - } - } + Paint paint); /// Draws many parts of an image - the [atlas] - onto the canvas. /// @@ -5920,6 +5738,443 @@ class Canvas extends NativeFieldWrapperClass1 { /// /// * [drawAtlas], which takes its arguments as objects rather than typed /// data lists. + void drawRawAtlas(Image atlas, + Float32List rstTransforms, + Float32List rects, + Int32List? colors, + BlendMode? blendMode, + Rect? cullRect, + Paint paint); + + /// 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); +} + +base class _NativeCanvas extends NativeFieldWrapperClass1 implements Canvas { + @pragma('vm:entry-point') + _NativeCanvas(PictureRecorder recorder, [ Rect? cullRect ]) { + if (recorder.isRecording) { + throw ArgumentError('"recorder" must not already be associated with another Canvas.'); + } + _recorder = recorder as _NativePictureRecorder; + _recorder!._canvas = this; + cullRect ??= Rect.largest; + _constructor(_recorder!, cullRect.left, cullRect.top, cullRect.right, cullRect.bottom); + } + + @Native, Double, Double, Double, Double)>(symbol: 'Canvas::Create') + external void _constructor(_NativePictureRecorder recorder, double left, double top, double right, double bottom); + + // The underlying DlCanvas is owned by the DisplayListBuilder 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. + _NativePictureRecorder? _recorder; + + @override + @Native)>(symbol: 'Canvas::save', isLeaf: true) + external void save(); + + @override + void saveLayer(Rect? bounds, Paint paint) { + if (bounds == null) { + _saveLayerWithoutBounds(paint._objects, paint._data); + } else { + assert(_rectIsValid(bounds)); + _saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, paint._objects, paint._data); + } + } + + @Native, Handle, Handle)>(symbol: 'Canvas::saveLayerWithoutBounds') + external void _saveLayerWithoutBounds(List? paintObjects, ByteData paintData); + + @Native, Double, Double, Double, Double, Handle, Handle)>(symbol: 'Canvas::saveLayer') + external void _saveLayer(double left, double top, double right, double bottom, List? paintObjects, ByteData paintData); + + @override + @Native)>(symbol: 'Canvas::restore', isLeaf: true) + external void restore(); + + @override + @Native, Int32)>(symbol: 'Canvas::restoreToCount', isLeaf: true) + external void restoreToCount(int count); + + @override + @Native)>(symbol: 'Canvas::getSaveCount', isLeaf: true) + external int getSaveCount(); + + @override + @Native, Double, Double)>(symbol: 'Canvas::translate', isLeaf: true) + external void translate(double dx, double dy); + + @override + void scale(double sx, [double? sy]) => _scale(sx, sy ?? sx); + + @Native, Double, Double)>(symbol: 'Canvas::scale', isLeaf: true) + external void _scale(double sx, double sy); + + @override + @Native, Double)>(symbol: 'Canvas::rotate', isLeaf: true) + external void rotate(double radians); + + @override + @Native, Double, Double)>(symbol: 'Canvas::skew', isLeaf: true) + external void skew(double sx, double sy); + + @override + void transform(Float64List matrix4) { + if (matrix4.length != 16) { + throw ArgumentError('"matrix4" must have 16 entries.'); + } + _transform(matrix4); + } + + @Native, Handle)>(symbol: 'Canvas::transform') + external void _transform(Float64List matrix4); + + @override + Float64List getTransform() { + final Float64List matrix4 = Float64List(16); + _getTransform(matrix4); + return matrix4; + } + + @Native, Handle)>(symbol: 'Canvas::getTransform') + external void _getTransform(Float64List matrix4); + + @override + void clipRect(Rect rect, { ClipOp clipOp = ClipOp.intersect, bool doAntiAlias = true }) { + assert(_rectIsValid(rect)); + _clipRect(rect.left, rect.top, rect.right, rect.bottom, clipOp.index, doAntiAlias); + } + + @Native, Double, Double, Double, Double, Int32, Bool)>(symbol: 'Canvas::clipRect', isLeaf: true) + external void _clipRect(double left, double top, double right, double bottom, int clipOp, bool doAntiAlias); + + @override + void clipRRect(RRect rrect, {bool doAntiAlias = true}) { + assert(_rrectIsValid(rrect)); + _clipRRect(rrect._getValue32(), doAntiAlias); + } + + @Native, Handle, Bool)>(symbol: 'Canvas::clipRRect') + external void _clipRRect(Float32List rrect, bool doAntiAlias); + + @override + void clipPath(Path path, {bool doAntiAlias = true}) { + _clipPath(path as _NativePath, doAntiAlias); + } + + @Native, Pointer, Bool)>(symbol: 'Canvas::clipPath') + external void _clipPath(_NativePath path, bool doAntiAlias); + + @override + Rect getLocalClipBounds() { + final Float64List bounds = Float64List(4); + _getLocalClipBounds(bounds); + return Rect.fromLTRB(bounds[0], bounds[1], bounds[2], bounds[3]); + } + + @Native, Handle)>(symbol: 'Canvas::getLocalClipBounds') + external void _getLocalClipBounds(Float64List bounds); + + @override + Rect getDestinationClipBounds() { + final Float64List bounds = Float64List(4); + _getDestinationClipBounds(bounds); + return Rect.fromLTRB(bounds[0], bounds[1], bounds[2], bounds[3]); + } + + @Native, Handle)>(symbol: 'Canvas::getDestinationClipBounds') + external void _getDestinationClipBounds(Float64List bounds); + + @override + void drawColor(Color color, BlendMode blendMode) { + _drawColor(color.value, blendMode.index); + } + + @Native, Uint32, Int32)>(symbol: 'Canvas::drawColor', isLeaf: true) + external void _drawColor(int color, int blendMode); + + @override + void drawLine(Offset p1, Offset p2, Paint paint) { + assert(_offsetIsValid(p1)); + assert(_offsetIsValid(p2)); + _drawLine(p1.dx, p1.dy, p2.dx, p2.dy, paint._objects, paint._data); + } + + @Native, Double, Double, Double, Double, Handle, Handle)>(symbol: 'Canvas::drawLine') + external void _drawLine(double x1, double y1, double x2, double y2, List? paintObjects, ByteData paintData); + + @override + void drawPaint(Paint paint) { + _drawPaint(paint._objects, paint._data); + } + + @Native, Handle, Handle)>(symbol: 'Canvas::drawPaint') + external void _drawPaint(List? paintObjects, ByteData paintData); + + @override + void drawRect(Rect rect, Paint paint) { + assert(_rectIsValid(rect)); + _drawRect(rect.left, rect.top, rect.right, rect.bottom, paint._objects, paint._data); + } + + @Native, Double, Double, Double, Double, Handle, Handle)>(symbol: 'Canvas::drawRect') + external void _drawRect(double left, double top, double right, double bottom, List? paintObjects, ByteData paintData); + + @override + void drawRRect(RRect rrect, Paint paint) { + assert(_rrectIsValid(rrect)); + _drawRRect(rrect._getValue32(), paint._objects, paint._data); + } + + @Native, Handle, Handle, Handle)>(symbol: 'Canvas::drawRRect') + external void _drawRRect(Float32List rrect, List? paintObjects, ByteData paintData); + + @override + void drawDRRect(RRect outer, RRect inner, Paint paint) { + assert(_rrectIsValid(outer)); + assert(_rrectIsValid(inner)); + _drawDRRect(outer._getValue32(), inner._getValue32(), paint._objects, paint._data); + } + + @Native, Handle, Handle, Handle, Handle)>(symbol: 'Canvas::drawDRRect') + external void _drawDRRect(Float32List outer, Float32List inner, List? paintObjects, ByteData paintData); + + @override + void drawOval(Rect rect, Paint paint) { + assert(_rectIsValid(rect)); + _drawOval(rect.left, rect.top, rect.right, rect.bottom, paint._objects, paint._data); + } + + @Native, Double, Double, Double, Double, Handle, Handle)>(symbol: 'Canvas::drawOval') + external void _drawOval(double left, double top, double right, double bottom, List? paintObjects, ByteData paintData); + + @override + void drawCircle(Offset c, double radius, Paint paint) { + assert(_offsetIsValid(c)); + _drawCircle(c.dx, c.dy, radius, paint._objects, paint._data); + } + + @Native, Double, Double, Double, Handle, Handle)>(symbol: 'Canvas::drawCircle') + external void _drawCircle(double x, double y, double radius, List? paintObjects, ByteData paintData); + + @override + void drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint) { + assert(_rectIsValid(rect)); + _drawArc(rect.left, rect.top, rect.right, rect.bottom, startAngle, sweepAngle, useCenter, paint._objects, paint._data); + } + + @Native, Double, Double, Double, Double, Double, Double, Bool, Handle, Handle)>(symbol: 'Canvas::drawArc') + external void _drawArc( + double left, + double top, + double right, + double bottom, + double startAngle, + double sweepAngle, + bool useCenter, + List? paintObjects, + ByteData paintData); + + @override + void drawPath(Path path, Paint paint) { + _drawPath(path as _NativePath, paint._objects, paint._data); + } + + @Native, Pointer, Handle, Handle)>(symbol: 'Canvas::drawPath') + external void _drawPath(_NativePath path, List? paintObjects, ByteData paintData); + + @override + void drawImage(Image image, Offset offset, Paint paint) { + assert(!image.debugDisposed); + assert(_offsetIsValid(offset)); + final String? error = _drawImage(image._image, offset.dx, offset.dy, paint._objects, paint._data, paint.filterQuality.index); + if (error != null) { + throw PictureRasterizationException._(error, stack: image._debugStack); + } + } + + @Native, Pointer, Double, Double, Handle, Handle, Int32)>(symbol: 'Canvas::drawImage') + external String? _drawImage(_Image image, double x, double y, List? paintObjects, ByteData paintData, int filterQualityIndex); + + @override + void drawImageRect(Image image, Rect src, Rect dst, Paint paint) { + assert(!image.debugDisposed); + assert(_rectIsValid(src)); + assert(_rectIsValid(dst)); + final String? error = _drawImageRect(image._image, + src.left, + src.top, + src.right, + src.bottom, + dst.left, + dst.top, + dst.right, + dst.bottom, + paint._objects, + paint._data, + paint.filterQuality.index); + if (error != null) { + throw PictureRasterizationException._(error, stack: image._debugStack); + } + } + + @Native, Pointer, Double, Double, Double, Double, Double, Double, Double, Double, Handle, Handle, Int32)>(symbol: 'Canvas::drawImageRect') + external String? _drawImageRect( + _Image image, + double srcLeft, + double srcTop, + double srcRight, + double srcBottom, + double dstLeft, + double dstTop, + double dstRight, + double dstBottom, + List? paintObjects, + ByteData paintData, + int filterQualityIndex); + + @override + void drawImageNine(Image image, Rect center, Rect dst, Paint paint) { + assert(!image.debugDisposed); + assert(_rectIsValid(center)); + assert(_rectIsValid(dst)); + final String? error = _drawImageNine(image._image, + center.left, + center.top, + center.right, + center.bottom, + dst.left, + dst.top, + dst.right, + dst.bottom, + paint._objects, + paint._data, + paint.filterQuality.index); + if (error != null) { + throw PictureRasterizationException._(error, stack: image._debugStack); + } + } + + @Native, Pointer, Double, Double, Double, Double, Double, Double, Double, Double, Handle, Handle, Int32)>(symbol: 'Canvas::drawImageNine') + external String? _drawImageNine( + _Image image, + double centerLeft, + double centerTop, + double centerRight, + double centerBottom, + double dstLeft, + double dstTop, + double dstRight, + double dstBottom, + List? paintObjects, + ByteData paintData, + int filterQualityIndex); + + @override + void drawPicture(Picture picture) { + assert(!picture.debugDisposed); + _drawPicture(picture as _NativePicture); + } + + @Native, Pointer)>(symbol: 'Canvas::drawPicture') + external void _drawPicture(_NativePicture picture); + + @override + void drawParagraph(Paragraph paragraph, Offset offset) { + final _NativeParagraph nativeParagraph = paragraph as _NativeParagraph; + assert(!nativeParagraph.debugDisposed); + assert(_offsetIsValid(offset)); + assert(!nativeParagraph._needsLayout); + nativeParagraph._paint(this, offset.dx, offset.dy); + } + + @override + void drawPoints(PointMode pointMode, List points, Paint paint) { + _drawPoints(paint._objects, paint._data, pointMode.index, _encodePointList(points)); + } + + @override + void drawRawPoints(PointMode pointMode, Float32List points, Paint paint) { + if (points.length % 2 != 0) { + throw ArgumentError('"points" must have an even number of values.'); + } + _drawPoints(paint._objects, paint._data, pointMode.index, points); + } + + @Native, Handle, Handle, Int32, Handle)>(symbol: 'Canvas::drawPoints') + external void _drawPoints(List? paintObjects, ByteData paintData, int pointMode, Float32List points); + + @override + void drawVertices(Vertices vertices, BlendMode blendMode, Paint paint) { + assert(!vertices.debugDisposed); + _drawVertices(vertices, blendMode.index, paint._objects, paint._data); + } + + @Native, Pointer, Int32, Handle, Handle)>(symbol: 'Canvas::drawVertices') + external void _drawVertices(Vertices vertices, int blendMode, List? paintObjects, ByteData paintData); + + @override + void drawAtlas(Image atlas, + List transforms, + List rects, + List? colors, + BlendMode? blendMode, + Rect? cullRect, + Paint paint) { + assert(!atlas.debugDisposed); + assert(colors == null || colors.isEmpty || blendMode != null); + + final int rectCount = rects.length; + if (transforms.length != rectCount) { + throw ArgumentError('"transforms" and "rects" lengths must match.'); + } + if (colors != null && colors.isNotEmpty && colors.length != rectCount) { + throw ArgumentError('If non-null, "colors" length must match that of "transforms" and "rects".'); + } + + final Float32List rstTransformBuffer = Float32List(rectCount * 4); + final Float32List rectBuffer = Float32List(rectCount * 4); + + for (int i = 0; i < rectCount; ++i) { + final int index0 = i * 4; + final int index1 = index0 + 1; + final int index2 = index0 + 2; + final int index3 = index0 + 3; + final RSTransform rstTransform = transforms[i]; + final Rect rect = rects[i]; + assert(_rectIsValid(rect)); + rstTransformBuffer[index0] = rstTransform.scos; + rstTransformBuffer[index1] = rstTransform.ssin; + rstTransformBuffer[index2] = rstTransform.tx; + rstTransformBuffer[index3] = rstTransform.ty; + rectBuffer[index0] = rect.left; + rectBuffer[index1] = rect.top; + rectBuffer[index2] = rect.right; + rectBuffer[index3] = rect.bottom; + } + + final Int32List? colorBuffer = (colors == null || colors.isEmpty) ? null : _encodeColorList(colors); + final Float32List? cullRectBuffer = cullRect?._getValue32(); + final int qualityIndex = paint.filterQuality.index; + + final String? error = _drawAtlas( + paint._objects, paint._data, qualityIndex, atlas._image, rstTransformBuffer, rectBuffer, + colorBuffer, (blendMode ?? BlendMode.src).index, cullRectBuffer + ); + + if (error != null) { + throw PictureRasterizationException._(error, stack: atlas._debugStack); + } + } + + @override void drawRawAtlas(Image atlas, Float32List rstTransforms, Float32List rects, @@ -5963,18 +6218,13 @@ class Canvas extends NativeFieldWrapperClass1 { int blendMode, Float32List? cullRect); - /// 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. + @override void drawShadow(Path path, Color color, double elevation, bool transparentOccluder) { - _drawShadow(path, color.value, elevation, transparentOccluder); + _drawShadow(path as _NativePath, color.value, elevation, transparentOccluder); } @Native, Pointer, Uint32, Double, Bool)>(symbol: 'Canvas::drawShadow') - external void _drawShadow(Path path, int color, double elevation, bool transparentOccluder); + external void _drawShadow(_NativePath path, int color, double elevation, bool transparentOccluder); } /// Signature for [Picture] lifecycle events. @@ -5987,15 +6237,7 @@ typedef PictureEventCallback = void Function(Picture picture); /// 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 NativeFieldWrapperClass1 { - /// 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._(); - +abstract class Picture { /// A callback that is invoked to report a picture creation. /// /// It's preferred to use [MemoryAllocations] in flutter/foundation.dart @@ -6015,6 +6257,54 @@ class Picture extends NativeFieldWrapperClass1 { /// 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. + Future toImage(int width, int height); + + /// Synchronously creates a handle to an image of this picture. + /// + /// {@template dart.ui.painting.Picture.toImageSync} + /// 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. + /// + /// The image object is created and returned synchronously, but is rasterized + /// asynchronously. If the rasterization fails, an exception will be thrown + /// when the image is drawn to a [Canvas]. + /// + /// If a GPU context is available, this image will be created as GPU resident + /// and not copied back to the host. This means the image will be more + /// efficient to draw. + /// + /// If no GPU context is available, the image will be rasterized on the CPU. + /// {@endtemplate} + Image toImageSync(int width, int height); + + /// Release the resources used by this object. The object is no longer usable + /// after this method is called. + void dispose(); + + /// Whether this reference to the underlying picture is [dispose]d. + /// + /// This only returns a valid value if asserts are enabled, and must not be + /// used otherwise. + bool get debugDisposed; + + /// 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; +} + +@pragma('vm:entry-point') +base class _NativePicture extends NativeFieldWrapperClass1 implements Picture { + /// 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') + _NativePicture._(); + + @override Future toImage(int width, int height) { assert(!_disposed); if (width <= 0 || height <= 0) { @@ -6034,23 +6324,7 @@ class Picture extends NativeFieldWrapperClass1 { @Native, Uint32, Uint32, Handle)>(symbol: 'Picture::toImage') external String? _toImage(int width, int height, void Function(_Image?) callback); - /// Synchronously creates a handle to an image of this picture. - /// - /// {@template dart.ui.painting.Picture.toImageSync} - /// 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. - /// - /// The image object is created and returned synchronously, but is rasterized - /// asynchronously. If the rasterization fails, an exception will be thrown - /// when the image is drawn to a [Canvas]. - /// - /// If a GPU context is available, this image will be created as GPU resident - /// and not copied back to the host. This means the image will be more - /// efficient to draw. - /// - /// If no GPU context is available, the image will be rasterized on the CPU. - /// {@endtemplate} + @override Image toImageSync(int width, int height) { assert(!_disposed); if (width <= 0 || height <= 0) { @@ -6065,15 +6339,14 @@ class Picture extends NativeFieldWrapperClass1 { @Native, Uint32, Uint32, Handle)>(symbol: 'Picture::toImageSync') external void _toImageSync(int width, int height, _Image outImage); - /// Release the resources used by this object. The object is no longer usable - /// after this method is called. + @override void dispose() { assert(!_disposed); assert(() { _disposed = true; return true; }()); - onDispose?.call(this); + Picture.onDispose?.call(this); _dispose(); } @@ -6083,10 +6356,8 @@ class Picture extends NativeFieldWrapperClass1 { external void _dispose(); bool _disposed = false; - /// Whether this reference to the underlying picture is [dispose]d. - /// - /// This only returns a valid value if asserts are enabled, and must not be - /// used otherwise. + + @override bool get debugDisposed { bool? disposed; assert(() { @@ -6096,10 +6367,7 @@ class Picture extends NativeFieldWrapperClass1 { return disposed ?? (throw StateError('Picture.debugDisposed is only available when asserts are enabled.')); } - /// 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. + @override @Native)>(symbol: 'Picture::GetAllocationSize', isLeaf: true) external int get approximateBytesUsed; } @@ -6108,15 +6376,11 @@ class Picture extends NativeFieldWrapperClass1 { /// /// To begin recording, construct a [Canvas] to record the commands. /// To end recording, use the [PictureRecorder.endRecording] method. -class PictureRecorder extends NativeFieldWrapperClass1 { +abstract class PictureRecorder { /// 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(); } - - @Native(symbol: 'PictureRecorder::Create') - external void _constructor(); + factory PictureRecorder() = _NativePictureRecorder; /// Whether this object is currently recording commands. /// @@ -6125,18 +6389,32 @@ class PictureRecorder extends NativeFieldWrapperClass1 { /// 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. - bool get isRecording => _canvas != null; + bool get isRecording; /// 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(); +} + +base class _NativePictureRecorder extends NativeFieldWrapperClass1 implements PictureRecorder { + @pragma('vm:entry-point') + _NativePictureRecorder() { _constructor(); } + + @Native(symbol: 'PictureRecorder::Create') + external void _constructor(); + + @override + bool get isRecording => _canvas != null; + + @override Picture endRecording() { if (_canvas == null) { throw StateError('PictureRecorder did not start recording.'); } - final Picture picture = Picture._(); + final _NativePicture picture = _NativePicture._(); _endRecording(picture); _canvas!._recorder = null; _canvas = null; @@ -6147,9 +6425,9 @@ class PictureRecorder extends NativeFieldWrapperClass1 { } @Native, Handle)>(symbol: 'PictureRecorder::endRecording') - external void _endRecording(Picture outPicture); + external void _endRecording(_NativePicture outPicture); - Canvas? _canvas; + _NativeCanvas? _canvas; } /// A single shadow. @@ -6356,7 +6634,7 @@ class Shadow { /// /// The creator of this object is responsible for calling [dispose] when it is /// no longer needed. -class ImmutableBuffer extends NativeFieldWrapperClass1 { +base class ImmutableBuffer extends NativeFieldWrapperClass1 { ImmutableBuffer._(this._length); /// Creates a copy of the data from a [Uint8List] suitable for internal use @@ -6458,8 +6736,69 @@ class ImmutableBuffer extends NativeFieldWrapperClass1 { /// /// Use this class to determine the height, width, and byte size of image data /// before decoding it. -class ImageDescriptor extends NativeFieldWrapperClass1 { - ImageDescriptor._(); +abstract class ImageDescriptor { + /// Creates an image descriptor from raw image pixels. + /// + /// The `pixels` parameter is the pixel data. They are packed in bytes in the + /// order described by `pixelFormat`, then grouped in rows, from left to right, + /// then top to bottom. + /// + /// 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. + factory ImageDescriptor.raw( + ImmutableBuffer buffer, { + required int width, + required int height, + int? rowBytes, + required PixelFormat pixelFormat, + }) = _NativeImageDescriptor.raw; + + /// Creates an image descriptor from encoded data in a supported format. + static Future encoded(ImmutableBuffer buffer) { + final _NativeImageDescriptor descriptor = _NativeImageDescriptor._(); + return _futurize((_Callback callback) { + return descriptor._initEncoded(buffer, callback); + }).then((_) => descriptor); + } + + /// The width, in pixels, of the image. + /// + /// On the Web, this is only supported for [raw] images. + int get width; + + /// The height, in pixels, of the image. + /// + /// On the Web, this is only supported for [raw] images. + int get height; + + /// The number of bytes per pixel in the image. + /// + /// On web, this is only supported for [raw] images. + int get bytesPerPixel; + + /// Release the resources used by this object. The object is no longer usable + /// after this method is called. + /// + /// This can't be a leaf call because the native function calls Dart API + /// (Dart_SetNativeInstanceField). + void dispose(); + + /// 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 instantiateCodec({int? targetWidth, int? targetHeight}); +} + +base class _NativeImageDescriptor extends NativeFieldWrapperClass1 implements ImageDescriptor { + _NativeImageDescriptor._(); /// Creates an image descriptor from raw image pixels. /// @@ -6471,7 +6810,7 @@ class ImageDescriptor extends NativeFieldWrapperClass1 { /// 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( + _NativeImageDescriptor.raw( ImmutableBuffer buffer, { required int width, required int height, @@ -6485,14 +6824,6 @@ class ImageDescriptor extends NativeFieldWrapperClass1 { _initRaw(this, buffer, width, height, rowBytes ?? -1, pixelFormat.index); } - /// Creates an image descriptor from encoded data in a supported format. - static Future encoded(ImmutableBuffer buffer) { - final ImageDescriptor descriptor = ImageDescriptor._(); - return _futurize((_Callback callback) { - return descriptor._initEncoded(buffer, callback); - }).then((_) => descriptor); - } - @Native, Handle)>(symbol: 'ImageDescriptor::initEncoded') external String? _initEncoded(ImmutableBuffer buffer, _Callback callback); @@ -6504,9 +6835,7 @@ class ImageDescriptor extends NativeFieldWrapperClass1 { @Native)>(symbol: 'ImageDescriptor::width', isLeaf: true) external int _getWidth(); - /// The width, in pixels, of the image. - /// - /// On the Web, this is only supported for [raw] images. + @override int get width => _width ??= _getWidth(); int? _height; @@ -6514,9 +6843,7 @@ class ImageDescriptor extends NativeFieldWrapperClass1 { @Native)>(symbol: 'ImageDescriptor::height', isLeaf: true) external int _getHeight(); - /// The height, in pixels, of the image. - /// - /// On the Web, this is only supported for [raw] images. + @override int get height => _height ??= _getHeight(); int? _bytesPerPixel; @@ -6524,28 +6851,14 @@ class ImageDescriptor extends NativeFieldWrapperClass1 { @Native)>(symbol: 'ImageDescriptor::bytesPerPixel', isLeaf: true) external int _getBytesPerPixel(); - /// The number of bytes per pixel in the image. - /// - /// On web, this is only supported for [raw] images. + @override int get bytesPerPixel => _bytesPerPixel ??= _getBytesPerPixel(); - /// Release the resources used by this object. The object is no longer usable - /// after this method is called. - /// - /// This can't be a leaf call because the native function calls Dart API - /// (Dart_SetNativeInstanceField). + @override @Native)>(symbol: 'ImageDescriptor::dispose') external void dispose(); - /// 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. + @override Future instantiateCodec({int? targetWidth, int? targetHeight}) async { if (targetWidth != null && targetWidth <= 0) { targetWidth = null; @@ -6567,7 +6880,7 @@ class ImageDescriptor extends NativeFieldWrapperClass1 { assert(targetWidth != null); assert(targetHeight != null); - final Codec codec = Codec._(); + final Codec codec = _NativeCodec._(); _instantiateCodec(codec, targetWidth!, targetHeight!); return codec; } diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index 747405fb8ee4f..0e26b0ca215a1 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -820,10 +820,10 @@ class PlatformDispatcher { semantics, use PlatformDispatcher.instance.views to get a [FlutterView] and call `updateSemantics`. ''') - void updateSemantics(SemanticsUpdate update) => _updateSemantics(update); + void updateSemantics(SemanticsUpdate update) => _updateSemantics(update as _NativeSemanticsUpdate); @Native)>(symbol: 'PlatformConfigurationNativeApi::UpdateSemantics') - external static void _updateSemantics(SemanticsUpdate update); + external static void _updateSemantics(_NativeSemanticsUpdate update); /// The system-reported default locale of the device. /// diff --git a/lib/ui/semantics.dart b/lib/ui/semantics.dart index bab51799b1306..832f56a5fdf96 100644 --- a/lib/ui/semantics.dart +++ b/lib/ui/semantics.dart @@ -585,7 +585,7 @@ class SemanticsFlag { /// spell out the string character by character when announcing the string. /// * [LocaleStringAttribute], which causes the assistive technologies to /// treat the string in the specific language. -abstract class StringAttribute extends NativeFieldWrapperClass1 { +abstract base class StringAttribute extends NativeFieldWrapperClass1 { StringAttribute._({ required this.range, }); @@ -611,7 +611,7 @@ abstract class StringAttribute extends NativeFieldWrapperClass1 { /// * [AttributedString], where the string attributes are used. /// * [LocaleStringAttribute], which causes the assistive technologies to /// treat the string in the specific language. -class SpellOutStringAttribute extends StringAttribute { +base class SpellOutStringAttribute extends StringAttribute { /// Creates a string attribute that denotes the text in [range] must be /// spell out when the assistive technologies announce the string. SpellOutStringAttribute({ @@ -642,7 +642,7 @@ class SpellOutStringAttribute extends StringAttribute { /// * [AttributedString], where the string attributes are used. /// * [SpellOutStringAttribute], which causes the assistive technologies to /// spell out the string character by character when announcing the string. -class LocaleStringAttribute extends StringAttribute { +base class LocaleStringAttribute extends StringAttribute { /// Creates a string attribute that denotes the text in [range] must be /// treated as the language specified by the [locale] when the assistive /// technologies announce the string. @@ -675,14 +675,9 @@ class LocaleStringAttribute extends StringAttribute { /// 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 NativeFieldWrapperClass1 { +abstract class SemanticsUpdateBuilder { /// Creates an empty [SemanticsUpdateBuilder] object. - @pragma('vm:entry-point') - SemanticsUpdateBuilder() { _constructor(); } - - @Native(symbol: 'SemanticsUpdateBuilder::Create') - external void _constructor(); + factory SemanticsUpdateBuilder() = _NativeSemanticsUpdateBuilder; /// Update the information associated with the node with the given `id`. /// @@ -765,6 +760,77 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass1 { /// z-direction starting at `elevation`. Basically, in the z-direction the /// node starts at `elevation` above the parent and ends at `elevation` + /// `thickness` above the parent. + void updateNode({ + required int id, + required int flags, + required int actions, + required int maxValueLength, + required int currentValueLength, + required int textSelectionBase, + required int textSelectionExtent, + required int platformViewId, + required int scrollChildren, + required int scrollIndex, + required double scrollPosition, + required double scrollExtentMax, + required double scrollExtentMin, + required double elevation, + required double thickness, + required Rect rect, + required String label, + required List labelAttributes, + required String value, + required List valueAttributes, + required String increasedValue, + required List increasedValueAttributes, + required String decreasedValue, + required List decreasedValueAttributes, + required String hint, + required List hintAttributes, + String? tooltip, + TextDirection? textDirection, + required Float64List transform, + required Int32List childrenInTraversalOrder, + required Int32List childrenInHitTestOrder, + required Int32List additionalActions, + }); + + /// Update the custom semantics action associated with the given `id`. + /// + /// The name of the action exposed to the user is the `label`. For overridden + /// standard actions this value is ignored. + /// + /// The `hint` should describe what happens when an action occurs, not the + /// manner in which a tap is accomplished. For example, use "delete" instead + /// of "double tap to delete". + /// + /// The text direction of the `hint` and `label` is the same as the global + /// window. + /// + /// For overridden standard actions, `overrideId` corresponds with a + /// [SemanticsAction.index] value. For custom actions this argument should not be + /// provided. + void updateCustomAction({required int id, String? label, String? hint, int overrideId = -1}); + + /// Creates a [SemanticsUpdate] object that encapsulates the updates recorded + /// by this object. + /// + /// The returned object can be passed to [PlatformDispatcher.updateSemantics] + /// to actually update the semantics retained by the system. + /// + /// This object is unusable after calling build. + SemanticsUpdate build(); +} + +@pragma('vm:entry-point') +base class _NativeSemanticsUpdateBuilder extends NativeFieldWrapperClass1 implements SemanticsUpdateBuilder { + @pragma('vm:entry-point') + _NativeSemanticsUpdateBuilder() { _constructor(); } + + @Native(symbol: 'SemanticsUpdateBuilder::Create') + external void _constructor(); + + @override void updateNode({ required int id, required int flags, @@ -913,41 +979,21 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass1 { Int32List childrenInHitTestOrder, Int32List additionalActions); - /// Update the custom semantics action associated with the given `id`. - /// - /// The name of the action exposed to the user is the `label`. For overridden - /// standard actions this value is ignored. - /// - /// The `hint` should describe what happens when an action occurs, not the - /// manner in which a tap is accomplished. For example, use "delete" instead - /// of "double tap to delete". - /// - /// The text direction of the `hint` and `label` is the same as the global - /// window. - /// - /// For overridden standard actions, `overrideId` corresponds with a - /// [SemanticsAction.index] value. For custom actions this argument should not be - /// provided. + @override void updateCustomAction({required int id, String? label, String? hint, int overrideId = -1}) { _updateCustomAction(id, label ?? '', hint ?? '', overrideId); } @Native, Int32, Handle, Handle, Int32)>(symbol: 'SemanticsUpdateBuilder::updateCustomAction') external void _updateCustomAction(int id, String label, String hint, int overrideId); - /// Creates a [SemanticsUpdate] object that encapsulates the updates recorded - /// by this object. - /// - /// The returned object can be passed to [PlatformDispatcher.updateSemantics] - /// to actually update the semantics retained by the system. - /// - /// This object is unusable after calling build. + @override SemanticsUpdate build() { - final SemanticsUpdate semanticsUpdate = SemanticsUpdate._(); + final _NativeSemanticsUpdate semanticsUpdate = _NativeSemanticsUpdate._(); _build(semanticsUpdate); return semanticsUpdate; } @Native, Handle)>(symbol: 'SemanticsUpdateBuilder::build') - external void _build(SemanticsUpdate outSemanticsUpdate); + external void _build(_NativeSemanticsUpdate outSemanticsUpdate); } /// An opaque object representing a batch of semantics updates. @@ -956,15 +1002,7 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass1 { /// /// Semantics updates can be applied to the system's retained semantics tree /// using the [PlatformDispatcher.updateSemantics] method. -@pragma('vm:entry-point') -class SemanticsUpdate extends NativeFieldWrapperClass1 { - /// 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._(); - +abstract class SemanticsUpdate { /// Releases the resources used by this semantics update. /// /// After calling this function, the semantics update is cannot be used @@ -972,6 +1010,19 @@ class SemanticsUpdate extends NativeFieldWrapperClass1 { /// /// This can't be a leaf call because the native function calls Dart API /// (Dart_SetNativeInstanceField). + void dispose(); +} + +@pragma('vm:entry-point') +base class _NativeSemanticsUpdate extends NativeFieldWrapperClass1 implements 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') + _NativeSemanticsUpdate._(); + + @override @Native)>(symbol: 'SemanticsUpdate::dispose') external void dispose(); } diff --git a/lib/ui/text.dart b/lib/ui/text.dart index 64ba28c447bac..d163f3a078776 100644 --- a/lib/ui/text.dart +++ b/lib/ui/text.dart @@ -2677,59 +2677,42 @@ class LineMetrics { /// /// Paragraphs can be displayed on a [Canvas] using the [Canvas.drawParagraph] /// method. -@pragma('vm:entry-point') -class Paragraph extends NativeFieldWrapperClass1 { - /// 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._(); - - bool _needsLayout = true; - +abstract class Paragraph { /// The amount of horizontal space this paragraph occupies. /// /// Valid only after [layout] has been called. - @Native)>(symbol: 'Paragraph::width', isLeaf: true) - external double get width; + double get width; /// The amount of vertical space this paragraph occupies. /// /// Valid only after [layout] has been called. - @Native)>(symbol: 'Paragraph::height', isLeaf: true) - external double get height; + double get height; /// 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. - @Native)>(symbol: 'Paragraph::longestLine', isLeaf: true) - external double get longestLine; + double get longestLine; /// The minimum width that this paragraph could be without failing to paint /// its contents within itself. /// /// Valid only after [layout] has been called. - @Native)>(symbol: 'Paragraph::minIntrinsicWidth', isLeaf: true) - external double get minIntrinsicWidth; + double get minIntrinsicWidth; /// Returns the smallest width beyond which increasing the width never /// decreases the height. /// /// Valid only after [layout] has been called. - @Native)>(symbol: 'Paragraph::maxIntrinsicWidth', isLeaf: true) - external double get maxIntrinsicWidth; + double get maxIntrinsicWidth; /// The distance from the top of the paragraph to the alphabetic /// baseline of the first line, in logical pixels. - @Native)>(symbol: 'Paragraph::alphabeticBaseline', isLeaf: true) - external double get alphabeticBaseline; + double get alphabeticBaseline; /// The distance from the top of the paragraph to the ideographic /// baseline of the first line, in logical pixels. - @Native)>(symbol: 'Paragraph::ideographicBaseline', isLeaf: true) - external double get ideographicBaseline; + double get ideographicBaseline; /// 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 @@ -2738,12 +2721,128 @@ class Paragraph extends NativeFieldWrapperClass1 { /// /// See the discussion of the `maxLines` and `ellipsis` arguments at /// [ParagraphStyle.new]. - @Native)>(symbol: 'Paragraph::didExceedMaxLines', isLeaf: true) - external bool get didExceedMaxLines; + bool get didExceedMaxLines; /// 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); + + /// Returns a list of text boxes that enclose the given text range. + /// + /// The [boxHeightStyle] and [boxWidthStyle] parameters allow customization + /// of how the boxes are bound vertically and horizontally. Both style + /// parameters default to the tight option, which will provide close-fitting + /// boxes and will not account for any line spacing. + /// + /// Coordinates of the TextBox are relative to the upper-left corner of the paragraph, + /// where positive y values indicate down. + /// + /// The [boxHeightStyle] and [boxWidthStyle] parameters must not be null. + /// + /// See [BoxHeightStyle] and [BoxWidthStyle] for full descriptions of each option. + List getBoxesForRange(int start, int end, {BoxHeightStyle boxHeightStyle = BoxHeightStyle.tight, BoxWidthStyle boxWidthStyle = BoxWidthStyle.tight}); + + /// Returns a list of text boxes that enclose all placeholders in the paragraph. + /// + /// The order of the boxes are in the same order as passed in through + /// [ParagraphBuilder.addPlaceholder]. + /// + /// Coordinates of the [TextBox] are relative to the upper-left corner of the paragraph, + /// where positive y values indicate down. + List getBoxesForPlaceholders(); + + /// Returns the text position closest to the given offset. + TextPosition getPositionForOffset(Offset offset); + + /// Returns the [TextRange] of the word at the given [TextPosition]. + /// + /// Characters not part of a word, such as spaces, symbols, and punctuation, + /// have word breaks on both sides. In such cases, this method will return + /// (offset, offset+1). Word boundaries are defined more precisely in Unicode + /// Standard Annex #29 http://www.unicode.org/reports/tr29/#Word_Boundaries + /// + /// The [TextPosition] is treated as caret position, its [TextPosition.affinity] + /// is used to determine which character this position points to. For example, + /// the word boundary at `TextPosition(offset: 5, affinity: TextPosition.upstream)` + /// of the `string = 'Hello word'` will return range (0, 5) because the position + /// points to the character 'o' instead of the space. + TextRange getWordBoundary(TextPosition position); + + /// Returns the [TextRange] of the line at the given [TextPosition]. + /// + /// The newline (if any) is returned as part of the range. + /// + /// Not valid until after layout. + /// + /// This can potentially be expensive, since it needs to compute the line + /// metrics, so use it sparingly. + TextRange getLineBoundary(TextPosition position); + + /// Returns the full list of [LineMetrics] that describe in detail the various + /// metrics of each laid out line. + /// + /// Not valid until after layout. + /// + /// This can potentially return a large amount of data, so it is not recommended + /// to repeatedly call this. Instead, cache the results. + List computeLineMetrics(); + + /// Release the resources used by this object. The object is no longer usable + /// after this method is called. + void dispose(); + + /// Whether this reference to the underlying picture is [dispose]d. + /// + /// This only returns a valid value if asserts are enabled, and must not be + /// used otherwise. + bool get debugDisposed; + } + +@pragma('vm:entry-point') +base class _NativeParagraph extends NativeFieldWrapperClass1 implements 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') + _NativeParagraph._(); + + bool _needsLayout = true; + + @override + @Native)>(symbol: 'Paragraph::width', isLeaf: true) + external double get width; + + @override + @Native)>(symbol: 'Paragraph::height', isLeaf: true) + external double get height; + + @override + @Native)>(symbol: 'Paragraph::longestLine', isLeaf: true) + external double get longestLine; + + @override + @Native)>(symbol: 'Paragraph::minIntrinsicWidth', isLeaf: true) + external double get minIntrinsicWidth; + + @override + @Native)>(symbol: 'Paragraph::maxIntrinsicWidth', isLeaf: true) + external double get maxIntrinsicWidth; + + @override + @Native)>(symbol: 'Paragraph::alphabeticBaseline', isLeaf: true) + external double get alphabeticBaseline; + + @override + @Native)>(symbol: 'Paragraph::ideographicBaseline', isLeaf: true) + external double get ideographicBaseline; + + @override + @Native)>(symbol: 'Paragraph::didExceedMaxLines', isLeaf: true) + external bool get didExceedMaxLines; + + @override void layout(ParagraphConstraints constraints) { _layout(constraints.width); assert(() { @@ -2770,19 +2869,7 @@ class Paragraph extends NativeFieldWrapperClass1 { return boxes; } - /// Returns a list of text boxes that enclose the given text range. - /// - /// The [boxHeightStyle] and [boxWidthStyle] parameters allow customization - /// of how the boxes are bound vertically and horizontally. Both style - /// parameters default to the tight option, which will provide close-fitting - /// boxes and will not account for any line spacing. - /// - /// Coordinates of the TextBox are relative to the upper-left corner of the paragraph, - /// where positive y values indicate down. - /// - /// The [boxHeightStyle] and [boxWidthStyle] parameters must not be null. - /// - /// See [BoxHeightStyle] and [BoxWidthStyle] for full descriptions of each option. + @override List getBoxesForRange(int start, int end, {BoxHeightStyle boxHeightStyle = BoxHeightStyle.tight, BoxWidthStyle boxWidthStyle = BoxWidthStyle.tight}) { return _decodeTextBoxes(_getBoxesForRange(start, end, boxHeightStyle.index, boxWidthStyle.index)); } @@ -2791,13 +2878,7 @@ class Paragraph extends NativeFieldWrapperClass1 { @Native, Uint32, Uint32, Uint32, Uint32)>(symbol: 'Paragraph::getRectsForRange') external Float32List _getBoxesForRange(int start, int end, int boxHeightStyle, int boxWidthStyle); - /// Returns a list of text boxes that enclose all placeholders in the paragraph. - /// - /// The order of the boxes are in the same order as passed in through - /// [ParagraphBuilder.addPlaceholder]. - /// - /// Coordinates of the [TextBox] are relative to the upper-left corner of the paragraph, - /// where positive y values indicate down. + @override List getBoxesForPlaceholders() { return _decodeTextBoxes(_getBoxesForPlaceholders()); } @@ -2805,7 +2886,7 @@ class Paragraph extends NativeFieldWrapperClass1 { @Native)>(symbol: 'Paragraph::getRectsForPlaceholders') external Float32List _getBoxesForPlaceholders(); - /// Returns the text position closest to the given offset. + @override TextPosition getPositionForOffset(Offset offset) { final List encoded = _getPositionForOffset(offset.dx, offset.dy); return TextPosition(offset: encoded[0], affinity: TextAffinity.values[encoded[1]]); @@ -2814,18 +2895,7 @@ class Paragraph extends NativeFieldWrapperClass1 { @Native, Double, Double)>(symbol: 'Paragraph::getPositionForOffset') external List _getPositionForOffset(double dx, double dy); - /// Returns the [TextRange] of the word at the given [TextPosition]. - /// - /// Characters not part of a word, such as spaces, symbols, and punctuation, - /// have word breaks on both sides. In such cases, this method will return - /// (offset, offset+1). Word boundaries are defined more precisely in Unicode - /// Standard Annex #29 http://www.unicode.org/reports/tr29/#Word_Boundaries - /// - /// The [TextPosition] is treated as caret position, its [TextPosition.affinity] - /// is used to determine which character this position points to. For example, - /// the word boundary at `TextPosition(offset: 5, affinity: TextPosition.upstream)` - /// of the `string = 'Hello word'` will return range (0, 5) because the position - /// points to the character 'o' instead of the space. + @override TextRange getWordBoundary(TextPosition position) { final int characterPosition; switch (position.affinity) { @@ -2841,14 +2911,7 @@ class Paragraph extends NativeFieldWrapperClass1 { @Native, Uint32)>(symbol: 'Paragraph::getWordBoundary') external List _getWordBoundary(int offset); - /// Returns the [TextRange] of the line at the given [TextPosition]. - /// - /// The newline (if any) is returned as part of the range. - /// - /// Not valid until after layout. - /// - /// This can potentially be expensive, since it needs to compute the line - /// metrics, so use it sparingly. + @override TextRange getLineBoundary(TextPosition position) { final List boundary = _getLineBoundary(position.offset); final TextRange line = TextRange(start: boundary[0], end: boundary[1]); @@ -2877,15 +2940,9 @@ class Paragraph extends NativeFieldWrapperClass1 { // in the C++ code. If we straighten out the C++ dependencies, we can remove // this indirection. @Native, Pointer, Double, Double)>(symbol: 'Paragraph::paint') - external void _paint(Canvas canvas, double x, double y); + external void _paint(_NativeCanvas canvas, double x, double y); - /// Returns the full list of [LineMetrics] that describe in detail the various - /// metrics of each laid out line. - /// - /// Not valid until after layout. - /// - /// This can potentially return a large amount of data, so it is not recommended - /// to repeatedly call this. Instead, cache the results. + @override List computeLineMetrics() { final Float64List encoded = _computeLineMetrics(); final int count = encoded.length ~/ 9; @@ -2910,8 +2967,7 @@ class Paragraph extends NativeFieldWrapperClass1 { @Native)>(symbol: 'Paragraph::computeLineMetrics') external Float64List _computeLineMetrics(); - /// Release the resources used by this object. The object is no longer usable - /// after this method is called. + @override void dispose() { assert(!_disposed); assert(() { @@ -2927,10 +2983,8 @@ class Paragraph extends NativeFieldWrapperClass1 { external void _dispose(); bool _disposed = false; - /// Whether this reference to the underlying picture is [dispose]d. - /// - /// This only returns a valid value if asserts are enabled, and must not be - /// used otherwise. + + @override bool get debugDisposed { bool? disposed; assert(() { @@ -2955,12 +3009,99 @@ class Paragraph extends NativeFieldWrapperClass1 { /// /// After constructing a [Paragraph], call [Paragraph.layout] on it and then /// paint it with [Canvas.drawParagraph]. -class ParagraphBuilder extends NativeFieldWrapperClass1 { - +abstract class ParagraphBuilder { /// Creates a new [ParagraphBuilder] object, which is used to create a /// [Paragraph]. + factory ParagraphBuilder(ParagraphStyle style) = _NativeParagraphBuilder; + + /// The number of placeholders currently in the paragraph. + int get placeholderCount; + + /// The scales of the placeholders in the paragraph. + List get placeholderScales; + + /// Applies the given style to the added text until [pop] is called. + /// + /// See [pop] for details. + void pushStyle(TextStyle style); + + /// 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(); + + /// Adds the given text to the paragraph. + /// + /// The text will be styled according to the current stack of text styles. + void addText(String text); + + /// 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 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, + TextBaseline? baseline, + }); + + /// 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(); +} + +base class _NativeParagraphBuilder extends NativeFieldWrapperClass1 implements ParagraphBuilder { @pragma('vm:entry-point') - ParagraphBuilder(ParagraphStyle style) + _NativeParagraphBuilder(ParagraphStyle style) : _defaultLeadingDistribution = style._leadingDistribution { List? strutFontFamilies; final StrutStyle? strutStyle = style._strutStyle; @@ -3005,18 +3146,17 @@ class ParagraphBuilder extends NativeFieldWrapperClass1 { String ellipsis, String locale); - /// The number of placeholders currently in the paragraph. + @override int get placeholderCount => _placeholderCount; int _placeholderCount = 0; - /// The scales of the placeholders in the paragraph. + @override List get placeholderScales => _placeholderScales; final List _placeholderScales = []; final TextLeadingDistribution _defaultLeadingDistribution; - /// Applies the given style to the added text until [pop] is called. - /// - /// See [pop] for details. + + @override void pushStyle(TextStyle style) { final List fullFontFamilies = []; fullFontFamilies.add(style._fontFamily); @@ -3112,18 +3252,11 @@ class ParagraphBuilder extends NativeFieldWrapperClass1 { 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. + @override @Native)>(symbol: 'ParagraphBuilder::pop', isLeaf: true) external void pop(); - /// Adds the given text to the paragraph. - /// - /// The text will be styled according to the current stack of text styles. + @override void addText(String text) { final String? error = _addText(text); if (error != null) { @@ -3134,53 +3267,7 @@ class ParagraphBuilder extends NativeFieldWrapperClass1 { @Native, Handle)>(symbol: 'ParagraphBuilder::addText') external String? _addText(String text); - /// 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 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. + @override void addPlaceholder(double width, double height, PlaceholderAlignment alignment, { double scale = 1.0, double? baselineOffset, @@ -3201,19 +3288,15 @@ class ParagraphBuilder extends NativeFieldWrapperClass1 { @Native, Double, Double, Uint32, Double, Uint32)>(symbol: 'ParagraphBuilder::addPlaceholder') external void _addPlaceholder(double width, double height, int alignment, double baselineOffset, int baseline); - /// 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. + @override Paragraph build() { - final Paragraph paragraph = Paragraph._(); + final _NativeParagraph paragraph = _NativeParagraph._(); _build(paragraph); return paragraph; } @Native, Handle)>(symbol: 'ParagraphBuilder::build') - external void _build(Paragraph outParagraph); + external void _build(_NativeParagraph outParagraph); } /// Loads a font from a buffer and makes it available for rendering text. diff --git a/lib/ui/window.dart b/lib/ui/window.dart index 304cdc341feb8..67bbda67e3c34 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -331,10 +331,10 @@ class FlutterView { /// scheduling of frames. /// * [RendererBinding], the Flutter framework class which manages layout and /// painting. - void render(Scene scene) => _render(scene); + void render(Scene scene) => _render(scene as _NativeScene); @Native)>(symbol: 'PlatformConfigurationNativeApi::Render') - external static void _render(Scene scene); + external static void _render(_NativeScene scene); /// Change the retained semantics data about this [FlutterView]. /// @@ -344,10 +344,10 @@ class FlutterView { /// /// This function disposes the given update, which means the semantics update /// cannot be used further. - void updateSemantics(SemanticsUpdate update) => _updateSemantics(update); + void updateSemantics(SemanticsUpdate update) => _updateSemantics(update as _NativeSemanticsUpdate); @Native)>(symbol: 'PlatformConfigurationNativeApi::UpdateSemantics') - external static void _updateSemantics(SemanticsUpdate update); + external static void _updateSemantics(_NativeSemanticsUpdate update); } /// Deprecated. Will be removed in a future version of Flutter. diff --git a/third_party/tonic/tests/fixtures/tonic_test.dart b/third_party/tonic/tests/fixtures/tonic_test.dart index b9e992b1a9f5a..f3c5f1c3982d1 100644 --- a/third_party/tonic/tests/fixtures/tonic_test.dart +++ b/third_party/tonic/tests/fixtures/tonic_test.dart @@ -127,7 +127,7 @@ void callEchoVector() { // Test helpers for calls with DartWrappable through Tonic. -class MyNativeClass extends NativeFieldWrapperClass1 { +base class MyNativeClass extends NativeFieldWrapperClass1 { MyNativeClass(int value) { _Create(this, value); }