• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5part of engine;
6
7final _supportsDecode =
8    js_util.hasProperty(js.JsObject(js.context['Image']), 'decode');
9
10class HtmlCodec implements ui.Codec {
11  final String src;
12
13  HtmlCodec(this.src);
14
15  @override
16  int get frameCount => 1;
17
18  @override
19  int get repetitionCount => 0;
20
21  @override
22  Future<ui.FrameInfo> getNextFrame() async {
23    StreamSubscription<html.Event> loadSubscription;
24    StreamSubscription<html.Event> errorSubscription;
25    final Completer<ui.FrameInfo> completer = Completer<ui.FrameInfo>();
26    final html.ImageElement imgElement = html.ImageElement();
27    // If the browser doesn't support asynchronous decoding of an image,
28    // then use the `onload` event to decide when it's ready to paint to the
29    // DOM. Unfortunately, this will case the image to be decoded synchronously
30    // on the main thread, and may cause dropped framed.
31    if (!_supportsDecode) {
32      loadSubscription = imgElement.onLoad.listen((html.Event event) {
33        loadSubscription.cancel();
34        errorSubscription.cancel();
35        final HtmlImage image = HtmlImage(
36          imgElement,
37          imgElement.naturalWidth,
38          imgElement.naturalHeight,
39        );
40        completer.complete(SingleFrameInfo(image));
41      });
42    }
43    errorSubscription = imgElement.onError.listen((html.Event event) {
44      loadSubscription?.cancel();
45      errorSubscription.cancel();
46      completer.completeError(event);
47    });
48    imgElement.src = src;
49    // If the browser supports asynchronous image decoding, use that instead
50    // of `onload`.
51    if (_supportsDecode) {
52      imgElement.decode().then((dynamic _) {
53        errorSubscription.cancel();
54        final HtmlImage image = HtmlImage(
55          imgElement,
56          imgElement.naturalWidth,
57          imgElement.naturalHeight,
58        );
59        completer.complete(SingleFrameInfo(image));
60      });
61    }
62    return completer.future;
63  }
64
65  @override
66  void dispose() {}
67}
68
69class HtmlBlobCodec extends HtmlCodec {
70  final html.Blob blob;
71
72  HtmlBlobCodec(this.blob) : super(html.Url.createObjectUrlFromBlob(blob));
73
74  @override
75  void dispose() {
76    html.Url.revokeObjectUrl(src);
77  }
78}
79
80class SingleFrameInfo implements ui.FrameInfo {
81  SingleFrameInfo(this.image);
82
83  @override
84  Duration get duration => const Duration(milliseconds: 0);
85
86  @override
87  final ui.Image image;
88}
89
90class HtmlImage implements ui.Image {
91  final html.ImageElement imgElement;
92
93  HtmlImage(this.imgElement, this.width, this.height);
94
95  @override
96  void dispose() {
97    // Do nothing. The codec that owns this image should take care of
98    // releasing the object url.
99  }
100
101  @override
102  final int width;
103
104  @override
105  final int height;
106
107  @override
108  Future<ByteData> toByteData(
109      {ui.ImageByteFormat format = ui.ImageByteFormat.rawRgba}) {
110    return futurize((Callback<ByteData> callback) {
111      return _toByteData(format.index, (Uint8List encoded) {
112        callback(encoded?.buffer?.asByteData());
113      });
114    });
115  }
116
117  /// Returns an error message on failure, null on success.
118  String _toByteData(int format, Callback<Uint8List> callback) => null;
119}
120