|
| 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 | + |
| 5 | +import 'dart:async'; |
| 6 | +import 'dart:html' as html; |
| 7 | +import 'dart:js_util' as js_util; |
| 8 | +import 'package:ui/src/engine.dart'; |
| 9 | + |
| 10 | +/// Polyfill for html.OffscreenCanvas that is not supported on some browsers. |
| 11 | +class OffScreenCanvas { |
| 12 | + html.OffscreenCanvas? offScreenCanvas; |
| 13 | + html.CanvasElement? canvasElement; |
| 14 | + int width; |
| 15 | + int height; |
| 16 | + static bool? _supported; |
| 17 | + |
| 18 | + OffScreenCanvas(this.width, this.height) { |
| 19 | + if (OffScreenCanvas.supported) { |
| 20 | + offScreenCanvas = html.OffscreenCanvas(width, height); |
| 21 | + } else { |
| 22 | + canvasElement = html.CanvasElement( |
| 23 | + width: width, |
| 24 | + height: height, |
| 25 | + ); |
| 26 | + canvasElement!.className = 'gl-canvas'; |
| 27 | + final double cssWidth = width / EnginePlatformDispatcher.browserDevicePixelRatio; |
| 28 | + final double cssHeight = height / EnginePlatformDispatcher.browserDevicePixelRatio; |
| 29 | + canvasElement!.style |
| 30 | + ..position = 'absolute' |
| 31 | + ..width = '${cssWidth}px' |
| 32 | + ..height = '${cssHeight}px'; |
| 33 | + } |
| 34 | + } |
| 35 | + |
| 36 | + void dispose() { |
| 37 | + offScreenCanvas = null; |
| 38 | + canvasElement = null; |
| 39 | + } |
| 40 | + |
| 41 | + /// Returns CanvasRenderContext2D or OffscreenCanvasRenderingContext2D to |
| 42 | + /// paint into. |
| 43 | + Object? getContext2d() { |
| 44 | + return (offScreenCanvas != null |
| 45 | + ? offScreenCanvas!.getContext('2d') |
| 46 | + : canvasElement!.getContext('2d')); |
| 47 | + } |
| 48 | + |
| 49 | + /// Feature detection for transferToImageBitmap on OffscreenCanvas. |
| 50 | + bool get transferToImageBitmapSupported => |
| 51 | + js_util.hasProperty(offScreenCanvas!, 'transferToImageBitmap'); |
| 52 | + |
| 53 | + /// Creates an ImageBitmap object from the most recently rendered image |
| 54 | + /// of the OffscreenCanvas. |
| 55 | + /// |
| 56 | + /// !Warning API still in experimental status, feature detect before using. |
| 57 | + Object? transferToImageBitmap() { |
| 58 | + return js_util.callMethod(offScreenCanvas!, 'transferToImageBitmap', |
| 59 | + <dynamic>[]); |
| 60 | + } |
| 61 | + |
| 62 | + /// Draws canvas contents to a rendering context. |
| 63 | + void transferImage(Object targetContext) { |
| 64 | + // Actual size of canvas may be larger than viewport size. Use |
| 65 | + // source/destination to draw part of the image data. |
| 66 | + js_util.callMethod(targetContext, 'drawImage', |
| 67 | + <dynamic>[offScreenCanvas ?? canvasElement!, 0, 0, width, height, |
| 68 | + 0, 0, width, height]); |
| 69 | + } |
| 70 | + |
| 71 | + /// Converts canvas contents to an image and returns as data url. |
| 72 | + Future<String> toDataUrl() { |
| 73 | + final Completer<String> completer = Completer<String>(); |
| 74 | + if (offScreenCanvas != null) { |
| 75 | + offScreenCanvas!.convertToBlob().then((html.Blob value) { |
| 76 | + final fileReader = html.FileReader(); |
| 77 | + fileReader.onLoad.listen((event) { |
| 78 | + completer.complete(js_util.getProperty( |
| 79 | + js_util.getProperty(event, 'target')!, 'result')!); |
| 80 | + }); |
| 81 | + fileReader.readAsDataUrl(value); |
| 82 | + }); |
| 83 | + return completer.future; |
| 84 | + } else { |
| 85 | + return Future.value(canvasElement!.toDataUrl()); |
| 86 | + } |
| 87 | + } |
| 88 | + |
| 89 | + /// Draws an image to canvas for both offscreen canvas canvas context2d. |
| 90 | + void drawImage(Object image, int x, int y, int width, int height) { |
| 91 | + js_util.callMethod( |
| 92 | + getContext2d()!, 'drawImage', <dynamic>[image, x, y, width, height]); |
| 93 | + } |
| 94 | + |
| 95 | + /// Feature detects OffscreenCanvas. |
| 96 | + static bool get supported => _supported ??= |
| 97 | + js_util.hasProperty(html.window, 'OffscreenCanvas'); |
| 98 | +} |
0 commit comments