f611404816
We'll switch to the correct context when necessary (e.g. before calls that talk to the GPU). This is achieved by adding in calls at the JS layer to switch the context before making a call that is known to talk to the GPU (e.g. draw calls on SkCanvas). Another implementation that was considered was to add a C++ shim in GrGLInterface that would switch the context before every call in the GPU - however, that seemed too difficult and would add extra overhead if a single draw* call talks to the GPU multiple times. Bug: skia:12255 Change-Id: I96e4c6b41a5bfcc9913aeaca7ccb125358048ad3 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/432136 Reviewed-by: Brian Salomon <bsalomon@google.com>
109 lines
4.6 KiB
JavaScript
109 lines
4.6 KiB
JavaScript
// Adds compile-time JS functions to augment the CanvasKit interface.
|
|
// Implementations in this file are considerate of GPU builds, i.e. some
|
|
// behavior is predicated on whether or not this is being compiled alongside
|
|
// gpu.js.
|
|
(function(CanvasKit){
|
|
CanvasKit._extraInitializations = CanvasKit._extraInitializations || [];
|
|
CanvasKit._extraInitializations.push(function() {
|
|
// Takes in an html id or a canvas element
|
|
CanvasKit.MakeSWCanvasSurface = function(idOrElement) {
|
|
var canvas = idOrElement;
|
|
if (canvas.tagName !== 'CANVAS') {
|
|
// TODO(nifong): unit test
|
|
canvas = document.getElementById(idOrElement);
|
|
if (!canvas) {
|
|
throw 'Canvas with id ' + idOrElement + ' was not found';
|
|
}
|
|
}
|
|
// Maybe better to use clientWidth/height. See:
|
|
// https://webglfundamentals.org/webgl/lessons/webgl-anti-patterns.html
|
|
var surface = CanvasKit.MakeSurface(canvas.width, canvas.height);
|
|
if (surface) {
|
|
surface._canvas = canvas;
|
|
}
|
|
return surface;
|
|
};
|
|
|
|
// Don't over-write the MakeCanvasSurface set by gpu.js if it exists.
|
|
if (!CanvasKit.MakeCanvasSurface) {
|
|
CanvasKit.MakeCanvasSurface = CanvasKit.MakeSWCanvasSurface;
|
|
}
|
|
|
|
// Note that color spaces are currently not supported in CPU surfaces. due to the limitation
|
|
// canvas.getContext('2d').putImageData imposes a limitation of using an RGBA_8888 color type.
|
|
// TODO(nifong): support WGC color spaces while still using an RGBA_8888 color type when
|
|
// on a cpu backend.
|
|
CanvasKit.MakeSurface = function(width, height) {
|
|
var imageInfo = {
|
|
'width': width,
|
|
'height': height,
|
|
'colorType': CanvasKit.ColorType.RGBA_8888,
|
|
// Since we are sending these pixels directly into the HTML canvas,
|
|
// (and those pixels are un-premultiplied, i.e. straight r,g,b,a)
|
|
'alphaType': CanvasKit.AlphaType.Unpremul,
|
|
'colorSpace': CanvasKit.ColorSpace.SRGB,
|
|
};
|
|
var pixelLen = width * height * 4; // it's 8888, so 4 bytes per pixel
|
|
// Allocate the buffer of pixels to be drawn into.
|
|
var pixelPtr = CanvasKit._malloc(pixelLen);
|
|
|
|
// Experiments with using RasterDirect vs Raster showed a 10% slowdown
|
|
// over the traditional Surface::MakeRaster approach. This was exacerbated when
|
|
// the surface was drawing to Premul and we had to convert to Unpremul each frame
|
|
// (up to a 10x further slowdown).
|
|
var surface = CanvasKit.Surface._makeRasterDirect(imageInfo, pixelPtr, width*4);
|
|
if (surface) {
|
|
surface._canvas = null;
|
|
surface._width = width;
|
|
surface._height = height;
|
|
surface._pixelLen = pixelLen;
|
|
|
|
surface._pixelPtr = pixelPtr;
|
|
// rasterDirectSurface does not initialize the pixels, so we clear them
|
|
// to transparent black.
|
|
surface.getCanvas().clear(CanvasKit.TRANSPARENT);
|
|
}
|
|
return surface;
|
|
};
|
|
|
|
CanvasKit.MakeRasterDirectSurface = function(imageInfo, mallocObj, bytesPerRow) {
|
|
return CanvasKit.Surface._makeRasterDirect(imageInfo, mallocObj['byteOffset'], bytesPerRow);
|
|
};
|
|
|
|
// For GPU builds, simply proxies to native code flush. For CPU builds,
|
|
// also updates the underlying HTML canvas, optionally with dirtyRect.
|
|
CanvasKit.Surface.prototype.flush = function(dirtyRect) {
|
|
CanvasKit.setCurrentContext(this._context);
|
|
this._flush();
|
|
// Do we have an HTML canvas to write the pixels to?
|
|
// We will not have a canvas if this a GPU build, for example.
|
|
if (this._canvas) {
|
|
var pixels = new Uint8ClampedArray(CanvasKit.HEAPU8.buffer, this._pixelPtr, this._pixelLen);
|
|
var imageData = new ImageData(pixels, this._width, this._height);
|
|
|
|
if (!dirtyRect) {
|
|
this._canvas.getContext('2d').putImageData(imageData, 0, 0);
|
|
} else {
|
|
this._canvas.getContext('2d').putImageData(imageData, 0, 0,
|
|
dirtyRect[0], dirtyRect[1],
|
|
dirtyRect[2] - dirtyRect[0],
|
|
dirtyRect[3] - dirtyRect[1]);
|
|
}
|
|
}
|
|
};
|
|
|
|
// Call dispose() instead of delete to clean up the underlying memory.
|
|
// TODO(kjlubick) get rid of this and just wrap around delete().
|
|
CanvasKit.Surface.prototype.dispose = function() {
|
|
if (this._pixelPtr) {
|
|
CanvasKit._free(this._pixelPtr);
|
|
}
|
|
this.delete();
|
|
};
|
|
|
|
CanvasKit.setCurrentContext = CanvasKit.setCurrentContext || function() {
|
|
// no op if this is a cpu-only build.
|
|
};
|
|
});
|
|
}(Module)); // When this file is loaded in, the high level object is "Module";
|