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

Commit c4ab087

Browse files
committed
[canvaskit] Add option for readPixels to use pre-malloc'd data
Bug: skia:10565 Change-Id: I777f887794cd0524ced4d65e86bd285a854386e5 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/331858 Reviewed-by: Nathaniel Nifong <nifong@google.com>
1 parent 9fe8391 commit c4ab087

File tree

5 files changed

+80
-16
lines changed

5 files changed

+80
-16
lines changed

modules/canvaskit/CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
`CanvasKit.Shader`.
1212
- `MakeRasterDirectSurface` for giving the user direct access to drawn pixels.
1313
- `getLineMetrics` to Paragraph.
14-
- `Canvas.saveLayerPaint` as an experimental, undocumented "fast path" if one only needs to pass the paint.
14+
- `Canvas.saveLayerPaint` as an experimental, undocumented "fast path" if one only needs to pass
15+
the paint.
1516

1617
### Breaking
1718
- `CanvasKit.MakePathFromSVGString` was renamed to `CanvasKit.Path.MakeFromSVGString`
@@ -23,6 +24,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2324
- `CanvasKit.Shader.Blend`, `...Color`, and `...Lerp` have been renamed to
2425
`CanvasKit.Shader.MakeBlend`, `...MakeColor` and `...MakeLerp` to align with naming conventions.
2526
The old names will be removed in an upcoming release.
27+
- `readPixels` now takes a malloc'd object as the last parameter. If provided, the data will be
28+
copied into there instead of allocating a new buffer.
2629

2730
### Removed
2831
- `CanvasKit.MakePathFromCmds`; Was deprecated in favor of `CanvasKit.Path.MakeFromCmds`.

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,9 @@ function canvasTests(CK: CanvasKit, canvas?: Canvas, paint?: Paint, path?: Path,
141141
const pixels = canvas.readPixels(0, 1, 2, 3); // $ExpectType Uint8Array
142142
const pixelsTwo = canvas.readPixels(4, 5, 6, 7, CK.AlphaType.Opaque, CK.ColorType.RGBA_1010102,
143143
CK.ColorSpace.DISPLAY_P3, 16);
144+
const m = CK.Malloc(Uint8Array, 20);
145+
canvas.readPixels(4, 5, 6, 7, CK.AlphaType.Opaque, CK.ColorType.RGBA_1010102,
146+
CK.ColorSpace.DISPLAY_P3, 16, m);
144147
canvas.restore();
145148
canvas.restoreToCount(2);
146149
canvas.rotate(1, 2, 3);
@@ -239,6 +242,14 @@ function imageTests(CK: CanvasKit, imgElement?: HTMLImageElement) {
239242
alphaType: CK.AlphaType.Unpremul,
240243
colorSpace: CK.ColorSpace.SRGB,
241244
}, 85, 1000);
245+
const m = CK.Malloc(Uint8Array, 10);
246+
img.readPixels({
247+
width: 79,
248+
height: 205,
249+
colorType: CK.ColorType.RGBA_8888,
250+
alphaType: CK.AlphaType.Unpremul,
251+
colorSpace: CK.ColorSpace.SRGB,
252+
}, 85, 1000, m);
242253
img.delete();
243254
}
244255

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

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,10 +1241,13 @@ export interface Canvas extends EmbindObject<Canvas> {
12411241
* @param alphaType - defaults to Unpremul
12421242
* @param colorType - defaults to RGBA_8888
12431243
* @param colorSpace - defaults to SRGB
1244+
* @param dest - If provided, the pixels will be copied into the allocated buffer allowing access to the
1245+
* pixels without allocating a new TypedArray.
12441246
* @param dstRowBytes
12451247
*/
12461248
readPixels(x: number, y: number, w: number, h: number, alphaType?: AlphaType,
1247-
colorType?: ColorType, colorSpace?: ColorSpace, dstRowBytes?: number): Uint8Array;
1249+
colorType?: ColorType, colorSpace?: ColorSpace, dstRowBytes?: number,
1250+
dest?: MallocObj): Uint8Array;
12481251

