blob: 3f8a2c7880479773d6ab3eaa70d997f4627774e4 [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.
// @dart = 2.6
part of engine;
typedef SubmitCallback = bool Function(SurfaceFrame, SkCanvas);
/// A frame which contains a canvas to be drawn into.
class SurfaceFrame {
final SkSurface skiaSurface;
final SubmitCallback submitCallback;
bool _submitted;
SurfaceFrame(this.skiaSurface, this.submitCallback)
: _submitted = false,
assert(submitCallback != null);
/// Submit this frame to be drawn.
bool submit() {
if (_submitted) {
return false;
}
return submitCallback(this, skiaCanvas);
}
SkCanvas get skiaCanvas => skiaSurface?.getCanvas();
}
/// A surface which can be drawn into by the compositor.
///
/// The underlying representation is a [SkSurface], which can be reused by
/// successive frames if they are the same size. Otherwise, a new [SkSurface] is
/// created.
class Surface {
SkSurface _surface;
html.Element htmlElement;
bool _addedToScene = false;
/// The default view embedder. Coordinates embedding platform views and
/// overlaying subsequent draw operations on top.
HtmlViewEmbedder viewEmbedder;
/// Acquire a frame of the given [size] containing a drawable canvas.
///
/// The given [size] is in physical pixels.
SurfaceFrame acquireFrame(ui.Size size) {
final SkSurface surface = acquireRenderSurface(size);
canvasKit.callMethod('setCurrentContext', <int>[surface.context]);
if (surface == null) {
return null;
}
SubmitCallback submitCallback =
(SurfaceFrame surfaceFrame, SkCanvas canvas) {
return _presentSurface(canvas);
};
return SurfaceFrame(surface, submitCallback);
}
SkSurface acquireRenderSurface(ui.Size size) {
if (!_createOrUpdateSurfaces(size)) {
return null;
}
return _surface;
}
void addToScene() {
if (!_addedToScene) {
skiaSceneHost.children.insert(0, htmlElement);
}
_addedToScene = true;
}
bool _createOrUpdateSurfaces(ui.Size size) {
if (_surface != null &&
size ==
ui.Size(
_surface.width().toDouble(),
_surface.height().toDouble(),
)) {
return true;
}
_surface?.dispose();
_surface = null;
htmlElement?.remove();
htmlElement = null;
_addedToScene = false;
if (size.isEmpty) {
html.window.console.error('Cannot create surfaces of empty size.');
return false;
}
_surface = _wrapHtmlCanvas(size);
if (_surface == null) {
html.window.console.error('Could not create a surface.');
return false;
}
return true;
}
SkSurface _wrapHtmlCanvas(ui.Size size) {
final ui.Size logicalSize = size / ui.window.devicePixelRatio;
final html.CanvasElement htmlCanvas = html.CanvasElement(
width: size.width.ceil(), height: size.height.ceil());
htmlCanvas.style
..position = 'absolute'
..width = '${logicalSize.width.ceil()}px'
..height = '${logicalSize.height.ceil()}px';
final int glContext = canvasKit.callMethod('GetWebGLContext', <dynamic>[
htmlCanvas,
// Default to no anti-aliasing. Paint commands can be explicitly
// anti-aliased by setting their `Paint` object's `antialias` property.
js.JsObject.jsify({'antialias': 0}),
]);
final js.JsObject grContext =
canvasKit.callMethod('MakeGrContext', <dynamic>[glContext]);
final js.JsObject skSurface =
canvasKit.callMethod('MakeOnScreenGLSurface', <dynamic>[
grContext,
size.width,
size.height,
]);
htmlElement = htmlCanvas;
if (skSurface == null) {
return null;
} else {
return SkSurface(skSurface, glContext);
}
}
bool _presentSurface(SkCanvas canvas) {
if (canvas == null) {
return false;
}
canvasKit.callMethod('setCurrentContext', <dynamic>[_surface.context]);
_surface.getCanvas().flush();
return true;
}
}
/// A Dart wrapper around Skia's SkSurface.
class SkSurface {
final js.JsObject _surface;
final int _glContext;
SkSurface(this._surface, this._glContext);
SkCanvas getCanvas() {
final js.JsObject skCanvas = _surface.callMethod('getCanvas');
return SkCanvas(skCanvas);
}
int get context => _glContext;
int width() => _surface.callMethod('width');
int height() => _surface.callMethod('height');
void dispose() {
_surface.callMethod('dispose');
}
}