part of dart._engine;
// 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.



/// 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();
    }
  }
}
