skia2/modules/canvaskit/tests/path.spec.js
Kevin Lubick f8823b5726 Reland "[canvaskit] Change SkRects to be arrays, not objects."
This is a reland of bdc214a50e

The CPU version of SkottieWasm is timing out for reasons unknown,
but the GPU version is happy. I think we can get rid of the CPU
version of the job since it has been more or less superseded by
the SkottieFrames one (and the latter is more stable).

Original change's description:
> [canvaskit] Change SkRects to be arrays, not objects.
>
> This changes several APIs, so there are lots of breaking
> notes in the Changelog.
>
> This made the "draw 100 colored regions" benchmark about
> 20% faster (1ms -> .8ms).
>
> In theory, rendering should stay the same.
>
> Change-Id: Ib80b15e2d980ad5d568fff4460d2b529766c1b36
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/312491
> Reviewed-by: Nathaniel Nifong <nifong@google.com>

Change-Id: I674aba85ecfb30b72e94cbaf89b2d97bfae3b7a4
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/315142
Reviewed-by: Nathaniel Nifong <nifong@google.com>
2020-09-03 14:31:38 +00:00

436 lines
14 KiB
JavaScript

describe('Path 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('path_api_example', (canvas) => {
const paint = new CanvasKit.SkPaint();
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.SkPath();
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);
path.arcToTangent(150, 100, 50, 200, 20);
path.lineTo(160, 160);
path.moveTo(20, 120);
path.lineTo(20, 120);
path.transform([2, 0, 0,
0, 2, 0,
0, 0, 1 ])
canvas.drawPath(path, paint);
const rrect = CanvasKit.RRectXY([100, 10, 140, 62], 10, 4);
const rrectPath = new CanvasKit.SkPath().addRRect(rrect, true);
canvas.drawPath(rrectPath, paint);
rrectPath.delete();
path.delete();
paint.delete();
// See PathKit for more tests, since they share implementation
});
it('can create a path from an SVG string', () => {
//.This is a parallelogram from
// https://upload.wikimedia.org/wikipedia/commons/e/e7/Simple_parallelogram.svg
const path = CanvasKit.MakePathFromSVGString('M 205,5 L 795,5 L 595,295 L 5,295 L 205,5 z');
const cmds = path.toCmds();
expect(cmds).toBeTruthy();
// 1 move, 4 lines, 1 close
// each element in cmds is an array, with index 0 being the verb, and the rest being args
expect(cmds.length).toBe(6);
expect(cmds).toEqual([[CanvasKit.MOVE_VERB, 205, 5],
[CanvasKit.LINE_VERB, 795, 5],
[CanvasKit.LINE_VERB, 595, 295],
[CanvasKit.LINE_VERB, 5, 295],
[CanvasKit.LINE_VERB, 205, 5],
[CanvasKit.CLOSE_VERB]]);
path.delete();
});
it('can create an SVG string from a path', () => {
const cmds = [[CanvasKit.MOVE_VERB, 205, 5],
[CanvasKit.LINE_VERB, 795, 5],
[CanvasKit.LINE_VERB, 595, 295],
[CanvasKit.LINE_VERB, 5, 295],
[CanvasKit.LINE_VERB, 205, 5],
[CanvasKit.CLOSE_VERB]];
const path = CanvasKit.SkPath.MakeFromCmds(cmds);
const svgStr = path.toSVGString();
// We output it in terse form, which is different than Wikipedia's version
expect(svgStr).toEqual('M205 5L795 5L595 295L5 295L205 5Z');
path.delete();
});
it('can create a path with malloced verbs, points, weights', () => {
const mVerbs = CanvasKit.Malloc(Uint8Array, 6);
const mPoints = CanvasKit.Malloc(Float32Array, 18);
const mWeights = CanvasKit.Malloc(Float32Array, 1);
mVerbs.toTypedArray().set([CanvasKit.MOVE_VERB, CanvasKit.LINE_VERB,
CanvasKit.QUAD_VERB, CanvasKit.CONIC_VERB, CanvasKit.CUBIC_VERB, CanvasKit.CLOSE_VERB
]);
mPoints.toTypedArray().set([
1,2, // moveTo
3,4, // lineTo
5,6,7,8, // quadTo
9,10,11,12, // conicTo
13,14,15,16,17,18, // cubicTo
]);
mWeights.toTypedArray().set([117]);
let path = CanvasKit.SkPath.MakeFromVerbsPointsWeights(mVerbs, mPoints, mWeights);
let cmds = path.toCmds();
expect(cmds).toEqual([
[CanvasKit.MOVE_VERB, 1, 2],
[CanvasKit.LINE_VERB, 3, 4],
[CanvasKit.QUAD_VERB, 5, 6, 7, 8],
[CanvasKit.CONIC_VERB, 9, 10, 11, 12, 117],
[CanvasKit.CUBIC_VERB, 13, 14, 15, 16, 17, 18],
[CanvasKit.CLOSE_VERB],
]);
path.delete();
// If given insufficient points, it stops early (but doesn't read out of bounds).
path = CanvasKit.SkPath.MakeFromVerbsPointsWeights(mVerbs, mPoints.subarray(0, 10), mWeights);
cmds = path.toCmds();
expect(cmds).toEqual([
[CanvasKit.MOVE_VERB, 1, 2],
[CanvasKit.LINE_VERB, 3, 4],
[CanvasKit.QUAD_VERB, 5, 6, 7, 8],
]);
path.delete();
CanvasKit.Free(mVerbs);
CanvasKit.Free(mPoints);
CanvasKit.Free(mWeights);
});
it('can create and update a path with verbs and points (no weights)', () => {
const path = CanvasKit.SkPath.MakeFromVerbsPointsWeights(
[CanvasKit.MOVE_VERB, CanvasKit.LINE_VERB],
[1,2, 3,4]);
let cmds = path.toCmds();
expect(cmds).toEqual([
[CanvasKit.MOVE_VERB, 1, 2],
[CanvasKit.LINE_VERB, 3, 4]
]);
path.addVerbsPointsWeights(
[CanvasKit.QUAD_VERB, CanvasKit.CLOSE_VERB],
[5,6,7,8],
);
cmds = path.toCmds();
expect(cmds).toEqual([
[CanvasKit.MOVE_VERB, 1, 2],
[CanvasKit.LINE_VERB, 3, 4],
[CanvasKit.QUAD_VERB, 5, 6, 7, 8],
[CanvasKit.CLOSE_VERB]
]);
path.delete();
});
it('can add points to a path in bulk', () => {
const mVerbs = CanvasKit.Malloc(Uint8Array, 6);
const mPoints = CanvasKit.Malloc(Float32Array, 18);
const mWeights = CanvasKit.Malloc(Float32Array, 1);
mVerbs.toTypedArray().set([CanvasKit.MOVE_VERB, CanvasKit.LINE_VERB,
CanvasKit.QUAD_VERB, CanvasKit.CONIC_VERB, CanvasKit.CUBIC_VERB, CanvasKit.CLOSE_VERB
]);
mPoints.toTypedArray().set([
1,2, // moveTo
3,4, // lineTo
5,6,7,8, // quadTo
9,10,11,12, // conicTo
13,14,15,16,17,18, // cubicTo
]);
mWeights.toTypedArray().set([117]);
const path = new CanvasKit.SkPath();
path.lineTo(77, 88);
path.addVerbsPointsWeights(mVerbs, mPoints, mWeights);
let cmds = path.toCmds();
expect(cmds).toEqual([
[CanvasKit.MOVE_VERB, 0, 0],
[CanvasKit.LINE_VERB, 77, 88],
[CanvasKit.MOVE_VERB, 1, 2],
[CanvasKit.LINE_VERB, 3, 4],
[CanvasKit.QUAD_VERB, 5, 6, 7, 8],
[CanvasKit.CONIC_VERB, 9, 10, 11, 12, 117],
[CanvasKit.CUBIC_VERB, 13, 14, 15, 16, 17, 18],
[CanvasKit.CLOSE_VERB],
]);
path.rewind();
cmds = path.toCmds();
expect(cmds).toEqual([]);
path.delete();
CanvasKit.Free(mVerbs);
CanvasKit.Free(mPoints);
CanvasKit.Free(mWeights);
});
gm('offset_path', (canvas) => {
const path = starPath(CanvasKit);
const paint = new CanvasKit.SkPaint();
paint.setStyle(CanvasKit.PaintStyle.Stroke);
paint.setStrokeWidth(5.0);
paint.setAntiAlias(true);
paint.setColor(CanvasKit.BLACK);
canvas.clear(CanvasKit.WHITE);
canvas.drawPath(path, paint);
path.offset(80, 40);
canvas.drawPath(path, paint);
path.delete();
paint.delete();
});
gm('oval_path', (canvas) => {
const paint = new CanvasKit.SkPaint();
paint.setStyle(CanvasKit.PaintStyle.Stroke);
paint.setStrokeWidth(5.0);
paint.setAntiAlias(true);
paint.setColor(CanvasKit.BLACK);
canvas.clear(CanvasKit.WHITE);
const path = new CanvasKit.SkPath();
path.moveTo(5, 5);
path.lineTo(10, 120);
path.addOval(CanvasKit.LTRBRect(10, 20, 100, 200), false, 3);
path.lineTo(300, 300);
canvas.drawPath(path, paint);
path.delete();
paint.delete();
});
gm('arcto_path', (canvas) => {
const paint = new CanvasKit.SkPaint();
paint.setStyle(CanvasKit.PaintStyle.Stroke);
paint.setStrokeWidth(5.0);
paint.setAntiAlias(true);
paint.setColor(CanvasKit.BLACK);
canvas.clear(CanvasKit.WHITE);
const path = new CanvasKit.SkPath();
// - x1, y1, x2, y2, radius
path.arcToTangent(40, 0, 40, 40, 40);
// - oval (as Rect), startAngle, sweepAngle, forceMoveTo
path.arcToOval(CanvasKit.LTRBRect(90, 10, 120, 200), 30, 300, true);
// - rx, ry, xAxisRotate, useSmallArc, isCCW, x, y
path.moveTo(5, 105);
path.arcToRotated(24, 24, 45, true, false, 82, 156);
canvas.drawPath(path, paint);
path.delete();
paint.delete();
});
gm('path_relative', (canvas) => {
const paint = new CanvasKit.SkPaint();
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.SkPath();
path.rMoveTo(20, 5)
.rLineTo(10, 15) // 30, 20
.rLineTo(10, -5); // 40, 10
path.rLineTo(10, 10); // 50, 20
path.rLineTo(10, -20); // 60, 0
path.rLineTo(-40, 5); // 20, 5
path.moveTo(20, 80)
.rCubicTo(70, -70, 140, 70, 170, -70); // 90, 10, 160, 150, 190, 10
path.moveTo(36, 148)
.rQuadTo(30, 40, 84, -12) // 66, 188, 120, 136
.lineTo(36, 148);
path.moveTo(150, 180)
.rArcTo(24, 24, 45, true, false, -68, -24); // 82, 156
path.lineTo(160, 160);
canvas.drawPath(path, paint);
path.delete();
paint.delete();
});
it('can measure a path', () => {
const path = new CanvasKit.SkPath();
path.moveTo(10, 10)
.lineTo(40, 50); // should be length 50 because of the 3/4/5 triangle rule
path.moveTo(80, 0)
.lineTo(80, 10)
.lineTo(100, 5)
.lineTo(80, 0);
const meas = new CanvasKit.SkPathMeasure(path, false, 1);
expect(meas.getLength()).toBeCloseTo(50.0, 3);
const pt = meas.getPosTan(28.7); // arbitrary point
expect(pt[0]).toBeCloseTo(27.22, 3); // x
expect(pt[1]).toBeCloseTo(32.96, 3); // y
expect(pt[2]).toBeCloseTo(0.6, 3); // dy
expect(pt[3]).toBeCloseTo(0.8, 3); // dy
const subpath = meas.getSegment(20, 40, true); // make sure this doesn't crash
expect(meas.nextContour()).toBeTruthy();
expect(meas.getLength()).toBeCloseTo(51.231, 3);
expect(meas.nextContour()).toBeFalsy();
path.delete();
});
it('can measure the contours of a path', () => {
const path = new CanvasKit.SkPath();
path.moveTo(10, 10)
.lineTo(40, 50); // should be length 50 because of the 3/4/5 triangle rule
path.moveTo(80, 0)
.lineTo(80, 10)
.lineTo(100, 5)
.lineTo(80, 0);
const meas = new CanvasKit.SkContourMeasureIter(path, false, 1);
let cont = meas.next();
expect(cont).toBeTruthy();
expect(cont.length()).toBeCloseTo(50.0, 3);
const pt = cont.getPosTan(28.7); // arbitrary point
expect(pt[0]).toBeCloseTo(27.22, 3); // x
expect(pt[1]).toBeCloseTo(32.96, 3); // y
expect(pt[2]).toBeCloseTo(0.6, 3); // dy
expect(pt[3]).toBeCloseTo(0.8, 3); // dy
const subpath = cont.getSegment(20, 40, true); // make sure this doesn't crash
cont.delete();
cont = meas.next();
expect(cont).toBeTruthy()
expect(cont.length()).toBeCloseTo(51.231, 3);
cont.delete();
expect(meas.next()).toBeFalsy();
meas.delete();
path.delete();
});
gm('drawpoly_path', (canvas) => {
const paint = new CanvasKit.SkPaint();
paint.setStrokeWidth(1.0);
paint.setAntiAlias(true);
paint.setColor(CanvasKit.Color(0, 0, 0, 1.0));
paint.setStyle(CanvasKit.PaintStyle.Stroke);
const points = [[5, 5], [30, 20], [55, 5], [55, 50], [30, 30], [5, 50]];
const pointsObj = CanvasKit.Malloc(Float32Array, 6 * 2);
const mPoints = pointsObj.toTypedArray();
mPoints.set([105, 105, 130, 120, 155, 105, 155, 150, 130, 130, 105, 150]);
const path = new CanvasKit.SkPath();
path.addPoly(points, true)
.moveTo(100, 0)
.addPoly(mPoints, true);
canvas.drawPath(path, paint);
CanvasKit.Free(pointsObj);
path.delete();
paint.delete();
});
// Test trim, adding paths to paths, and a bunch of other path methods.
gm('trim_path', (canvas) => {
canvas.clear(CanvasKit.WHITE);
const paint = new CanvasKit.SkPaint();
paint.setStrokeWidth(1.0);
paint.setAntiAlias(true);
paint.setColor(CanvasKit.Color(0, 0, 0, 1.0));
paint.setStyle(CanvasKit.PaintStyle.Stroke);
const arcpath = new CanvasKit.SkPath();
arcpath.arc(400, 400, 100, 0, -90, false) // x, y, radius, startAngle, endAngle, ccw
.dash(3, 1, 0)
.conicTo(10, 20, 30, 40, 5)
.rConicTo(60, 70, 80, 90, 5)
.trim(0.2, 1, false);
const path = new CanvasKit.SkPath();
path.addArc(CanvasKit.LTRBRect(10, 20, 100, 200), 30, 300)
.addRect(CanvasKit.LTRBRect(200, 200, 300, 300)) // test single arg, default cw
.addRect(CanvasKit.LTRBRect(240, 240, 260, 260), true) // test two arg, true means ccw
.addRect([260, 260, 290, 290], true) // test five arg, true means ccw
.addRRect([300, 10, 500, 290, // SkRect in LTRB order
60, 60, 60, 60, 60, 60, 60, 60], // all radii are the same
false) // ccw
.addRRect(CanvasKit.RRectXY([350, 60, 450, 240], 20, 80), true) // SkRect, rx, ry, ccw
.addPath(arcpath)
.transform(0.54, -0.84, 390.35,
0.84, 0.54, -114.53,
0, 0, 1);
canvas.drawPath(path, paint);
path.delete();
paint.delete();
});
});