12491252
/**
12501253
* Removes changes to the current matrix and clip since Canvas state was
@@ -1566,9 +1569,13 @@ export interface Image extends EmbindObject<Image> {
15661569
* @param imageInfo - describes the destination format of the pixels.
15671570
* @param srcX
15681571
* @param srcY
1569-
* @returns a Uint8Array if RGB_8888 was requested, Float32Array if RGBA_F32 was requested.
1572+
* @param dest - If provided, the pixels will be copied into the allocated buffer allowing access to the
1573+
* pixels without allocating a new TypedArray.
1574+
* @returns a Uint8Array if RGB_8888 was requested, Float32Array if RGBA_F32 was requested. null will be returned
1575+
* on any error.
1576+
*
15701577
*/
1571-
readPixels(imageInfo: ImageInfo, srcX: number, srcY: number): Uint8Array | Float32Array | null;
1578+
readPixels(imageInfo: ImageInfo, srcX: number, srcY: number, dest?: MallocObj): Uint8Array | Float32Array | null;
15721579

15731580
/**
15741581
* Return the width in pixels of the image.

modules/canvaskit/interface.js

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -920,7 +920,7 @@ CanvasKit.onRuntimeInitialized = function() {
920920
return this._makeShader(xTileMode, yTileMode, localMatrixPtr);
921921
};
922922

923-
CanvasKit.Image.prototype.readPixels = function(imageInfo, srcX, srcY) {
923+
CanvasKit.Image.prototype.readPixels = function(imageInfo, srcX, srcY, destMallocObj) {
924924
var rowBytes;
925925
// Important to use ['string'] notation here, otherwise the closure compiler will
926926
// minify away the colorType.
@@ -936,13 +936,26 @@ CanvasKit.onRuntimeInitialized = function() {
936936
return;
937937
}
938938
var pBytes = rowBytes * imageInfo.height;
939-
var pPtr = CanvasKit._malloc(pBytes);
939+
var pPtr;
940+
if (destMallocObj) {
941+
pPtr = destMallocObj['byteOffset'];
942+
} else {
943+
pPtr = CanvasKit._malloc(pBytes);
944+
}
940945

941946
if (!this._readPixels(imageInfo, pPtr, rowBytes, srcX, srcY)) {
942947
Debug('Could not read pixels with the given inputs');
948+
if (!destMallocObj) {
949+
CanvasKit._free(pPtr);
950+
}
943951
return null;
944952
}
945953

954+
// If the user provided us a buffer to copy into, we don't need to allocate a new TypedArray.
955+
if (destMallocObj) {
956+
return destMallocObj['toTypedArray'](); // Return the typed array wrapper w/o allocating.
957+
}
958+
946959
// Put those pixels into a typed array of the right format and then
947960
// make a copy with slice() that we can return.
948961
var retVal = null;
@@ -1163,9 +1176,10 @@ CanvasKit.onRuntimeInitialized = function() {
11631176
return rv;
11641177
};
11651178

1166-
// returns Uint8Array
1179+
// TODO(kjlubick) align this API with Image.readPixels
11671180
CanvasKit.Canvas.prototype.readPixels = function(x, y, w, h, alphaType,
1168-
colorType, colorSpace, dstRowBytes) {
1181+
colorType, colorSpace, dstRowBytes,
1182+
destMallocObj) {
11691183
// supply defaults (which are compatible with HTMLCanvas's getImageData)
11701184
alphaType = alphaType || CanvasKit.AlphaType.Unpremul;
11711185
colorType = colorType || CanvasKit.ColorType.RGBA_8888;
@@ -1176,24 +1190,37 @@ CanvasKit.onRuntimeInitialized = function() {
11761190
}
11771191
dstRowBytes = dstRowBytes || (pixBytes * w);
11781192

1179-
var len = h * dstRowBytes
1180-
var pptr = CanvasKit._malloc(len);
1193+
var len = h * dstRowBytes;
1194+
var pPtr;
1195+
if (destMallocObj) {
1196+
pPtr = destMallocObj['byteOffset'];
1197+
} else {
1198+
pPtr = CanvasKit._malloc(len);
1199+
}
1200+
11811201
var ok = this._readPixels({
11821202
'width': w,
11831203
'height': h,
11841204
'colorType': colorType,
11851205
'alphaType': alphaType,
11861206
'colorSpace': colorSpace,
1187-
}, pptr, dstRowBytes, x, y);
1207+
}, pPtr, dstRowBytes, x, y);
11881208
if (!ok) {
1189-
CanvasKit._free(pptr);
1209+
if (!destMallocObj) {
1210+
CanvasKit._free(pPtr);
1211+
}
11901212
return null;
11911213
}
11921214

1215+
// If the user provided us a buffer to copy into, we don't need to allocate a new TypedArray.
1216+
if (destMallocObj) {
1217+
return destMallocObj['toTypedArray'](); // Return the typed array wrapper w/o allocating.
1218+
}
1219+
11931220
// The first typed array is just a view into memory. Because we will
11941221
// be free-ing that, we call slice to make a persistent copy.
1195-
var pixels = new Uint8Array(CanvasKit.HEAPU8.buffer, pptr, len).slice();
1196-
CanvasKit._free(pptr);
1222+
var pixels = new Uint8Array(CanvasKit.HEAPU8.buffer, pPtr, len).slice();
1223+
CanvasKit._free(pPtr);
11971224
return pixels;
11981225
};
11991226

modules/canvaskit/tests/core.spec.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,15 @@ describe('Core canvas behavior', () => {
152152
// requires 4 bytes (R, G, B, A).
153153
expect(pixels.length).toEqual(512 * 512 * 4);
154154

155+
// Make enough space for a 5x5 8888 surface (4 bytes for R, G, B, A)
156+
const rdsData = CanvasKit.Malloc(Uint8Array, 512 * 5*512 * 4);
157+
const pixels2 = rdsData.toTypedArray();
158+
pixels2[0] = 127; // sentinel value, should be overwritten by readPixels.
159+
img.readPixels(imageInfo, 0, 0, rdsData);
160+
expect(rdsData.toTypedArray()[0]).toEqual(pixels[0]);
161+
155162
img.delete();
163+
CanvasKit.Free(rdsData);
156164
done();
157165
})();
158166
});
@@ -888,9 +896,17 @@ describe('Core canvas behavior', () => {
888896
expect(CanvasKit.ColorSpace.Equals(info.colorSpace, colorSpace))
889897
.toBeTruthy("Surface not created with correct color space.");
890898

891-
const pixels = surface.getCanvas().readPixels(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
892-
CanvasKit.AlphaType.Unpremul, CanvasKit.ColorType.RGBA_8888, colorSpace);
899+
const mObj = CanvasKit.Malloc(Uint8Array, CANVAS_WIDTH * CANVAS_HEIGHT * 4);
900+
mObj.toTypedArray()[0] = 127; // sentinel value. Should be overwritten by readPixels.
901+
const canvas = surface.getCanvas();
902+
canvas.clear(CanvasKit.TRANSPARENT);
903+
const pixels = canvas.readPixels(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
904+
CanvasKit.AlphaType.Unpremul, CanvasKit.ColorType.RGBA_8888, colorSpace, null, mObj);
893905
expect(pixels).toBeTruthy('Could not read pixels from surface');
906+
expect(pixels[0] !== 127).toBeTruthy();
907+
expect(pixels[0]).toEqual(mObj.toTypedArray()[0]);
908+
CanvasKit.Free(mObj);
909+
surface.delete();
894910
});
895911
it('Can create a Display P3 surface', () => {
896912
const colorSpace = CanvasKit.ColorSpace.DISPLAY_P3;

0 commit comments

Comments
 (0)