Skip to content
This repository has been archived by the owner on Feb 25, 2025. It is now read-only.

Commit

Permalink
Use H5vcc CanvasKit implementation if it is detected. (#31191)
Browse files Browse the repository at this point in the history
  • Loading branch information
b-luk authored Feb 9, 2022
1 parent ba8f1c5 commit 1eafb40
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 0 deletions.
29 changes: 29 additions & 0 deletions lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ import '../profiler.dart';
/// Entrypoint into the CanvasKit API.
late CanvasKit canvasKit;

/// Whether to use a CanvasKit implementation provided by a JavaScript
/// `window.h5vcc.canvasKit` object.
///
/// Cobalt may use this object to expose a native implementation of the
/// CanvasKit bindings. If this exists, use it instead of using the normal
/// downloaded CanvasKit library.
final bool useH5vccCanvasKit = h5vcc != null;

/// Sets the [CanvasKit] object on `window` so we can use `@JS()` to bind to
/// static APIs.
///
Expand All @@ -39,6 +47,18 @@ external set windowFlutterCanvasKit(CanvasKit? value);
@JS('window.flutterCanvasKit')
external CanvasKit? get windowFlutterCanvasKit;

@JS('window.h5vcc')
external H5vcc? get h5vcc;

@JS('window.h5vcc')
external set debugH5vccSetter(H5vcc? value);

@JS()
@anonymous
abstract class H5vcc {
external CanvasKit? get canvasKit;
}

@JS()
@anonymous
class CanvasKit {
Expand Down Expand Up @@ -132,6 +152,15 @@ class CanvasKit {
Object src,
SkPartialImageInfo info,
);

/// Gets a Skia surface from Cobalt's h5vcc object.
///
/// This is only applicable when running on Cobalt and when using Cobalt's
/// h5vcc CanvasKit bindings.
///
/// On Cobalt, this is the only way to get a Skia surface. Other CanvasKit
/// Make...Surface methods are not supported.
external SkSurface getH5vccSkSurface();
}

@JS('window.CanvasKitInit')
Expand Down
7 changes: 7 additions & 0 deletions lib/web_ui/lib/src/engine/canvaskit/initialization.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import '../embedder.dart';
import '../safe_browser_api.dart';
import 'canvaskit_api.dart';
import 'fonts.dart';
import 'util.dart';

/// Whether to use CanvasKit as the rendering backend.
final bool useCanvasKit = FlutterConfiguration.flutterWebAutoDetect
Expand Down Expand Up @@ -47,6 +48,12 @@ String canvasKitWasmModuleUrl(String canvasKitBase, String file) =>
Future<void> initializeCanvasKit({String? canvasKitBase}) async {
if (windowFlutterCanvasKit != null) {
canvasKit = windowFlutterCanvasKit!;
} else if (useH5vccCanvasKit) {
if (h5vcc?.canvasKit == null) {
throw CanvasKitError('H5vcc CanvasKit implementation not found.');
}
canvasKit = h5vcc!.canvasKit!;
windowFlutterCanvasKit = canvasKit;
} else {
canvasKit = await downloadCanvasKit(canvasKitBase: canvasKitBase);
windowFlutterCanvasKit = canvasKit;
Expand Down
5 changes: 5 additions & 0 deletions lib/web_ui/lib/src/engine/canvaskit/surface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ class Surface {

/// Creates a <canvas> and SkSurface for the given [size].
CkSurface createOrUpdateSurface(ui.Size size) {
if (useH5vccCanvasKit) {
_surface ??= CkSurface(canvasKit.getH5vccSkSurface(), null);
return _surface!;
}

if (size.isEmpty) {
throw CanvasKitError('Cannot create surfaces of empty size.');
}
Expand Down
73 changes: 73 additions & 0 deletions lib/web_ui/test/canvaskit/h5vcc_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// 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 'dart:html' as html;
import 'dart:js' as js;

import 'package:test/bootstrap/browser.dart';
import 'package:test/test.dart';
import 'package:ui/src/engine.dart';
import 'package:ui/ui.dart' as ui;

import 'common.dart';

void main() {
internalBootstrapBrowserTest(() => testMain);
}

void testMain() {
group('H5vcc patched CanvasKit', () {
int getH5vccSkSurfaceCalledCount = 0;

setUpAll(() async {
// Set `window.h5vcc` to PatchedH5vcc which uses a downloaded CanvasKit.
final CanvasKit downloadedCanvasKit = await downloadCanvasKit();
debugH5vccSetter = PatchedH5vcc(downloadedCanvasKit);

// Monkey-patch the getH5vccSkSurface function of
// `window.h5vcc.canvasKit`.
js.context['h5vcc']['canvasKit']['getH5vccSkSurface'] = () {
getH5vccSkSurfaceCalledCount++;

// Returns a fake [SkSurface] object with a minimal implementation.
return js.JsObject.jsify(<String, dynamic>{
'dispose': () {}
});
};
});

setUpCanvasKitTest();

setUp(() {
getH5vccSkSurfaceCalledCount = 0;
});

test('sets useH5vccCanvasKit', () {
expect(useH5vccCanvasKit, true);
});

test('API includes patched getH5vccSkSurface', () {
expect(canvasKit.getH5vccSkSurface, isNotNull);
});

test('Surface acquireFrame uses getH5vccSkSurface', () {
final Surface surface = SurfaceFactory.instance.getSurface();
surface.acquireFrame(ui.Size.zero);
expect(getH5vccSkSurfaceCalledCount, 1);

// No <canvas> element should be created.
expect(
flutterViewEmbedder.glassPaneElement!.querySelectorAll<html.Element>('canvas'),
isEmpty,
);
});
}, testOn: 'chrome');
}

class PatchedH5vcc implements H5vcc {
@override
final CanvasKit canvasKit;

PatchedH5vcc(this.canvasKit);
}

0 comments on commit 1eafb40

Please sign in to comment.