Skip to content

Commit a8f4c91

Browse files
committed
[canvaskit] Expose MakeRasterDirectSurface
This will let clients have faster access to the drawn data and give them control about calling Canvas2D.putImageData. Bug: skia:10565 Change-Id: Ia3da7867be3f671ab20fdb182ec758b77c0f63b8 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/331316 Reviewed-by: Mike Reed <reed@google.com> Reviewed-by: Nathaniel Nifong <nifong@google.com>
1 parent 97469f4 commit a8f4c91

File tree

6 files changed

+54
-4
lines changed

6 files changed

+54
-4
lines changed

modules/canvaskit/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99
## Added
1010
- `MakeFractalNoise`, `MakeImprovedNoise`, and `MakeTurbulence` have been added to
1111
`CanvasKit.Shader`.
12+
- `MakeRasterDirectSurface` for giving the user direct access to drawn pixels.
1213

1314
### Breaking
1415
- `CanvasKit.MakePathFromSVGString` was renamed to `CanvasKit.Path.MakeFromSVGString`

modules/canvaskit/canvaskit/types/canvaskit-wasm-tests.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,14 @@ function surfaceTests(CK: CanvasKit) {
773773
enableExtensionsByDefault: 2,
774774
})!;
775775
const surfaceSeven = CK.MakeSurface(200, 200)!; // $ExpectType Surface
776+
const m = CK.Malloc(Uint8Array, 5 * 5 * 4);
777+
const surfaceEight = CK.MakeRasterDirectSurface({
778+
width: 5,
779+
height: 5,
780+
colorType: CK.ColorType.RGBA_8888,
781+
alphaType: CK.AlphaType.Premul,
782+
colorSpace: CK.ColorSpace.SRGB,
783+
}, m, 20);
776784

