1cb93a386Sopenharmony_cidescribe('Canvas Behavior', () => {
2cb93a386Sopenharmony_ci    let container;
3cb93a386Sopenharmony_ci
4cb93a386Sopenharmony_ci    beforeEach(async () => {
5cb93a386Sopenharmony_ci        await LoadCanvasKit;
6cb93a386Sopenharmony_ci        container = document.createElement('div');
7cb93a386Sopenharmony_ci        container.innerHTML = `
8cb93a386Sopenharmony_ci            <canvas width=600 height=600 id=test></canvas>
9cb93a386Sopenharmony_ci            <canvas width=600 height=600 id=report></canvas>`;
10cb93a386Sopenharmony_ci        document.body.appendChild(container);
11cb93a386Sopenharmony_ci    });
12cb93a386Sopenharmony_ci
13cb93a386Sopenharmony_ci    afterEach(() => {
14cb93a386Sopenharmony_ci        document.body.removeChild(container);
15cb93a386Sopenharmony_ci    });
16cb93a386Sopenharmony_ci
17cb93a386Sopenharmony_ci    gm('canvas_api_example', (canvas) => {
18cb93a386Sopenharmony_ci        const paint = new CanvasKit.Paint();
19cb93a386Sopenharmony_ci        paint.setStrokeWidth(2.0);
20cb93a386Sopenharmony_ci        paint.setAntiAlias(true);
21cb93a386Sopenharmony_ci        paint.setColor(CanvasKit.Color(0, 0, 0, 1.0));
22cb93a386Sopenharmony_ci        paint.setStyle(CanvasKit.PaintStyle.Stroke);
23cb93a386Sopenharmony_ci
24cb93a386Sopenharmony_ci        canvas.drawLine(3, 10, 30, 15, paint);
25cb93a386Sopenharmony_ci        const rrect = CanvasKit.RRectXY([5, 35, 45, 80], 15, 10);
26cb93a386Sopenharmony_ci        canvas.drawRRect(rrect, paint);
27cb93a386Sopenharmony_ci
28cb93a386Sopenharmony_ci        canvas.drawOval(CanvasKit.LTRBRect(5, 35, 45, 80), paint);
29cb93a386Sopenharmony_ci
30cb93a386Sopenharmony_ci        canvas.drawArc(CanvasKit.LTRBRect(55, 35, 95, 80), 15, 270, true, paint);
31cb93a386Sopenharmony_ci
32cb93a386Sopenharmony_ci        const font = new CanvasKit.Font(null, 20);
33cb93a386Sopenharmony_ci        canvas.drawText('this is ascii text', 5, 100, paint, font);
34cb93a386Sopenharmony_ci
35cb93a386Sopenharmony_ci        const blob = CanvasKit.TextBlob.MakeFromText('Unicode chars � é É ص', font);
36cb93a386Sopenharmony_ci        canvas.drawTextBlob(blob, 5, 130, paint);
37cb93a386Sopenharmony_ci
38cb93a386Sopenharmony_ci        font.delete();
39cb93a386Sopenharmony_ci        blob.delete();
40cb93a386Sopenharmony_ci        paint.delete();
41cb93a386Sopenharmony_ci        // See canvas2d for more API tests
42cb93a386Sopenharmony_ci    });
43cb93a386Sopenharmony_ci
44cb93a386Sopenharmony_ci    gm('effect_and_text_example', (canvas) => {
45cb93a386Sopenharmony_ci        const path = starPath(CanvasKit);
46cb93a386Sopenharmony_ci        const paint = new CanvasKit.Paint();
47cb93a386Sopenharmony_ci
48cb93a386Sopenharmony_ci        const textPaint = new CanvasKit.Paint();
49cb93a386Sopenharmony_ci        textPaint.setColor(CanvasKit.Color(40, 0, 0, 1.0));
50cb93a386Sopenharmony_ci        textPaint.setAntiAlias(true);
51cb93a386Sopenharmony_ci
52cb93a386Sopenharmony_ci        const textFont = new CanvasKit.Font(null, 30);
53cb93a386Sopenharmony_ci
54cb93a386Sopenharmony_ci        const dpe = CanvasKit.PathEffect.MakeDash([15, 5, 5, 10], 1);
55cb93a386Sopenharmony_ci
56cb93a386Sopenharmony_ci        paint.setPathEffect(dpe);
57cb93a386Sopenharmony_ci        paint.setStyle(CanvasKit.PaintStyle.Stroke);
58cb93a386Sopenharmony_ci        paint.setStrokeWidth(5.0);
59cb93a386Sopenharmony_ci        paint.setAntiAlias(true);
60cb93a386Sopenharmony_ci        paint.setColor(CanvasKit.Color(66, 129, 164, 1.0));
61cb93a386Sopenharmony_ci
62cb93a386Sopenharmony_ci        canvas.clear(CanvasKit.Color(255, 255, 255, 1.0));
63cb93a386Sopenharmony_ci
64cb93a386Sopenharmony_ci        canvas.drawPath(path, paint);
65cb93a386Sopenharmony_ci        canvas.drawText('This is text', 10, 280, textPaint, textFont);
66cb93a386Sopenharmony_ci
67cb93a386Sopenharmony_ci        dpe.delete();
68cb93a386Sopenharmony_ci        path.delete();
69cb93a386Sopenharmony_ci        paint.delete();
70cb93a386Sopenharmony_ci        textFont.delete();
71cb93a386Sopenharmony_ci        textPaint.delete();
72cb93a386Sopenharmony_ci    });
73cb93a386Sopenharmony_ci
74cb93a386Sopenharmony_ci    gm('patheffects_canvas', (canvas) => {
75cb93a386Sopenharmony_ci        canvas.clear(CanvasKit.WHITE);
76cb93a386Sopenharmony_ci        const path = starPath(CanvasKit, 100, 100, 100);
77cb93a386Sopenharmony_ci        const paint = new CanvasKit.Paint();
78cb93a386Sopenharmony_ci
79cb93a386Sopenharmony_ci        const cornerEffect = CanvasKit.PathEffect.MakeCorner(10);
80cb93a386Sopenharmony_ci        const discreteEffect = CanvasKit.PathEffect.MakeDiscrete(5, 10, 0);
81cb93a386Sopenharmony_ci
82cb93a386Sopenharmony_ci        paint.setPathEffect(cornerEffect);
83cb93a386Sopenharmony_ci        paint.setStyle(CanvasKit.PaintStyle.Stroke);
84cb93a386Sopenharmony_ci        paint.setStrokeWidth(5.0);
85cb93a386Sopenharmony_ci        paint.setAntiAlias(true);
86cb93a386Sopenharmony_ci        paint.setColor(CanvasKit.Color(66, 129, 164, 1.0));
87cb93a386Sopenharmony_ci        canvas.drawPath(path, paint);
88cb93a386Sopenharmony_ci
89cb93a386Sopenharmony_ci        canvas.translate(200, 0);
90cb93a386Sopenharmony_ci
91cb93a386Sopenharmony_ci        paint.setPathEffect(discreteEffect);
92cb93a386Sopenharmony_ci        canvas.drawPath(path, paint);
93cb93a386Sopenharmony_ci
94cb93a386Sopenharmony_ci        cornerEffect.delete();
95cb93a386Sopenharmony_ci        path.delete();
96cb93a386Sopenharmony_ci        paint.delete();
97cb93a386Sopenharmony_ci    });
98cb93a386Sopenharmony_ci
99cb93a386Sopenharmony_ci    it('returns the depth of the save state stack', () => {
100cb93a386Sopenharmony_ci        const canvas = new CanvasKit.Canvas();
101cb93a386Sopenharmony_ci        expect(canvas.getSaveCount()).toEqual(1);
102cb93a386Sopenharmony_ci        canvas.save();
103cb93a386Sopenharmony_ci        canvas.save();
104cb93a386Sopenharmony_ci        canvas.restore();
105cb93a386Sopenharmony_ci        canvas.save();
106cb93a386Sopenharmony_ci        canvas.save();
107cb93a386Sopenharmony_ci        expect(canvas.getSaveCount()).toEqual(4);
108cb93a386Sopenharmony_ci        // does nothing, by the SkCanvas API
109cb93a386Sopenharmony_ci        canvas.restoreToCount(500);
110cb93a386Sopenharmony_ci        expect(canvas.getSaveCount()).toEqual(4);
111cb93a386Sopenharmony_ci        canvas.restore();
112cb93a386Sopenharmony_ci        expect(canvas.getSaveCount()).toEqual(3);
113cb93a386Sopenharmony_ci        canvas.save();
114cb93a386Sopenharmony_ci        canvas.restoreToCount(2);
115cb93a386Sopenharmony_ci        expect(canvas.getSaveCount()).toEqual(2);
116cb93a386Sopenharmony_ci    });
117cb93a386Sopenharmony_ci
118cb93a386Sopenharmony_ci    gm('circle_canvas', (canvas) => {
119cb93a386Sopenharmony_ci        const path = starPath(CanvasKit);
120cb93a386Sopenharmony_ci
121cb93a386Sopenharmony_ci        const paint = new CanvasKit.Paint();
122cb93a386Sopenharmony_ci
123cb93a386Sopenharmony_ci        paint.setStyle(CanvasKit.PaintStyle.Stroke);
124cb93a386Sopenharmony_ci        paint.setStrokeWidth(5.0);
125cb93a386Sopenharmony_ci        paint.setAntiAlias(true);
126cb93a386Sopenharmony_ci        paint.setColor(CanvasKit.CYAN);
127cb93a386Sopenharmony_ci
128cb93a386Sopenharmony_ci        canvas.clear(CanvasKit.WHITE);
129cb93a386Sopenharmony_ci
130cb93a386Sopenharmony_ci        canvas.drawCircle(30, 50, 15, paint);
131cb93a386Sopenharmony_ci
132cb93a386Sopenharmony_ci        paint.setStyle(CanvasKit.PaintStyle.Fill);
133cb93a386Sopenharmony_ci        paint.setColor(CanvasKit.RED);
134cb93a386Sopenharmony_ci        canvas.drawCircle(130, 80, 60, paint);
135cb93a386Sopenharmony_ci        canvas.drawCircle(20, 150, 60, paint);
136cb93a386Sopenharmony_ci
137cb93a386Sopenharmony_ci        path.delete();
138cb93a386Sopenharmony_ci        paint.delete();
139cb93a386Sopenharmony_ci    });
140cb93a386Sopenharmony_ci
141cb93a386Sopenharmony_ci    gm('rrect_canvas', (canvas) => {
142cb93a386Sopenharmony_ci        const path = starPath(CanvasKit);
143cb93a386Sopenharmony_ci
144cb93a386Sopenharmony_ci        const paint = new CanvasKit.Paint();
145cb93a386Sopenharmony_ci
146cb93a386Sopenharmony_ci        paint.setStyle(CanvasKit.PaintStyle.Stroke);
147cb93a386Sopenharmony_ci        paint.setStrokeWidth(3.0);
148cb93a386Sopenharmony_ci        paint.setAntiAlias(true);
149cb93a386Sopenharmony_ci        paint.setColor(CanvasKit.BLACK);
150cb93a386Sopenharmony_ci
151cb93a386Sopenharmony_ci        canvas.clear(CanvasKit.WHITE);
152cb93a386Sopenharmony_ci
153cb93a386Sopenharmony_ci        canvas.drawRRect(CanvasKit.RRectXY(
154cb93a386Sopenharmony_ci            CanvasKit.LTRBRect(10, 10, 50, 50), 5, 10), paint);
155cb93a386Sopenharmony_ci
156cb93a386Sopenharmony_ci        canvas.drawRRect(CanvasKit.RRectXY(
157cb93a386Sopenharmony_ci            CanvasKit.LTRBRect(60, 10, 110, 50), 10, 5), paint);
158cb93a386Sopenharmony_ci
159cb93a386Sopenharmony_ci        canvas.drawRRect(CanvasKit.RRectXY(
160cb93a386Sopenharmony_ci            CanvasKit.LTRBRect(10, 60, 210, 260), 0, 30), paint);
161cb93a386Sopenharmony_ci
162cb93a386Sopenharmony_ci        canvas.drawRRect(CanvasKit.RRectXY(
163cb93a386Sopenharmony_ci            CanvasKit.LTRBRect(50, 90, 160, 210), 30, 30), paint);
164cb93a386Sopenharmony_ci
165cb93a386Sopenharmony_ci        path.delete();
166cb93a386Sopenharmony_ci        paint.delete();
167cb93a386Sopenharmony_ci    });
168cb93a386Sopenharmony_ci
169cb93a386Sopenharmony_ci    gm('rrect_8corners_canvas', (canvas) => {
170cb93a386Sopenharmony_ci        const path = starPath(CanvasKit);
171cb93a386Sopenharmony_ci
172cb93a386Sopenharmony_ci        const paint = new CanvasKit.Paint();
173cb93a386Sopenharmony_ci
174cb93a386Sopenharmony_ci        paint.setStyle(CanvasKit.PaintStyle.Stroke);
175cb93a386Sopenharmony_ci        paint.setStrokeWidth(3.0);
176cb93a386Sopenharmony_ci        paint.setAntiAlias(true);
177cb93a386Sopenharmony_ci        paint.setColor(CanvasKit.BLACK);
178cb93a386Sopenharmony_ci
179cb93a386Sopenharmony_ci        canvas.clear(CanvasKit.WHITE);
180cb93a386Sopenharmony_ci
181cb93a386Sopenharmony_ci        canvas.drawRRect([10, 10, 210, 210,
182cb93a386Sopenharmony_ci          // top left corner, going clockwise
183cb93a386Sopenharmony_ci          10, 30,
184cb93a386Sopenharmony_ci          30, 10,
185cb93a386Sopenharmony_ci          50, 75,
186cb93a386Sopenharmony_ci          120, 120,
187cb93a386Sopenharmony_ci        ], paint);
188cb93a386Sopenharmony_ci
189cb93a386Sopenharmony_ci        path.delete();
190cb93a386Sopenharmony_ci        paint.delete();
191cb93a386Sopenharmony_ci    });
192cb93a386Sopenharmony_ci
193cb93a386Sopenharmony_ci    // As above, except with the array passed in via malloc'd memory.
194cb93a386Sopenharmony_ci    gm('rrect_8corners_malloc_canvas', (canvas) => {
195cb93a386Sopenharmony_ci        const path = starPath(CanvasKit);
196cb93a386Sopenharmony_ci
197cb93a386Sopenharmony_ci        const paint = new CanvasKit.Paint();
198cb93a386Sopenharmony_ci
199cb93a386Sopenharmony_ci        paint.setStyle(CanvasKit.PaintStyle.Stroke);
200cb93a386Sopenharmony_ci        paint.setStrokeWidth(3.0);
201cb93a386Sopenharmony_ci        paint.setAntiAlias(true);
202cb93a386Sopenharmony_ci        paint.setColor(CanvasKit.BLACK);
203cb93a386Sopenharmony_ci
204cb93a386Sopenharmony_ci        canvas.clear(CanvasKit.WHITE);
205cb93a386Sopenharmony_ci
206cb93a386Sopenharmony_ci        const rrect = CanvasKit.Malloc(Float32Array, 12);
207cb93a386Sopenharmony_ci        rrect.toTypedArray().set([10, 10, 210, 210,
208cb93a386Sopenharmony_ci          // top left corner, going clockwise
209cb93a386Sopenharmony_ci          10, 30,
210cb93a386Sopenharmony_ci          30, 10,
211cb93a386Sopenharmony_ci          50, 75,
212cb93a386Sopenharmony_ci          120, 120,
213cb93a386Sopenharmony_ci        ]);
214cb93a386Sopenharmony_ci
215cb93a386Sopenharmony_ci        canvas.drawRRect(rrect, paint);
216cb93a386Sopenharmony_ci
217cb93a386Sopenharmony_ci        CanvasKit.Free(rrect);
218cb93a386Sopenharmony_ci        path.delete();
219cb93a386Sopenharmony_ci        paint.delete();
220cb93a386Sopenharmony_ci    });
221cb93a386Sopenharmony_ci
222cb93a386Sopenharmony_ci    gm('drawDRRect_canvas', (canvas) => {
223cb93a386Sopenharmony_ci        const path = starPath(CanvasKit);
224cb93a386Sopenharmony_ci
225cb93a386Sopenharmony_ci        const paint = new CanvasKit.Paint();
226cb93a386Sopenharmony_ci
227cb93a386Sopenharmony_ci        paint.setStyle(CanvasKit.PaintStyle.Fill);
228cb93a386Sopenharmony_ci        paint.setStrokeWidth(3.0);
229cb93a386Sopenharmony_ci        paint.setAntiAlias(true);
230cb93a386Sopenharmony_ci        paint.setColor(CanvasKit.BLACK);
231cb93a386Sopenharmony_ci
232cb93a386Sopenharmony_ci        canvas.clear(CanvasKit.WHITE);
233cb93a386Sopenharmony_ci
234cb93a386Sopenharmony_ci        const outer = CanvasKit.RRectXY(CanvasKit.LTRBRect(10, 60, 210, 260), 10, 5);
235cb93a386Sopenharmony_ci        const inner = CanvasKit.RRectXY(CanvasKit.LTRBRect(50, 90, 160, 210), 30, 30);
236cb93a386Sopenharmony_ci
237cb93a386Sopenharmony_ci        canvas.drawDRRect(outer, inner, paint);
238cb93a386Sopenharmony_ci
239cb93a386Sopenharmony_ci        path.delete();
240cb93a386Sopenharmony_ci        paint.delete();
241cb93a386Sopenharmony_ci    });
242cb93a386Sopenharmony_ci
243cb93a386Sopenharmony_ci    gm('colorfilters_canvas', (canvas) => {
244cb93a386Sopenharmony_ci        const paint = new CanvasKit.Paint();
245cb93a386Sopenharmony_ci
246cb93a386Sopenharmony_ci        const blue = CanvasKit.ColorFilter.MakeBlend(
247cb93a386Sopenharmony_ci            CanvasKit.BLUE, CanvasKit.BlendMode.SrcIn);
248cb93a386Sopenharmony_ci        const red =  CanvasKit.ColorFilter.MakeBlend(
249cb93a386Sopenharmony_ci            CanvasKit.Color(255, 0, 0, 0.8), CanvasKit.BlendMode.SrcOver);
250cb93a386Sopenharmony_ci        const lerp = CanvasKit.ColorFilter.MakeLerp(0.6, red, blue);
251cb93a386Sopenharmony_ci
252cb93a386Sopenharmony_ci        paint.setStyle(CanvasKit.PaintStyle.Fill);
253cb93a386Sopenharmony_ci        paint.setAntiAlias(true);
254cb93a386Sopenharmony_ci
255cb93a386Sopenharmony_ci        canvas.clear(CanvasKit.Color(230, 230, 230));
256cb93a386Sopenharmony_ci
257cb93a386Sopenharmony_ci        paint.setColorFilter(blue)
258cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(10, 10, 60, 60), paint);
259cb93a386Sopenharmony_ci        paint.setColorFilter(lerp)
260cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(50, 10, 100, 60), paint);
261cb93a386Sopenharmony_ci        paint.setColorFilter(red)
262cb93a386Sopenharmony_ci        canvas.drawRect4f(90, 10, 140, 60, paint);
263cb93a386Sopenharmony_ci
264cb93a386Sopenharmony_ci        const r = CanvasKit.ColorMatrix.rotated(0, .707, -.707);
265cb93a386Sopenharmony_ci        const b = CanvasKit.ColorMatrix.rotated(2, .5, .866);
266cb93a386Sopenharmony_ci        const s = CanvasKit.ColorMatrix.scaled(0.9, 1.5, 0.8, 0.8);
267cb93a386Sopenharmony_ci        let cm = CanvasKit.ColorMatrix.concat(r, s);
268cb93a386Sopenharmony_ci        cm = CanvasKit.ColorMatrix.concat(cm, b);
269cb93a386Sopenharmony_ci        CanvasKit.ColorMatrix.postTranslate(cm, 20, 0, -10, 0);
270cb93a386Sopenharmony_ci
271cb93a386Sopenharmony_ci        const mat = CanvasKit.ColorFilter.MakeMatrix(cm);
272cb93a386Sopenharmony_ci        const final = CanvasKit.ColorFilter.MakeCompose(mat, lerp);
273cb93a386Sopenharmony_ci
274cb93a386Sopenharmony_ci        paint.setColorFilter(final)
275cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(10, 70, 140, 120), paint);
276cb93a386Sopenharmony_ci
277cb93a386Sopenharmony_ci        paint.delete();
278cb93a386Sopenharmony_ci        blue.delete();
279cb93a386Sopenharmony_ci        red.delete();
280cb93a386Sopenharmony_ci        lerp.delete();
281cb93a386Sopenharmony_ci        final.delete();
282cb93a386Sopenharmony_ci    });
283cb93a386Sopenharmony_ci
284cb93a386Sopenharmony_ci    gm('blendmodes_canvas', (canvas) => {
285cb93a386Sopenharmony_ci        canvas.clear(CanvasKit.WHITE);
286cb93a386Sopenharmony_ci
287cb93a386Sopenharmony_ci        const blendModeNames = Object.keys(CanvasKit.BlendMode).filter((key) => key !== 'values');
288cb93a386Sopenharmony_ci
289cb93a386Sopenharmony_ci        const PASTEL_MUSTARD_YELLOW = CanvasKit.Color(248, 213, 85, 1.0);
290cb93a386Sopenharmony_ci        const PASTEL_SKY_BLUE = CanvasKit.Color(74, 174, 245, 1.0);
291cb93a386Sopenharmony_ci
292cb93a386Sopenharmony_ci        const shapePaint = new CanvasKit.Paint();
293cb93a386Sopenharmony_ci        shapePaint.setColor(PASTEL_MUSTARD_YELLOW);
294cb93a386Sopenharmony_ci        shapePaint.setAntiAlias(true);
295cb93a386Sopenharmony_ci
296cb93a386Sopenharmony_ci        const textPaint = new CanvasKit.Paint();
297cb93a386Sopenharmony_ci        textPaint.setAntiAlias(true);
298cb93a386Sopenharmony_ci
299cb93a386Sopenharmony_ci        const textFont = new CanvasKit.Font(null, 10);
300cb93a386Sopenharmony_ci
301cb93a386Sopenharmony_ci        let x = 10;
302cb93a386Sopenharmony_ci        let y = 20;
303cb93a386Sopenharmony_ci        for (const blendModeName of blendModeNames) {
304cb93a386Sopenharmony_ci            // Draw a checkerboard for each blend mode.
305cb93a386Sopenharmony_ci            // Each checkerboard is labelled with a blendmode's name.
306cb93a386Sopenharmony_ci            canvas.drawText(blendModeName, x, y - 5, textPaint, textFont);
307cb93a386Sopenharmony_ci            drawCheckerboard(canvas, x, y, x + 80, y + 80);
308cb93a386Sopenharmony_ci
309cb93a386Sopenharmony_ci            // A blue square is drawn on to each checkerboard with yellow circle.
310cb93a386Sopenharmony_ci            // In each checkerboard the blue square is drawn using a different blendmode.
311cb93a386Sopenharmony_ci            const blendMode = CanvasKit.BlendMode[blendModeName];
312cb93a386Sopenharmony_ci            canvas.drawOval(CanvasKit.LTRBRect(x + 5, y + 5, x + 55, y + 55), shapePaint);
313cb93a386Sopenharmony_ci            drawRectangle(x + 30, y + 30, x + 70, y + 70, PASTEL_SKY_BLUE, blendMode);
314cb93a386Sopenharmony_ci
315cb93a386Sopenharmony_ci            x += 90;
316cb93a386Sopenharmony_ci            if (x > 500) {
317cb93a386Sopenharmony_ci                x = 10;
318cb93a386Sopenharmony_ci                y += 110;
319cb93a386Sopenharmony_ci            }
320cb93a386Sopenharmony_ci        }
321cb93a386Sopenharmony_ci
322cb93a386Sopenharmony_ci        function drawCheckerboard(canvas, x1, y1, x2, y2) {
323cb93a386Sopenharmony_ci            const CHECKERBOARD_SQUARE_SIZE = 5;
324cb93a386Sopenharmony_ci            const GREY = CanvasKit.Color(220, 220, 220, 0.5);
325cb93a386Sopenharmony_ci            // Draw black border and white background for checkerboard
326cb93a386Sopenharmony_ci            drawRectangle(x1-1, y1-1, x2+1, y2+1, CanvasKit.BLACK);
327cb93a386Sopenharmony_ci            drawRectangle(x1, y1, x2, y2, CanvasKit.WHITE);
328cb93a386Sopenharmony_ci
329cb93a386Sopenharmony_ci            // Draw checkerboard squares
330cb93a386Sopenharmony_ci            const numberOfColumns = (x2 - x1) / CHECKERBOARD_SQUARE_SIZE;
331cb93a386Sopenharmony_ci            const numberOfRows = (y2 - y1) / CHECKERBOARD_SQUARE_SIZE
332cb93a386Sopenharmony_ci
333cb93a386Sopenharmony_ci            for (let row = 0; row < numberOfRows; row++) {
334cb93a386Sopenharmony_ci                for (let column = 0; column < numberOfColumns; column++) {
335cb93a386Sopenharmony_ci                    const rowIsEven = row % 2 === 0;
336cb93a386Sopenharmony_ci                    const columnIsEven = column % 2 === 0;
337cb93a386Sopenharmony_ci
338cb93a386Sopenharmony_ci                    if ((rowIsEven && !columnIsEven) || (!rowIsEven && columnIsEven)) {
339cb93a386Sopenharmony_ci                        drawRectangle(
340cb93a386Sopenharmony_ci                            x1 + CHECKERBOARD_SQUARE_SIZE * row,
341cb93a386Sopenharmony_ci                            y1 + CHECKERBOARD_SQUARE_SIZE * column,
342cb93a386Sopenharmony_ci                            Math.min(x1 + CHECKERBOARD_SQUARE_SIZE * row + CHECKERBOARD_SQUARE_SIZE, x2),
343cb93a386Sopenharmony_ci                            Math.min(y1 + CHECKERBOARD_SQUARE_SIZE * column + CHECKERBOARD_SQUARE_SIZE, y2),
344cb93a386Sopenharmony_ci                            GREY
345cb93a386Sopenharmony_ci                        );
346cb93a386Sopenharmony_ci                    }
347cb93a386Sopenharmony_ci                }
348cb93a386Sopenharmony_ci            }
349cb93a386Sopenharmony_ci        }
350cb93a386Sopenharmony_ci
351cb93a386Sopenharmony_ci        function drawRectangle(x1, y1, x2, y2, color, blendMode=CanvasKit.BlendMode.srcOver) {
352cb93a386Sopenharmony_ci            canvas.save();
353cb93a386Sopenharmony_ci            canvas.clipRect(CanvasKit.LTRBRect(x1, y1, x2, y2), CanvasKit.ClipOp.Intersect, true);
354cb93a386Sopenharmony_ci            canvas.drawColor(color, blendMode);
355cb93a386Sopenharmony_ci            canvas.restore();
356cb93a386Sopenharmony_ci        }
357cb93a386Sopenharmony_ci    });
358cb93a386Sopenharmony_ci
359cb93a386Sopenharmony_ci    gm('colorfilters_malloc_canvas', (canvas) => {
360cb93a386Sopenharmony_ci        const paint = new CanvasKit.Paint();
361cb93a386Sopenharmony_ci
362cb93a386Sopenharmony_ci        const src = [
363cb93a386Sopenharmony_ci             0.8,   0.45,      2,   0,  20,
364cb93a386Sopenharmony_ci            0.53, -0.918,  0.566,   0,   0,
365cb93a386Sopenharmony_ci            0.53, -0.918, -0.566,   0, -10,
366cb93a386Sopenharmony_ci               0,      0,      0, 0.8,   0,
367cb93a386Sopenharmony_ci        ]
368cb93a386Sopenharmony_ci        const colorObj = new CanvasKit.Malloc(Float32Array, 20);
369cb93a386Sopenharmony_ci        const cm = colorObj.toTypedArray();
370cb93a386Sopenharmony_ci        for (i in src) {
371cb93a386Sopenharmony_ci            cm[i] = src[i];
372cb93a386Sopenharmony_ci        }
373cb93a386Sopenharmony_ci        // MakeMatrix will free the malloc'd array when it is done with it.
374cb93a386Sopenharmony_ci        const final = CanvasKit.ColorFilter.MakeMatrix(cm);
375cb93a386Sopenharmony_ci
376cb93a386Sopenharmony_ci        paint.setColorFilter(final)
377cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(10, 70, 140, 120), paint);
378cb93a386Sopenharmony_ci
379cb93a386Sopenharmony_ci        CanvasKit.Free(colorObj);
380cb93a386Sopenharmony_ci        paint.delete();
381cb93a386Sopenharmony_ci        final.delete();
382cb93a386Sopenharmony_ci    });
383cb93a386Sopenharmony_ci
384cb93a386Sopenharmony_ci    gm('clips_canvas', (canvas) => {
385cb93a386Sopenharmony_ci        const path = starPath(CanvasKit);
386cb93a386Sopenharmony_ci        const paint = new CanvasKit.Paint();
387cb93a386Sopenharmony_ci        paint.setColor(CanvasKit.BLUE);
388cb93a386Sopenharmony_ci        const rrect = CanvasKit.RRectXY(CanvasKit.LTRBRect(300, 300, 500, 500), 40, 40);
389cb93a386Sopenharmony_ci
390cb93a386Sopenharmony_ci        canvas.save();
391cb93a386Sopenharmony_ci        // draw magenta around the outside edge of an rrect.
392cb93a386Sopenharmony_ci        canvas.clipRRect(rrect, CanvasKit.ClipOp.Difference, true);
393cb93a386Sopenharmony_ci        canvas.drawColorComponents(250/255, 30/255, 240/255, 0.9, CanvasKit.BlendMode.SrcOver);
394cb93a386Sopenharmony_ci        canvas.restore();
395cb93a386Sopenharmony_ci
396cb93a386Sopenharmony_ci        // draw grey inside of a star pattern, then the blue star on top
397cb93a386Sopenharmony_ci        canvas.clipPath(path, CanvasKit.ClipOp.Intersect, false);
398cb93a386Sopenharmony_ci        canvas.drawColorInt(CanvasKit.ColorAsInt(200, 200, 200, 255), CanvasKit.BlendMode.SrcOver);
399cb93a386Sopenharmony_ci        canvas.drawPath(path, paint);
400cb93a386Sopenharmony_ci
401cb93a386Sopenharmony_ci        path.delete();
402cb93a386Sopenharmony_ci        paint.delete();
403cb93a386Sopenharmony_ci    });
404cb93a386Sopenharmony_ci
405cb93a386Sopenharmony_ci    // inspired by https://fiddle.skia.org/c/feb2a08bb09ede5309678d6a0ab3f981
406cb93a386Sopenharmony_ci    gm('savelayer_rect_paint_canvas', (canvas) => {
407cb93a386Sopenharmony_ci        canvas.clear(CanvasKit.WHITE);
408cb93a386Sopenharmony_ci        const redPaint = new CanvasKit.Paint();
409cb93a386Sopenharmony_ci        redPaint.setColor(CanvasKit.RED);
410cb93a386Sopenharmony_ci        const solidBluePaint = new CanvasKit.Paint();
411cb93a386Sopenharmony_ci        solidBluePaint.setColor(CanvasKit.BLUE);
412cb93a386Sopenharmony_ci
413cb93a386Sopenharmony_ci        const thirtyBluePaint = new CanvasKit.Paint();
414cb93a386Sopenharmony_ci        thirtyBluePaint.setColor(CanvasKit.BLUE);
415cb93a386Sopenharmony_ci        thirtyBluePaint.setAlphaf(0.3);
416cb93a386Sopenharmony_ci
417cb93a386Sopenharmony_ci        const alpha = new CanvasKit.Paint();
418cb93a386Sopenharmony_ci        alpha.setAlphaf(0.3);
419cb93a386Sopenharmony_ci
420cb93a386Sopenharmony_ci        // Draw 4 solid red rectangles on the 0th layer.
421cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(10, 10, 60, 60), redPaint);
422cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(150, 10, 200, 60), redPaint);
423cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(10, 70, 60, 120), redPaint);
424cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(150, 70, 200, 120), redPaint);
425cb93a386Sopenharmony_ci
426cb93a386Sopenharmony_ci        // Draw 2 blue rectangles that overlap. One is solid, the other
427cb93a386Sopenharmony_ci        // is 30% transparent. We should see purple from the right one,
428cb93a386Sopenharmony_ci        // the left one overlaps the red because it is opaque.
429cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(30, 10, 80, 60), solidBluePaint);
430cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(170, 10, 220, 60), thirtyBluePaint);
431cb93a386Sopenharmony_ci
432cb93a386Sopenharmony_ci        // Save a new layer. When the 1st layer gets merged onto the
433cb93a386Sopenharmony_ci        // 0th layer (i.e. when restore() is called), it will use the provided
434cb93a386Sopenharmony_ci        // paint to do so. The provided paint is set to have 30% opacity, but
435cb93a386Sopenharmony_ci        // it could also have things set like blend modes or image filters.
436cb93a386Sopenharmony_ci        // The rectangle is just a hint, so I've set it to be the area that
437cb93a386Sopenharmony_ci        // we actually draw in before restore is called. It could also be omitted,
438cb93a386Sopenharmony_ci        // see the test below.
439cb93a386Sopenharmony_ci        canvas.saveLayer(alpha, CanvasKit.LTRBRect(10, 10, 220, 180));
440cb93a386Sopenharmony_ci
441cb93a386Sopenharmony_ci        // Draw the same blue overlapping rectangles as before. Notice in the
442cb93a386Sopenharmony_ci        // final output, we have two different shades of purple instead of the
443cb93a386Sopenharmony_ci        // solid blue overwriting the red. This proves the opacity was applied.
444cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(30, 70, 80, 120), solidBluePaint);
445cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(170, 70, 220, 120), thirtyBluePaint);
446cb93a386Sopenharmony_ci
447cb93a386Sopenharmony_ci        // We draw two more sets of overlapping red and blue rectangles. Notice
448cb93a386Sopenharmony_ci        // the solid blue overwrites the red. This proves that the opacity from
449cb93a386Sopenharmony_ci        // the alpha paint isn't available when the drawing happens - it only
450cb93a386Sopenharmony_ci        // matters when restore() is called.
451cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(10, 130, 60, 180), redPaint);
452cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(30, 130, 80, 180), solidBluePaint);
453cb93a386Sopenharmony_ci
454cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(150, 130, 200, 180), redPaint);
455cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(170, 130, 220, 180), thirtyBluePaint);
456cb93a386Sopenharmony_ci
457cb93a386Sopenharmony_ci        canvas.restore();
458cb93a386Sopenharmony_ci
459cb93a386Sopenharmony_ci        redPaint.delete();
460cb93a386Sopenharmony_ci        solidBluePaint.delete();
461cb93a386Sopenharmony_ci        thirtyBluePaint.delete();
462cb93a386Sopenharmony_ci        alpha.delete();
463cb93a386Sopenharmony_ci    });
464cb93a386Sopenharmony_ci
465cb93a386Sopenharmony_ci    // identical to the test above, except the save layer only has the paint, not
466cb93a386Sopenharmony_ci    // the rectangle.
467cb93a386Sopenharmony_ci    gm('savelayer_paint_canvas', (canvas) => {
468cb93a386Sopenharmony_ci        canvas.clear(CanvasKit.WHITE);
469cb93a386Sopenharmony_ci        const redPaint = new CanvasKit.Paint();
470cb93a386Sopenharmony_ci        redPaint.setColor(CanvasKit.RED);
471cb93a386Sopenharmony_ci        const solidBluePaint = new CanvasKit.Paint();
472cb93a386Sopenharmony_ci        solidBluePaint.setColor(CanvasKit.BLUE);
473cb93a386Sopenharmony_ci
474cb93a386Sopenharmony_ci        const thirtyBluePaint = new CanvasKit.Paint();
475cb93a386Sopenharmony_ci        thirtyBluePaint.setColor(CanvasKit.BLUE);
476cb93a386Sopenharmony_ci        thirtyBluePaint.setAlphaf(0.3);
477cb93a386Sopenharmony_ci
478cb93a386Sopenharmony_ci        const alpha = new CanvasKit.Paint();
479cb93a386Sopenharmony_ci        alpha.setAlphaf(0.3);
480cb93a386Sopenharmony_ci
481cb93a386Sopenharmony_ci        // Draw 4 solid red rectangles on the 0th layer.
482cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(10, 10, 60, 60), redPaint);
483cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(150, 10, 200, 60), redPaint);
484cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(10, 70, 60, 120), redPaint);
485cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(150, 70, 200, 120), redPaint);
486cb93a386Sopenharmony_ci
487cb93a386Sopenharmony_ci        // Draw 2 blue rectangles that overlap. One is solid, the other
488cb93a386Sopenharmony_ci        // is 30% transparent. We should see purple from the right one,
489cb93a386Sopenharmony_ci        // the left one overlaps the red because it is opaque.
490cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(30, 10, 80, 60), solidBluePaint);
491cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(170, 10, 220, 60), thirtyBluePaint);
492cb93a386Sopenharmony_ci
493cb93a386Sopenharmony_ci        // Save a new layer. When the 1st layer gets merged onto the
494cb93a386Sopenharmony_ci        // 0th layer (i.e. when restore() is called), it will use the provided
495cb93a386Sopenharmony_ci        // paint to do so. The provided paint is set to have 30% opacity, but
496cb93a386Sopenharmony_ci        // it could also have things set like blend modes or image filters.
497cb93a386Sopenharmony_ci        canvas.saveLayerPaint(alpha);
498cb93a386Sopenharmony_ci
499cb93a386Sopenharmony_ci        // Draw the same blue overlapping rectangles as before. Notice in the
500cb93a386Sopenharmony_ci        // final output, we have two different shades of purple instead of the
501cb93a386Sopenharmony_ci        // solid blue overwriting the red. This proves the opacity was applied.
502cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(30, 70, 80, 120), solidBluePaint);
503cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(170, 70, 220, 120), thirtyBluePaint);
504cb93a386Sopenharmony_ci
505cb93a386Sopenharmony_ci        // We draw two more sets of overlapping red and blue rectangles. Notice
506cb93a386Sopenharmony_ci        // the solid blue overwrites the red. This proves that the opacity from
507cb93a386Sopenharmony_ci        // the alpha paint isn't available when the drawing happens - it only
508cb93a386Sopenharmony_ci        // matters when restore() is called.
509cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(10, 130, 60, 180), redPaint);
510cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(30, 130, 80, 180), solidBluePaint);
511cb93a386Sopenharmony_ci
512cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(150, 130, 200, 180), redPaint);
513cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(170, 130, 220, 180), thirtyBluePaint);
514cb93a386Sopenharmony_ci
515cb93a386Sopenharmony_ci        canvas.restore();
516cb93a386Sopenharmony_ci
517cb93a386Sopenharmony_ci        redPaint.delete();
518cb93a386Sopenharmony_ci        solidBluePaint.delete();
519cb93a386Sopenharmony_ci        thirtyBluePaint.delete();
520cb93a386Sopenharmony_ci        alpha.delete();
521cb93a386Sopenharmony_ci    });
522cb93a386Sopenharmony_ci
523cb93a386Sopenharmony_ci    gm('savelayerrec_canvas', (canvas) => {
524cb93a386Sopenharmony_ci        // Note: fiddle.skia.org quietly draws a white background before doing
525cb93a386Sopenharmony_ci        // other things, which is noticed in cases like this where we use saveLayer
526cb93a386Sopenharmony_ci        // with the rec struct.
527cb93a386Sopenharmony_ci        canvas.clear(CanvasKit.WHITE);
528cb93a386Sopenharmony_ci        canvas.scale(8, 8);
529cb93a386Sopenharmony_ci        const redPaint = new CanvasKit.Paint();
530cb93a386Sopenharmony_ci        redPaint.setColor(CanvasKit.RED);
531cb93a386Sopenharmony_ci        redPaint.setAntiAlias(true);
532cb93a386Sopenharmony_ci        canvas.drawCircle(21, 21, 8, redPaint);
533cb93a386Sopenharmony_ci
534cb93a386Sopenharmony_ci        const bluePaint = new CanvasKit.Paint();
535cb93a386Sopenharmony_ci        bluePaint.setColor(CanvasKit.BLUE);
536cb93a386Sopenharmony_ci        canvas.drawCircle(31, 21, 8, bluePaint);
537cb93a386Sopenharmony_ci
538cb93a386Sopenharmony_ci        const blurIF = CanvasKit.ImageFilter.MakeBlur(8, 0.2, CanvasKit.TileMode.Decal, null);
539cb93a386Sopenharmony_ci
540cb93a386Sopenharmony_ci        const count = canvas.saveLayer(null, null, blurIF, 0);
541cb93a386Sopenharmony_ci        expect(count).toEqual(1);
542cb93a386Sopenharmony_ci        canvas.scale(1/4, 1/4);
543cb93a386Sopenharmony_ci        canvas.drawCircle(125, 85, 8, redPaint);
544cb93a386Sopenharmony_ci        canvas.restore();
545cb93a386Sopenharmony_ci
546cb93a386Sopenharmony_ci        blurIF.delete();
547cb93a386Sopenharmony_ci        redPaint.delete();
548cb93a386Sopenharmony_ci        bluePaint.delete();
549cb93a386Sopenharmony_ci    });
550cb93a386Sopenharmony_ci
551cb93a386Sopenharmony_ci    gm('drawpoints_canvas', (canvas) => {
552cb93a386Sopenharmony_ci        canvas.clear(CanvasKit.WHITE);
553cb93a386Sopenharmony_ci        const paint = new CanvasKit.Paint();
554cb93a386Sopenharmony_ci        paint.setAntiAlias(true);
555cb93a386Sopenharmony_ci        paint.setStyle(CanvasKit.PaintStyle.Stroke);
556cb93a386Sopenharmony_ci        paint.setStrokeWidth(10);
557cb93a386Sopenharmony_ci        paint.setColor(CanvasKit.Color(153, 204, 162, 0.82));
558cb93a386Sopenharmony_ci
559cb93a386Sopenharmony_ci        const points = [32, 16, 48, 48, 16, 32];
560cb93a386Sopenharmony_ci
561cb93a386Sopenharmony_ci        const caps = [CanvasKit.StrokeCap.Round, CanvasKit.StrokeCap.Square,
562cb93a386Sopenharmony_ci                      CanvasKit.StrokeCap.Butt];
563cb93a386Sopenharmony_ci        const joins = [CanvasKit.StrokeJoin.Round, CanvasKit.StrokeJoin.Miter,
564cb93a386Sopenharmony_ci                       CanvasKit.StrokeJoin.Bevel];
565cb93a386Sopenharmony_ci        const modes = [CanvasKit.PointMode.Points, CanvasKit.PointMode.Lines,
566cb93a386Sopenharmony_ci                       CanvasKit.PointMode.Polygon];
567cb93a386Sopenharmony_ci
568cb93a386Sopenharmony_ci        for (let i = 0; i < caps.length; i++) {
569cb93a386Sopenharmony_ci            paint.setStrokeCap(caps[i]);
570cb93a386Sopenharmony_ci            paint.setStrokeJoin(joins[i]);
571cb93a386Sopenharmony_ci
572cb93a386Sopenharmony_ci            for (const m of modes) {
573cb93a386Sopenharmony_ci                canvas.drawPoints(m, points, paint);
574cb93a386Sopenharmony_ci                canvas.translate(64, 0);
575cb93a386Sopenharmony_ci            }
576cb93a386Sopenharmony_ci            // Try with the malloc approach. Note that the drawPoints
577cb93a386Sopenharmony_ci            // will free the pointer when done.
578cb93a386Sopenharmony_ci            const mPointsObj = CanvasKit.Malloc(Float32Array, 3*2);
579cb93a386Sopenharmony_ci            const mPoints = mPointsObj.toTypedArray();
580cb93a386Sopenharmony_ci            mPoints.set([32, 16, 48, 48, 16, 32]);
581cb93a386Sopenharmony_ci
582cb93a386Sopenharmony_ci            // The obj from Malloc can be passed in instead of the typed array.
583cb93a386Sopenharmony_ci            canvas.drawPoints(CanvasKit.PointMode.Polygon, mPointsObj, paint);
584cb93a386Sopenharmony_ci            canvas.translate(-192, 64);
585cb93a386Sopenharmony_ci            CanvasKit.Free(mPointsObj);
586cb93a386Sopenharmony_ci        }
587cb93a386Sopenharmony_ci
588cb93a386Sopenharmony_ci        paint.delete();
589cb93a386Sopenharmony_ci    });
590cb93a386Sopenharmony_ci
591cb93a386Sopenharmony_ci    gm('drawPoints in different modes', (canvas) => {
592cb93a386Sopenharmony_ci        canvas.clear(CanvasKit.WHITE);
593cb93a386Sopenharmony_ci        // From https://bugs.chromium.org/p/skia/issues/detail?id=11012
594cb93a386Sopenharmony_ci        const boxPaint = new CanvasKit.Paint();
595cb93a386Sopenharmony_ci        boxPaint.setStyle(CanvasKit.PaintStyle.Stroke);
596cb93a386Sopenharmony_ci        boxPaint.setStrokeWidth(1);
597cb93a386Sopenharmony_ci
598cb93a386Sopenharmony_ci        const paint = new CanvasKit.Paint();
599cb93a386Sopenharmony_ci        paint.setStyle(CanvasKit.PaintStyle.Stroke);
600cb93a386Sopenharmony_ci        paint.setStrokeWidth(5);
601cb93a386Sopenharmony_ci        paint.setStrokeCap(CanvasKit.StrokeCap.Round);
602cb93a386Sopenharmony_ci        paint.setColorInt(0xFF0000FF); // Blue
603cb93a386Sopenharmony_ci        paint.setAntiAlias(true);
604cb93a386Sopenharmony_ci
605cb93a386Sopenharmony_ci        const points = Float32Array.of(40, 40, 80, 40, 120, 80, 160, 80);
606cb93a386Sopenharmony_ci
607cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(35, 35, 165, 85), boxPaint);
608cb93a386Sopenharmony_ci        canvas.drawPoints(CanvasKit.PointMode.Points, points, paint);
609cb93a386Sopenharmony_ci
610cb93a386Sopenharmony_ci        canvas.translate(0, 50);
611cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(35, 35, 165, 85), boxPaint);
612cb93a386Sopenharmony_ci        canvas.drawPoints(CanvasKit.PointMode.Lines, points, paint);
613cb93a386Sopenharmony_ci
614cb93a386Sopenharmony_ci        canvas.translate(0, 50);
615cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(35, 35, 165, 85), boxPaint);
616cb93a386Sopenharmony_ci        canvas.drawPoints(CanvasKit.PointMode.Polygon, points, paint);
617cb93a386Sopenharmony_ci
618cb93a386Sopenharmony_ci        // The control version using drawPath
619cb93a386Sopenharmony_ci        canvas.translate(0, 50);
620cb93a386Sopenharmony_ci        canvas.drawRect(CanvasKit.LTRBRect(35, 35, 165, 85), boxPaint);
621cb93a386Sopenharmony_ci        const path = new CanvasKit.Path();
622cb93a386Sopenharmony_ci        path.moveTo(40, 40);
623cb93a386Sopenharmony_ci        path.lineTo(80, 40);
624cb93a386Sopenharmony_ci        path.lineTo(120, 80);
625cb93a386Sopenharmony_ci        path.lineTo(160, 80);
626cb93a386Sopenharmony_ci        paint.setColorInt(0xFFFF0000); // RED
627cb93a386Sopenharmony_ci        canvas.drawPath(path, paint);
628cb93a386Sopenharmony_ci
629cb93a386Sopenharmony_ci        paint.delete();
630cb93a386Sopenharmony_ci        path.delete();
631cb93a386Sopenharmony_ci        boxPaint.delete();
632cb93a386Sopenharmony_ci    });
633cb93a386Sopenharmony_ci
634cb93a386Sopenharmony_ci    gm('drawImageNine_canvas', (canvas, fetchedByteBuffers) => {
635cb93a386Sopenharmony_ci        const img = CanvasKit.MakeImageFromEncoded(fetchedByteBuffers[0]);
636cb93a386Sopenharmony_ci        expect(img).toBeTruthy();
637cb93a386Sopenharmony_ci
638cb93a386Sopenharmony_ci        canvas.clear(CanvasKit.WHITE);
639cb93a386Sopenharmony_ci        const paint = new CanvasKit.Paint();
640cb93a386Sopenharmony_ci
641cb93a386Sopenharmony_ci        canvas.drawImageNine(img, CanvasKit.LTRBiRect(40, 40, 400, 300),
642cb93a386Sopenharmony_ci            CanvasKit.LTRBRect(5, 5, 300, 650), CanvasKit.FilterMode.Nearest, paint);
643cb93a386Sopenharmony_ci        paint.delete();
644cb93a386Sopenharmony_ci        img.delete();
645cb93a386Sopenharmony_ci    }, '/assets/mandrill_512.png');
646cb93a386Sopenharmony_ci
647cb93a386Sopenharmony_ci        // This should be a nice, clear image.
648cb93a386Sopenharmony_ci    gm('makeImageShaderCubic_canvas', (canvas, fetchedByteBuffers) => {
649cb93a386Sopenharmony_ci        const img = CanvasKit.MakeImageFromEncoded(fetchedByteBuffers[0]);
650cb93a386Sopenharmony_ci        expect(img).toBeTruthy();
651cb93a386Sopenharmony_ci
652cb93a386Sopenharmony_ci        canvas.clear(CanvasKit.WHITE);
653cb93a386Sopenharmony_ci        const paint = new CanvasKit.Paint();
654cb93a386Sopenharmony_ci        const shader = img.makeShaderCubic(CanvasKit.TileMode.Decal, CanvasKit.TileMode.Clamp,
655cb93a386Sopenharmony_ci                                           1/3 /*B*/, 1/3 /*C*/,
656cb93a386Sopenharmony_ci                                           CanvasKit.Matrix.rotated(0.1));
657cb93a386Sopenharmony_ci        paint.setShader(shader);
658cb93a386Sopenharmony_ci
659cb93a386Sopenharmony_ci        canvas.drawPaint(paint);
660cb93a386Sopenharmony_ci        paint.delete();
661cb93a386Sopenharmony_ci        shader.delete();
662cb93a386Sopenharmony_ci        img.delete();
663cb93a386Sopenharmony_ci    }, '/assets/mandrill_512.png');
664cb93a386Sopenharmony_ci
665cb93a386Sopenharmony_ci    // This will look more blocky than the version above.
666cb93a386Sopenharmony_ci    gm('makeImageShaderOptions_canvas', (canvas, fetchedByteBuffers) => {
667cb93a386Sopenharmony_ci        const img = CanvasKit.MakeImageFromEncoded(fetchedByteBuffers[0]);
668cb93a386Sopenharmony_ci        expect(img).toBeTruthy();
669cb93a386Sopenharmony_ci        const imgWithMipMap = img.makeCopyWithDefaultMipmaps();
670cb93a386Sopenharmony_ci
671cb93a386Sopenharmony_ci        canvas.clear(CanvasKit.WHITE);
672cb93a386Sopenharmony_ci        const paint = new CanvasKit.Paint();
673cb93a386Sopenharmony_ci        const shader = imgWithMipMap.makeShaderOptions(CanvasKit.TileMode.Decal,
674cb93a386Sopenharmony_ci                                                       CanvasKit.TileMode.Clamp,
675cb93a386Sopenharmony_ci                                                       CanvasKit.FilterMode.Nearest,
676cb93a386Sopenharmony_ci                                                       CanvasKit.MipmapMode.Linear,
677cb93a386Sopenharmony_ci                                                       CanvasKit.Matrix.rotated(0.1));
678cb93a386Sopenharmony_ci        paint.setShader(shader);
679cb93a386Sopenharmony_ci
680cb93a386Sopenharmony_ci        canvas.drawPaint(paint);
681cb93a386Sopenharmony_ci        paint.delete();
682cb93a386Sopenharmony_ci        shader.delete();
683cb93a386Sopenharmony_ci        img.delete();
684cb93a386Sopenharmony_ci        imgWithMipMap.delete();
685cb93a386Sopenharmony_ci    }, '/assets/mandrill_512.png');
686cb93a386Sopenharmony_ci
687cb93a386Sopenharmony_ci    gm('drawvertices_canvas', (canvas) => {
688cb93a386Sopenharmony_ci        const paint = new CanvasKit.Paint();
689cb93a386Sopenharmony_ci        paint.setAntiAlias(true);
690cb93a386Sopenharmony_ci
691cb93a386Sopenharmony_ci        const points = [0, 0,  250, 0,  100, 100,  0, 250];
692cb93a386Sopenharmony_ci        // 2d float color array
693cb93a386Sopenharmony_ci        const colors = [CanvasKit.RED, CanvasKit.BLUE,
694cb93a386Sopenharmony_ci                        CanvasKit.YELLOW, CanvasKit.CYAN];
695cb93a386Sopenharmony_ci        const vertices = CanvasKit.MakeVertices(CanvasKit.VertexMode.TriangleFan,
696cb93a386Sopenharmony_ci            points, null /*textureCoordinates*/, colors, false /*isVolatile*/);
697cb93a386Sopenharmony_ci
698cb93a386Sopenharmony_ci        const bounds = vertices.bounds();
699cb93a386Sopenharmony_ci        expect(bounds).toEqual(CanvasKit.LTRBRect(0, 0, 250, 250));
700cb93a386Sopenharmony_ci
701cb93a386Sopenharmony_ci        canvas.drawVertices(vertices, CanvasKit.BlendMode.Src, paint);
702cb93a386Sopenharmony_ci        vertices.delete();
703cb93a386Sopenharmony_ci        paint.delete();
704cb93a386Sopenharmony_ci    });
705cb93a386Sopenharmony_ci
706cb93a386Sopenharmony_ci    gm('drawvertices_canvas_flat_floats', (canvas) => {
707cb93a386Sopenharmony_ci        const paint = new CanvasKit.Paint();
708cb93a386Sopenharmony_ci        paint.setAntiAlias(true);
709cb93a386Sopenharmony_ci
710cb93a386Sopenharmony_ci        const points = [0, 0,  250, 0,  100, 100,  0, 250];
711cb93a386Sopenharmony_ci        // 1d float color array
712cb93a386Sopenharmony_ci        const colors = Float32Array.of(...CanvasKit.RED, ...CanvasKit.BLUE,
713cb93a386Sopenharmony_ci                                       ...CanvasKit.YELLOW, ...CanvasKit.CYAN);
714cb93a386Sopenharmony_ci        const vertices = CanvasKit.MakeVertices(CanvasKit.VertexMode.TriangleFan,
715cb93a386Sopenharmony_ci            points, null /*textureCoordinates*/, colors, false /*isVolatile*/);
716cb93a386Sopenharmony_ci
717cb93a386Sopenharmony_ci        const bounds = vertices.bounds();
718cb93a386Sopenharmony_ci        expect(bounds).toEqual(CanvasKit.LTRBRect(0, 0, 250, 250));
719cb93a386Sopenharmony_ci
720cb93a386Sopenharmony_ci        canvas.drawVertices(vertices, CanvasKit.BlendMode.Src, paint);
721cb93a386Sopenharmony_ci        vertices.delete();
722cb93a386Sopenharmony_ci        paint.delete();
723cb93a386Sopenharmony_ci    });
724cb93a386Sopenharmony_ci
725cb93a386Sopenharmony_ci    gm('drawvertices_texture_canvas', (canvas, fetchedByteBuffers) => {
726cb93a386Sopenharmony_ci        const img = CanvasKit.MakeImageFromEncoded(fetchedByteBuffers[0]);
727cb93a386Sopenharmony_ci
728cb93a386Sopenharmony_ci        const paint = new CanvasKit.Paint();
729cb93a386Sopenharmony_ci        paint.setAntiAlias(true);
730cb93a386Sopenharmony_ci
731cb93a386Sopenharmony_ci        const points = [
732cb93a386Sopenharmony_ci             70, 170,   40, 90,  130, 150,  100, 50,
733cb93a386Sopenharmony_ci            225, 150,  225, 60,  310, 180,  330, 100,
734cb93a386Sopenharmony_ci        ];
735cb93a386Sopenharmony_ci        const textureCoordinates = [
736cb93a386Sopenharmony_ci              0, 240,    0, 0,   80, 240,   80, 0,
737cb93a386Sopenharmony_ci            160, 240,  160, 0,  240, 240,  240, 0,
738cb93a386Sopenharmony_ci        ];
739cb93a386Sopenharmony_ci        const vertices = CanvasKit.MakeVertices(CanvasKit.VertexMode.TrianglesStrip,
740cb93a386Sopenharmony_ci            points, textureCoordinates, null /* colors */, false /*isVolatile*/);
741cb93a386Sopenharmony_ci
742cb93a386Sopenharmony_ci        const shader = img.makeShaderCubic(CanvasKit.TileMode.Repeat, CanvasKit.TileMode.Mirror,
743cb93a386Sopenharmony_ci            1/3 /*B*/, 1/3 /*C*/,);
744cb93a386Sopenharmony_ci        paint.setShader(shader);
745cb93a386Sopenharmony_ci        canvas.drawVertices(vertices, CanvasKit.BlendMode.Src, paint);
746cb93a386Sopenharmony_ci
747cb93a386Sopenharmony_ci        shader.delete();
748cb93a386Sopenharmony_ci        vertices.delete();
749cb93a386Sopenharmony_ci        paint.delete();
750cb93a386Sopenharmony_ci        img.delete();
751cb93a386Sopenharmony_ci    }, '/assets/brickwork-texture.jpg');
752cb93a386Sopenharmony_ci
753cb93a386Sopenharmony_ci    it('can change the 3x3 matrix on the canvas and read it back', () => {
754cb93a386Sopenharmony_ci        const canvas = new CanvasKit.Canvas();
755cb93a386Sopenharmony_ci
756cb93a386Sopenharmony_ci        let matr = canvas.getTotalMatrix();
757cb93a386Sopenharmony_ci        expect(matr).toEqual(CanvasKit.Matrix.identity());
758cb93a386Sopenharmony_ci
759cb93a386Sopenharmony_ci        // This fills the internal _scratch4x4MatrixPtr with garbage (aka sentinel) values to
760cb93a386Sopenharmony_ci        // make sure the 3x3 matrix properly sets these to 0 when it uses the same buffer.
761cb93a386Sopenharmony_ci        canvas.save();
762cb93a386Sopenharmony_ci        const garbageMatrix = new Float32Array(16);
763cb93a386Sopenharmony_ci        garbageMatrix.fill(-3);
764cb93a386Sopenharmony_ci        canvas.concat(garbageMatrix);
765cb93a386Sopenharmony_ci        canvas.restore();
766cb93a386Sopenharmony_ci
767cb93a386Sopenharmony_ci        canvas.concat(CanvasKit.Matrix.rotated(Math.PI/4));
768cb93a386Sopenharmony_ci        const d = new DOMMatrix().translate(20, 10);
769cb93a386Sopenharmony_ci        canvas.concat(d);
770cb93a386Sopenharmony_ci
771cb93a386Sopenharmony_ci        matr = canvas.getTotalMatrix();
772cb93a386Sopenharmony_ci        const expected = CanvasKit.Matrix.multiply(
773cb93a386Sopenharmony_ci            CanvasKit.Matrix.rotated(Math.PI/4),
774cb93a386Sopenharmony_ci            CanvasKit.Matrix.translated(20, 10)
775cb93a386Sopenharmony_ci        );
776cb93a386Sopenharmony_ci        expect3x3MatricesToMatch(expected, matr);
777cb93a386Sopenharmony_ci
778cb93a386Sopenharmony_ci        // The 3x3 should be expanded into a 4x4, with 0s in the 3rd row and column.
779cb93a386Sopenharmony_ci        matr = canvas.getLocalToDevice();
780cb93a386Sopenharmony_ci        expect4x4MatricesToMatch([
781cb93a386Sopenharmony_ci            0.707106, -0.707106, 0,  7.071067,
782cb93a386Sopenharmony_ci            0.707106,  0.707106, 0, 21.213203,
783cb93a386Sopenharmony_ci            0       ,  0       , 0,  0       ,
784cb93a386Sopenharmony_ci            0       ,  0       , 0,  1       ], matr);
785cb93a386Sopenharmony_ci    });
786cb93a386Sopenharmony_ci
787cb93a386Sopenharmony_ci    it('can accept a 3x2 matrix', () => {
788cb93a386Sopenharmony_ci        const canvas = new CanvasKit.Canvas();
789cb93a386Sopenharmony_ci
790cb93a386Sopenharmony_ci        let matr = canvas.getTotalMatrix();
791cb93a386Sopenharmony_ci        expect(matr).toEqual(CanvasKit.Matrix.identity());
792cb93a386Sopenharmony_ci
793cb93a386Sopenharmony_ci        // This fills the internal _scratch4x4MatrixPtr with garbage (aka sentinel) values to
794cb93a386Sopenharmony_ci        // make sure the 3x2 matrix properly sets these to 0 when it uses the same buffer.
795cb93a386Sopenharmony_ci        canvas.save();
796cb93a386Sopenharmony_ci        const garbageMatrix = new Float32Array(16);
797cb93a386Sopenharmony_ci        garbageMatrix.fill(-3);
798cb93a386Sopenharmony_ci        canvas.concat(garbageMatrix);
799cb93a386Sopenharmony_ci        canvas.restore();
800cb93a386Sopenharmony_ci
801cb93a386Sopenharmony_ci        canvas.concat([1.4, -0.2, 12,
802cb93a386Sopenharmony_ci                       0.2,  1.4, 24]);
803cb93a386Sopenharmony_ci
804cb93a386Sopenharmony_ci        matr = canvas.getTotalMatrix();
805cb93a386Sopenharmony_ci        const expected = [1.4, -0.2, 12,
806cb93a386Sopenharmony_ci                          0.2,  1.4, 24,
807cb93a386Sopenharmony_ci                            0,    0,  1];
808cb93a386Sopenharmony_ci        expect3x3MatricesToMatch(expected, matr);
809cb93a386Sopenharmony_ci
810cb93a386Sopenharmony_ci        // The 3x2 should be expanded into a 4x4, with 0s in the 3rd row and column
811cb93a386Sopenharmony_ci        // and the perspective filled in.
812cb93a386Sopenharmony_ci        matr = canvas.getLocalToDevice();
813cb93a386Sopenharmony_ci        expect4x4MatricesToMatch([
814cb93a386Sopenharmony_ci            1.4, -0.2, 0, 12,
815cb93a386Sopenharmony_ci            0.2,  1.4, 0, 24,
816cb93a386Sopenharmony_ci            0  ,  0  , 0,  0,
817cb93a386Sopenharmony_ci            0  ,  0  , 0,  1], matr);
818cb93a386Sopenharmony_ci    });
819cb93a386Sopenharmony_ci
820cb93a386Sopenharmony_ci    it('can mark a CTM and retrieve it', () => {
821cb93a386Sopenharmony_ci        const canvas = new CanvasKit.Canvas();
822cb93a386Sopenharmony_ci
823cb93a386Sopenharmony_ci        canvas.concat(CanvasKit.M44.rotated([0, 1, 0], Math.PI/4));
824cb93a386Sopenharmony_ci        canvas.concat(CanvasKit.M44.rotated([1, 0, 1], Math.PI/8));
825cb93a386Sopenharmony_ci        canvas.markCTM('krispykreme');
826cb93a386Sopenharmony_ci
827cb93a386Sopenharmony_ci        const expected = CanvasKit.M44.multiply(
828cb93a386Sopenharmony_ci          CanvasKit.M44.rotated([0, 1, 0], Math.PI/4),
829cb93a386Sopenharmony_ci          CanvasKit.M44.rotated([1, 0, 1], Math.PI/8),
830cb93a386Sopenharmony_ci        );
831cb93a386Sopenharmony_ci
832cb93a386Sopenharmony_ci        expect4x4MatricesToMatch(expected, canvas.findMarkedCTM('krispykreme'));
833cb93a386Sopenharmony_ci    });
834cb93a386Sopenharmony_ci
835cb93a386Sopenharmony_ci    it('returns null for an invalid CTM marker', () => {
836cb93a386Sopenharmony_ci        const canvas = new CanvasKit.Canvas();
837cb93a386Sopenharmony_ci        expect(canvas.findMarkedCTM('dunkindonuts')).toBeNull();
838cb93a386Sopenharmony_ci    });
839cb93a386Sopenharmony_ci
840cb93a386Sopenharmony_ci    it('can change the 4x4 matrix on the canvas and read it back', () => {
841cb93a386Sopenharmony_ci        const canvas = new CanvasKit.Canvas();
842cb93a386Sopenharmony_ci
843cb93a386Sopenharmony_ci        let matr = canvas.getLocalToDevice();
844cb93a386Sopenharmony_ci        expect(matr).toEqual(CanvasKit.M44.identity());
845cb93a386Sopenharmony_ci
846cb93a386Sopenharmony_ci        canvas.concat(CanvasKit.M44.rotated([0, 1, 0], Math.PI/4));
847cb93a386Sopenharmony_ci        canvas.concat(CanvasKit.M44.rotated([1, 0, 1], Math.PI/8));
848cb93a386Sopenharmony_ci
849cb93a386Sopenharmony_ci        const expected = CanvasKit.M44.multiply(
850cb93a386Sopenharmony_ci          CanvasKit.M44.rotated([0, 1, 0], Math.PI/4),
851cb93a386Sopenharmony_ci          CanvasKit.M44.rotated([1, 0, 1], Math.PI/8),
852cb93a386Sopenharmony_ci        );
853cb93a386Sopenharmony_ci
854cb93a386Sopenharmony_ci        expect4x4MatricesToMatch(expected, canvas.getLocalToDevice());
855cb93a386Sopenharmony_ci        // TODO(kjlubick) add test for DOMMatrix
856cb93a386Sopenharmony_ci        // TODO(nifong) add more involved test for camera-related math.
857cb93a386Sopenharmony_ci    });
858cb93a386Sopenharmony_ci
859cb93a386Sopenharmony_ci    gm('concat_with4x4_canvas', (canvas) => {
860cb93a386Sopenharmony_ci        const path = starPath(CanvasKit, CANVAS_WIDTH/2, CANVAS_HEIGHT/2);
861cb93a386Sopenharmony_ci        const paint = new CanvasKit.Paint();
862cb93a386Sopenharmony_ci        paint.setAntiAlias(true);
863cb93a386Sopenharmony_ci        canvas.clear(CanvasKit.WHITE);
864cb93a386Sopenharmony_ci
865cb93a386Sopenharmony_ci        // Rotate it a bit on all 3 major axis, centered on the screen.
866cb93a386Sopenharmony_ci        // To play with rotations, see https://jsfiddle.skia.org/canvaskit/0525300405796aa87c3b84cc0d5748516fca0045d7d6d9c7840710ab771edcd4
867cb93a386Sopenharmony_ci        const turn = CanvasKit.M44.multiply(
868cb93a386Sopenharmony_ci          CanvasKit.M44.translated([CANVAS_WIDTH/2, 0, 0]),
869cb93a386Sopenharmony_ci          CanvasKit.M44.rotated([1, 0, 0], Math.PI/3),
870cb93a386Sopenharmony_ci          CanvasKit.M44.rotated([0, 1, 0], Math.PI/4),
871cb93a386Sopenharmony_ci          CanvasKit.M44.rotated([0, 0, 1], Math.PI/16),
872cb93a386Sopenharmony_ci          CanvasKit.M44.translated([-CANVAS_WIDTH/2, 0, 0]),
873cb93a386Sopenharmony_ci        );
874cb93a386Sopenharmony_ci        canvas.concat(turn);
875cb93a386Sopenharmony_ci
876cb93a386Sopenharmony_ci        // Draw some stripes to help the eye detect the turn
877cb93a386Sopenharmony_ci        const stripeWidth = 10;
878cb93a386Sopenharmony_ci        paint.setColor(CanvasKit.BLACK);
879cb93a386Sopenharmony_ci        for (let i = 0; i < CANVAS_WIDTH; i += 2*stripeWidth) {
880cb93a386Sopenharmony_ci            canvas.drawRect(CanvasKit.LTRBRect(i, 0, i + stripeWidth, CANVAS_HEIGHT), paint);
881cb93a386Sopenharmony_ci        }
882cb93a386Sopenharmony_ci
883cb93a386Sopenharmony_ci        paint.setColor(CanvasKit.YELLOW);
884cb93a386Sopenharmony_ci        canvas.drawPath(path, paint);
885cb93a386Sopenharmony_ci        paint.delete();
886cb93a386Sopenharmony_ci        path.delete();
887cb93a386Sopenharmony_ci    });
888cb93a386Sopenharmony_ci
889cb93a386Sopenharmony_ci    gm('particles_canvas', (canvas) => {
890cb93a386Sopenharmony_ci        const curveParticles = {
891cb93a386Sopenharmony_ci            'MaxCount': 1000,
892cb93a386Sopenharmony_ci            'Drawable': {
893cb93a386Sopenharmony_ci               'Type': 'SkCircleDrawable',
894cb93a386Sopenharmony_ci               'Radius': 2
895cb93a386Sopenharmony_ci            },
896cb93a386Sopenharmony_ci            'Code': [
897cb93a386Sopenharmony_ci               `void effectSpawn(inout Effect effect) {
898cb93a386Sopenharmony_ci                  effect.rate = 200;
899cb93a386Sopenharmony_ci                  effect.color = float4(1, 0, 0, 1);
900cb93a386Sopenharmony_ci                }
901cb93a386Sopenharmony_ci                void spawn(inout Particle p) {
902cb93a386Sopenharmony_ci                  p.lifetime = 3 + rand(p.seed);
903cb93a386Sopenharmony_ci                  p.vel.y = -50;
904cb93a386Sopenharmony_ci                }
905cb93a386Sopenharmony_ci
906cb93a386Sopenharmony_ci                void update(inout Particle p) {
907cb93a386Sopenharmony_ci                  float w = mix(15, 3, p.age);
908cb93a386Sopenharmony_ci                  p.pos.x = sin(radians(p.age * 320)) * mix(25, 10, p.age) + mix(-w, w, rand(p.seed));
909cb93a386Sopenharmony_ci                  if (rand(p.seed) < 0.5) { p.pos.x = -p.pos.x; }
910cb93a386Sopenharmony_ci
911cb93a386Sopenharmony_ci                  p.color.g = (mix(75, 220, p.age) + mix(-30, 30, rand(p.seed))) / 255;
912cb93a386Sopenharmony_ci                }`
913cb93a386Sopenharmony_ci            ],
914cb93a386Sopenharmony_ci            'Bindings': []
915cb93a386Sopenharmony_ci        };
916cb93a386Sopenharmony_ci
917cb93a386Sopenharmony_ci        const particles = CanvasKit.MakeParticles(JSON.stringify(curveParticles));
918cb93a386Sopenharmony_ci        particles.start(0, true);
919cb93a386Sopenharmony_ci        particles.setPosition([0, 0]);
920cb93a386Sopenharmony_ci
921cb93a386Sopenharmony_ci        const paint = new CanvasKit.Paint();
922cb93a386Sopenharmony_ci        paint.setAntiAlias(true);
923cb93a386Sopenharmony_ci        paint.setColor(CanvasKit.WHITE);
924cb93a386Sopenharmony_ci        const font = new CanvasKit.Font(null, 12);
925cb93a386Sopenharmony_ci
926cb93a386Sopenharmony_ci        canvas.clear(CanvasKit.BLACK);
927cb93a386Sopenharmony_ci
928cb93a386Sopenharmony_ci        // Draw a 5x5 set of different times in the particle system
929cb93a386Sopenharmony_ci        // like a filmstrip of motion of particles.
930cb93a386Sopenharmony_ci        const LEFT_MARGIN = 90;
931cb93a386Sopenharmony_ci        const TOP_MARGIN = 100;
932cb93a386Sopenharmony_ci        for (let row = 0; row < 5; row++) {
933cb93a386Sopenharmony_ci            for (let column = 0; column < 5; column++) {
934cb93a386Sopenharmony_ci                canvas.save();
935cb93a386Sopenharmony_ci                canvas.translate(LEFT_MARGIN + column*100, TOP_MARGIN + row*100);
936cb93a386Sopenharmony_ci
937cb93a386Sopenharmony_ci                // Time moves in row-major order in increments of 0.02.
938cb93a386Sopenharmony_ci                const particleTime = row/10 + column/50;
939cb93a386Sopenharmony_ci
940cb93a386Sopenharmony_ci                canvas.drawText('time ' + particleTime.toFixed(2), -30, 20, paint, font);
941cb93a386Sopenharmony_ci                particles.update(particleTime);
942cb93a386Sopenharmony_ci
943cb93a386Sopenharmony_ci                particles.draw(canvas);
944cb93a386Sopenharmony_ci                canvas.restore();
945cb93a386Sopenharmony_ci            }
946cb93a386Sopenharmony_ci        }
947cb93a386Sopenharmony_ci    });
948cb93a386Sopenharmony_ci});
949cb93a386Sopenharmony_ci
950cb93a386Sopenharmony_ciconst expect3x3MatricesToMatch = (expected, actual) => {
951cb93a386Sopenharmony_ci    expect(expected.length).toEqual(9);
952cb93a386Sopenharmony_ci    expect(actual.length).toEqual(9);
953cb93a386Sopenharmony_ci    for (let i = 0; i < expected.length; i++) {
954cb93a386Sopenharmony_ci        expect(expected[i]).toBeCloseTo(actual[i], 5);
955cb93a386Sopenharmony_ci    }
956cb93a386Sopenharmony_ci};
957cb93a386Sopenharmony_ci
958cb93a386Sopenharmony_ciconst expect4x4MatricesToMatch = (expected, actual) => {
959cb93a386Sopenharmony_ci    expect(expected.length).toEqual(16);
960cb93a386Sopenharmony_ci    expect(actual.length).toEqual(16);
961cb93a386Sopenharmony_ci    for (let i = 0; i < expected.length; i++) {
962cb93a386Sopenharmony_ci        expect(expected[i]).toBeCloseTo(actual[i], 5);
963cb93a386Sopenharmony_ci    }
964cb93a386Sopenharmony_ci};
965