2018-09-20 21:39:31 +00:00
|
|
|
<!DOCTYPE html>
|
|
|
|
<title>CanvasKit (Skia via Web Assembly)</title>
|
|
|
|
<meta charset="utf-8" />
|
|
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
|
|
|
|
<style>
|
2019-03-18 15:20:53 +00:00
|
|
|
canvas, img {
|
2018-09-20 21:39:31 +00:00
|
|
|
border: 1px dashed #AAA;
|
|
|
|
}
|
2021-05-05 17:19:49 +00:00
|
|
|
#api5_c, #api6_c {
|
|
|
|
width: 300px;
|
|
|
|
height: 300px;
|
|
|
|
}
|
2018-09-20 21:39:31 +00:00
|
|
|
|
|
|
|
</style>
|
|
|
|
|
2018-11-26 16:47:54 +00:00
|
|
|
<h2>Drop in replacement for HTML Canvas (e.g. node.js)</h2>
|
|
|
|
<img id=api1 width=300 height=300>
|
|
|
|
<canvas id=api1_c width=300 height=300></canvas>
|
|
|
|
<img id=api2 width=300 height=300>
|
|
|
|
<canvas id=api2_c width=300 height=300></canvas>
|
|
|
|
<img id=api3 width=300 height=300>
|
|
|
|
<canvas id=api3_c width=300 height=300></canvas>
|
2018-11-27 18:26:59 +00:00
|
|
|
<img id=api4 width=300 height=300>
|
|
|
|
<canvas id=api4_c width=300 height=300></canvas>
|
2018-11-28 17:51:56 +00:00
|
|
|
<img id=api5 width=300 height=300>
|
|
|
|
<canvas id=api5_c width=300 height=300></canvas>
|
2018-11-29 20:07:02 +00:00
|
|
|
<img id=api6 width=300 height=300>
|
|
|
|
<canvas id=api6_c width=300 height=300></canvas>
|
2018-12-07 13:29:52 +00:00
|
|
|
<img id=api7 width=300 height=300>
|
|
|
|
<canvas id=api7_c width=300 height=300></canvas>
|
2018-12-07 18:03:08 +00:00
|
|
|
<img id=api8 width=300 height=300>
|
|
|
|
<canvas id=api8_c width=300 height=300></canvas>
|
2018-11-26 16:47:54 +00:00
|
|
|
|
2019-03-08 15:04:28 +00:00
|
|
|
<h2> CanvasKit expands the functionality of a stock HTML canvas</h2>
|
2018-11-03 11:51:19 +00:00
|
|
|
<canvas id=vertex1 width=300 height=300></canvas>
|
|
|
|
<canvas id=gradient1 width=300 height=300></canvas>
|
2018-09-20 21:39:31 +00:00
|
|
|
<canvas id=patheffect width=300 height=300></canvas>
|
|
|
|
<canvas id=paths width=200 height=200></canvas>
|
2020-05-19 18:34:41 +00:00
|
|
|
<canvas id=pathperson width=300 height=300></canvas>
|
2018-09-20 21:39:31 +00:00
|
|
|
<canvas id=ink width=300 height=300></canvas>
|
2019-03-08 15:04:28 +00:00
|
|
|
<canvas id=surfaces width=300 height=300></canvas>
|
2019-03-29 14:39:52 +00:00
|
|
|
<canvas id=atlas width=300 height=300></canvas>
|
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 id=decode width=300 height=300></canvas>
|
2018-09-20 21:39:31 +00:00
|
|
|
|
2021-02-23 13:27:46 +00:00
|
|
|
<h2> CanvasKit can allow for text measurement/placement (e.g. breaking, kerning)</h2>
|
2019-03-15 19:36:29 +00:00
|
|
|
<canvas id=textonpath width=300 height=300></canvas>
|
2021-04-14 18:49:35 +00:00
|
|
|
<canvas id=drawGlyphs width=300 height=300></canvas>
|
2019-02-22 15:04:06 +00:00
|
|
|
|
2021-03-29 20:38:01 +00:00
|
|
|
<h2> Interactive drawPatch</h2>
|
|
|
|
<canvas id=interdrawpatch width=512 height=512></canvas>
|
|
|
|
|
2021-11-12 16:04:17 +00:00
|
|
|
<script type="text/javascript" src="/build/canvaskit.js"></script>
|
2018-09-20 21:39:31 +00:00
|
|
|
|
|
|
|
<script type="text/javascript" charset="utf-8">
|
|
|
|
|
|
|
|
var CanvasKit = null;
|
2020-05-26 15:28:55 +00:00
|
|
|
var cdn = 'https://storage.googleapis.com/skia-cdn/misc/';
|
2018-10-30 19:05:04 +00:00
|
|
|
|
2021-11-12 16:04:17 +00:00
|
|
|
const ckLoaded = CanvasKitInit({locateFile: (file) => '/build/'+file});
|
2018-12-12 15:35:13 +00:00
|
|
|
|
2020-05-26 15:28:55 +00:00
|
|
|
const loadRoboto = fetch(cdn + 'Roboto-Regular.ttf').then((response) => response.arrayBuffer());
|
|
|
|
const loadNotoSerif = fetch(cdn + 'NotoSerif-Regular.ttf').then((response) => response.arrayBuffer());
|
|
|
|
const loadTestImage = fetch(cdn + 'test.png').then((response) => response.arrayBuffer());
|
|
|
|
|
|
|
|
// Examples which only require canvaskit
|
|
|
|
ckLoaded.then((CK) => {
|
2019-01-04 19:16:19 +00:00
|
|
|
CanvasKit = CK;
|
|
|
|
PathExample(CanvasKit);
|
|
|
|
InkExample(CanvasKit);
|
2020-05-19 18:34:41 +00:00
|
|
|
PathPersonExample(CanvasKit);
|
2020-05-26 15:28:55 +00:00
|
|
|
VertexAPI1(CanvasKit);
|
|
|
|
GradiantAPI1(CanvasKit);
|
|
|
|
TextOnPathAPI1(CanvasKit);
|
2021-04-14 18:49:35 +00:00
|
|
|
DrawGlyphsAPI1(CanvasKit);
|
2020-05-26 15:28:55 +00:00
|
|
|
SurfaceAPI1(CanvasKit);
|
2021-11-16 19:09:44 +00:00
|
|
|
if (CanvasKit.MakeCanvas){
|
|
|
|
CanvasAPI1(CanvasKit);
|
|
|
|
CanvasAPI2(CanvasKit);
|
|
|
|
CanvasAPI3(CanvasKit);
|
|
|
|
CanvasAPI4(CanvasKit);
|
|
|
|
CanvasAPI5(CanvasKit);
|
|
|
|
CanvasAPI6(CanvasKit);
|
|
|
|
CanvasAPI7(CanvasKit);
|
|
|
|
CanvasAPI8(CanvasKit);
|
|
|
|
} else {
|
|
|
|
console.log("Skipping CanvasAPI1 because it's not compiled in");
|
|
|
|
}
|
2021-03-29 20:38:01 +00:00
|
|
|
InteractivePatch(CanvasKit);
|
2018-12-12 15:35:13 +00:00
|
|
|
});
|
|
|
|
|
2020-05-26 15:28:55 +00:00
|
|
|
// Examples requiring external resources
|
|
|
|
Promise.all([ckLoaded, loadRoboto]).then((results) => {DrawingExample(...results)});
|
|
|
|
Promise.all([ckLoaded, loadTestImage]).then((results) => {AtlasAPI1(...results)});
|
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
|
|
|
Promise.all([ckLoaded, loadTestImage]).then((results) => {DecodeAPI(...results)});
|
2019-03-29 14:39:52 +00:00
|
|
|
|
2018-12-12 15:35:13 +00:00
|
|
|
function DrawingExample(CanvasKit, robotoData) {
|
|
|
|
if (!robotoData || !CanvasKit) {
|
|
|
|
return;
|
|
|
|
}
|
2018-10-17 11:57:18 +00:00
|
|
|
const surface = CanvasKit.MakeCanvasSurface('patheffect');
|
2018-09-20 21:39:31 +00:00
|
|
|
if (!surface) {
|
2018-10-17 11:57:18 +00:00
|
|
|
console.error('Could not make surface');
|
|
|
|
return;
|
2018-09-20 21:39:31 +00:00
|
|
|
}
|
|
|
|
|
2020-10-07 20:09:22 +00:00
|
|
|
const paint = new CanvasKit.Paint();
|
2021-10-21 15:35:04 +00:00
|
|
|
const roboto = CanvasKit.Typeface.MakeFreeTypeFaceFromData(robotoData);
|
2018-12-12 15:35:13 +00:00
|
|
|
|
2020-10-07 20:09:22 +00:00
|
|
|
const textPaint = new CanvasKit.Paint();
|
2018-11-03 11:51:19 +00:00
|
|
|
textPaint.setColor(CanvasKit.RED);
|
2018-09-20 21:39:31 +00:00
|
|
|
textPaint.setAntiAlias(true);
|
2019-01-14 13:36:08 +00:00
|
|
|
|
2020-10-07 20:09:22 +00:00
|
|
|
const textFont = new CanvasKit.Font(roboto, 30);
|
2018-09-20 21:39:31 +00:00
|
|
|
|
|
|
|
let i = 0;
|
|
|
|
|
|
|
|
let X = 128;
|
|
|
|
let Y = 128;
|
|
|
|
|
2019-03-19 13:34:37 +00:00
|
|
|
function drawFrame(canvas) {
|
2018-09-20 21:39:31 +00:00
|
|
|
const path = starPath(CanvasKit, X, Y);
|
2018-12-14 21:10:38 +00:00
|
|
|
// Some animations see performance improvements by marking their
|
|
|
|
// paths as volatile.
|
|
|
|
path.setIsVolatile(true);
|
2020-10-07 20:09:22 +00:00
|
|
|
const dpe = CanvasKit.PathEffect.MakeDash([15, 5, 5, 10], i/5);
|
2018-09-20 21:39:31 +00:00
|
|
|
i++;
|
|
|
|
|
|
|
|
paint.setPathEffect(dpe);
|
2018-11-03 11:51:19 +00:00
|
|
|
paint.setStyle(CanvasKit.PaintStyle.Stroke);
|
2018-09-20 21:39:31 +00:00
|
|
|
paint.setStrokeWidth(5.0 + -3 * Math.cos(i/30));
|
|
|
|
paint.setAntiAlias(true);
|
|
|
|
paint.setColor(CanvasKit.Color(66, 129, 164, 1.0));
|
|
|
|
|
2018-11-03 11:51:19 +00:00
|
|
|
canvas.clear(CanvasKit.TRANSPARENT);
|
2018-09-20 21:39:31 +00:00
|
|
|
|
|
|
|
canvas.drawPath(path, paint);
|
2019-02-22 15:04:06 +00:00
|
|
|
canvas.drawText('Try Clicking!', 10, 280, textPaint, textFont);
|
2018-10-17 11:57:18 +00:00
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
dpe.delete();
|
|
|
|
path.delete();
|
2019-03-19 13:34:37 +00:00
|
|
|
surface.requestAnimationFrame(drawFrame);
|
2018-09-20 21:39:31 +00:00
|
|
|
}
|
2019-03-19 13:34:37 +00:00
|
|
|
surface.requestAnimationFrame(drawFrame);
|
2018-09-20 21:39:31 +00:00
|
|
|
|
2018-10-02 15:33:52 +00:00
|
|
|
// Make animation interactive
|
|
|
|
let interact = (e) => {
|
|
|
|
if (!e.pressure) {
|
|
|
|
return;
|
|
|
|
}
|
2018-09-20 21:39:31 +00:00
|
|
|
X = e.offsetX;
|
|
|
|
Y = e.offsetY;
|
2018-10-02 15:33:52 +00:00
|
|
|
};
|
|
|
|
document.getElementById('patheffect').addEventListener('pointermove', interact);
|
|
|
|
document.getElementById('patheffect').addEventListener('pointerdown', interact);
|
|
|
|
preventScrolling(document.getElementById('patheffect'));
|
2018-09-20 21:39:31 +00:00
|
|
|
// A client would need to delete this if it didn't go on for ever.
|
2019-01-14 13:36:08 +00:00
|
|
|
// paint.delete();
|
|
|
|
// textPaint.delete();
|
|
|
|
// textFont.delete();
|
2018-09-20 21:39:31 +00:00
|
|
|
}
|
|
|
|
|
2021-03-29 20:38:01 +00:00
|
|
|
function InteractivePatch(CanvasKit) {
|
|
|
|
const ELEM = 'interdrawpatch';
|
|
|
|
const surface = CanvasKit.MakeCanvasSurface(ELEM);
|
|
|
|
if (!surface) {
|
|
|
|
console.error('Could not make surface');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let live_corner, live_index;
|
|
|
|
|
|
|
|
const paint = new CanvasKit.Paint();
|
|
|
|
const pts_paint = new CanvasKit.Paint();
|
|
|
|
pts_paint.setStyle(CanvasKit.PaintStyle.Stroke);
|
|
|
|
pts_paint.setStrokeWidth(9);
|
|
|
|
pts_paint.setStrokeCap(CanvasKit.StrokeCap.Round);
|
|
|
|
|
|
|
|
const line_paint = new CanvasKit.Paint();
|
|
|
|
line_paint.setStyle(CanvasKit.PaintStyle.Stroke);
|
|
|
|
line_paint.setStrokeWidth(2);
|
|
|
|
|
|
|
|
const colors = [CanvasKit.RED, CanvasKit.BLUE, CanvasKit.YELLOW, CanvasKit.CYAN];
|
|
|
|
|
|
|
|
const patch = [
|
|
|
|
[ 10,170, 10, 10, 170, 10], // prev_vector, point, next_vector
|
|
|
|
[340, 10, 500, 10, 500,170],
|
|
|
|
[500,340, 500,500, 340,500],
|
|
|
|
[170,500, 10,500, 10,340],
|
|
|
|
];
|
|
|
|
|
|
|
|
function get_corner(corner, index) {
|
|
|
|
return [corner[index*2+0], corner[index*2+1]];
|
|
|
|
}
|
|
|
|
|
|
|
|
function push_xy(array, xy) {
|
|
|
|
array.push(xy[0], xy[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
function patch_to_cubics(patch) {
|
|
|
|
const array = [];
|
|
|
|
push_xy(array, get_corner(patch[0],1));
|
|
|
|
push_xy(array, get_corner(patch[0],2));
|
|
|
|
for (let i = 1; i < 4; ++i) {
|
|
|
|
push_xy(array, get_corner(patch[i],0));
|
|
|
|
push_xy(array, get_corner(patch[i],1));
|
|
|
|
push_xy(array, get_corner(patch[i],2));
|
|
|
|
}
|
|
|
|
push_xy(array, get_corner(patch[0],0));
|
|
|
|
|
|
|
|
return array;
|
|
|
|
}
|
|
|
|
|
|
|
|
function drawFrame(canvas) {
|
|
|
|
const cubics = patch_to_cubics(patch);
|
|
|
|
|
|
|
|
canvas.drawColor(CanvasKit.WHITE);
|
|
|
|
canvas.drawPatch(cubics, colors, null, null, paint);
|
|
|
|
if (live_corner) {
|
|
|
|
canvas.drawPoints(CanvasKit.PointMode.Polygon, live_corner, line_paint);
|
|
|
|
}
|
|
|
|
canvas.drawPoints(CanvasKit.PointMode.Points, cubics, pts_paint);
|
|
|
|
|
|
|
|
surface.requestAnimationFrame(drawFrame);
|
|
|
|
}
|
|
|
|
|
|
|
|
surface.requestAnimationFrame(drawFrame);
|
|
|
|
|
|
|
|
function length2(x, y) {
|
|
|
|
return x*x + y*y;
|
|
|
|
}
|
|
|
|
function hit_test(x,y, x1,y1) {
|
|
|
|
return length2(x-x1, y-y1) <= 10*10;
|
|
|
|
}
|
|
|
|
function pointer_up(e) {
|
|
|
|
live_corner = null;
|
|
|
|
live_index = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
function pointer_down(e) {
|
|
|
|
live_corner = null;
|
|
|
|
live_index = null;
|
|
|
|
for (p of patch) {
|
|
|
|
for (let i = 0; i < 6; i += 2) {
|
|
|
|
if (hit_test(p[i], p[i+1], e.offsetX, e.offsetY)) {
|
|
|
|
live_corner = p;
|
|
|
|
live_index = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function pointer_move(e) {
|
|
|
|
if (e.pressure && live_corner) {
|
|
|
|
if (live_index == 2) {
|
|
|
|
// corner
|
|
|
|
const dx = e.offsetX - live_corner[2];
|
|
|
|
const dy = e.offsetY - live_corner[3];
|
|
|
|
for (let i = 0; i < 3; ++i) {
|
|
|
|
live_corner[i*2+0] += dx;
|
|
|
|
live_corner[i*2+1] += dy;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// control-point
|
|
|
|
live_corner[live_index+0] = e.offsetX;
|
|
|
|
live_corner[live_index+1] = e.offsetY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
document.getElementById(ELEM).addEventListener('pointermove', pointer_move);
|
|
|
|
document.getElementById(ELEM).addEventListener('pointerdown', pointer_down);
|
|
|
|
document.getElementById(ELEM).addEventListener('pointerup', pointer_up);
|
|
|
|
preventScrolling(document.getElementById(ELEM));
|
|
|
|
}
|
|
|
|
|
2020-05-19 18:34:41 +00:00
|
|
|
function PathPersonExample(CanvasKit) {
|
|
|
|
const surface = CanvasKit.MakeSWCanvasSurface('pathperson');
|
|
|
|
if (!surface) {
|
|
|
|
console.error('Could not make surface');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
function drawFrame(canvas) {
|
2020-10-07 20:09:22 +00:00
|
|
|
const paint = new CanvasKit.Paint();
|
2020-05-19 18:34:41 +00:00
|
|
|
paint.setStrokeWidth(1.0);
|
|
|
|
paint.setAntiAlias(true);
|
|
|
|
paint.setColor(CanvasKit.Color(0, 0, 0, 1.0));
|
|
|
|
paint.setStyle(CanvasKit.PaintStyle.Stroke);
|
|
|
|
|
2020-10-07 20:09:22 +00:00
|
|
|
const path = new CanvasKit.Path();
|
2020-05-19 18:34:41 +00:00
|
|
|
path.moveTo(10, 10);
|
|
|
|
path.lineTo(100, 10);
|
|
|
|
path.moveTo(10, 10);
|
|
|
|
path.lineTo(10, 200);
|
|
|
|
path.moveTo(10, 100);
|
|
|
|
path.lineTo(100,100);
|
|
|
|
path.moveTo(10, 200);
|
|
|
|
path.lineTo(100, 200);
|
|
|
|
|
|
|
|
canvas.drawPath(path, paint);
|
|
|
|
path.delete();
|
|
|
|
paint.delete();
|
|
|
|
}
|
|
|
|
// Intentionally just draw frame once
|
|
|
|
surface.drawOnce(drawFrame);
|
|
|
|
}
|
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
function PathExample(CanvasKit) {
|
2019-03-29 14:39:52 +00:00
|
|
|
const surface = CanvasKit.MakeSWCanvasSurface('paths');
|
2018-09-20 21:39:31 +00:00
|
|
|
if (!surface) {
|
2018-10-17 11:57:18 +00:00
|
|
|
console.error('Could not make surface');
|
|
|
|
return;
|
2018-09-20 21:39:31 +00:00
|
|
|
}
|
|
|
|
|
2019-03-19 13:34:37 +00:00
|
|
|
function drawFrame(canvas) {
|
2020-10-07 20:09:22 +00:00
|
|
|
const paint = new CanvasKit.Paint();
|
2018-09-20 21:39:31 +00:00
|
|
|
paint.setStrokeWidth(1.0);
|
|
|
|
paint.setAntiAlias(true);
|
|
|
|
paint.setColor(CanvasKit.Color(0, 0, 0, 1.0));
|
2018-11-03 11:51:19 +00:00
|
|
|
paint.setStyle(CanvasKit.PaintStyle.Stroke);
|
2018-09-20 21:39:31 +00:00
|
|
|
|
2020-10-07 20:09:22 +00:00
|
|
|
const path = new CanvasKit.Path();
|
2018-09-20 21:39:31 +00:00
|
|
|
path.moveTo(20, 5);
|
|
|
|
path.lineTo(30, 20);
|
|
|
|
path.lineTo(40, 10);
|
|
|
|
path.lineTo(50, 20);
|
|
|
|
path.lineTo(60, 0);
|
|
|
|
path.lineTo(20, 5);
|
|
|
|
|
|
|
|
path.moveTo(20, 80);
|
|
|
|
path.cubicTo(90, 10, 160, 150, 190, 10);
|
|
|
|
|
|
|
|
path.moveTo(36, 148);
|
|
|
|
path.quadTo(66, 188, 120, 136);
|
|
|
|
path.lineTo(36, 148);
|
|
|
|
|
|
|
|
path.moveTo(150, 180);
|
2020-09-03 14:02:10 +00:00
|
|
|
path.arcToTangent(150, 100, 50, 200, 20);
|
2018-09-20 21:39:31 +00:00
|
|
|
path.lineTo(160, 160);
|
|
|
|
|
|
|
|
path.moveTo(20, 120);
|
|
|
|
path.lineTo(20, 120);
|
|
|
|
|
|
|
|
canvas.drawPath(path, paint);
|
|
|
|
|
2020-09-03 14:02:10 +00:00
|
|
|
const rrect = CanvasKit.RRectXY([100, 10, 140, 62], 10, 4);
|
2019-01-07 16:08:55 +00:00
|
|
|
|
2020-10-07 20:09:22 +00:00
|
|
|
const rrectPath = new CanvasKit.Path().addRRect(rrect, true);
|
2019-01-07 16:08:55 +00:00
|
|
|
|
2020-09-03 14:02:10 +00:00
|
|
|
canvas.drawPath(rrectPath, paint);
|
|
|
|
|
|
|
|
rrectPath.delete();
|
2018-09-20 21:39:31 +00:00
|
|
|
path.delete();
|
|
|
|
paint.delete();
|
|
|
|
}
|
2020-01-22 21:49:41 +00:00
|
|
|
// Intentionally just draw frame once
|
|
|
|
surface.drawOnce(drawFrame);
|
2018-09-20 21:39:31 +00:00
|
|
|
}
|
|
|
|
|
2018-10-02 15:33:52 +00:00
|
|
|
function preventScrolling(canvas) {
|
|
|
|
canvas.addEventListener('touchmove', (e) => {
|
|
|
|
// Prevents touch events in the canvas from scrolling the canvas.
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
});
|
|
|
|
}
|
2018-09-20 21:39:31 +00:00
|
|
|
|
|
|
|
function InkExample(CanvasKit) {
|
2018-10-17 11:57:18 +00:00
|
|
|
const surface = CanvasKit.MakeCanvasSurface('ink');
|
2018-09-20 21:39:31 +00:00
|
|
|
if (!surface) {
|
2018-10-17 11:57:18 +00:00
|
|
|
console.error('Could not make surface');
|
|
|
|
return;
|
2018-09-20 21:39:31 +00:00
|
|
|
}
|
|
|
|
|
2020-10-07 20:09:22 +00:00
|
|
|
let paint = new CanvasKit.Paint();
|
2018-09-20 21:39:31 +00:00
|
|
|
paint.setAntiAlias(true);
|
|
|
|
paint.setColor(CanvasKit.Color(0, 0, 0, 1.0));
|
2018-11-03 11:51:19 +00:00
|
|
|
paint.setStyle(CanvasKit.PaintStyle.Stroke);
|
2018-09-20 21:39:31 +00:00
|
|
|
paint.setStrokeWidth(4.0);
|
2020-10-07 20:09:22 +00:00
|
|
|
paint.setPathEffect(CanvasKit.PathEffect.MakeCorner(50));
|
2018-09-20 21:39:31 +00:00
|
|
|
|
2018-09-21 14:14:30 +00:00
|
|
|
// Draw I N K
|
2020-10-07 20:09:22 +00:00
|
|
|
let path = new CanvasKit.Path();
|
2018-09-21 14:14:30 +00:00
|
|
|
path.moveTo(80, 30);
|
|
|
|
path.lineTo(80, 80);
|
|
|
|
|
|
|
|
path.moveTo(100, 80);
|
|
|
|
path.lineTo(100, 15);
|
|
|
|
path.lineTo(130, 95);
|
|
|
|
path.lineTo(130, 30);
|
|
|
|
|
|
|
|
path.moveTo(150, 30);
|
|
|
|
path.lineTo(150, 80);
|
|
|
|
path.moveTo(170, 30);
|
|
|
|
path.lineTo(150, 55);
|
|
|
|
path.lineTo(170, 80);
|
2018-09-20 21:39:31 +00:00
|
|
|
|
|
|
|
let paths = [path];
|
|
|
|
let paints = [paint];
|
|
|
|
|
2019-03-19 13:34:37 +00:00
|
|
|
function drawFrame(canvas) {
|
2018-10-17 11:57:18 +00:00
|
|
|
canvas.clear(CanvasKit.Color(255, 255, 255, 1.0));
|
2018-09-20 21:39:31 +00:00
|
|
|
|
|
|
|
for (let i = 0; i < paints.length && i < paths.length; i++) {
|
|
|
|
canvas.drawPath(paths[i], paints[i]);
|
|
|
|
}
|
|
|
|
|
2019-03-19 13:34:37 +00:00
|
|
|
surface.requestAnimationFrame(drawFrame);
|
2018-09-20 21:39:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let hold = false;
|
2018-10-02 15:33:52 +00:00
|
|
|
let interact = (e) => {
|
|
|
|
let type = e.type;
|
|
|
|
if (type === 'lostpointercapture' || type === 'pointerup' || !e.pressure ) {
|
2018-09-20 21:39:31 +00:00
|
|
|
hold = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (hold) {
|
|
|
|
path.lineTo(e.offsetX, e.offsetY);
|
|
|
|
} else {
|
|
|
|
paint = paint.copy();
|
|
|
|
paint.setColor(CanvasKit.Color(Math.random() * 255, Math.random() * 255, Math.random() * 255, Math.random() + .2));
|
|
|
|
paints.push(paint);
|
2020-10-07 20:09:22 +00:00
|
|
|
path = new CanvasKit.Path();
|
2018-09-20 21:39:31 +00:00
|
|
|
paths.push(path);
|
|
|
|
path.moveTo(e.offsetX, e.offsetY);
|
|
|
|
}
|
|
|
|
hold = true;
|
2018-10-02 15:33:52 +00:00
|
|
|
};
|
|
|
|
document.getElementById('ink').addEventListener('pointermove', interact);
|
|
|
|
document.getElementById('ink').addEventListener('pointerdown', interact);
|
|
|
|
document.getElementById('ink').addEventListener('lostpointercapture', interact);
|
|
|
|
document.getElementById('ink').addEventListener('pointerup', interact);
|
|
|
|
preventScrolling(document.getElementById('ink'));
|
2019-03-19 13:34:37 +00:00
|
|
|
surface.requestAnimationFrame(drawFrame);
|
2018-09-20 21:39:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function starPath(CanvasKit, X=128, Y=128, R=116) {
|
2020-10-07 20:09:22 +00:00
|
|
|
let p = new CanvasKit.Path();
|
2018-09-20 21:39:31 +00:00
|
|
|
p.moveTo(X + R, Y);
|
|
|
|
for (let i = 1; i < 8; i++) {
|
|
|
|
let a = 2.6927937 * i;
|
|
|
|
p.lineTo(X + R * Math.cos(a), Y + R * Math.sin(a));
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2018-10-19 18:34:34 +00:00
|
|
|
function CanvasAPI1(CanvasKit) {
|
2018-11-20 17:51:16 +00:00
|
|
|
let skcanvas = CanvasKit.MakeCanvas(300, 300);
|
|
|
|
let realCanvas = document.getElementById('api1_c');
|
|
|
|
|
2020-02-24 21:30:20 +00:00
|
|
|
let skPromise = fetch(cdn + 'test.png')
|
2018-12-03 17:31:04 +00:00
|
|
|
// if clients want to use a Blob, they are responsible
|
|
|
|
// for reading it themselves.
|
|
|
|
.then((response) => response.arrayBuffer())
|
|
|
|
.then((buffer) => {
|
|
|
|
skcanvas._img = skcanvas.decodeImage(buffer);
|
|
|
|
});
|
2020-02-24 21:30:20 +00:00
|
|
|
let realPromise = fetch(cdn + 'test.png')
|
2018-12-03 17:31:04 +00:00
|
|
|
.then((response) => response.blob())
|
|
|
|
.then((blob) => createImageBitmap(blob))
|
|
|
|
.then((bitmap) => {
|
|
|
|
realCanvas._img = bitmap;
|
|
|
|
});
|
|
|
|
|
2018-12-14 20:03:41 +00:00
|
|
|
let realFontLoaded = new FontFace('Bungee', 'url(/tests/assets/Bungee-Regular.ttf)', {
|
|
|
|
'family': 'Bungee',
|
|
|
|
'style': 'normal',
|
|
|
|
'weight': '400',
|
|
|
|
}).load().then((font) => {
|
|
|
|
document.fonts.add(font);
|
|
|
|
});
|
2018-12-03 17:31:04 +00:00
|
|
|
|
2018-12-14 20:03:41 +00:00
|
|
|
let skFontLoaded = fetch('/tests/assets/Bungee-Regular.ttf').then(
|
|
|
|
(response) => response.arrayBuffer()).then(
|
|
|
|
(buffer) => {
|
|
|
|
// loadFont is synchronous
|
|
|
|
skcanvas.loadFont(buffer, {
|
|
|
|
'family': 'Bungee',
|
|
|
|
'style': 'normal',
|
|
|
|
'weight': '400',
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
Promise.all([realPromise, skPromise, realFontLoaded, skFontLoaded]).then(() => {
|
2018-12-03 17:31:04 +00:00
|
|
|
for (let canvas of [skcanvas, realCanvas]) {
|
|
|
|
let ctx = canvas.getContext('2d');
|
|
|
|
ctx.fillStyle = '#EEE';
|
|
|
|
ctx.fillRect(0, 0, 300, 300);
|
|
|
|
ctx.fillStyle = 'black';
|
2018-12-14 20:03:41 +00:00
|
|
|
ctx.font = '26px Bungee';
|
2018-12-03 17:31:04 +00:00
|
|
|
ctx.rotate(.1);
|
2018-12-14 20:03:41 +00:00
|
|
|
ctx.fillText('Awesome ', 25, 100);
|
2021-02-23 13:29:04 +00:00
|
|
|
ctx.strokeText('Groovy!', 200, 100);
|
2018-12-03 17:31:04 +00:00
|
|
|
|
|
|
|
// Draw line under Awesome
|
|
|
|
ctx.strokeStyle = 'rgba(125,0,0,0.5)';
|
|
|
|
ctx.beginPath();
|
2018-12-04 18:57:36 +00:00
|
|
|
ctx.lineWidth = 6;
|
2018-12-14 20:03:41 +00:00
|
|
|
ctx.moveTo(25, 105);
|
2021-02-23 13:29:04 +00:00
|
|
|
ctx.lineTo(200, 105);
|
2018-12-03 17:31:04 +00:00
|
|
|
ctx.stroke();
|
|
|
|
|
|
|
|
// squished vertically
|
2020-12-04 14:10:39 +00:00
|
|
|
ctx.globalAlpha = 0.7;
|
2018-12-03 17:31:04 +00:00
|
|
|
ctx.imageSmoothingQuality = 'medium';
|
|
|
|
ctx.drawImage(canvas._img, 150, 150, 150, 100);
|
|
|
|
ctx.rotate(-.2);
|
|
|
|
ctx.imageSmoothingEnabled = false;
|
|
|
|
ctx.drawImage(canvas._img, 100, 150, 400, 350, 10, 200, 150, 100);
|
|
|
|
|
2018-12-04 18:57:36 +00:00
|
|
|
let idata = ctx.getImageData(80, 220, 40, 45);
|
|
|
|
ctx.putImageData(idata, 250, 10);
|
|
|
|
ctx.putImageData(idata, 200, 10, 20, 10, 20, 30);
|
|
|
|
ctx.resetTransform();
|
|
|
|
ctx.strokeStyle = 'black';
|
|
|
|
ctx.lineWidth = 1;
|
|
|
|
ctx.strokeRect(200, 10, 40, 45);
|
|
|
|
|
|
|
|
idata = ctx.createImageData(10, 20);
|
|
|
|
ctx.putImageData(idata, 10, 10);
|
|
|
|
}
|
2018-12-03 17:31:04 +00:00
|
|
|
|
|
|
|
document.getElementById('api1').src = skcanvas.toDataURL();
|
|
|
|
skcanvas.dispose();
|
|
|
|
});
|
2018-10-19 18:34:34 +00:00
|
|
|
|
2018-11-20 17:51:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function CanvasAPI2(CanvasKit) {
|
2018-11-26 16:47:54 +00:00
|
|
|
let skcanvas = CanvasKit.MakeCanvas(300, 300);
|
2018-11-20 17:51:16 +00:00
|
|
|
let realCanvas = document.getElementById('api2_c');
|
2018-11-26 16:47:54 +00:00
|
|
|
realCanvas.width = 300;
|
|
|
|
realCanvas.height = 300;
|
2018-11-20 17:51:16 +00:00
|
|
|
|
2018-12-17 21:01:36 +00:00
|
|
|
// svg data for a clock
|
|
|
|
skcanvas._path = skcanvas.makePath2D('M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z');
|
|
|
|
realCanvas._path = new Path2D('M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z');
|
|
|
|
|
2018-11-20 17:51:16 +00:00
|
|
|
for (let canvas of [skcanvas, realCanvas]) {
|
|
|
|
let ctx = canvas.getContext('2d');
|
2018-11-26 16:47:54 +00:00
|
|
|
ctx.scale(1.5, 1.5);
|
2018-11-20 17:51:16 +00:00
|
|
|
ctx.moveTo(20, 5);
|
|
|
|
ctx.lineTo(30, 20);
|
|
|
|
ctx.lineTo(40, 10);
|
|
|
|
ctx.lineTo(50, 20);
|
|
|
|
ctx.lineTo(60, 0);
|
|
|
|
ctx.lineTo(20, 5);
|
|
|
|
|
|
|
|
ctx.moveTo(20, 80);
|
|
|
|
ctx.bezierCurveTo(90, 10, 160, 150, 190, 10);
|
|
|
|
|
|
|
|
ctx.moveTo(36, 148);
|
|
|
|
ctx.quadraticCurveTo(66, 188, 120, 136);
|
|
|
|
ctx.lineTo(36, 148);
|
|
|
|
|
|
|
|
ctx.rect(5, 170, 20, 25);
|
|
|
|
|
|
|
|
ctx.moveTo(150, 180);
|
|
|
|
ctx.arcTo(150, 100, 50, 200, 20);
|
|
|
|
ctx.lineTo(160, 160);
|
|
|
|
|
|
|
|
ctx.moveTo(20, 120);
|
|
|
|
ctx.arc(20, 120, 18, 0, 1.75 * Math.PI);
|
|
|
|
ctx.lineTo(20, 120);
|
|
|
|
|
|
|
|
ctx.moveTo(150, 5);
|
|
|
|
ctx.ellipse(130, 25, 30, 10, -1*Math.PI/8, Math.PI/6, 1.5*Math.PI);
|
|
|
|
|
|
|
|
ctx.lineWidth = 4/3;
|
|
|
|
ctx.stroke();
|
2018-12-07 18:03:08 +00:00
|
|
|
|
2018-12-17 21:01:36 +00:00
|
|
|
// make a clock
|
|
|
|
ctx.stroke(canvas._path);
|
|
|
|
|
2018-12-07 18:03:08 +00:00
|
|
|
// Test edgecases and draw direction
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.arc(50, 100, 10, Math.PI, -Math.PI/2);
|
|
|
|
ctx.stroke();
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.arc(75, 100, 10, Math.PI, -Math.PI/2, true);
|
|
|
|
ctx.stroke();
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.arc(100, 100, 10, Math.PI, 100.1 * Math.PI, true);
|
|
|
|
ctx.stroke();
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.arc(125, 100, 10, Math.PI, 100.1 * Math.PI, false);
|
|
|
|
ctx.stroke();
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.ellipse(155, 100, 10, 15, Math.PI/8, 100.1 * Math.PI, Math.PI, true);
|
|
|
|
ctx.stroke();
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.ellipse(180, 100, 10, 15, Math.PI/8, Math.PI, 100.1 * Math.PI, true);
|
|
|
|
ctx.stroke();
|
2018-11-20 17:51:16 +00:00
|
|
|
}
|
|
|
|
document.getElementById('api2').src = skcanvas.toDataURL();
|
2018-12-07 18:03:08 +00:00
|
|
|
skcanvas.dispose();
|
2018-10-19 18:34:34 +00:00
|
|
|
}
|
|
|
|
|
2018-11-26 16:47:54 +00:00
|
|
|
function CanvasAPI3(CanvasKit) {
|
|
|
|
let skcanvas = CanvasKit.MakeCanvas(300, 300);
|
|
|
|
let realCanvas = document.getElementById('api3_c');
|
|
|
|
realCanvas.width = 300;
|
|
|
|
realCanvas.height = 300;
|
|
|
|
|
|
|
|
for (let canvas of [skcanvas, realCanvas]) {
|
|
|
|
let ctx = canvas.getContext('2d');
|
|
|
|
ctx.rect(10, 10, 20, 20);
|
|
|
|
|
|
|
|
ctx.scale(2.0, 4.0);
|
|
|
|
ctx.rect(30, 10, 20, 20);
|
|
|
|
ctx.resetTransform();
|
|
|
|
|
|
|
|
ctx.rotate(Math.PI / 3);
|
|
|
|
ctx.rect(50, 10, 20, 20);
|
|
|
|
ctx.resetTransform();
|
|
|
|
|
|
|
|
ctx.translate(30, -2);
|
|
|
|
ctx.rect(70, 10, 20, 20);
|
|
|
|
ctx.resetTransform();
|
|
|
|
|
|
|
|
ctx.translate(60, 0);
|
|
|
|
ctx.rotate(Math.PI / 6);
|
2021-05-05 17:19:49 +00:00
|
|
|
ctx.transform(1.5, 0, 0, 0.5, 0, 0); // effectively scale
|
2018-11-26 16:47:54 +00:00
|
|
|
ctx.rect(90, 10, 20, 20);
|
|
|
|
ctx.resetTransform();
|
|
|
|
|
2018-12-06 15:14:10 +00:00
|
|
|
ctx.save();
|
2018-11-26 16:47:54 +00:00
|
|
|
ctx.setTransform(2, 0, -.5, 2.5, -40, 120);
|
|
|
|
ctx.rect(110, 10, 20, 20);
|
|
|
|
ctx.lineTo(110, 0);
|
2018-12-06 15:14:10 +00:00
|
|
|
ctx.restore();
|
2018-11-26 16:47:54 +00:00
|
|
|
ctx.lineTo(220, 120);
|
|
|
|
|
|
|
|
ctx.scale(3.0, 3.0);
|
2018-12-12 15:35:13 +00:00
|
|
|
ctx.font = '6pt Noto Mono';
|
2018-11-26 16:47:54 +00:00
|
|
|
ctx.fillText('This text should be huge', 10, 80);
|
|
|
|
ctx.resetTransform();
|
|
|
|
|
|
|
|
ctx.strokeStyle = 'black';
|
|
|
|
ctx.lineWidth = 2;
|
|
|
|
ctx.stroke();
|
|
|
|
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.moveTo(250, 30);
|
|
|
|
ctx.lineTo(250, 80);
|
|
|
|
ctx.scale(3.0, 3.0);
|
|
|
|
ctx.lineTo(280/3, 90/3);
|
|
|
|
ctx.closePath();
|
|
|
|
ctx.strokeStyle = 'black';
|
|
|
|
ctx.lineWidth = 5;
|
|
|
|
ctx.stroke();
|
|
|
|
|
|
|
|
}
|
|
|
|
document.getElementById('api3').src = skcanvas.toDataURL();
|
2018-12-07 18:03:08 +00:00
|
|
|
skcanvas.dispose();
|
2018-11-26 16:47:54 +00:00
|
|
|
}
|
|
|
|
|
2018-11-27 18:26:59 +00:00
|
|
|
function CanvasAPI4(CanvasKit) {
|
|
|
|
let skcanvas = CanvasKit.MakeCanvas(300, 300);
|
|
|
|
let realCanvas = document.getElementById('api4_c');
|
|
|
|
realCanvas.width = 300;
|
|
|
|
realCanvas.height = 300;
|
|
|
|
|
|
|
|
for (let canvas of [skcanvas, realCanvas]) {
|
|
|
|
let ctx = canvas.getContext('2d');
|
|
|
|
|
|
|
|
ctx.strokeStyle = '#000';
|
|
|
|
ctx.fillStyle = '#CCC';
|
|
|
|
ctx.shadowColor = 'rebeccapurple';
|
|
|
|
ctx.shadowBlur = 1;
|
|
|
|
ctx.shadowOffsetX = 3;
|
|
|
|
ctx.shadowOffsetY = -8;
|
|
|
|
ctx.rect(10, 10, 30, 30);
|
|
|
|
|
|
|
|
ctx.save();
|
|
|
|
ctx.strokeStyle = '#C00';
|
|
|
|
ctx.fillStyle = '#00C';
|
|
|
|
ctx.shadowBlur = 0;
|
|
|
|
ctx.shadowColor = 'transparent';
|
|
|
|
|
|
|
|
ctx.stroke();
|
|
|
|
|
|
|
|
ctx.restore();
|
|
|
|
ctx.fill();
|
|
|
|
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.moveTo(36, 148);
|
|
|
|
ctx.quadraticCurveTo(66, 188, 120, 136);
|
|
|
|
ctx.closePath();
|
|
|
|
ctx.stroke();
|
|
|
|
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.shadowColor = '#993366AA';
|
|
|
|
ctx.shadowOffsetX = 8;
|
|
|
|
ctx.shadowBlur = 5;
|
|
|
|
ctx.setTransform(2, 0, -.5, 2.5, -40, 120);
|
|
|
|
ctx.rect(110, 10, 20, 20);
|
|
|
|
ctx.lineTo(110, 0);
|
|
|
|
ctx.resetTransform();
|
|
|
|
ctx.lineTo(220, 120);
|
|
|
|
ctx.stroke();
|
|
|
|
|
|
|
|
ctx.fillStyle = 'green';
|
2018-12-12 15:35:13 +00:00
|
|
|
ctx.font = '16pt Noto Mono';
|
2018-11-27 18:26:59 +00:00
|
|
|
ctx.fillText('This should be shadowed', 20, 80);
|
2018-12-06 15:14:10 +00:00
|
|
|
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.lineWidth = 6;
|
|
|
|
ctx.ellipse(10, 290, 30, 30, 0, 0, Math.PI * 2);
|
|
|
|
ctx.scale(2, 1);
|
2020-12-04 14:10:39 +00:00
|
|
|
ctx.moveTo(10, 290);
|
2018-12-06 15:14:10 +00:00
|
|
|
ctx.ellipse(10, 290, 30, 60, 0, 0, Math.PI * 2);
|
|
|
|
ctx.resetTransform();
|
|
|
|
ctx.scale(3, 1);
|
2020-12-04 14:10:39 +00:00
|
|
|
ctx.moveTo(10, 290);
|
2018-12-06 15:14:10 +00:00
|
|
|
ctx.ellipse(10, 290, 30, 90, 0, 0, Math.PI * 2);
|
|
|
|
ctx.stroke();
|
2018-11-27 18:26:59 +00:00
|
|
|
}
|
|
|
|
document.getElementById('api4').src = skcanvas.toDataURL();
|
2018-12-07 18:03:08 +00:00
|
|
|
skcanvas.dispose();
|
2018-11-27 18:26:59 +00:00
|
|
|
}
|
|
|
|
|
2018-11-28 17:51:56 +00:00
|
|
|
function CanvasAPI5(CanvasKit) {
|
|
|
|
let skcanvas = CanvasKit.MakeCanvas(600, 600);
|
|
|
|
let realCanvas = document.getElementById('api5_c');
|
|
|
|
realCanvas.width = 600;
|
|
|
|
realCanvas.height = 600;
|
|
|
|
|
|
|
|
for (let canvas of [skcanvas, realCanvas]) {
|
|
|
|
let ctx = canvas.getContext('2d');
|
|
|
|
ctx.scale(1.1, 1.1);
|
|
|
|
ctx.translate(10, 10);
|
|
|
|
// Shouldn't impact the fillRect calls
|
|
|
|
ctx.setLineDash([5, 3]);
|
|
|
|
|
|
|
|
ctx.fillStyle = 'rgba(200, 0, 100, 0.81)';
|
|
|
|
ctx.fillRect(20, 30, 100, 100);
|
|
|
|
|
|
|
|
ctx.globalAlpha = 0.81;
|
|
|
|
ctx.fillStyle = 'rgba(200, 0, 100, 1.0)';
|
|
|
|
ctx.fillRect(120, 30, 100, 100);
|
|
|
|
// This shouldn't do anything
|
|
|
|
ctx.globalAlpha = 0.1;
|
|
|
|
|
|
|
|
ctx.fillStyle = 'rgba(200, 0, 100, 0.9)';
|
|
|
|
ctx.globalAlpha = 0.9;
|
|
|
|
// Intentional no-op to check ordering
|
|
|
|
ctx.clearRect(220, 30, 100, 100);
|
|
|
|
ctx.fillRect(220, 30, 100, 100);
|
|
|
|
|
|
|
|
ctx.fillRect(320, 30, 100, 100);
|
|
|
|
ctx.clearRect(330, 40, 80, 80);
|
|
|
|
|
|
|
|
ctx.strokeStyle = 'blue';
|
|
|
|
ctx.lineWidth = 3;
|
|
|
|
ctx.setLineDash([5, 3]);
|
|
|
|
ctx.strokeRect(20, 150, 100, 100);
|
|
|
|
ctx.setLineDash([50, 30]);
|
|
|
|
ctx.strokeRect(125, 150, 100, 100);
|
|
|
|
ctx.lineDashOffset = 25;
|
|
|
|
ctx.strokeRect(230, 150, 100, 100);
|
|
|
|
ctx.setLineDash([2, 5, 9]);
|
|
|
|
ctx.strokeRect(335, 150, 100, 100);
|
|
|
|
|
|
|
|
ctx.setLineDash([5, 2]);
|
|
|
|
ctx.moveTo(336, 400);
|
|
|
|
ctx.quadraticCurveTo(366, 488, 120, 450);
|
|
|
|
ctx.lineTo(300, 400);
|
|
|
|
ctx.stroke();
|
|
|
|
|
2018-12-12 15:35:13 +00:00
|
|
|
ctx.font = '36pt Noto Mono';
|
2018-11-28 17:51:56 +00:00
|
|
|
ctx.strokeText('Dashed', 20, 350);
|
|
|
|
ctx.fillText('Not Dashed', 20, 400);
|
|
|
|
|
|
|
|
}
|
|
|
|
document.getElementById('api5').src = skcanvas.toDataURL();
|
2018-12-07 18:03:08 +00:00
|
|
|
skcanvas.dispose();
|
2018-11-28 17:51:56 +00:00
|
|
|
}
|
|
|
|
|
2018-11-29 20:07:02 +00:00
|
|
|
function CanvasAPI6(CanvasKit) {
|
|
|
|
let skcanvas = CanvasKit.MakeCanvas(600, 600);
|
|
|
|
let realCanvas = document.getElementById('api6_c');
|
|
|
|
realCanvas.width = 600;
|
|
|
|
realCanvas.height = 600;
|
|
|
|
|
|
|
|
for (let canvas of [skcanvas, realCanvas]) {
|
|
|
|
let ctx = canvas.getContext('2d');
|
|
|
|
|
2018-12-18 12:43:02 +00:00
|
|
|
let rgradient = ctx.createRadialGradient(200, 300, 10, 100, 100, 300);
|
2018-11-29 20:07:02 +00:00
|
|
|
|
|
|
|
// Add three color stops
|
|
|
|
rgradient.addColorStop(0, 'red');
|
|
|
|
rgradient.addColorStop(0.7, 'white');
|
|
|
|
rgradient.addColorStop(1, 'blue');
|
|
|
|
|
|
|
|
ctx.fillStyle = rgradient;
|
|
|
|
ctx.globalAlpha = 0.7;
|
|
|
|
ctx.fillRect(0, 0, 600, 600);
|
|
|
|
ctx.globalAlpha = 0.95;
|
|
|
|
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.arc(300, 100, 90, 0, Math.PI*1.66);
|
|
|
|
ctx.closePath();
|
|
|
|
ctx.strokeStyle = 'yellow';
|
|
|
|
ctx.lineWidth = 5;
|
|
|
|
ctx.stroke();
|
|
|
|
ctx.save();
|
|
|
|
ctx.clip();
|
|
|
|
|
2018-12-18 12:43:02 +00:00
|
|
|
let lgradient = ctx.createLinearGradient(200, 20, 420, 40);
|
2018-11-29 20:07:02 +00:00
|
|
|
|
|
|
|
// Add three color stops
|
|
|
|
lgradient.addColorStop(0, 'green');
|
|
|
|
lgradient.addColorStop(0.5, 'cyan');
|
|
|
|
lgradient.addColorStop(1, 'orange');
|
|
|
|
|
|
|
|
ctx.fillStyle = lgradient;
|
|
|
|
|
|
|
|
ctx.fillRect(200, 30, 200, 300);
|
|
|
|
|
|
|
|
ctx.restore();
|
|
|
|
ctx.fillRect(550, 550, 40, 40);
|
|
|
|
|
|
|
|
}
|
|
|
|
document.getElementById('api6').src = skcanvas.toDataURL();
|
2018-12-07 18:03:08 +00:00
|
|
|
skcanvas.dispose();
|
2018-11-29 20:07:02 +00:00
|
|
|
}
|
|
|
|
|
2018-12-07 13:29:52 +00:00
|
|
|
function CanvasAPI7(CanvasKit) {
|
|
|
|
let skcanvas = CanvasKit.MakeCanvas(300, 300);
|
|
|
|
let realCanvas = document.getElementById('api7_c');
|
|
|
|
|
2020-02-24 21:30:20 +00:00
|
|
|
let skPromise = fetch(cdn + 'test.png')
|
2018-12-07 13:29:52 +00:00
|
|
|
// if clients want to use a Blob, they are responsible
|
|
|
|
// for reading it themselves.
|
|
|
|
.then((response) => response.arrayBuffer())
|
|
|
|
.then((buffer) => {
|
|
|
|
skcanvas._img = skcanvas.decodeImage(buffer);
|
|
|
|
});
|
2020-02-24 21:30:20 +00:00
|
|
|
let realPromise = fetch(cdn + 'test.png')
|
2018-12-07 13:29:52 +00:00
|
|
|
.then((response) => response.blob())
|
|
|
|
.then((blob) => createImageBitmap(blob))
|
|
|
|
.then((bitmap) => {
|
|
|
|
realCanvas._img = bitmap;
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
Promise.all([realPromise, skPromise]).then(() => {
|
|
|
|
for (let canvas of [skcanvas, realCanvas]) {
|
|
|
|
let ctx = canvas.getContext('2d');
|
|
|
|
ctx.fillStyle = '#EEE';
|
|
|
|
ctx.fillRect(0, 0, 300, 300);
|
|
|
|
ctx.lineWidth = 20;
|
|
|
|
ctx.scale(0.1, 0.2);
|
|
|
|
|
|
|
|
let pattern = ctx.createPattern(canvas._img, 'repeat');
|
|
|
|
ctx.fillStyle = pattern;
|
|
|
|
ctx.fillRect(0, 0, 1500, 750);
|
|
|
|
|
|
|
|
pattern = ctx.createPattern(canvas._img, 'repeat-x');
|
|
|
|
ctx.fillStyle = pattern;
|
|
|
|
ctx.fillRect(1500, 0, 3000, 750);
|
|
|
|
|
2020-12-04 14:10:39 +00:00
|
|
|
ctx.globalAlpha = 0.7;
|
2018-12-07 13:29:52 +00:00
|
|
|
pattern = ctx.createPattern(canvas._img, 'repeat-y');
|
|
|
|
ctx.fillStyle = pattern;
|
|
|
|
ctx.fillRect(0, 750, 1500, 1500);
|
|
|
|
ctx.strokeRect(0, 750, 1500, 1500);
|
|
|
|
|
|
|
|
pattern = ctx.createPattern(canvas._img, 'no-repeat');
|
|
|
|
ctx.fillStyle = pattern;
|
|
|
|
pattern.setTransform({a: 1, b: -.1, c:.1, d: 0.5, e: 1800, f:800});
|
|
|
|
ctx.fillRect(0, 0, 3000, 1500);
|
|
|
|
}
|
|
|
|
|
|
|
|
document.getElementById('api7').src = skcanvas.toDataURL();
|
|
|
|
skcanvas.dispose();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-12-07 18:03:08 +00:00
|
|
|
function CanvasAPI8(CanvasKit) {
|
|
|
|
let skcanvas = CanvasKit.MakeCanvas(300, 300);
|
|
|
|
let realCanvas = document.getElementById('api8_c');
|
|
|
|
|
|
|
|
function drawPoint(ctx, x, y, color) {
|
|
|
|
ctx.fillStyle = color;
|
|
|
|
ctx.fillRect(x, y, 1, 1);
|
|
|
|
}
|
|
|
|
const IN = 'purple';
|
|
|
|
const OUT = 'orange';
|
|
|
|
const SCALE = 4;
|
|
|
|
|
|
|
|
const pts = [[3, 3], [4, 4], [5, 5], [10, 10], [8, 10], [6, 10],
|
|
|
|
[6.5, 9], [15, 10], [17, 10], [17, 11], [24, 24],
|
|
|
|
[25, 25], [26, 26], [27, 27]];
|
|
|
|
|
|
|
|
const tests = [
|
|
|
|
{
|
|
|
|
xOffset: 0,
|
|
|
|
yOffset: 0,
|
|
|
|
fillType: 'nonzero',
|
|
|
|
strokeWidth: 0,
|
|
|
|
testFn: (ctx, x, y) => ctx.isPointInPath(x * SCALE, y * SCALE, 'nonzero'),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
xOffset: 30,
|
|
|
|
yOffset: 0,
|
|
|
|
fillType: 'evenodd',
|
|
|
|
strokeWidth: 0,
|
|
|
|
testFn: (ctx, x, y) => ctx.isPointInPath(x * SCALE, y * SCALE, 'evenodd'),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
xOffset: 0,
|
|
|
|
yOffset: 30,
|
|
|
|
fillType: null,
|
|
|
|
strokeWidth: 1,
|
|
|
|
testFn: (ctx, x, y) => ctx.isPointInStroke(x * SCALE, y * SCALE),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
xOffset: 30,
|
|
|
|
yOffset: 30,
|
|
|
|
fillType: null,
|
|
|
|
strokeWidth: 2,
|
|
|
|
testFn: (ctx, x, y) => ctx.isPointInStroke(x * SCALE, y * SCALE),
|
|
|
|
},
|
2018-12-07 15:09:11 +00:00
|
|
|
];
|
2018-12-07 18:03:08 +00:00
|
|
|
|
|
|
|
for (let canvas of [skcanvas, realCanvas]) {
|
|
|
|
let ctx = canvas.getContext('2d');
|
2018-12-12 15:35:13 +00:00
|
|
|
ctx.font = '11px Noto Mono';
|
2018-12-07 18:03:08 +00:00
|
|
|
// Draw some visual aids
|
|
|
|
ctx.fillText('path-nonzero', 30, 15);
|
|
|
|
ctx.fillText('path-evenodd', 150, 15);
|
|
|
|
ctx.fillText('stroke-1px-wide', 30, 130);
|
|
|
|
ctx.fillText('stroke-2px-wide', 150, 130);
|
|
|
|
ctx.fillText('purple is IN, orange is OUT', 10, 280);
|
|
|
|
|
|
|
|
// Scale up to make single pixels easier to see
|
|
|
|
ctx.scale(SCALE, SCALE);
|
|
|
|
for (let test of tests) {
|
|
|
|
ctx.beginPath();
|
|
|
|
let xOffset = test.xOffset;
|
|
|
|
let yOffset = test.yOffset;
|
|
|
|
|
|
|
|
ctx.fillStyle = '#AAA';
|
|
|
|
ctx.lineWidth = test.strokeWidth;
|
|
|
|
ctx.rect(5+xOffset, 5+yOffset, 20, 20);
|
|
|
|
ctx.arc(15+xOffset, 15+yOffset, 8, 0, Math.PI*2, false);
|
|
|
|
if (test.fillType) {
|
|
|
|
ctx.fill(test.fillType);
|
|
|
|
} else {
|
|
|
|
ctx.stroke();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let pt of pts) {
|
|
|
|
let [x, y] = pt;
|
|
|
|
x += xOffset;
|
|
|
|
y += yOffset;
|
|
|
|
// naively apply transform when querying because the points queried
|
|
|
|
// ignore the CTM.
|
|
|
|
if (test.testFn(ctx, x, y)) {
|
|
|
|
drawPoint(ctx, x, y, IN);
|
|
|
|
} else {
|
|
|
|
drawPoint(ctx, x, y, OUT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
document.getElementById('api8').src = skcanvas.toDataURL();
|
|
|
|
skcanvas.dispose();
|
|
|
|
}
|
|
|
|
|
2018-11-03 11:51:19 +00:00
|
|
|
function VertexAPI1(CanvasKit) {
|
|
|
|
const surface = CanvasKit.MakeCanvasSurface('vertex1');
|
|
|
|
if (!surface) {
|
|
|
|
console.error('Could not make surface');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const canvas = surface.getCanvas();
|
2020-10-07 20:09:22 +00:00
|
|
|
let paint = new CanvasKit.Paint();
|
2018-11-03 11:51:19 +00:00
|
|
|
|
|
|
|
// See https://fiddle.skia.org/c/f48b22eaad1bb7adcc3faaa321754af6
|
|
|
|
// for original c++ version.
|
2020-12-04 14:10:39 +00:00
|
|
|
let points = [0, 0, 250, 0, 100, 100, 0, 250];
|
2018-11-03 11:51:19 +00:00
|
|
|
let colors = [CanvasKit.RED, CanvasKit.BLUE,
|
|
|
|
CanvasKit.YELLOW, CanvasKit.CYAN];
|
2020-10-07 20:09:22 +00:00
|
|
|
let vertices = CanvasKit.MakeVertices(CanvasKit.VertexMode.TriangleFan,
|
2019-03-06 13:25:36 +00:00
|
|
|
points, null, colors,
|
|
|
|
false /*isVolatile*/);
|
2018-11-03 11:51:19 +00:00
|
|
|
|
|
|
|
canvas.drawVertices(vertices, CanvasKit.BlendMode.Src, paint);
|
|
|
|
|
|
|
|
vertices.delete();
|
|
|
|
|
|
|
|
// See https://fiddle.skia.org/c/e8bdae9bea3227758989028424fcac3d
|
|
|
|
// for original c++ version.
|
2020-12-04 14:10:39 +00:00
|
|
|
points = [300, 300, 50, 300, 200, 200, 300, 50 ];
|
|
|
|
let texs = [ 0, 0, 0, 250, 250, 250, 250, 0 ];
|
2020-10-07 20:09:22 +00:00
|
|
|
vertices = CanvasKit.MakeVertices(CanvasKit.VertexMode.TriangleFan,
|
2018-11-03 11:51:19 +00:00
|
|
|
points, texs, colors);
|
|
|
|
|
2020-10-07 20:09:22 +00:00
|
|
|
let shader = CanvasKit.Shader.MakeLinearGradient([0, 0], [250, 0],
|
2018-11-03 11:51:19 +00:00
|
|
|
colors, null, CanvasKit.TileMode.Clamp);
|
|
|
|
paint.setShader(shader);
|
|
|
|
|
|
|
|
canvas.drawVertices(vertices, CanvasKit.BlendMode.Darken, paint);
|
|
|
|
surface.flush();
|
|
|
|
|
|
|
|
shader.delete();
|
|
|
|
paint.delete();
|
|
|
|
surface.delete();
|
|
|
|
}
|
|
|
|
|
|
|
|
function GradiantAPI1(CanvasKit) {
|
2019-03-29 14:39:52 +00:00
|
|
|
const surface = CanvasKit.MakeSWCanvasSurface('gradient1');
|
2018-11-03 11:51:19 +00:00
|
|
|
if (!surface) {
|
|
|
|
console.error('Could not make surface');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const canvas = surface.getCanvas();
|
2020-10-07 20:09:22 +00:00
|
|
|
let paint = new CanvasKit.Paint();
|
2018-11-03 11:51:19 +00:00
|
|
|
|
|
|
|
// See https://fiddle.skia.org/c/f48b22eaad1bb7adcc3faaa321754af6
|
|
|
|
// for original c++ version.
|
|
|
|
let colors = [CanvasKit.BLUE, CanvasKit.YELLOW, CanvasKit.RED];
|
|
|
|
let pos = [0, .7, 1.0];
|
|
|
|
let transform = [2, 0, 0,
|
|
|
|
0, 2, 0,
|
2020-12-04 14:10:39 +00:00
|
|
|
0, 0, 1];
|
|
|
|
let shader = CanvasKit.Shader.MakeRadialGradient([150, 150], 130, colors,
|
2018-11-03 11:51:19 +00:00
|
|
|
pos, CanvasKit.TileMode.Mirror, transform);
|
|
|
|
|
|
|
|
paint.setShader(shader);
|
2020-10-07 20:09:22 +00:00
|
|
|
const textFont = new CanvasKit.Font(null, 75);
|
|
|
|
const textBlob = CanvasKit.TextBlob.MakeFromText('Radial', textFont);
|
2019-01-14 13:36:08 +00:00
|
|
|
|
|
|
|
canvas.drawTextBlob(textBlob, 10, 200, paint);
|
2020-12-04 14:10:39 +00:00
|
|
|
paint.delete();
|
2019-01-14 13:36:08 +00:00
|
|
|
textFont.delete();
|
|
|
|
textBlob.delete();
|
2018-11-03 11:51:19 +00:00
|
|
|
surface.flush();
|
|
|
|
}
|
2019-02-22 15:04:06 +00:00
|
|
|
|
2019-03-15 19:36:29 +00:00
|
|
|
function TextOnPathAPI1(CanvasKit) {
|
2019-03-29 14:39:52 +00:00
|
|
|
const surface = CanvasKit.MakeSWCanvasSurface('textonpath');
|
2019-03-15 19:36:29 +00:00
|
|
|
if (!surface) {
|
|
|
|
console.error('Could not make surface');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const canvas = surface.getCanvas();
|
2020-10-07 20:09:22 +00:00
|
|
|
const paint = new CanvasKit.Paint();
|
2019-03-15 19:36:29 +00:00
|
|
|
paint.setStyle(CanvasKit.PaintStyle.Stroke);
|
2019-03-29 14:39:52 +00:00
|
|
|
paint.setAntiAlias(true);
|
2019-03-15 19:36:29 +00:00
|
|
|
|
2020-10-07 20:09:22 +00:00
|
|
|
const font = new CanvasKit.Font(null, 24);
|
|
|
|
const fontPaint = new CanvasKit.Paint();
|
2019-03-15 19:36:29 +00:00
|
|
|
fontPaint.setStyle(CanvasKit.PaintStyle.Fill);
|
2019-03-29 14:39:52 +00:00
|
|
|
fontPaint.setAntiAlias(true);
|
2019-03-15 19:36:29 +00:00
|
|
|
|
2020-10-07 20:09:22 +00:00
|
|
|
const arc = new CanvasKit.Path();
|
2020-09-03 14:02:10 +00:00
|
|
|
arc.arcToOval(CanvasKit.LTRBRect(20, 40, 280, 300), -160, 140, true);
|
2019-03-15 19:36:29 +00:00
|
|
|
arc.lineTo(210, 140);
|
2020-09-03 14:02:10 +00:00
|
|
|
arc.arcToOval(CanvasKit.LTRBRect(20, 0, 280, 260), 160, -140, true);
|
2019-03-15 19:36:29 +00:00
|
|
|
|
|
|
|
const str = 'This téxt should follow the curve across contours...';
|
2020-10-07 20:09:22 +00:00
|
|
|
const textBlob = CanvasKit.TextBlob.MakeOnPath(str, arc, font);
|
2019-03-15 19:36:29 +00:00
|
|
|
|
|
|
|
canvas.drawPath(arc, paint);
|
|
|
|
canvas.drawTextBlob(textBlob, 0, 0, fontPaint);
|
|
|
|
|
|
|
|
surface.flush();
|
|
|
|
|
|
|
|
textBlob.delete();
|
|
|
|
arc.delete();
|
|
|
|
paint.delete();
|
|
|
|
font.delete();
|
|
|
|
fontPaint.delete();
|
|
|
|
}
|
|
|
|
|
2021-04-14 18:49:35 +00:00
|
|
|
function DrawGlyphsAPI1(CanvasKit) {
|
|
|
|
const surface = CanvasKit.MakeSWCanvasSurface('drawGlyphs');
|
|
|
|
if (!surface) {
|
|
|
|
console.error('Could not make surface');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const canvas = surface.getCanvas();
|
|
|
|
const paint = new CanvasKit.Paint();
|
|
|
|
const font = new CanvasKit.Font(null, 16);
|
|
|
|
paint.setAntiAlias(true);
|
|
|
|
|
|
|
|
let glyphs = [];
|
|
|
|
let positions = [];
|
|
|
|
for (let i = 0; i < 256; ++i) {
|
|
|
|
glyphs.push(i);
|
|
|
|
positions.push((i % 16) * 16);
|
|
|
|
positions.push(Math.round(i/16) * 16);
|
|
|
|
}
|
|
|
|
canvas.drawGlyphs(glyphs, positions, 16, 20, font, paint);
|
|
|
|
|
|
|
|
surface.flush();
|
|
|
|
|
|
|
|
paint.delete();
|
|
|
|
font.delete();
|
|
|
|
}
|
|
|
|
|
2019-03-08 15:04:28 +00:00
|
|
|
function SurfaceAPI1(CanvasKit) {
|
|
|
|
const surface = CanvasKit.MakeCanvasSurface('surfaces');
|
|
|
|
if (!surface) {
|
|
|
|
console.error('Could not make surface');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// create a subsurface as a temporary workspace.
|
|
|
|
const subSurface = surface.makeSurface({
|
|
|
|
width: 50,
|
|
|
|
height: 50,
|
|
|
|
alphaType: CanvasKit.AlphaType.Premul,
|
|
|
|
colorType: CanvasKit.ColorType.RGBA_8888,
|
2020-10-07 20:09:22 +00:00
|
|
|
colorSpace: CanvasKit.ColorSpace.SRGB,
|
2019-03-08 15:04:28 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
if (!subSurface) {
|
|
|
|
console.error('Could not make subsurface');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// draw a small "scene"
|
2020-10-07 20:09:22 +00:00
|
|
|
const paint = new CanvasKit.Paint();
|
2019-03-08 15:04:28 +00:00
|
|
|
paint.setColor(CanvasKit.Color(139, 228, 135, 0.95)); // greenish
|
|
|
|
paint.setStyle(CanvasKit.PaintStyle.Fill);
|
|
|
|
paint.setAntiAlias(true);
|
|
|
|
|
|
|
|
const subCanvas = subSurface.getCanvas();
|
|
|
|
subCanvas.clear(CanvasKit.BLACK);
|
|
|
|
subCanvas.drawRect(CanvasKit.LTRBRect(5, 15, 45, 40), paint);
|
|
|
|
|
|
|
|
paint.setColor(CanvasKit.Color(214, 93, 244)); // purplish
|
|
|
|
for (let i = 0; i < 10; i++) {
|
|
|
|
const x = Math.random() * 50;
|
|
|
|
const y = Math.random() * 50;
|
|
|
|
|
|
|
|
subCanvas.drawOval(CanvasKit.XYWHRect(x, y, 6, 6), paint);
|
|
|
|
}
|
|
|
|
|
2020-10-07 20:09:22 +00:00
|
|
|
// Snap it off as an Image - this image will be in the form the
|
2019-03-08 15:04:28 +00:00
|
|
|
// parent surface prefers (e.g. Texture for GPU / Raster for CPU).
|
|
|
|
const img = subSurface.makeImageSnapshot();
|
|
|
|
|
2019-03-19 13:34:37 +00:00
|
|
|
// clean up the temporary surface (which also cleans up subCanvas)
|
2019-03-08 15:04:28 +00:00
|
|
|
subSurface.delete();
|
|
|
|
paint.delete();
|
|
|
|
|
|
|
|
// Make it repeat a bunch with a shader
|
2021-02-23 14:40:07 +00:00
|
|
|
const pattern = img.makeShaderCubic(CanvasKit.TileMode.Repeat, CanvasKit.TileMode.Mirror,
|
|
|
|
1/3, 1/3);
|
2020-10-07 20:09:22 +00:00
|
|
|
const patternPaint = new CanvasKit.Paint();
|
2019-03-08 15:04:28 +00:00
|
|
|
patternPaint.setShader(pattern);
|
|
|
|
|
|
|
|
let i = 0;
|
|
|
|
|
2019-03-19 13:34:37 +00:00
|
|
|
function drawFrame(canvas) {
|
2019-03-08 15:04:28 +00:00
|
|
|
i++;
|
|
|
|
canvas.clear(CanvasKit.WHITE);
|
|
|
|
|
|
|
|
canvas.drawOval(CanvasKit.LTRBRect(i % 60, i % 60, 300 - (i% 60), 300 - (i % 60)), patternPaint);
|
2019-03-19 13:34:37 +00:00
|
|
|
surface.requestAnimationFrame(drawFrame);
|
2019-03-29 14:39:52 +00:00
|
|
|
}
|
|
|
|
surface.requestAnimationFrame(drawFrame);
|
|
|
|
}
|
|
|
|
|
|
|
|
function AtlasAPI1(CanvasKit, imgData) {
|
|
|
|
if (!CanvasKit || !imgData) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const surface = CanvasKit.MakeCanvasSurface('atlas');
|
|
|
|
if (!surface) {
|
|
|
|
console.error('Could not make surface');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const img = CanvasKit.MakeImageFromEncoded(imgData);
|
|
|
|
|
2020-10-07 20:09:22 +00:00
|
|
|
const paint = new CanvasKit.Paint();
|
2019-03-29 14:39:52 +00:00
|
|
|
paint.setColor(CanvasKit.Color(0, 0, 0, 0.8));
|
|
|
|
|
2021-06-02 14:36:06 +00:00
|
|
|
// Allocate space for 2 rectangles.
|
|
|
|
const srcs = CanvasKit.Malloc(Float32Array, 8);
|
|
|
|
srcs.toTypedArray().set([
|
|
|
|
0, 0, 250, 250, // LTRB
|
|
|
|
250, 0, 500, 250
|
|
|
|
]);
|
|
|
|
|
|
|
|
// Allocate space for 2 RSXForms
|
|
|
|
const dsts = CanvasKit.Malloc(Float32Array, 8);
|
|
|
|
dsts.toTypedArray().set([
|
|
|
|
.5, 0, 0, 0, // scos, ssin, tx, ty
|
|
|
|
0, .8, 200, 100
|
|
|
|
]);
|
|
|
|
|
|
|
|
// Allocate space for 4 colors.
|
|
|
|
const colors = new CanvasKit.Malloc(Uint32Array, 2);
|
|
|
|
colors.toTypedArray().set([
|
|
|
|
CanvasKit.ColorAsInt( 85, 170, 10, 128), // light green
|
|
|
|
CanvasKit.ColorAsInt( 51, 51, 191, 128), // light blue
|
|
|
|
]);
|
2019-03-29 14:39:52 +00:00
|
|
|
|
|
|
|
let i = 0;
|
|
|
|
|
|
|
|
function drawFrame(canvas) {
|
|
|
|
canvas.clear(CanvasKit.WHITE);
|
|
|
|
i++;
|
|
|
|
let scale = 0.5 + Math.sin(i/40)/4;
|
|
|
|
|
|
|
|
// update the coordinates of existing sprites - note that this
|
|
|
|
// does not require a full re-copy of the full array; they are
|
|
|
|
// updated in-place.
|
2021-06-02 14:36:06 +00:00
|
|
|
dsts.toTypedArray().set([0.5, 0, (2*i)%200, (5*Math.round(i/200)) % 200], 0);
|
|
|
|
dsts.toTypedArray().set([scale*Math.sin(i/20), scale*Math.cos(i/20), 200, 100], 4);
|
2019-03-29 14:39:52 +00:00
|
|
|
|
2021-03-25 17:13:23 +00:00
|
|
|
canvas.drawAtlas(img, srcs, dsts, paint, CanvasKit.BlendMode.Plus, colors,
|
|
|
|
{filter: CanvasKit.FilterMode.Nearest});
|
2020-10-07 20:09:22 +00:00
|
|
|
surface.requestAnimationFrame(drawFrame);
|
2019-03-08 15:04:28 +00:00
|
|
|
}
|
2019-03-19 13:34:37 +00:00
|
|
|
surface.requestAnimationFrame(drawFrame);
|
2019-03-08 15:04:28 +00:00
|
|
|
|
|
|
|
}
|
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
|
|
|
|
|
|
|
async function DecodeAPI(CanvasKit, imgData) {
|
|
|
|
if (!CanvasKit || !imgData) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const surface = CanvasKit.MakeCanvasSurface('decode');
|
|
|
|
if (!surface) {
|
|
|
|
console.error('Could not make surface');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const blob = new Blob([ imgData ]);
|
|
|
|
// ImageBitmap is not supported in Safari
|
|
|
|
const imageBitmap = await createImageBitmap(blob);
|
|
|
|
const img = await CanvasKit.MakeImageFromCanvasImageSource(imageBitmap);
|
|
|
|
|
|
|
|
surface.drawOnce((canvas) => {
|
|
|
|
canvas.drawImage(img, 0, 0, null);
|
|
|
|
});
|
|
|
|
}
|
2018-09-20 21:39:31 +00:00
|
|
|
</script>
|