mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[web:canvaskit] migrate Paint API to UniqueRef (flutter/engine#41230)
Migrate Paint API to `UniqueRef`. This includes `Paint`, `ImageFilter` (and all subtypes), `ColorFilter` (and all subtypes). Also fix the following memory leaks: * `CkPaint` is frequently used by layers where a one-off paint object is created, used, and immediately dropped. `CkPaint` now has a `dispose` method, and all one-off usages now dispose of the paint after they are done. * `CkColorFilter.initRawImageFilter` was leaking the `SkColorFilter` created by `_initRawColorFilter` inside the expression. * `CkManagedSkImageFilterConvertible.imageFilter` now takes a closure, which allows the implementation decide on the lifetime of the `SkImageFilter` vended to the caller. Because `CkColorFilter` is a const class it cannot store C++ instances inside its own fields, so it creates a temporary `SkImageFilter` class to be used by the caller and then it needs to delete it. Now it does.
This commit is contained in:
parent
108eb0fa89
commit
2fcdf5a731
@ -291,18 +291,20 @@ class CkCanvas {
|
||||
|
||||
void saveLayerWithFilter(ui.Rect bounds, ui.ImageFilter filter,
|
||||
[CkPaint? paint]) {
|
||||
final CkManagedSkImageFilterConvertible convertible;
|
||||
if (filter is ui.ColorFilter) {
|
||||
convertible = createCkColorFilter(filter as EngineColorFilter)!;
|
||||
} else {
|
||||
convertible = filter as CkManagedSkImageFilterConvertible;
|
||||
}
|
||||
return skCanvas.saveLayer(
|
||||
paint?.skiaObject,
|
||||
toSkRect(bounds),
|
||||
convertible.imageFilter.skiaObject,
|
||||
0,
|
||||
);
|
||||
final CkManagedSkImageFilterConvertible convertible;
|
||||
if (filter is ui.ColorFilter) {
|
||||
convertible = createCkColorFilter(filter as EngineColorFilter)!;
|
||||
} else {
|
||||
convertible = filter as CkManagedSkImageFilterConvertible;
|
||||
}
|
||||
convertible.imageFilter((SkImageFilter filter) {
|
||||
skCanvas.saveLayer(
|
||||
paint?.skiaObject,
|
||||
toSkRect(bounds),
|
||||
filter,
|
||||
0,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void scale(double sx, double sy) {
|
||||
@ -1173,11 +1175,13 @@ class CkSaveLayerWithFilterCommand extends CkPaintCommand {
|
||||
} else {
|
||||
convertible = filter as CkManagedSkImageFilterConvertible;
|
||||
}
|
||||
return canvas.saveLayer(
|
||||
paint?.skiaObject,
|
||||
toSkRect(bounds),
|
||||
convertible.imageFilter.skiaObject,
|
||||
0,
|
||||
);
|
||||
convertible.imageFilter((SkImageFilter filter) {
|
||||
canvas.saveLayer(
|
||||
paint?.skiaObject,
|
||||
toSkRect(bounds),
|
||||
filter,
|
||||
0,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,31 +10,25 @@ import '../color_filter.dart';
|
||||
import '../util.dart';
|
||||
import 'canvaskit_api.dart';
|
||||
import 'image_filter.dart';
|
||||
import 'skia_object_cache.dart';
|
||||
import 'native_memory.dart';
|
||||
|
||||
/// A concrete [ManagedSkiaObject] subclass that owns a [SkColorFilter] and
|
||||
/// manages its lifecycle.
|
||||
/// Owns a [SkColorFilter] and manages its lifecycle.
|
||||
///
|
||||
/// Seealso:
|
||||
/// See also:
|
||||
///
|
||||
/// * [CkPaint.colorFilter], which uses a [ManagedSkColorFilter] to manage
|
||||
/// the lifecycle of its [SkColorFilter].
|
||||
class ManagedSkColorFilter extends ManagedSkiaObject<SkColorFilter> {
|
||||
class ManagedSkColorFilter {
|
||||
ManagedSkColorFilter(CkColorFilter ckColorFilter)
|
||||
: colorFilter = ckColorFilter;
|
||||
: colorFilter = ckColorFilter {
|
||||
_ref = UniqueRef<SkColorFilter>(this, colorFilter._initRawColorFilter(), 'ColorFilter');
|
||||
}
|
||||
|
||||
final CkColorFilter colorFilter;
|
||||
|
||||
@override
|
||||
SkColorFilter createDefault() => colorFilter._initRawColorFilter();
|
||||
late final UniqueRef<SkColorFilter> _ref;
|
||||
|
||||
@override
|
||||
SkColorFilter resurrect() => colorFilter._initRawColorFilter();
|
||||
|
||||
@override
|
||||
void delete() {
|
||||
rawSkiaObject?.delete();
|
||||
}
|
||||
SkColorFilter get skiaObject => _ref.nativeObject;
|
||||
|
||||
@override
|
||||
int get hashCode => colorFilter.hashCode;
|
||||
@ -51,64 +45,83 @@ class ManagedSkColorFilter extends ManagedSkiaObject<SkColorFilter> {
|
||||
String toString() => colorFilter.toString();
|
||||
}
|
||||
|
||||
/// A [ui.ColorFilter] backed by Skia's [SkColorFilter].
|
||||
///
|
||||
/// Additionally, this class provides the interface for converting itself to a
|
||||
/// [ManagedSkiaObject] that manages a skia image filter.
|
||||
abstract class CkColorFilter
|
||||
implements CkManagedSkImageFilterConvertible {
|
||||
/// CanvasKit implementation of [ui.ColorFilter].
|
||||
abstract class CkColorFilter implements CkManagedSkImageFilterConvertible {
|
||||
const CkColorFilter();
|
||||
|
||||
/// Called by [ManagedSkiaObject.createDefault] and
|
||||
/// [ManagedSkiaObject.resurrect] to create a new [SkImageFilter], when this
|
||||
/// filter is used as an [ImageFilter].
|
||||
SkImageFilter initRawImageFilter() =>
|
||||
canvasKit.ImageFilter.MakeColorFilter(_initRawColorFilter(), null);
|
||||
/// Converts this color filter into an image filter.
|
||||
///
|
||||
/// Passes the ownership of the returned [SkImageFilter] to the caller. It is
|
||||
/// the caller's responsibility to manage the lifecycle of the returned value.
|
||||
SkImageFilter initRawImageFilter() {
|
||||
final SkColorFilter skColorFilter = _initRawColorFilter();
|
||||
final SkImageFilter result = canvasKit.ImageFilter.MakeColorFilter(skColorFilter, null);
|
||||
|
||||
/// Called by [ManagedSkiaObject.createDefault] and
|
||||
/// [ManagedSkiaObject.resurrect] to create a new [SkColorFilter], when this
|
||||
/// filter is used as a [ColorFilter].
|
||||
// The underlying SkColorFilter is now owned by the SkImageFilter, so we
|
||||
// need to drop the reference to allow it to be collected.
|
||||
skColorFilter.delete();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Creates a Skia object based on the properties of this color filter.
|
||||
///
|
||||
/// Passes the ownership of the returned [SkColorFilter] to the caller. It is
|
||||
/// the caller's responsibility to manage the lifecycle of the returned value.
|
||||
SkColorFilter _initRawColorFilter();
|
||||
|
||||
@override
|
||||
ManagedSkiaObject<SkImageFilter> get imageFilter =>
|
||||
CkColorFilterImageFilter(colorFilter: this);
|
||||
void imageFilter(SkImageFilterBorrow borrow) {
|
||||
// Since ColorFilter has a const constructor it cannot store dynamically
|
||||
// created Skia objects. Therefore a new SkImageFilter is created every time
|
||||
// it's used. However, once used it's no longer needed, so it's deleted
|
||||
// immediately to free memory.
|
||||
final SkImageFilter skImageFilter = initRawImageFilter();
|
||||
borrow(skImageFilter);
|
||||
skImageFilter.delete();
|
||||
}
|
||||
}
|
||||
|
||||
/// A reusable identity transform matrix.
|
||||
///
|
||||
/// WARNING: DO NOT MUTATE THIS MATRIX! It is a shared global singleton.
|
||||
Float32List _identityTransform = _computeIdentityTransform();
|
||||
|
||||
Float32List _computeIdentityTransform() {
|
||||
final Float32List result = Float32List(20);
|
||||
const List<int> translationIndices = <int>[0, 6, 12, 18];
|
||||
for (final int i in translationIndices) {
|
||||
result[i] = 1;
|
||||
}
|
||||
_identityTransform = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
SkColorFilter createSkColorFilterFromColorAndBlendMode(ui.Color color, ui.BlendMode blendMode) {
|
||||
/// Return the identity matrix when the color opacity is 0. Replicates
|
||||
/// effect of applying no filter
|
||||
if (color.opacity == 0) {
|
||||
return canvasKit.ColorFilter.MakeMatrix(_identityTransform);
|
||||
}
|
||||
final SkColorFilter? filter = canvasKit.ColorFilter.MakeBlend(
|
||||
toSharedSkColor1(color),
|
||||
toSkBlendMode(blendMode),
|
||||
);
|
||||
if (filter == null) {
|
||||
throw ArgumentError('Invalid parameters for blend mode ColorFilter');
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
|
||||
|
||||
class CkBlendModeColorFilter extends CkColorFilter {
|
||||
const CkBlendModeColorFilter(this.color, this.blendMode);
|
||||
|
||||
final ui.Color color;
|
||||
final ui.BlendMode blendMode;
|
||||
|
||||
static Float32List get identityTransform => _identityTransform ?? _computeIdentityTransform();
|
||||
static Float32List? _identityTransform;
|
||||
|
||||
static Float32List _computeIdentityTransform() {
|
||||
final Float32List result = Float32List(20);
|
||||
const List<int> translationIndices = <int>[0, 6, 12, 18];
|
||||
for (final int i in translationIndices) {
|
||||
result[i] = 1;
|
||||
}
|
||||
_identityTransform = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
SkColorFilter _initRawColorFilter() {
|
||||
/// Return the identity matrix when the color opacity is 0. Replicates
|
||||
/// effect of applying no filter
|
||||
if (color.opacity == 0) {
|
||||
return canvasKit.ColorFilter.MakeMatrix(identityTransform);
|
||||
}
|
||||
final SkColorFilter? filter = canvasKit.ColorFilter.MakeBlend(
|
||||
toSharedSkColor1(color),
|
||||
toSkBlendMode(blendMode),
|
||||
);
|
||||
if (filter == null) {
|
||||
throw ArgumentError('Invalid parameters for blend mode ColorFilter');
|
||||
}
|
||||
return filter;
|
||||
return createSkColorFilterFromColorAndBlendMode(color, blendMode);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@ -137,12 +137,14 @@ CkImage scaleImage(SkImage image, int? targetWidth, int? targetHeight) {
|
||||
final CkPictureRecorder recorder = CkPictureRecorder();
|
||||
final CkCanvas canvas = recorder.beginRecording(ui.Rect.largest);
|
||||
|
||||
final CkPaint paint = CkPaint();
|
||||
canvas.drawImageRect(
|
||||
CkImage(image),
|
||||
ui.Rect.fromLTWH(0, 0, image.width(), image.height()),
|
||||
ui.Rect.fromLTWH(0, 0, targetWidth!.toDouble(), targetHeight!.toDouble()),
|
||||
CkPaint()
|
||||
paint,
|
||||
);
|
||||
paint.dispose();
|
||||
|
||||
final CkPicture picture = recorder.endRecording();
|
||||
final ui.Image finalImage = picture.toImageSync(
|
||||
|
||||
@ -10,7 +10,9 @@ import 'package:ui/ui.dart' as ui;
|
||||
import '../util.dart';
|
||||
import 'canvaskit_api.dart';
|
||||
import 'color_filter.dart';
|
||||
import 'skia_object_cache.dart';
|
||||
import 'native_memory.dart';
|
||||
|
||||
typedef SkImageFilterBorrow = void Function(SkImageFilter);
|
||||
|
||||
/// An [ImageFilter] that can create a managed skia [SkImageFilter] object.
|
||||
///
|
||||
@ -20,14 +22,13 @@ import 'skia_object_cache.dart';
|
||||
///
|
||||
/// Currently implemented by [CkImageFilter] and [CkColorFilter].
|
||||
abstract class CkManagedSkImageFilterConvertible implements ui.ImageFilter {
|
||||
ManagedSkiaObject<SkImageFilter> get imageFilter;
|
||||
void imageFilter(SkImageFilterBorrow borrow);
|
||||
}
|
||||
|
||||
/// The CanvasKit implementation of [ui.ImageFilter].
|
||||
///
|
||||
/// Currently only supports `blur`, `matrix`, and ColorFilters.
|
||||
abstract class CkImageFilter extends ManagedSkiaObject<SkImageFilter>
|
||||
implements CkManagedSkImageFilterConvertible {
|
||||
abstract class CkImageFilter implements CkManagedSkImageFilterConvertible {
|
||||
factory CkImageFilter.blur(
|
||||
{required double sigmaX,
|
||||
required double sigmaY,
|
||||
@ -39,31 +40,26 @@ abstract class CkImageFilter extends ManagedSkiaObject<SkImageFilter>
|
||||
required ui.FilterQuality filterQuality}) = _CkMatrixImageFilter;
|
||||
|
||||
CkImageFilter._();
|
||||
|
||||
@override
|
||||
ManagedSkiaObject<SkImageFilter> get imageFilter => this;
|
||||
|
||||
SkImageFilter _initSkiaObject();
|
||||
|
||||
@override
|
||||
SkImageFilter createDefault() => _initSkiaObject();
|
||||
|
||||
@override
|
||||
SkImageFilter resurrect() => _initSkiaObject();
|
||||
|
||||
@override
|
||||
void delete() {
|
||||
rawSkiaObject?.delete();
|
||||
}
|
||||
}
|
||||
|
||||
class CkColorFilterImageFilter extends CkImageFilter {
|
||||
CkColorFilterImageFilter({required this.colorFilter}) : super._();
|
||||
CkColorFilterImageFilter({required this.colorFilter}) : super._() {
|
||||
final SkImageFilter skImageFilter = colorFilter.initRawImageFilter();
|
||||
_ref = UniqueRef<SkImageFilter>(this, skImageFilter, 'ImageFilter.color');
|
||||
}
|
||||
|
||||
final CkColorFilter colorFilter;
|
||||
|
||||
late final UniqueRef<SkImageFilter> _ref;
|
||||
|
||||
@override
|
||||
SkImageFilter _initSkiaObject() => colorFilter.initRawImageFilter();
|
||||
void imageFilter(SkImageFilterBorrow borrow) {
|
||||
borrow(_ref.nativeObject);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_ref.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => colorFilter.hashCode;
|
||||
@ -84,12 +80,38 @@ class CkColorFilterImageFilter extends CkImageFilter {
|
||||
class _CkBlurImageFilter extends CkImageFilter {
|
||||
_CkBlurImageFilter(
|
||||
{required this.sigmaX, required this.sigmaY, required this.tileMode})
|
||||
: super._();
|
||||
: super._() {
|
||||
/// Return the identity matrix when both sigmaX and sigmaY are 0. Replicates
|
||||
/// effect of applying no filter
|
||||
final SkImageFilter skImageFilter;
|
||||
if (sigmaX == 0 && sigmaY == 0) {
|
||||
skImageFilter = canvasKit.ImageFilter.MakeMatrixTransform(
|
||||
toSkMatrixFromFloat32(Matrix4.identity().storage),
|
||||
toSkFilterOptions(ui.FilterQuality.none),
|
||||
null
|
||||
);
|
||||
} else {
|
||||
skImageFilter = canvasKit.ImageFilter.MakeBlur(
|
||||
sigmaX,
|
||||
sigmaY,
|
||||
toSkTileMode(tileMode),
|
||||
null,
|
||||
);
|
||||
}
|
||||
_ref = UniqueRef<SkImageFilter>(this, skImageFilter, 'ImageFilter.blur');
|
||||
}
|
||||
|
||||
final double sigmaX;
|
||||
final double sigmaY;
|
||||
final ui.TileMode tileMode;
|
||||
|
||||
late final UniqueRef<SkImageFilter> _ref;
|
||||
|
||||
@override
|
||||
void imageFilter(SkImageFilterBorrow borrow) {
|
||||
borrow(_ref.nativeObject);
|
||||
}
|
||||
|
||||
String get _modeString {
|
||||
switch (tileMode) {
|
||||
case ui.TileMode.clamp:
|
||||
@ -103,25 +125,6 @@ class _CkBlurImageFilter extends CkImageFilter {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
SkImageFilter _initSkiaObject() {
|
||||
/// Return the identity matrix when both sigmaX and sigmaY are 0. Replicates
|
||||
/// effect of applying no filter
|
||||
if (sigmaX == 0 && sigmaY == 0) {
|
||||
return canvasKit.ImageFilter.MakeMatrixTransform(
|
||||
toSkMatrixFromFloat32(Matrix4.identity().storage),
|
||||
toSkFilterOptions(ui.FilterQuality.none),
|
||||
null
|
||||
);
|
||||
}
|
||||
return canvasKit.ImageFilter.MakeBlur(
|
||||
sigmaX,
|
||||
sigmaY,
|
||||
toSkTileMode(tileMode),
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (runtimeType != other.runtimeType) {
|
||||
@ -146,18 +149,23 @@ class _CkMatrixImageFilter extends CkImageFilter {
|
||||
_CkMatrixImageFilter(
|
||||
{required Float64List matrix, required this.filterQuality})
|
||||
: matrix = Float64List.fromList(matrix),
|
||||
super._();
|
||||
|
||||
final Float64List matrix;
|
||||
final ui.FilterQuality filterQuality;
|
||||
|
||||
@override
|
||||
SkImageFilter _initSkiaObject() {
|
||||
return canvasKit.ImageFilter.MakeMatrixTransform(
|
||||
super._() {
|
||||
final SkImageFilter skImageFilter = canvasKit.ImageFilter.MakeMatrixTransform(
|
||||
toSkMatrixFromFloat64(matrix),
|
||||
toSkFilterOptions(filterQuality),
|
||||
null,
|
||||
);
|
||||
_ref = UniqueRef<SkImageFilter>(this, skImageFilter, 'ImageFilter.matrix');
|
||||
}
|
||||
|
||||
final Float64List matrix;
|
||||
final ui.FilterQuality filterQuality;
|
||||
|
||||
late final UniqueRef<SkImageFilter> _ref;
|
||||
|
||||
@override
|
||||
void imageFilter(SkImageFilterBorrow borrow) {
|
||||
borrow(_ref.nativeObject);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@ -179,6 +179,7 @@ class BackdropFilterEngineLayer extends ContainerLayer
|
||||
final CkPaint paint = CkPaint()..blendMode = _blendMode;
|
||||
paintContext.internalNodesCanvas
|
||||
.saveLayerWithFilter(paintBounds, _filter, paint);
|
||||
paint.dispose();
|
||||
paintChildren(paintContext);
|
||||
paintContext.internalNodesCanvas.restore();
|
||||
}
|
||||
@ -340,6 +341,7 @@ class OpacityEngineLayer extends ContainerLayer
|
||||
final ui.Rect saveLayerBounds = paintBounds.shift(-_offset);
|
||||
|
||||
paintContext.internalNodesCanvas.saveLayer(saveLayerBounds, paint);
|
||||
paint.dispose();
|
||||
paintChildren(paintContext);
|
||||
// Restore twice: once for the translate and once for the saveLayer.
|
||||
paintContext.internalNodesCanvas.restore();
|
||||
@ -403,6 +405,7 @@ class ImageFilterEngineLayer extends ContainerLayer
|
||||
final CkPaint paint = CkPaint();
|
||||
paint.imageFilter = _filter;
|
||||
paintContext.internalNodesCanvas.saveLayer(paintBounds, paint);
|
||||
paint.dispose();
|
||||
paintChildren(paintContext);
|
||||
paintContext.internalNodesCanvas.restore();
|
||||
paintContext.internalNodesCanvas.restore();
|
||||
@ -439,6 +442,7 @@ class ShaderMaskEngineLayer extends ContainerLayer
|
||||
|
||||
paintContext.leafNodesCanvas!.drawRect(
|
||||
ui.Rect.fromLTWH(0, 0, maskRect.width, maskRect.height), paint);
|
||||
paint.dispose();
|
||||
paintContext.leafNodesCanvas!.restore();
|
||||
|
||||
paintContext.internalNodesCanvas.restore();
|
||||
@ -539,6 +543,7 @@ class PhysicalShapeEngineLayer extends ContainerLayer
|
||||
// anti-aliased drawPath will always have such artifacts.
|
||||
paintContext.leafNodesCanvas!.drawPaint(paint);
|
||||
}
|
||||
paint.dispose();
|
||||
|
||||
paintChildren(paintContext);
|
||||
|
||||
@ -570,6 +575,7 @@ class ColorFilterEngineLayer extends ContainerLayer
|
||||
paint.colorFilter = filter;
|
||||
|
||||
paintContext.internalNodesCanvas.saveLayer(paintBounds, paint);
|
||||
paint.dispose();
|
||||
paintChildren(paintContext);
|
||||
paintContext.internalNodesCanvas.restore();
|
||||
}
|
||||
|
||||
@ -5,33 +5,25 @@
|
||||
import 'package:ui/ui.dart' as ui;
|
||||
|
||||
import 'canvaskit_api.dart';
|
||||
import 'skia_object_cache.dart';
|
||||
import 'native_memory.dart';
|
||||
|
||||
/// The CanvasKit implementation of [ui.MaskFilter].
|
||||
class CkMaskFilter extends ManagedSkiaObject<SkMaskFilter> {
|
||||
class CkMaskFilter {
|
||||
CkMaskFilter.blur(ui.BlurStyle blurStyle, double sigma)
|
||||
: _blurStyle = blurStyle,
|
||||
_sigma = sigma;
|
||||
|
||||
final ui.BlurStyle _blurStyle;
|
||||
final double _sigma;
|
||||
|
||||
@override
|
||||
SkMaskFilter createDefault() => _initSkiaObject();
|
||||
|
||||
@override
|
||||
SkMaskFilter resurrect() => _initSkiaObject();
|
||||
|
||||
SkMaskFilter _initSkiaObject() {
|
||||
return canvasKit.MaskFilter.MakeBlur(
|
||||
_sigma = sigma {
|
||||
final SkMaskFilter skMaskFilter = canvasKit.MaskFilter.MakeBlur(
|
||||
toSkBlurStyle(_blurStyle),
|
||||
_sigma,
|
||||
true,
|
||||
)!;
|
||||
_ref = UniqueRef<SkMaskFilter>(this, skMaskFilter, 'MaskFilter');
|
||||
}
|
||||
|
||||
@override
|
||||
void delete() {
|
||||
rawSkiaObject?.delete();
|
||||
}
|
||||
final ui.BlurStyle _blurStyle;
|
||||
final double _sigma;
|
||||
|
||||
late final UniqueRef<SkMaskFilter> _ref;
|
||||
|
||||
SkMaskFilter get skiaObject => _ref.nativeObject;
|
||||
}
|
||||
|
||||
@ -22,11 +22,25 @@ import 'skia_object_cache.dart';
|
||||
///
|
||||
/// This class is backed by a Skia object that must be explicitly
|
||||
/// deleted to avoid a memory leak. This is done by extending [SkiaObject].
|
||||
class CkPaint extends ManagedSkiaObject<SkPaint> implements ui.Paint {
|
||||
CkPaint();
|
||||
class CkPaint implements ui.Paint {
|
||||
CkPaint() : skiaObject = SkPaint() {
|
||||
skiaObject.setAntiAlias(_isAntiAlias);
|
||||
skiaObject.setColorInt(_defaultPaintColor.toDouble());
|
||||
_ref = UniqueRef<SkPaint>(this, skiaObject, 'Paint');
|
||||
}
|
||||
|
||||
final SkPaint skiaObject;
|
||||
late final UniqueRef<SkPaint> _ref;
|
||||
CkManagedSkImageFilterConvertible? _imageFilter;
|
||||
|
||||
static const int _defaultPaintColor = 0xFF000000;
|
||||
|
||||
/// Returns the native reference to the underlying [SkPaint] object.
|
||||
///
|
||||
/// This should only be used in tests.
|
||||
@visibleForTesting
|
||||
UniqueRef<SkPaint> get debugRef => _ref;
|
||||
|
||||
@override
|
||||
ui.BlendMode get blendMode => _blendMode;
|
||||
@override
|
||||
@ -260,50 +274,28 @@ class CkPaint extends ManagedSkiaObject<SkPaint> implements ui.Paint {
|
||||
if (_imageFilter == value) {
|
||||
return;
|
||||
}
|
||||
final CkManagedSkImageFilterConvertible? filter;
|
||||
if (value is ui.ColorFilter) {
|
||||
_imageFilter = createCkColorFilter(value as EngineColorFilter);
|
||||
filter = createCkColorFilter(value as EngineColorFilter);
|
||||
}
|
||||
else {
|
||||
_imageFilter = value as CkManagedSkImageFilterConvertible?;
|
||||
filter = value as CkManagedSkImageFilterConvertible?;
|
||||
}
|
||||
_managedImageFilter = _imageFilter?.imageFilter;
|
||||
skiaObject.setImageFilter(_managedImageFilter?.skiaObject);
|
||||
|
||||
if (filter != null) {
|
||||
filter.imageFilter((SkImageFilter skImageFilter) {
|
||||
skiaObject.setImageFilter(skImageFilter);
|
||||
});
|
||||
}
|
||||
|
||||
_imageFilter = filter;
|
||||
}
|
||||
|
||||
CkManagedSkImageFilterConvertible? _imageFilter;
|
||||
ManagedSkiaObject<SkImageFilter>? _managedImageFilter;
|
||||
|
||||
@override
|
||||
SkPaint createDefault() {
|
||||
final SkPaint paint = SkPaint();
|
||||
paint.setAntiAlias(_isAntiAlias);
|
||||
paint.setColorInt(_color.toDouble());
|
||||
return paint;
|
||||
}
|
||||
|
||||
@override
|
||||
SkPaint resurrect() {
|
||||
final SkPaint paint = SkPaint();
|
||||
// No need to do anything for `invertColors`. If it was set, then it
|
||||
// updated `_managedColorFilter`.
|
||||
paint.setBlendMode(toSkBlendMode(_blendMode));
|
||||
paint.setStyle(toSkPaintStyle(_style));
|
||||
paint.setStrokeWidth(_strokeWidth);
|
||||
paint.setAntiAlias(_isAntiAlias);
|
||||
paint.setColorInt(_color.toDouble());
|
||||
paint.setShader(_shader?.getSkShader(_filterQuality));
|
||||
paint.setMaskFilter(_ckMaskFilter?.skiaObject);
|
||||
paint.setColorFilter(_effectiveColorFilter?.skiaObject);
|
||||
paint.setImageFilter(_managedImageFilter?.skiaObject);
|
||||
paint.setStrokeCap(toSkStrokeCap(_strokeCap));
|
||||
paint.setStrokeJoin(toSkStrokeJoin(_strokeJoin));
|
||||
paint.setStrokeMiter(_strokeMiterLimit);
|
||||
return paint;
|
||||
}
|
||||
|
||||
@override
|
||||
void delete() {
|
||||
rawSkiaObject?.delete();
|
||||
/// Disposes of this paint object.
|
||||
///
|
||||
/// This object cannot be used again after calling this method.
|
||||
void dispose() {
|
||||
_ref.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -13,7 +13,6 @@ import 'font_fallbacks.dart';
|
||||
import 'native_memory.dart';
|
||||
import 'painting.dart';
|
||||
import 'renderer.dart';
|
||||
import 'skia_object_cache.dart';
|
||||
import 'text_fragmenter.dart';
|
||||
import 'util.dart';
|
||||
|
||||
@ -552,16 +551,6 @@ SkFontStyle toSkFontStyle(ui.FontWeight? fontWeight, ui.FontStyle? fontStyle) {
|
||||
}
|
||||
|
||||
/// The CanvasKit implementation of [ui.Paragraph].
|
||||
///
|
||||
/// This class does not use [ManagedSkiaObject] because it requires that its
|
||||
/// memory is reclaimed synchronously. This protects our memory usage from
|
||||
/// blowing up if within a single frame the framework needs to layout a lot of
|
||||
/// paragraphs. One common use-case is `ListView.builder`, which needs to layout
|
||||
/// more of its content than it actually renders to compute the scroll position.
|
||||
/// More generally, this protects from the pattern of laying out a lot of text
|
||||
/// while painting a small subset of it. To achieve this a
|
||||
/// [SynchronousSkiaObjectCache] is used that limits the number of live laid out
|
||||
/// paragraphs at any point in time within or outside the frame.
|
||||
class CkParagraph implements ui.Paragraph {
|
||||
CkParagraph(SkParagraph skParagraph, this._paragraphStyle) {
|
||||
_ref = UniqueRef<SkParagraph>(this, skParagraph, 'Paragraph');
|
||||
|
||||
@ -52,8 +52,11 @@ void testMain() {
|
||||
test('can be constructed', () {
|
||||
final CkImageFilter imageFilter = CkImageFilter.blur(sigmaX: 5, sigmaY: 10, tileMode: ui.TileMode.clamp);
|
||||
expect(imageFilter, isA<CkImageFilter>());
|
||||
expect(imageFilter.createDefault(), isNotNull);
|
||||
expect(imageFilter.resurrect(), isNotNull);
|
||||
SkImageFilter? skFilter;
|
||||
imageFilter.imageFilter((SkImageFilter value) {
|
||||
skFilter = value;
|
||||
});
|
||||
expect(skFilter, isNotNull);
|
||||
});
|
||||
|
||||
|
||||
@ -82,11 +85,12 @@ void testMain() {
|
||||
final CkPaint paint = CkPaint();
|
||||
paint.imageFilter = CkImageFilter.blur(sigmaX: 5, sigmaY: 10, tileMode: ui.TileMode.clamp);
|
||||
|
||||
final ManagedSkiaObject<Object> managedFilter = paint.imageFilter! as ManagedSkiaObject<Object>;
|
||||
final Object skiaFilter = managedFilter.skiaObject;
|
||||
final CkManagedSkImageFilterConvertible managedFilter1 = paint.imageFilter! as CkManagedSkImageFilterConvertible;
|
||||
|
||||
paint.imageFilter = CkImageFilter.blur(sigmaX: 5, sigmaY: 10, tileMode: ui.TileMode.clamp);
|
||||
expect((paint.imageFilter! as ManagedSkiaObject<Object>).skiaObject, same(skiaFilter));
|
||||
final CkManagedSkImageFilterConvertible managedFilter2 = paint.imageFilter! as CkManagedSkImageFilterConvertible;
|
||||
|
||||
expect(managedFilter1, same(managedFilter2));
|
||||
});
|
||||
|
||||
test('does not throw for both sigmaX and sigmaY set to 0', () async {
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:test/bootstrap/browser.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:ui/src/engine.dart';
|
||||
|
||||
import '../common/matchers.dart';
|
||||
import 'common.dart';
|
||||
|
||||
void main() {
|
||||
internalBootstrapBrowserTest(() => testMain);
|
||||
}
|
||||
|
||||
void testMain() {
|
||||
group('CkPaint', () {
|
||||
setUpCanvasKitTest();
|
||||
|
||||
test('lifecycle', () {
|
||||
final CkPaint paint = CkPaint();
|
||||
expect(paint.skiaObject, isNotNull);
|
||||
expect(paint.debugRef.isDisposed, isFalse);
|
||||
paint.dispose();
|
||||
expect(paint.debugRef.isDisposed, isTrue);
|
||||
expect(
|
||||
reason: 'Cannot dispose more than once',
|
||||
() => paint.dispose(),
|
||||
throwsA(isAssertionError),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user