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