skia2/modules/canvaskit/tests/core.spec.js

1062 lines
42 KiB
JavaScript
Raw Normal View History

describe('Core canvas behavior', () => {
let container;
beforeEach(async () => {
await LoadCanvasKit;
container = document.createElement('div');
container.innerHTML = `
<canvas width=600 height=600 id=test></canvas>
<canvas width=600 height=600 id=report></canvas>`;
document.body.appendChild(container);
});
afterEach(() => {
document.body.removeChild(container);
});
gm('picture_test', (canvas) => {
const spr = new CanvasKit.PictureRecorder();
const rcanvas = spr.beginRecording(
CanvasKit.LTRBRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT));
const paint = new CanvasKit.Paint();
paint.setStrokeWidth(2.0);
paint.setAntiAlias(true);
paint.setColor(CanvasKit.Color(0, 0, 0, 1.0));
paint.setStyle(CanvasKit.PaintStyle.Stroke);
rcanvas.drawRRect(CanvasKit.RRectXY([5, 35, 45, 80], 15, 10), paint);
const font = new CanvasKit.Font(null, 20);
rcanvas.drawText('this picture has a round rect', 5, 100, paint, font);
const pic = spr.finishRecordingAsPicture();
spr.delete();
paint.delete();
canvas.drawPicture(pic);
// test that file saving functionality throws no errors
// Unfortunately jasmine spy objects can't fake their type so we can't verify it downloads
// a nonzero sized file.
pic.saveAsFile('foo.skp');
pic.delete();
});
const uIntColorToCanvasKitColor = (c) => {
return CanvasKit.Color(
(c >> 16) & 0xFF,
(c >> 8) & 0xFF,
(c >> 0) & 0xFF,
((c >> 24) & 0xFF) / 255
);
}
it('can compute tonal colors', () => {
const input = {
ambient: CanvasKit.BLUE,
spot: CanvasKit.RED,
};
const out = CanvasKit.computeTonalColors(input);
expect(new Float32Array(out.ambient)).toEqual(CanvasKit.BLACK);
const expectedSpot = [0.173, 0, 0, 0.969];
expect(out.spot.length).toEqual(4);
expect(out.spot[0]).toBeCloseTo(expectedSpot[0], 3);
expect(out.spot[1]).toBeCloseTo(expectedSpot[1], 3);
expect(out.spot[2]).toBeCloseTo(expectedSpot[2], 3);
expect(out.spot[3]).toBeCloseTo(expectedSpot[3], 3);
});
it('can compute tonal colors with malloced values', () => {
const ambientColor = CanvasKit.Malloc(Float32Array, 4);
ambientColor.toTypedArray().set(CanvasKit.BLUE);
const spotColor = CanvasKit.Malloc(Float32Array, 4);
spotColor.toTypedArray().set(CanvasKit.RED);
const input = {
ambient: ambientColor,
spot: spotColor,
};
const out = CanvasKit.computeTonalColors(input);
expect(new Float32Array(out.ambient)).toEqual(CanvasKit.BLACK);
const expectedSpot = [0.173, 0, 0, 0.969];
expect(out.spot.length).toEqual(4);
expect(out.spot[0]).toBeCloseTo(expectedSpot[0], 3);
expect(out.spot[1]).toBeCloseTo(expectedSpot[1], 3);
expect(out.spot[2]).toBeCloseTo(expectedSpot[2], 3);
expect(out.spot[3]).toBeCloseTo(expectedSpot[3], 3);
});
// This helper is used for all the MakeImageFromEncoded tests.
// TODO(kjlubick): rewrite this and callers to use gm
function decodeAndDrawSingleFrameImage(imgName, goldName, done) {
const imgPromise = fetch(imgName)
.then((response) => response.arrayBuffer());
Promise.all([imgPromise, LoadCanvasKit]).then((values) => {
const imgData = values[0];
expect(imgData).toBeTruthy();
catchException(done, () => {
let img = CanvasKit.MakeImageFromEncoded(imgData);
expect(img).toBeTruthy();
const surface = CanvasKit.MakeCanvasSurface('test');
expect(surface).toBeTruthy('Could not make surface');
if (!surface) {
done();
return;
}
const canvas = surface.getCanvas();
let paint = new CanvasKit.Paint();
canvas.drawImage(img, 0, 0, paint);
paint.delete();
img.delete();
reportSurface(surface, goldName, done);
})();
});
}
it('can decode and draw a png', (done) => {
decodeAndDrawSingleFrameImage('/assets/mandrill_512.png', 'drawImage_png', done);
});
it('can decode and draw a jpg', (done) => {
decodeAndDrawSingleFrameImage('/assets/mandrill_h1v1.jpg', 'drawImage_jpg', done);
});
it('can decode and draw a (still) gif', (done) => {
decodeAndDrawSingleFrameImage('/assets/flightAnim.gif', 'drawImage_gif', done);
});
it('can decode and draw a still webp', (done) => {
decodeAndDrawSingleFrameImage('/assets/color_wheel.webp', 'drawImage_webp', done);
});
it('can readPixels from an Image', (done) => {
const imgPromise = fetch('/assets/mandrill_512.png')
.then((response) => response.arrayBuffer());
Promise.all([imgPromise, LoadCanvasKit]).then((values) => {
const imgData = values[0];
expect(imgData).toBeTruthy();
catchException(done, () => {
let img = CanvasKit.MakeImageFromEncoded(imgData);
expect(img).toBeTruthy();
const imageInfo = {
alphaType: CanvasKit.AlphaType.Unpremul,
colorType: CanvasKit.ColorType.RGBA_8888,
colorSpace: CanvasKit.ColorSpace.SRGB,
width: img.width(),
height: img.height(),
};
const pixels = img.readPixels(imageInfo, 0, 0);
// We know the image is 512 by 512 pixels in size, each pixel
// requires 4 bytes (R, G, B, A).
expect(pixels.length).toEqual(512 * 512 * 4);
img.delete();
done();
})();
});
});
gm('drawDrawable_animated_gif', (canvas, fetchedByteBuffers) => {
let aImg = CanvasKit.MakeAnimatedImageFromEncoded(fetchedByteBuffers[0]);
expect(aImg).toBeTruthy();
expect(aImg.getRepetitionCount()).toEqual(-1); // infinite loop
expect(aImg.width()).toEqual(320);
expect(aImg.height()).toEqual(240);
expect(aImg.getFrameCount()).toEqual(60);
let img = aImg.makeImageAtCurrentFrame();
canvas.drawImage(img, 0, 0, null);
img.delete(); // This is annoying, so we added drawImageAtCurrentFrame
let c = aImg.decodeNextFrame();
expect(c).not.toEqual(-1);
canvas.drawImageAtCurrentFrame(aImg, 300, 0, null);
for(let i = 0; i < 10; i++) {
c = aImg.decodeNextFrame();
expect(c).not.toEqual(-1);
}
canvas.drawImageAtCurrentFrame(aImg, 0, 300, null);
for(let i = 0; i < 10; i++) {
c = aImg.decodeNextFrame();
expect(c).not.toEqual(-1);
}
canvas.drawImageAtCurrentFrame(aImg, 300, 300, null);
aImg.delete();
}, '/assets/flightAnim.gif');
gm('1x4_from_scratch', (canvas) => {
canvas.clear(CanvasKit.WHITE);
const paint = new CanvasKit.Paint();
// This creates and draws an Image that is 1 pixel wide, 4 pixels tall with
// the colors listed below.
const pixels = Uint8Array.from([
255, 0, 0, 255, // opaque red
0, 255, 0, 255, // opaque green
0, 0, 255, 255, // opaque blue
255, 0, 255, 100, // transparent purple
]);
const img = CanvasKit.MakeImage(pixels, 1, 4, CanvasKit.AlphaType.Unpremul, CanvasKit.ColorType.RGBA_8888,
CanvasKit.ColorSpace.SRGB);
canvas.drawImage(img, 1, 1, paint);
img.delete();
paint.delete();
});
gm('draw_atlas_with_builders', (canvas, fetchedByteBuffers) => {
const atlas = CanvasKit.MakeImageFromEncoded(fetchedByteBuffers[0]);
expect(atlas).toBeTruthy();
canvas.clear(CanvasKit.WHITE);
const paint = new CanvasKit.Paint();
paint.setColor(CanvasKit.Color(0, 0, 0, 0.8));
const srcs = new CanvasKit.RectBuilder();
// left top right bottom
srcs.push( 0, 0, 256, 256);
srcs.push(256, 0, 512, 256);
srcs.push( 0, 256, 256, 512);
srcs.push(256, 256, 512, 512);
const dsts = new CanvasKit.RSXFormBuilder();
// scos, ssin, tx, ty
dsts.push(0.5, 0, 20, 20);
dsts.push(0.5, 0, 300, 20);
dsts.push(0.5, 0, 20, 300);
dsts.push(0.5, 0, 300, 300);
const colors = new CanvasKit.ColorBuilder();
// note that the ColorBuilder expects int colors to be pushed.
// pushing float colors to it only causes weird problems way downstream.
// It does no type checking.
colors.push(CanvasKit.ColorAsInt( 85, 170, 10, 128)); // light green
colors.push(CanvasKit.ColorAsInt( 51, 51, 191, 128)); // light blue
colors.push(CanvasKit.ColorAsInt( 0, 0, 0, 128));
colors.push(CanvasKit.ColorAsInt(256, 256, 256, 128));
canvas.drawAtlas(atlas, srcs, dsts, paint, CanvasKit.BlendMode.Modulate, colors);
atlas.delete();
paint.delete();
}, '/assets/mandrill_512.png')
gm('draw_atlas_with_arrays', (canvas, fetchedByteBuffers) => {
const atlas = CanvasKit.MakeImageFromEncoded(fetchedByteBuffers[0]);
expect(atlas).toBeTruthy();
canvas.clear(CanvasKit.WHITE);
const paint = new CanvasKit.Paint();
paint.setColor(CanvasKit.Color(0, 0, 0, 0.8));
const srcs = [
0, 0, 256, 256,
256, 0, 512, 256,
0, 256, 256, 512,
256, 256, 512, 512,
];
const dsts = [
0.5, 0, 20, 20,
0.5, 0, 300, 20,
0.5, 0, 20, 300,
0.5, 0, 300, 300,
];
const colors = Uint32Array.of(
CanvasKit.ColorAsInt( 85, 170, 10, 128), // light green
CanvasKit.ColorAsInt( 51, 51, 191, 128), // light blue
CanvasKit.ColorAsInt( 0, 0, 0, 128),
CanvasKit.ColorAsInt(256, 256, 256, 128),
);
canvas.drawAtlas(atlas, srcs, dsts, paint, CanvasKit.BlendMode.Modulate, colors);
atlas.delete();
paint.delete();
Added CanvasKit.MakeImageFromCanvasImageSource which is useful as an alternative to CanvasKit.MakeImageFromEncoded, when used with Browser APIs for loading/decoding images. - `CanvasKit.MakeImageFromCanvasImageSource` takes either an HTMLImageElement, SVGImageElement, HTMLVideoElement, HTMLCanvasElement, ImageBitmap, or OffscreenCanvas and returns an SkImage. This function is an alternative to `CanvasKit.MakeImageFromEncoded` for creating SkImages when loading and decoding images. In the future, codesize of CanvasKit may be able to be reduced by removing image codecs in wasm, if browser APIs for decoding images are used along with `CanvasKit.MakeImageFromCanvasImageSource` instead of `CanvasKit.MakeImageFromEncoded`. - Three usage examples of `CanvasKit.MakeImageFromCanvasImageSource` in core.spec.ts. These examples use browser APIs to decode images including 2d canvas, bitmaprenderer canvas, HTMLImageElement and Blob. - Added support for asynchronous callbacks in perfs and tests. Here are notes on the image decoding approaches we tested and perfed in the process of finding ways to use Browser APIs to decode images: 1. pipeline: ArrayBuffer → ImageData → ctx.putImageData → context.getImageData → Uint8Array → CanvasKit.MakeImage ❌ Problem: ImageData constructor expects decoded bytes already. 2. interface.js - CanvasKit.ExperimentalCanvas2DMakeImageFromEncoded (async function) pipeline: ArrayBuffer → Blob -> HTMLImageElement -> draw on Canvas2d -> context.getImageData → Uint8Array → CanvasKit.MakeImage ✅ Works ⏱ Performance: 3rd place (in my testing locally) 3. interface.js - CanvasKit.ExperimentalCanvas2DMakeImageFromEncoded2 (async function) ArrayBuffer → Blob → ImageBitmap → draw on Canvas2d → context.getImageData → Uint8Array → CanvasKit.MakeImage ✅ Works ⏱ Performance: 2nd place (in my testing locally) 4. interface.js - CanvasKit.ExperimentalCanvas2DMakeImageFromEncoded3 (async function) ArrayBuffer → Blob → ImageBitmap → draw on canvas 1 using bitmaprenderer context → draw canvas 1 on canvas 2 using drawImage → context2d.getImageData → Uint8Array → CanvasKit.MakeImage ✅ Works ⏱ Performance: 1st place (in my testing locally) - quite surprising, this in some ways seems to be a more roundabout way of CanvasKit.ExperimentalCanvas2DMakeImageFromEncoded2, but it seems bitmaprenderer context is fairly fast. Bug: skia:10360 Change-Id: I6fe94b8196dfd1ad0d8929f04bb1697da537ca18 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/295390 Reviewed-by: Kevin Lubick <kjlubick@google.com>
2020-06-15 18:53:27 +00:00
}, '/assets/mandrill_512.png');
gm('image_decoding_methods', async (canvas) => {
canvas.clear(CanvasKit.WHITE);
const IMAGE_FILE_PATHS = [
'/assets/brickwork-texture.jpg',
'/assets/mandrill_512.png',
'/assets/color_wheel.gif'
];
let row = 1;
// Test 4 different methods of decoding an image for each of the three images in
// IMAGE_FILE_PATHS.
// Resulting Images are drawn to visually show that all methods decode correctly.
Added CanvasKit.MakeImageFromCanvasImageSource which is useful as an alternative to CanvasKit.MakeImageFromEncoded, when used with Browser APIs for loading/decoding images. - `CanvasKit.MakeImageFromCanvasImageSource` takes either an HTMLImageElement, SVGImageElement, HTMLVideoElement, HTMLCanvasElement, ImageBitmap, or OffscreenCanvas and returns an SkImage. This function is an alternative to `CanvasKit.MakeImageFromEncoded` for creating SkImages when loading and decoding images. In the future, codesize of CanvasKit may be able to be reduced by removing image codecs in wasm, if browser APIs for decoding images are used along with `CanvasKit.MakeImageFromCanvasImageSource` instead of `CanvasKit.MakeImageFromEncoded`. - Three usage examples of `CanvasKit.MakeImageFromCanvasImageSource` in core.spec.ts. These examples use browser APIs to decode images including 2d canvas, bitmaprenderer canvas, HTMLImageElement and Blob. - Added support for asynchronous callbacks in perfs and tests. Here are notes on the image decoding approaches we tested and perfed in the process of finding ways to use Browser APIs to decode images: 1. pipeline: ArrayBuffer → ImageData → ctx.putImageData → context.getImageData → Uint8Array → CanvasKit.MakeImage ❌ Problem: ImageData constructor expects decoded bytes already. 2. interface.js - CanvasKit.ExperimentalCanvas2DMakeImageFromEncoded (async function) pipeline: ArrayBuffer → Blob -> HTMLImageElement -> draw on Canvas2d -> context.getImageData → Uint8Array → CanvasKit.MakeImage ✅ Works ⏱ Performance: 3rd place (in my testing locally) 3. interface.js - CanvasKit.ExperimentalCanvas2DMakeImageFromEncoded2 (async function) ArrayBuffer → Blob → ImageBitmap → draw on Canvas2d → context.getImageData → Uint8Array → CanvasKit.MakeImage ✅ Works ⏱ Performance: 2nd place (in my testing locally) 4. interface.js - CanvasKit.ExperimentalCanvas2DMakeImageFromEncoded3 (async function) ArrayBuffer → Blob → ImageBitmap → draw on canvas 1 using bitmaprenderer context → draw canvas 1 on canvas 2 using drawImage → context2d.getImageData → Uint8Array → CanvasKit.MakeImage ✅ Works ⏱ Performance: 1st place (in my testing locally) - quite surprising, this in some ways seems to be a more roundabout way of CanvasKit.ExperimentalCanvas2DMakeImageFromEncoded2, but it seems bitmaprenderer context is fairly fast. Bug: skia:10360 Change-Id: I6fe94b8196dfd1ad0d8929f04bb1697da537ca18 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/295390 Reviewed-by: Kevin Lubick <kjlubick@google.com>
2020-06-15 18:53:27 +00:00
for (const imageFilePath of IMAGE_FILE_PATHS) {
const response = await fetch(imageFilePath);
const arrayBuffer = await response.arrayBuffer();
// response.blob() is preferable when you don't need both a Blob *and* an ArrayBuffer.
const blob = new Blob([ arrayBuffer ]);
// Method 1 - decode TypedArray using wasm codecs:
const skImage1 = CanvasKit.MakeImageFromEncoded(arrayBuffer);
// Method 2 (slower and does not work in Safari) decode using ImageBitmap:
const imageBitmap = await createImageBitmap(blob);
// Testing showed that transferring an ImageBitmap to a canvas using the 'bitmaprenderer'
// context and passing that canvas to CanvasKit.MakeImageFromCanvasImageSource() is
// marginally faster than passing ImageBitmap to
// CanvasKit.MakeImageFromCanvasImageSource() directly.
const canvasBitmapElement = document.createElement('canvas');
canvasBitmapElement.width = imageBitmap.width;
canvasBitmapElement.height = imageBitmap.height;
const ctxBitmap = canvasBitmapElement.getContext('bitmaprenderer');
ctxBitmap.transferFromImageBitmap(imageBitmap);
const skImage2 = CanvasKit.MakeImageFromCanvasImageSource(canvasBitmapElement);
// Method 3 (slowest) decode using HTMLImageElement directly:
const image = new Image();
// Testing showed that waiting for a load event is faster than waiting on image.decode()
// HTMLImageElement.decode() reference: https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decode
const promise1 = new Promise((resolve) => image.addEventListener('load', resolve));
image.src = imageFilePath;
await promise1;
const skImage3 = CanvasKit.MakeImageFromCanvasImageSource(image);
// Method 4 (roundabout, but works if all you have is a Blob) decode from Blob using
// HTMLImageElement:
const imageObjectUrl = URL.createObjectURL( blob );
const image2 = new Image();
const promise2 = new Promise((resolve) => image2.addEventListener('load', resolve));
image2.src = imageObjectUrl;
await promise2;
const skImage4 = CanvasKit.MakeImageFromCanvasImageSource(image2);
// Draw decoded images
const sourceRect = CanvasKit.XYWHRect(0,0, 150, 150);
canvas.drawImageRect(skImage1, sourceRect, CanvasKit.XYWHRect(0,row * 100, 90, 90), null, false);
canvas.drawImageRect(skImage2, sourceRect, CanvasKit.XYWHRect(100,row * 100, 90, 90), null, false);
canvas.drawImageRect(skImage3, sourceRect, CanvasKit.XYWHRect(200,row * 100, 90, 90), null, false);
canvas.drawImageRect(skImage4, sourceRect, CanvasKit.XYWHRect(300,row * 100, 90, 90), null, false);
row++;
}
//Label images with the method used to decode them
const paint = new CanvasKit.Paint();
const textFont = new CanvasKit.Font(null, 7);
Added CanvasKit.MakeImageFromCanvasImageSource which is useful as an alternative to CanvasKit.MakeImageFromEncoded, when used with Browser APIs for loading/decoding images. - `CanvasKit.MakeImageFromCanvasImageSource` takes either an HTMLImageElement, SVGImageElement, HTMLVideoElement, HTMLCanvasElement, ImageBitmap, or OffscreenCanvas and returns an SkImage. This function is an alternative to `CanvasKit.MakeImageFromEncoded` for creating SkImages when loading and decoding images. In the future, codesize of CanvasKit may be able to be reduced by removing image codecs in wasm, if browser APIs for decoding images are used along with `CanvasKit.MakeImageFromCanvasImageSource` instead of `CanvasKit.MakeImageFromEncoded`. - Three usage examples of `CanvasKit.MakeImageFromCanvasImageSource` in core.spec.ts. These examples use browser APIs to decode images including 2d canvas, bitmaprenderer canvas, HTMLImageElement and Blob. - Added support for asynchronous callbacks in perfs and tests. Here are notes on the image decoding approaches we tested and perfed in the process of finding ways to use Browser APIs to decode images: 1. pipeline: ArrayBuffer → ImageData → ctx.putImageData → context.getImageData → Uint8Array → CanvasKit.MakeImage ❌ Problem: ImageData constructor expects decoded bytes already. 2. interface.js - CanvasKit.ExperimentalCanvas2DMakeImageFromEncoded (async function) pipeline: ArrayBuffer → Blob -> HTMLImageElement -> draw on Canvas2d -> context.getImageData → Uint8Array → CanvasKit.MakeImage ✅ Works ⏱ Performance: 3rd place (in my testing locally) 3. interface.js - CanvasKit.ExperimentalCanvas2DMakeImageFromEncoded2 (async function) ArrayBuffer → Blob → ImageBitmap → draw on Canvas2d → context.getImageData → Uint8Array → CanvasKit.MakeImage ✅ Works ⏱ Performance: 2nd place (in my testing locally) 4. interface.js - CanvasKit.ExperimentalCanvas2DMakeImageFromEncoded3 (async function) ArrayBuffer → Blob → ImageBitmap → draw on canvas 1 using bitmaprenderer context → draw canvas 1 on canvas 2 using drawImage → context2d.getImageData → Uint8Array → CanvasKit.MakeImage ✅ Works ⏱ Performance: 1st place (in my testing locally) - quite surprising, this in some ways seems to be a more roundabout way of CanvasKit.ExperimentalCanvas2DMakeImageFromEncoded2, but it seems bitmaprenderer context is fairly fast. Bug: skia:10360 Change-Id: I6fe94b8196dfd1ad0d8929f04bb1697da537ca18 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/295390 Reviewed-by: Kevin Lubick <kjlubick@google.com>
2020-06-15 18:53:27 +00:00
canvas.drawText('WASM Decoding', 0, 90, paint, textFont);
canvas.drawText('ImageBitmap Decoding', 100, 90, paint, textFont);
canvas.drawText('HTMLImageEl Decoding', 200, 90, paint, textFont);
canvas.drawText('Blob Decoding', 300, 90, paint, textFont);
});
gm('sweep_gradient', (canvas) => {
const paint = new CanvasKit.Paint();
const shader = CanvasKit.Shader.MakeSweepGradient(
100, 100, // X, Y coordinates
[CanvasKit.GREEN, CanvasKit.BLUE],
[0.0, 1.0],
CanvasKit.TileMode.Clamp,
);
expect(shader).toBeTruthy('Could not make shader');
paint.setShader(shader);
canvas.drawPaint(paint);
paint.delete();
shader.delete();
});
// TODO(kjlubick): There's a lot of shared code between the gradient gms
// It would be best to deduplicate that in a nice DAMP way.
// Inspired by https://fiddle.skia.org/c/b29ce50a341510784ac7d5281586d076
gm('linear_gradients', (canvas) => {
canvas.clear(CanvasKit.WHITE);
canvas.scale(2, 2);
const strokePaint = new CanvasKit.Paint();
strokePaint.setStyle(CanvasKit.PaintStyle.Stroke);
strokePaint.setColor(CanvasKit.BLACK);
const paint = new CanvasKit.Paint();
paint.setStyle(CanvasKit.PaintStyle.Fill);
const transparentGreen = CanvasKit.Color(0, 255, 255, 0);
const lgs = CanvasKit.Shader.MakeLinearGradient(
[0, 0], [50, 100], // start and stop points
[transparentGreen, CanvasKit.BLUE, CanvasKit.RED],
[0, 0.65, 1.0],
CanvasKit.TileMode.Mirror
);
paint.setShader(lgs);
let r = CanvasKit.LTRBRect(0, 0, 100, 100);
canvas.drawRect(r, paint);
canvas.drawRect(r, strokePaint);
const lgsPremul = CanvasKit.Shader.MakeLinearGradient(
[100, 0], [150, 100], // start and stop points
Uint32Array.of(
CanvasKit.ColorAsInt(0, 255, 255, 0),
CanvasKit.ColorAsInt(0, 0, 255, 255),
CanvasKit.ColorAsInt(255, 0, 0, 255)),
[0, 0.65, 1.0],
CanvasKit.TileMode.Mirror,
null, // no local matrix
1 // interpolate colors in premul
);
paint.setShader(lgsPremul);
r = CanvasKit.LTRBRect(100, 0, 200, 100);
canvas.drawRect(r, paint);
canvas.drawRect(r, strokePaint);
const lgs45 = CanvasKit.Shader.MakeLinearGradient(
[0, 100], [50, 200], // start and stop points
Float32Array.of(...transparentGreen, ...CanvasKit.BLUE, ...CanvasKit.RED),
[0, 0.65, 1.0],
CanvasKit.TileMode.Mirror,
CanvasKit.Matrix.rotated(Math.PI/4, 0, 100),
);
paint.setShader(lgs45);
r = CanvasKit.LTRBRect(0, 100, 100, 200);
canvas.drawRect(r, paint);
canvas.drawRect(r, strokePaint);
// malloc'd color array
const colors = CanvasKit.Malloc(Float32Array, 12);
const typedColorsArray = colors.toTypedArray();
typedColorsArray.set(transparentGreen, 0);
typedColorsArray.set(CanvasKit.BLUE, 4);
typedColorsArray.set(CanvasKit.RED, 8);
const lgs45Premul = CanvasKit.Shader.MakeLinearGradient(
[100, 100], [150, 200], // start and stop points
typedColorsArray,
[0, 0.65, 1.0],
CanvasKit.TileMode.Mirror,
CanvasKit.Matrix.rotated(Math.PI/4, 100, 100),
1 // interpolate colors in premul
);
CanvasKit.Free(colors);
paint.setShader(lgs45Premul);
r = CanvasKit.LTRBRect(100, 100, 200, 200);
canvas.drawRect(r, paint);
canvas.drawRect(r, strokePaint);
lgs.delete();
lgs45.delete();
lgsPremul.delete();
lgs45Premul.delete();
strokePaint.delete();
paint.delete();
});
gm('radial_gradients', (canvas) => {
canvas.clear(CanvasKit.WHITE);
canvas.scale(2, 2);
const strokePaint = new CanvasKit.Paint();
strokePaint.setStyle(CanvasKit.PaintStyle.Stroke);
strokePaint.setColor(CanvasKit.BLACK);
const paint = new CanvasKit.Paint();
paint.setStyle(CanvasKit.PaintStyle.Fill);
const transparentGreen = CanvasKit.Color(0, 255, 255, 0);
const rgs = CanvasKit.Shader.MakeRadialGradient(
[50, 50], 50, // center, radius
[transparentGreen, CanvasKit.BLUE, CanvasKit.RED],
[0, 0.65, 1.0],
CanvasKit.TileMode.Mirror
);
paint.setShader(rgs);
let r = CanvasKit.LTRBRect(0, 0, 100, 100);
canvas.drawRect(r, paint);
canvas.drawRect(r, strokePaint);
const rgsPremul = CanvasKit.Shader.MakeRadialGradient(
[150, 50], 50, // center, radius
[transparentGreen, CanvasKit.BLUE, CanvasKit.RED],
[0, 0.65, 1.0],
CanvasKit.TileMode.Mirror,
null, // no local matrix
1 // interpolate colors in premul
);
paint.setShader(rgsPremul);
r = CanvasKit.LTRBRect(100, 0, 200, 100);
canvas.drawRect(r, paint);
canvas.drawRect(r, strokePaint);
const rgsSkew = CanvasKit.Shader.MakeRadialGradient(
[50, 150], 50, // center, radius
[transparentGreen, CanvasKit.BLUE, CanvasKit.RED],
[0, 0.65, 1.0],
CanvasKit.TileMode.Mirror,
CanvasKit.Matrix.skewed(0.5, 0, 100, 100),
null, // color space
);
paint.setShader(rgsSkew);
r = CanvasKit.LTRBRect(0, 100, 100, 200);
canvas.drawRect(r, paint);
canvas.drawRect(r, strokePaint);
const rgsSkewPremul = CanvasKit.Shader.MakeRadialGradient(
[150, 150], 50, // center, radius
[transparentGreen, CanvasKit.BLUE, CanvasKit.RED],
[0, 0.65, 1.0],
CanvasKit.TileMode.Mirror,
CanvasKit.Matrix.skewed(0.5, 0, 100, 100),
1, // interpolate colors in premul
null, // color space
);
paint.setShader(rgsSkewPremul);
r = CanvasKit.LTRBRect(100, 100, 200, 200);
canvas.drawRect(r, paint);
canvas.drawRect(r, strokePaint);
rgs.delete();
rgsPremul.delete();
rgsSkew.delete();
rgsSkewPremul.delete();
strokePaint.delete();
paint.delete();
});
gm('conical_gradients', (canvas) => {
canvas.clear(CanvasKit.WHITE);
canvas.scale(2, 2);
const strokePaint = new CanvasKit.Paint();
strokePaint.setStyle(CanvasKit.PaintStyle.Stroke);
strokePaint.setColor(CanvasKit.BLACK);
const paint = new CanvasKit.Paint();
paint.setStyle(CanvasKit.PaintStyle.Fill);
paint.setAntiAlias(true);
const transparentGreen = CanvasKit.Color(0, 255, 255, 0);
const cgs = CanvasKit.Shader.MakeTwoPointConicalGradient(
[80, 10], 15, // start, radius
[10, 110], 60, // end, radius
[transparentGreen, CanvasKit.BLUE, CanvasKit.RED],
[0, 0.65, 1.0],
CanvasKit.TileMode.Mirror,
null, // no local matrix
);
paint.setShader(cgs);
let r = CanvasKit.LTRBRect(0, 0, 100, 100);
canvas.drawRect(r, paint);
canvas.drawRect(r, strokePaint);
const cgsPremul = CanvasKit.Shader.MakeTwoPointConicalGradient(
[180, 10], 15, // start, radius
[110, 110], 60, // end, radius
[transparentGreen, CanvasKit.BLUE, CanvasKit.RED],
[0, 0.65, 1.0],
CanvasKit.TileMode.Mirror,
null, // no local matrix
1, // interpolate colors in premul
null, // color space
);
paint.setShader(cgsPremul);
r = CanvasKit.LTRBRect(100, 0, 200, 100);
canvas.drawRect(r, paint);
canvas.drawRect(r, strokePaint);
const cgs45 = CanvasKit.Shader.MakeTwoPointConicalGradient(
[80, 110], 15, // start, radius
[10, 210], 60, // end, radius
[transparentGreen, CanvasKit.BLUE, CanvasKit.RED],
[0, 0.65, 1.0],
CanvasKit.TileMode.Mirror,
CanvasKit.Matrix.rotated(Math.PI/4, 0, 100),
null, // color space
);
paint.setShader(cgs45);
r = CanvasKit.LTRBRect(0, 100, 100, 200);
canvas.drawRect(r, paint);
canvas.drawRect(r, strokePaint);
const cgs45Premul = CanvasKit.Shader.MakeTwoPointConicalGradient(
[180, 110], 15, // start, radius
[110, 210], 60, // end, radius
[transparentGreen, CanvasKit.BLUE, CanvasKit.RED],
[0, 0.65, 1.0],
CanvasKit.TileMode.Mirror,
CanvasKit.Matrix.rotated(Math.PI/4, 100, 100),
1, // interpolate colors in premul
null, // color space
);
paint.setShader(cgs45Premul);
r = CanvasKit.LTRBRect(100, 100, 200, 200);
canvas.drawRect(r, paint);
canvas.drawRect(r, strokePaint);
cgs.delete();
cgsPremul.delete();
cgs45.delete();
strokePaint.delete();
paint.delete();
});
gm('blur_filters', (canvas) => {
const pathUL = starPath(CanvasKit, 100, 100, 80);
const pathBR = starPath(CanvasKit, 400, 300, 80);
const paint = new CanvasKit.Paint();
const textFont = new CanvasKit.Font(null, 24);
canvas.drawText('Above: MaskFilter', 20, 220, paint, textFont);
canvas.drawText('Right: ImageFilter', 20, 260, paint, textFont);
paint.setColor(CanvasKit.BLUE);
const blurMask = CanvasKit.MaskFilter.MakeBlur(CanvasKit.BlurStyle.Normal, 5, true);
paint.setMaskFilter(blurMask);
canvas.drawPath(pathUL, paint);
const blurIF = CanvasKit.ImageFilter.MakeBlur(8, 1, CanvasKit.TileMode.Decal, null);
paint.setImageFilter(blurIF);
canvas.drawPath(pathBR, paint);
pathUL.delete();
pathBR.delete();
paint.delete();
blurMask.delete();
blurIF.delete();
textFont.delete();
});
gm('combined_filters', (canvas, fetchedByteBuffers) => {
const img = CanvasKit.MakeImageFromEncoded(fetchedByteBuffers[0]);
expect(img).toBeTruthy();
canvas.clear(CanvasKit.WHITE);
const paint = new CanvasKit.Paint();
paint.setAntiAlias(true);
paint.setColor(CanvasKit.Color(0, 255, 0, 1.0));
const redCF = CanvasKit.ColorFilter.MakeBlend(
CanvasKit.Color(255, 0, 0, 0.1), CanvasKit.BlendMode.SrcOver);
const redIF = CanvasKit.ImageFilter.MakeColorFilter(redCF, null);
const blurIF = CanvasKit.ImageFilter.MakeBlur(8, 0.2, CanvasKit.TileMode.Decal, null);
const combined = CanvasKit.ImageFilter.MakeCompose(redIF, blurIF);
// rotate 10 degrees centered on 200, 200
const m = CanvasKit.Matrix.rotated(Math.PI/18, 200, 200);
const rotated = CanvasKit.ImageFilter.MakeMatrixTransform(m, CanvasKit.FilterQuality.Medium, combined);
paint.setImageFilter(rotated);
//canvas.rotate(10, 200, 200);
canvas.drawImage(img, 0, 0, paint);
canvas.drawRect(CanvasKit.LTRBRect(5, 35, 45, 80), paint);
paint.delete();
redIF.delete();
redCF.delete();
blurIF.delete();
combined.delete();
rotated.delete();
img.delete();
}, '/assets/mandrill_512.png');
gm('animated_filters', (canvas, fetchedByteBuffers) => {
const img = CanvasKit.MakeAnimatedImageFromEncoded(fetchedByteBuffers[0]);
expect(img).toBeTruthy();
img.decodeNextFrame();
img.decodeNextFrame();
canvas.clear(CanvasKit.WHITE);
const paint = new CanvasKit.Paint();
paint.setAntiAlias(true);
paint.setColor(CanvasKit.Color(0, 255, 0, 1.0));
const redCF = CanvasKit.ColorFilter.MakeBlend(
CanvasKit.Color(255, 0, 0, 0.1), CanvasKit.BlendMode.SrcOver);
const redIF = CanvasKit.ImageFilter.MakeColorFilter(redCF, null);
const blurIF = CanvasKit.ImageFilter.MakeBlur(8, 0.2, CanvasKit.TileMode.Decal, null);
const combined = CanvasKit.ImageFilter.MakeCompose(redIF, blurIF);
paint.setImageFilter(combined);
const frame = img.getCurrentFrame();
canvas.drawImage(frame, 100, 50, paint);
paint.delete();
redIF.delete();
redCF.delete();
blurIF.delete();
combined.delete();
frame.delete();
img.delete();
}, '/assets/flightAnim.gif');
gm('drawImage_skp', (canvas, fetchedByteBuffers) => {
const pic = CanvasKit.MakePicture(fetchedByteBuffers[0]);
expect(pic).toBeTruthy();
canvas.clear(CanvasKit.TRANSPARENT);
canvas.drawPicture(pic);
}, '/assets/red_line.skp');
it('can draw once using drawOnce utility method', (done) => {
const surface = CanvasKit.MakeCanvasSurface('test');
expect(surface).toBeTruthy('Could not make surface');
if (!surface) {
done();
return;
}
const drawFrame = (canvas) => {
const paint = new CanvasKit.Paint();
paint.setStrokeWidth(1.0);
paint.setAntiAlias(true);
paint.setColor(CanvasKit.Color(0, 0, 0, 1.0));
paint.setStyle(CanvasKit.PaintStyle.Stroke);
const path = new CanvasKit.Path();
path.moveTo(20, 5);
path.lineTo(30, 20);
path.lineTo(40, 10);
canvas.drawPath(path, paint);
path.delete();
paint.delete();
// surface hasn't been flushed yet (nor should we call flush
// ourselves), so reportSurface would likely be blank if we
// were to call it.
done();
}
surface.drawOnce(drawFrame);
// Reminder: drawOnce is async. In this test, we are just making
// sure the drawOnce function is there and doesn't crash, so we can
// just call done() when the frame is rendered.
});
it('can draw client-supplied dirty rects', (done) => {
// dirty rects are only honored by software (CPU) canvases today.
const surface = CanvasKit.MakeSWCanvasSurface('test');
expect(surface).toBeTruthy('Could not make surface');
if (!surface) {
done();
return;
}
const drawFrame = (canvas) => {
const paint = new CanvasKit.Paint();
paint.setStrokeWidth(1.0);
paint.setAntiAlias(true);
paint.setColor(CanvasKit.Color(0, 0, 0, 1.0));
paint.setStyle(CanvasKit.PaintStyle.Stroke);
const path = new CanvasKit.Path();
path.moveTo(20, 5);
path.lineTo(30, 20);
path.lineTo(40, 10);
canvas.drawPath(path, paint);
path.delete();
paint.delete();
done();
}
const dirtyRect = CanvasKit.XYWHRect(10, 10, 15, 15);
surface.drawOnce(drawFrame, dirtyRect);
// We simply ensure that passing a dirty rect doesn't crash.
});
it('can use DecodeCache APIs', () => {
const initialLimit = CanvasKit.getDecodeCacheLimitBytes();
expect(initialLimit).toBeGreaterThan(1024 * 1024);
const newLimit = 42 * 1024 * 1024;
CanvasKit.setDecodeCacheLimitBytes(newLimit);
expect(CanvasKit.getDecodeCacheLimitBytes()).toEqual(newLimit);
// We cannot make any assumptions about this value,
// so we just make sure it doesn't crash.
CanvasKit.getDecodeCacheUsedBytes();
});
gm('combined_shaders', (canvas) => {
const rShader = CanvasKit.Shader.Color(CanvasKit.Color(255, 0, 0, 1.0)); // deprecated
const gShader = CanvasKit.Shader.MakeColor(CanvasKit.Color(0, 255, 0, 0.6));
const bShader = CanvasKit.Shader.MakeColor(CanvasKit.Color(0, 0, 255, 1.0));
const rgShader = CanvasKit.Shader.MakeBlend(CanvasKit.BlendMode.SrcOver, rShader, gShader);
const p = new CanvasKit.Paint();
p.setShader(rgShader);
canvas.drawPaint(p);
const gbShader = CanvasKit.Shader.MakeLerp(0.5, gShader, bShader);
p.setShader(gbShader);
canvas.drawRect(CanvasKit.LTRBRect(5, 100, 300, 400), p);
rShader.delete();
gShader.delete();
bShader.delete();
rgShader.delete();
gbShader.delete();
p.delete();
});
it('exports consts correctly', () => {
expect(CanvasKit.TRANSPARENT).toEqual(Float32Array.of(0, 0, 0, 0));
expect(CanvasKit.RED).toEqual(Float32Array.of(1, 0, 0, 1));
expect(CanvasKit.QUAD_VERB).toEqual(2);
expect(CanvasKit.CONIC_VERB).toEqual(3);
expect(CanvasKit.SaveLayerInitWithPrevious).toEqual(4);
expect(CanvasKit.SaveLayerF16ColorType).toEqual(16);
});
it('can set color on a paint and get it as four floats', () => {
const paint = new CanvasKit.Paint();
paint.setColor(CanvasKit.Color4f(3.3, 2.2, 1.1, 0.5));
expect(paint.getColor()).toEqual(Float32Array.of(3.3, 2.2, 1.1, 0.5));
paint.setColorComponents(0.5, 0.6, 0.7, 0.8);
expect(paint.getColor()).toEqual(Float32Array.of(0.5, 0.6, 0.7, 0.8));
paint.setColorInt(CanvasKit.ColorAsInt(50, 100, 150, 200));
let color = paint.getColor();
expect(color.length).toEqual(4);
expect(color[0]).toBeCloseTo(50/255, 5); // Red
expect(color[1]).toBeCloseTo(100/255, 5); // Green
expect(color[2]).toBeCloseTo(150/255, 5); // Blue
expect(color[3]).toBeCloseTo(200/255, 5); // Alpha
paint.setColorInt(0xFF000000);
expect(paint.getColor()).toEqual(Float32Array.of(0, 0, 0, 1.0));
});
gm('draw shadow', (canvas) => {
const lightRadius = 20;
const flags = 0;
const lightPos = [500,500,20];
const zPlaneParams = [0,0,1];
const path = starPath(CanvasKit);
canvas.drawShadow(path, zPlaneParams, lightPos, lightRadius,
CanvasKit.BLACK, CanvasKit.MAGENTA, flags);
})
gm('fractal_noise_shader', (canvas) => {
const shader = CanvasKit.Shader.MakeFractalNoise(0.1, 0.05, 2, 0, 0, 0);
const paint = new CanvasKit.Paint();
paint.setColor(CanvasKit.BLACK);
paint.setShader(shader);
canvas.drawPaint(paint);
paint.delete();
shader.delete();
});
gm('turbulance_shader', (canvas) => {
const shader = CanvasKit.Shader.MakeTurbulence(0.1, 0.05, 2, 117, 0, 0);
const paint = new CanvasKit.Paint();
paint.setColor(CanvasKit.BLACK);
paint.setShader(shader);
canvas.drawPaint(paint);
paint.delete();
shader.delete();
});
gm('fractal_noise_tiled_shader', (canvas) => {
const shader = CanvasKit.Shader.MakeFractalNoise(0.1, 0.05, 2, 0, 80, 80);
const paint = new CanvasKit.Paint();
paint.setColor(CanvasKit.BLACK);
paint.setShader(shader);
canvas.drawPaint(paint);
paint.delete();
shader.delete();
});
gm('turbulance_tiled_shader', (canvas) => {
const shader = CanvasKit.Shader.MakeTurbulence(0.1, 0.05, 2, 117, 80, 80);
const paint = new CanvasKit.Paint();
paint.setColor(CanvasKit.BLACK);
paint.setShader(shader);
canvas.drawPaint(paint);
paint.delete();
shader.delete();
});
gm('improved_noise_shader', (canvas) => {
const shader = CanvasKit.Shader.MakeImprovedNoise(0.1, 0.05, 2, 10);
const paint = new CanvasKit.Paint();
paint.setColor(CanvasKit.BLACK);
paint.setShader(shader);
canvas.drawPaint(paint);
paint.delete();
shader.delete();
});
describe('ColorSpace Support', () => {
it('Can create an SRGB 8888 surface', () => {
const colorSpace = CanvasKit.ColorSpace.SRGB;
const surface = CanvasKit.MakeCanvasSurface('test', CanvasKit.ColorSpace.SRGB);
expect(surface).toBeTruthy('Could not make surface');
let info = surface.imageInfo();
expect(info.alphaType).toEqual(CanvasKit.AlphaType.Unpremul);
expect(info.colorType).toEqual(CanvasKit.ColorType.RGBA_8888);
expect(CanvasKit.ColorSpace.Equals(info.colorSpace, colorSpace))
.toBeTruthy("Surface not created with correct color space.");
const pixels = surface.getCanvas().readPixels(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
CanvasKit.AlphaType.Unpremul, CanvasKit.ColorType.RGBA_8888, colorSpace);
expect(pixels).toBeTruthy('Could not read pixels from surface');
});
it('Can create a Display P3 surface', () => {
const colorSpace = CanvasKit.ColorSpace.DISPLAY_P3;
const surface = CanvasKit.MakeCanvasSurface('test', CanvasKit.ColorSpace.DISPLAY_P3);
expect(surface).toBeTruthy('Could not make surface');
if (!surface.reportBackendTypeIsGPU()) {
console.log('Not expecting color space support in cpu backed suface.');
return;
}
let info = surface.imageInfo();
expect(info.alphaType).toEqual(CanvasKit.AlphaType.Unpremul);
expect(info.colorType).toEqual(CanvasKit.ColorType.RGBA_F16);
expect(CanvasKit.ColorSpace.Equals(info.colorSpace, colorSpace))
.toBeTruthy("Surface not created with correct color space.");
const pixels = surface.getCanvas().readPixels(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
CanvasKit.AlphaType.Unpremul, CanvasKit.ColorType.RGBA_F16, colorSpace);
expect(pixels).toBeTruthy('Could not read pixels from surface');
});
it('Can create an Adobe RGB surface', () => {
const colorSpace = CanvasKit.ColorSpace.ADOBE_RGB;
const surface = CanvasKit.MakeCanvasSurface('test', CanvasKit.ColorSpace.ADOBE_RGB);
expect(surface).toBeTruthy('Could not make surface');
if (!surface.reportBackendTypeIsGPU()) {
console.log('Not expecting color space support in cpu backed suface.');
return;
}
let info = surface.imageInfo();
expect(info.alphaType).toEqual(CanvasKit.AlphaType.Unpremul);
expect(info.colorType).toEqual(CanvasKit.ColorType.RGBA_F16);
expect(CanvasKit.ColorSpace.Equals(info.colorSpace, colorSpace))
.toBeTruthy("Surface not created with correct color space.");
const pixels = surface.getCanvas().readPixels(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
CanvasKit.AlphaType.Unpremul, CanvasKit.ColorType.RGBA_F16, colorSpace);
expect(pixels).toBeTruthy('Could not read pixels from surface');
});
it('combine draws from several color spaces', () => {
const surface = CanvasKit.MakeCanvasSurface('test', CanvasKit.ColorSpace.ADOBE_RGB);
expect(surface).toBeTruthy('Could not make surface');
if (!surface.reportBackendTypeIsGPU()) {
console.log('Not expecting color space support in cpu backed suface.');
return;
}
const canvas = surface.getCanvas();
let paint = new CanvasKit.Paint();
paint.setColor(CanvasKit.RED, CanvasKit.ColorSpace.ADOBE_RGB);
canvas.drawPaint(paint);
paint.setColor(CanvasKit.RED, CanvasKit.ColorSpace.DISPLAY_P3); // 93.7 in adobeRGB
canvas.drawRect(CanvasKit.LTRBRect(200, 0, 400, 600), paint);
paint.setColor(CanvasKit.RED, CanvasKit.ColorSpace.SRGB); // 85.9 in adobeRGB
canvas.drawRect(CanvasKit.LTRBRect(400, 0, 600, 600), paint);
// this test paints three bands of red, each the maximum red that a color space supports.
// They are each represented by skia by some color in the Adobe RGB space of the surface,
// as floats between 0 and 1.
// TODO(nifong) readpixels and verify correctness after f32 readpixels bug is fixed
});
}); // end describe('ColorSpace Support')
describe('DOMMatrix support', () => {
gm('sweep_gradient_dommatrix', (canvas) => {
const paint = new CanvasKit.Paint();
const shader = CanvasKit.Shader.MakeSweepGradient(
100, 100, // x y coordinates
[CanvasKit.GREEN, CanvasKit.BLUE],
[0.0, 1.0],
CanvasKit.TileMode.Clamp,
new DOMMatrix().translate(-10, 100),
);
expect(shader).toBeTruthy('Could not make shader');
if (!shader) {
return;
}
paint.setShader(shader);
canvas.drawPaint(paint);
paint.delete();
shader.delete();
});
const radiansToDegrees = (rad) => {
return (rad / Math.PI) * 180;
}
// this should draw the same as concat_with4x4_canvas
gm('concat_dommatrix', (canvas) => {
const path = starPath(CanvasKit, CANVAS_WIDTH/2, CANVAS_HEIGHT/2);
const paint = new CanvasKit.Paint();
paint.setAntiAlias(true);
canvas.clear(CanvasKit.WHITE);
canvas.concat(new DOMMatrix().translate(CANVAS_WIDTH/2, 0, 0));
canvas.concat(new DOMMatrix().rotateAxisAngle(1, 0, 0, radiansToDegrees(Math.PI/3)));
canvas.concat(new DOMMatrix().rotateAxisAngle(0, 1, 0, radiansToDegrees(Math.PI/4)));
canvas.concat(new DOMMatrix().rotateAxisAngle(0, 0, 1, radiansToDegrees(Math.PI/16)));
canvas.concat(new DOMMatrix().translate(-CANVAS_WIDTH/2, 0, 0));
const localMatrix = canvas.getLocalToDevice();
expect4x4MatricesToMatch([
0.693519, -0.137949, 0.707106, 91.944030,
0.698150, 0.370924, -0.612372, -209.445297,
-0.177806, 0.918359, 0.353553, 53.342029,
0 , 0 , 0 , 1 ], localMatrix);
// Draw some stripes to help the eye detect the turn
const stripeWidth = 10;
paint.setColor(CanvasKit.BLACK);
for (let i = 0; i < CANVAS_WIDTH; i += 2*stripeWidth) {
canvas.drawRect(CanvasKit.LTRBRect(i, 0, i + stripeWidth, CANVAS_HEIGHT), paint);
}
paint.setColor(CanvasKit.YELLOW);
canvas.drawPath(path, paint);
paint.delete();
path.delete();
});
}); // end describe('DOMMatrix support')
it('can call subarray on a Malloced object', () => {
const mThings = CanvasKit.Malloc(Float32Array, 6);
mThings.toTypedArray().set([4, 5, 6, 7, 8, 9]);
expectTypedArraysToEqual(Float32Array.of(4, 5, 6, 7, 8, 9), mThings.toTypedArray());
expectTypedArraysToEqual(Float32Array.of(4, 5, 6, 7, 8, 9), mThings.subarray(0));
expectTypedArraysToEqual(Float32Array.of(7, 8, 9), mThings.subarray(3));
expectTypedArraysToEqual(Float32Array.of(7), mThings.subarray(3, 4));
expectTypedArraysToEqual(Float32Array.of(7, 8), mThings.subarray(3, 5));
// mutations on the subarray affect the entire array (because they are backed by the
// same memory)
mThings.subarray(3)[0] = 100.5;
expectTypedArraysToEqual(Float32Array.of(4, 5, 6, 100.5, 8, 9), mThings.toTypedArray());
CanvasKit.Free(mThings);
});
function expectTypedArraysToEqual(expected, actual) {
expect(expected.constructor.name).toEqual(actual.constructor.name);
expect(expected.length).toEqual(actual.length);
for (let i = 0; i < expected.length; i++) {
expect(expected[i]).toBeCloseTo(actual[i], 5, `element ${i}`);
}
}
it('can create a RasterDirectSurface', () => {
// Make enough space for a 5x5 8888 surface (4 bytes for R, G, B, A)
const rdsData = CanvasKit.Malloc(Uint8Array, 5 * 5 * 4);
const surface = CanvasKit.MakeRasterDirectSurface({
'width': 5,
'height': 5,
'colorType': CanvasKit.ColorType.RGBA_8888,
'alphaType': CanvasKit.AlphaType.Premul,
'colorSpace': CanvasKit.ColorSpace.SRGB,
}, rdsData, 5 * 4);
surface.getCanvas().clear(CanvasKit.Color(200, 100, 0, 0.8));
const pixels = rdsData.toTypedArray();
// Check that the first pixels colors are right.
expect(pixels[0]).toEqual(160); // red (premul, 0.8 * 200)
expect(pixels[1]).toEqual(80); // green (premul, 0.8 * 100)
expect(pixels[2]).toEqual(0); // blue (premul, not that it matters)
expect(pixels[3]).toEqual(204); // alpha (0.8 * 255)
surface.delete();
CanvasKit.Free(rdsData);
});
});