xref: /aosp_15_r20/external/skia/modules/canvaskit/cpu.js (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker// Adds compile-time JS functions to augment the CanvasKit interface.
2*c8dee2aaSAndroid Build Coastguard Worker// Implementations in this file are considerate of GPU builds, i.e. some
3*c8dee2aaSAndroid Build Coastguard Worker// behavior is predicated on whether or not this is being compiled alongside
4*c8dee2aaSAndroid Build Coastguard Worker// webgl.js or webgpu.js.
5*c8dee2aaSAndroid Build Coastguard Worker(function(CanvasKit){
6*c8dee2aaSAndroid Build Coastguard Worker  CanvasKit._extraInitializations = CanvasKit._extraInitializations || [];
7*c8dee2aaSAndroid Build Coastguard Worker  CanvasKit._extraInitializations.push(function() {
8*c8dee2aaSAndroid Build Coastguard Worker    // Takes in an html id or a canvas element
9*c8dee2aaSAndroid Build Coastguard Worker    CanvasKit.MakeSWCanvasSurface = function(idOrElement) {
10*c8dee2aaSAndroid Build Coastguard Worker      var canvas = idOrElement;
11*c8dee2aaSAndroid Build Coastguard Worker      var isHTMLCanvas = typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement;
12*c8dee2aaSAndroid Build Coastguard Worker      var isOffscreenCanvas = typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas;
13*c8dee2aaSAndroid Build Coastguard Worker      if (!isHTMLCanvas && !isOffscreenCanvas) {
14*c8dee2aaSAndroid Build Coastguard Worker        canvas = document.getElementById(idOrElement);
15*c8dee2aaSAndroid Build Coastguard Worker        if (!canvas) {
16*c8dee2aaSAndroid Build Coastguard Worker          throw 'Canvas with id ' + idOrElement + ' was not found';
17*c8dee2aaSAndroid Build Coastguard Worker        }
18*c8dee2aaSAndroid Build Coastguard Worker      }
19*c8dee2aaSAndroid Build Coastguard Worker      // Maybe better to use clientWidth/height.  See:
20*c8dee2aaSAndroid Build Coastguard Worker      // https://webglfundamentals.org/webgl/lessons/webgl-anti-patterns.html
21*c8dee2aaSAndroid Build Coastguard Worker      var surface = CanvasKit.MakeSurface(canvas.width, canvas.height);
22*c8dee2aaSAndroid Build Coastguard Worker      if (surface) {
23*c8dee2aaSAndroid Build Coastguard Worker        surface._canvas = canvas;
24*c8dee2aaSAndroid Build Coastguard Worker      }
25*c8dee2aaSAndroid Build Coastguard Worker      return surface;
26*c8dee2aaSAndroid Build Coastguard Worker    };
27*c8dee2aaSAndroid Build Coastguard Worker
28*c8dee2aaSAndroid Build Coastguard Worker    // Don't over-write the MakeCanvasSurface set by gpu.js if it exists.
29*c8dee2aaSAndroid Build Coastguard Worker    if (!CanvasKit.MakeCanvasSurface) {
30*c8dee2aaSAndroid Build Coastguard Worker      CanvasKit.MakeCanvasSurface = CanvasKit.MakeSWCanvasSurface;
31*c8dee2aaSAndroid Build Coastguard Worker    }
32*c8dee2aaSAndroid Build Coastguard Worker
33*c8dee2aaSAndroid Build Coastguard Worker    // Note that color spaces are currently not supported in CPU surfaces. due to the limitation
34*c8dee2aaSAndroid Build Coastguard Worker    // canvas.getContext('2d').putImageData imposes a limitation of using an RGBA_8888 color type.
35*c8dee2aaSAndroid Build Coastguard Worker    // TODO(nifong): support WGC color spaces while still using an RGBA_8888 color type when
36*c8dee2aaSAndroid Build Coastguard Worker    // on a cpu backend.
37*c8dee2aaSAndroid Build Coastguard Worker    CanvasKit.MakeSurface = function(width, height) {
38*c8dee2aaSAndroid Build Coastguard Worker      var imageInfo = {
39*c8dee2aaSAndroid Build Coastguard Worker        'width':  width,
40*c8dee2aaSAndroid Build Coastguard Worker        'height': height,
41*c8dee2aaSAndroid Build Coastguard Worker        'colorType': CanvasKit.ColorType.RGBA_8888,
42*c8dee2aaSAndroid Build Coastguard Worker        // Since we are sending these pixels directly into the HTML canvas,
43*c8dee2aaSAndroid Build Coastguard Worker        // (and those pixels are un-premultiplied, i.e. straight r,g,b,a)
44*c8dee2aaSAndroid Build Coastguard Worker        'alphaType': CanvasKit.AlphaType.Unpremul,
45*c8dee2aaSAndroid Build Coastguard Worker        'colorSpace': CanvasKit.ColorSpace.SRGB,
46*c8dee2aaSAndroid Build Coastguard Worker      };
47*c8dee2aaSAndroid Build Coastguard Worker      var pixelLen = width * height * 4; // it's 8888, so 4 bytes per pixel
48*c8dee2aaSAndroid Build Coastguard Worker      // Allocate the buffer of pixels to be drawn into.
49*c8dee2aaSAndroid Build Coastguard Worker      var pixelPtr = CanvasKit._malloc(pixelLen);
50*c8dee2aaSAndroid Build Coastguard Worker
51*c8dee2aaSAndroid Build Coastguard Worker      // Experiments with using RasterDirect vs Raster showed a 10% slowdown
52*c8dee2aaSAndroid Build Coastguard Worker      // over the traditional Surface::MakeRaster approach. This was exacerbated when
53*c8dee2aaSAndroid Build Coastguard Worker      // the surface was drawing to Premul and we had to convert to Unpremul each frame
54*c8dee2aaSAndroid Build Coastguard Worker      // (up to a 10x further slowdown).
55*c8dee2aaSAndroid Build Coastguard Worker      var surface = CanvasKit.Surface._makeRasterDirect(imageInfo, pixelPtr, width*4);
56*c8dee2aaSAndroid Build Coastguard Worker      if (surface) {
57*c8dee2aaSAndroid Build Coastguard Worker        surface._canvas = null;
58*c8dee2aaSAndroid Build Coastguard Worker        surface._width = width;
59*c8dee2aaSAndroid Build Coastguard Worker        surface._height = height;
60*c8dee2aaSAndroid Build Coastguard Worker        surface._pixelLen = pixelLen;
61*c8dee2aaSAndroid Build Coastguard Worker
62*c8dee2aaSAndroid Build Coastguard Worker        surface._pixelPtr = pixelPtr;
63*c8dee2aaSAndroid Build Coastguard Worker        // rasterDirectSurface does not initialize the pixels, so we clear them
64*c8dee2aaSAndroid Build Coastguard Worker        // to transparent black.
65*c8dee2aaSAndroid Build Coastguard Worker        surface.getCanvas().clear(CanvasKit.TRANSPARENT);
66*c8dee2aaSAndroid Build Coastguard Worker      }
67*c8dee2aaSAndroid Build Coastguard Worker      return surface;
68*c8dee2aaSAndroid Build Coastguard Worker    };
69*c8dee2aaSAndroid Build Coastguard Worker
70*c8dee2aaSAndroid Build Coastguard Worker    CanvasKit.MakeRasterDirectSurface = function(imageInfo, mallocObj, bytesPerRow) {
71*c8dee2aaSAndroid Build Coastguard Worker      return CanvasKit.Surface._makeRasterDirect(imageInfo, mallocObj['byteOffset'], bytesPerRow);
72*c8dee2aaSAndroid Build Coastguard Worker    };
73*c8dee2aaSAndroid Build Coastguard Worker
74*c8dee2aaSAndroid Build Coastguard Worker    // For GPU builds, simply proxies to native code flush.  For CPU builds,
75*c8dee2aaSAndroid Build Coastguard Worker    // also updates the underlying HTML canvas, optionally with dirtyRect.
76*c8dee2aaSAndroid Build Coastguard Worker    CanvasKit.Surface.prototype.flush = function(dirtyRect) {
77*c8dee2aaSAndroid Build Coastguard Worker      CanvasKit.setCurrentContext(this._context);
78*c8dee2aaSAndroid Build Coastguard Worker      this._flush();
79*c8dee2aaSAndroid Build Coastguard Worker      // Do we have an HTML canvas to write the pixels to?
80*c8dee2aaSAndroid Build Coastguard Worker      // We will not have a canvas if this a GPU build, for example.
81*c8dee2aaSAndroid Build Coastguard Worker      if (this._canvas) {
82*c8dee2aaSAndroid Build Coastguard Worker        var pixels = new Uint8ClampedArray(CanvasKit.HEAPU8.buffer, this._pixelPtr, this._pixelLen);
83*c8dee2aaSAndroid Build Coastguard Worker        var imageData = new ImageData(pixels, this._width, this._height);
84*c8dee2aaSAndroid Build Coastguard Worker
85*c8dee2aaSAndroid Build Coastguard Worker        if (!dirtyRect) {
86*c8dee2aaSAndroid Build Coastguard Worker          this._canvas.getContext('2d').putImageData(imageData, 0, 0);
87*c8dee2aaSAndroid Build Coastguard Worker        } else {
88*c8dee2aaSAndroid Build Coastguard Worker          this._canvas.getContext('2d').putImageData(imageData, 0, 0,
89*c8dee2aaSAndroid Build Coastguard Worker                                                     dirtyRect[0], dirtyRect[1],
90*c8dee2aaSAndroid Build Coastguard Worker                                                     dirtyRect[2] - dirtyRect[0],
91*c8dee2aaSAndroid Build Coastguard Worker                                                     dirtyRect[3] - dirtyRect[1]);
92*c8dee2aaSAndroid Build Coastguard Worker        }
93*c8dee2aaSAndroid Build Coastguard Worker      }
94*c8dee2aaSAndroid Build Coastguard Worker    };
95*c8dee2aaSAndroid Build Coastguard Worker
96*c8dee2aaSAndroid Build Coastguard Worker    // Call dispose() instead of delete to clean up the underlying memory.
97*c8dee2aaSAndroid Build Coastguard Worker    // TODO(kjlubick) get rid of this and just wrap around delete().
98*c8dee2aaSAndroid Build Coastguard Worker    CanvasKit.Surface.prototype.dispose = function() {
99*c8dee2aaSAndroid Build Coastguard Worker      if (this._pixelPtr) {
100*c8dee2aaSAndroid Build Coastguard Worker        CanvasKit._free(this._pixelPtr);
101*c8dee2aaSAndroid Build Coastguard Worker      }
102*c8dee2aaSAndroid Build Coastguard Worker      this.delete();
103*c8dee2aaSAndroid Build Coastguard Worker    };
104*c8dee2aaSAndroid Build Coastguard Worker
105*c8dee2aaSAndroid Build Coastguard Worker    CanvasKit.setCurrentContext = CanvasKit.setCurrentContext || function() {
106*c8dee2aaSAndroid Build Coastguard Worker       // no op if this is a cpu-only build.
107*c8dee2aaSAndroid Build Coastguard Worker    };
108*c8dee2aaSAndroid Build Coastguard Worker
109*c8dee2aaSAndroid Build Coastguard Worker    CanvasKit.getCurrentGrDirectContext = CanvasKit.getCurrentGrDirectContext || function() {
110*c8dee2aaSAndroid Build Coastguard Worker      // No GrDirectContexts without a GPU backend.
111*c8dee2aaSAndroid Build Coastguard Worker      return null;
112*c8dee2aaSAndroid Build Coastguard Worker    };
113*c8dee2aaSAndroid Build Coastguard Worker  });
114*c8dee2aaSAndroid Build Coastguard Worker}(Module)); // When this file is loaded in, the high level object is "Module";
115