blob: 2591f7f303dfa948039df2b4628e2e5e30806c34 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:ui/ui.dart' as ui;
import '../profiler.dart';
import 'canvas.dart';
import 'canvaskit_api.dart';
import 'image.dart';
import 'skia_object_cache.dart';
/// Implements [ui.Picture] on top of [SkPicture].
///
/// Unlike most other [ManagedSkiaObject] implementations, instances of this
/// class may have their Skia counterparts deleted before finalization registry
/// or [SkiaObjectCache] decide to delete it.
class CkPicture extends ManagedSkiaObject<SkPicture> implements ui.Picture {
final ui.Rect? cullRect;
final CkPictureSnapshot? _snapshot;
CkPicture(SkPicture picture, this.cullRect, this._snapshot) :
assert(
browserSupportsFinalizationRegistry && _snapshot == null ||
_snapshot != null,
'If the browser does not support FinalizationRegistry (WeakRef), then we must have a picture snapshot to be able to resurrect it.',
), super(picture);
@override
int get approximateBytesUsed => 0;
/// Whether the picture has been disposed of.
///
/// This is indended to be used in tests and assertions only.
bool get debugIsDisposed => _isDisposed;
/// This is set to true when [dispose] is called and is never reset back to
/// false.
///
/// This extra flag is necessary on top of [rawSkiaObject] because
/// [rawSkiaObject] being null does not indicate permanent deletion. See
/// similar flag [SkiaObjectBox.isDeletedPermanently].
bool _isDisposed = false;
@override
void dispose() {
assert(!_isDisposed, 'Object has been disposed.');
if (Instrumentation.enabled) {
Instrumentation.instance.incrementCounter('Picture disposed');
}
_isDisposed = true;
_snapshot?.dispose();
// Emulate what SkiaObjectCache does.
rawSkiaObject?.delete();
rawSkiaObject = null;
}
@override
Future<ui.Image> toImage(int width, int height) async {
assert(!_isDisposed);
final SkSurface skSurface = canvasKit.MakeSurface(width, height);
final SkCanvas skCanvas = skSurface.getCanvas();
skCanvas.drawPicture(skiaObject);
final SkImage skImage = skSurface.makeImageSnapshot();
skSurface.dispose();
return CkImage(skImage);
}
@override
bool get isResurrectionExpensive => true;
@override
SkPicture createDefault() {
// The default object is supplied in the constructor.
throw StateError('Unreachable code');
}
@override
SkPicture resurrect() {
// If a picture has been explicitly disposed of, it can no longer be
// resurrected. An attempt to resurrect after the framework told the
// engine to dispose of the picture likely indicates a bug in the engine.
assert(!_isDisposed);
return _snapshot!.toPicture();
}
@override
void delete() {
// This method may be called after [dispose], in which case there's nothing
// left to do. The Skia object is deleted permanently.
if (!_isDisposed) {
rawSkiaObject?.delete();
}
}
}