777785
surfaceOne.flush();
778786
const canvas = surfaceTwo.getCanvas(); // $ExpectType Canvas
@@ -789,7 +797,7 @@ function surfaceTests(CK: CanvasKit) {
789797

790798
const ctx = CK.GetWebGLContext(canvasEl); // $ExpectType number
791799
const grCtx = CK.MakeGrContext(ctx);
792-
const surfaceEight = CK.MakeOnScreenGLSurface(grCtx, 100, 400, // $ExpectType Surface
800+
const surfaceNine = CK.MakeOnScreenGLSurface(grCtx, 100, 400, // $ExpectType Surface
793801
CK.ColorSpace.ADOBE_RGB)!;
794802

795803
const rt = CK.MakeRenderTarget(grCtx, 100, 200); // $ExpectType Surface | null

modules/canvaskit/canvaskit/types/index.d.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,19 @@ export interface CanvasKit {
167167
*/
168168
MakeCanvasSurface(canvas: HTMLCanvasElement | string): Surface | null;
169169

170+
/**
171+
* Creates a Raster (CPU) Surface that will draw into the provided Malloc'd buffer. This allows
172+
* clients to efficiently be able to read the current pixels w/o having to copy.
173+
* The length of pixels must be at least height * bytesPerRow bytes big.
174+
* @param ii
175+
* @param pixels
176+
* @param bytesPerRow - How many bytes are per row. This is at least width * bytesPerColorType. For example,
177+
* an 8888 ColorType has 4 bytes per pixel, so a 5 pixel wide 8888 surface needs at least
178+
* 5 * 4 = 20 bytesPerRow. Some clients may have more than the usual to make the data line
179+
* up with a particular multiple.
180+
*/
181+
MakeRasterDirectSurface(ii: ImageInfo, pixels: MallocObj, bytesPerRow: number): Surface | null;
182+
170183
/**
171184
* Creates a CPU backed (aka raster) surface.
172185
* @param canvas - either the canvas element itself or a string with the DOM id of it.

modules/canvaskit/cpu.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
// Allocate the buffer of pixels to be drawn into.
4949
var pixelPtr = CanvasKit._malloc(pixelLen);
5050

51+
// TODO(kjlubick) don't use RDS, as it will always copy if snapping off an SkImage.
5152
var surface = this._getRasterDirectSurface(imageInfo, pixelPtr, width*4);
5253
if (surface) {
5354
surface._canvas = null;
@@ -63,6 +64,10 @@
6364
return surface;
6465
};
6566

67+
CanvasKit.MakeRasterDirectSurface = function(imageInfo, mallocObj, bytesPerRow) {
68+
return this._getRasterDirectSurface(imageInfo, mallocObj['byteOffset'], bytesPerRow);
69+
};
70+
6671
// For GPU builds, simply proxies to native code flush. For CPU builds,
6772
// also updates the underlying HTML canvas, optionally with dirtyRect.
6873
CanvasKit.Surface.prototype.flush = function(dirtyRect) {

modules/canvaskit/externs.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ var CanvasKit = {
5555
MakeParticles: function() {},
5656
MakeVertices: function() {},
5757
MakeSurface: function() {},
58+
MakeRasterDirectSurface: function() {},
5859
MakeWebGLCanvasSurface: function() {},
5960
Malloc: function() {},
6061
MallocGlyphIDs: function() {},

modules/canvaskit/tests/core.spec.js

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -882,7 +882,7 @@ describe('Core canvas behavior', () => {
882882
const colorSpace = CanvasKit.ColorSpace.SRGB;
883883
const surface = CanvasKit.MakeCanvasSurface('test', CanvasKit.ColorSpace.SRGB);
884884
expect(surface).toBeTruthy('Could not make surface');
885-
let info = surface.imageInfo()
885+
let info = surface.imageInfo();
886886
expect(info.alphaType).toEqual(CanvasKit.AlphaType.Unpremul);
887887
expect(info.colorType).toEqual(CanvasKit.ColorType.RGBA_8888);
888888
expect(CanvasKit.ColorSpace.Equals(info.colorSpace, colorSpace))
@@ -900,7 +900,7 @@ describe('Core canvas behavior', () => {
900900
console.log('Not expecting color space support in cpu backed suface.');
901901
return;
902902
}
903-
let info = surface.imageInfo()
903+
let info = surface.imageInfo();
904904
expect(info.alphaType).toEqual(CanvasKit.AlphaType.Unpremul);
905905
expect(info.colorType).toEqual(CanvasKit.ColorType.RGBA_F16);
906906
expect(CanvasKit.ColorSpace.Equals(info.colorSpace, colorSpace))
@@ -918,7 +918,7 @@ describe('Core canvas behavior', () => {
918918
console.log('Not expecting color space support in cpu backed suface.');
919919
return;
920920
}
921-
let info = surface.imageInfo()
921+
let info = surface.imageInfo();
922922
expect(info.alphaType).toEqual(CanvasKit.AlphaType.Unpremul);
923923
expect(info.colorType).toEqual(CanvasKit.ColorType.RGBA_F16);
924924
expect(CanvasKit.ColorSpace.Equals(info.colorSpace, colorSpace))
@@ -1036,4 +1036,26 @@ describe('Core canvas behavior', () => {
10361036
expect(expected[i]).toBeCloseTo(actual[i], 5, `element ${i}`);
10371037
}
10381038
}
1039+
1040+
it('can create a RasterDirectSurface', () => {
1041+
// Make enough space for a 5x5 8888 surface (4 bytes for R, G, B, A)
1042+
const rdsData = CanvasKit.Malloc(Uint8Array, 5 * 5 * 4);
1043+
const surface = CanvasKit.MakeRasterDirectSurface({
1044+
'width': 5,
1045+
'height': 5,
1046+
'colorType': CanvasKit.ColorType.RGBA_8888,
1047+
'alphaType': CanvasKit.AlphaType.Premul,
1048+
'colorSpace': CanvasKit.ColorSpace.SRGB,
1049+
}, rdsData, 5 * 4);
1050+
1051+
surface.getCanvas().clear(CanvasKit.Color(200, 100, 0, 0.8));
1052+
const pixels = rdsData.toTypedArray();
1053+
// Check that the first pixels colors are right.
1054+
expect(pixels[0]).toEqual(160); // red (premul, 0.8 * 200)
1055+
expect(pixels[1]).toEqual(80); // green (premul, 0.8 * 100)
1056+
expect(pixels[2]).toEqual(0); // blue (premul, not that it matters)
1057+
expect(pixels[3]).toEqual(204); // alpha (0.8 * 255)
1058+
surface.delete();
1059+
CanvasKit.Free(rdsData);
1060+
});
10391061
});

0 commit comments

Comments
 (0)