• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1const onlytests = [];
2const tests = [];
3// In all tests, the canvas is 600 by 600 px.
4// tests should NOT call ctx.surface.flush()
5// flush is done by benchmark.js
6
7function randomColorTwo(CanvasKit, i, j) {
8    c = [1, 1, 1, 1];
9    c[i] = Math.random();
10    c[j] = Math.random();
11    return CanvasKit.Color4f(...c);
12}
13
14function randomColor(CanvasKit) {
15    return CanvasKit.Color4f(Math.random(), Math.random(), Math.random(), Math.random());
16}
17
18function starPath(CanvasKit, X=128, Y=128, R=116) {
19    const p = new CanvasKit.Path();
20    p.moveTo(X + R, Y);
21    for (let i = 1; i < 8; i++) {
22      let a = 2.6927937 * i;
23      p.lineTo(X + R * Math.cos(a), Y + R * Math.sin(a));
24    }
25    p.close();
26    return p;
27}
28
29tests.push({
30    description: 'Draw 10K colored rect clips',
31    setup: function(CanvasKit, ctx) {
32        ctx.canvas = ctx.surface.getCanvas();
33    },
34    test: function(CanvasKit, ctx) {
35        // Draw a lot of colored squares.
36        for (let i=0; i<10000; i++) {
37            const x = Math.random()*550;
38            const y = Math.random()*550;
39            ctx.canvas.save();
40            ctx.canvas.clipRect(CanvasKit.LTRBRect(x, y, x+50, y+50),
41                                CanvasKit.ClipOp.Intersect, false);
42            ctx.canvas.drawColor(randomColorTwo(CanvasKit, 0, 1), CanvasKit.BlendMode.SrcOver);
43            ctx.canvas.restore();
44        }
45    },
46    teardown: function(CanvasKit, ctx) {},
47    perfKey: 'canvas_drawColor',
48});
49
50tests.push({
51    description: 'Draw 10K colored ellipses',
52    setup: function(CanvasKit, ctx) {
53        ctx.canvas = ctx.surface.getCanvas();
54
55        ctx.paint = new CanvasKit.Paint();
56        ctx.paint.setAntiAlias(true);
57        ctx.paint.setStyle(CanvasKit.PaintStyle.Fill);
58    },
59    test: function(CanvasKit, ctx) {
60        for (let i=0; i<10000; i++) {
61            const x = Math.random()*550;
62            const y = Math.random()*550;
63            ctx.paint.setColor(randomColorTwo(CanvasKit, 1, 2));
64            ctx.canvas.drawOval(CanvasKit.LTRBRect(x, y, x+50, y+50), ctx.paint);
65        }
66    },
67    teardown: function(CanvasKit, ctx) {
68        ctx.paint.delete();
69    },
70    perfKey: 'canvas_drawOval',
71});
72
73tests.push({
74    description: 'Draw 10K colored roundRects',
75    setup: function(CanvasKit, ctx) {
76        ctx.canvas = ctx.surface.getCanvas();
77
78        ctx.paint = new CanvasKit.Paint();
79        ctx.paint.setAntiAlias(true);
80        ctx.paint.setStyle(CanvasKit.PaintStyle.Fill);
81    },
82    test: function(CanvasKit, ctx) {
83        for (let i=0; i<10000; i++) {
84            const x = Math.random()*550;
85            const y = Math.random()*550;
86            ctx.paint.setColor(randomColorTwo(CanvasKit, 0, 2));
87            const rr = CanvasKit.RRectXY(CanvasKit.LTRBRect(x, y, x+50, y+50), 10, 10,);
88            ctx.canvas.drawRRect(rr, ctx.paint);
89        }
90    },
91    teardown: function(CanvasKit, ctx) {
92        ctx.paint.delete();
93    },
94    perfKey: 'canvas_drawRRect',
95});
96
97tests.push({
98    description: 'Draw 10K colored rects',
99    setup: function(CanvasKit, ctx) {
100        ctx.canvas = ctx.surface.getCanvas();
101
102        ctx.paint = new CanvasKit.Paint();
103        ctx.paint.setAntiAlias(true);
104        ctx.paint.setStyle(CanvasKit.PaintStyle.Fill);
105    },
106    test: function(CanvasKit, ctx) {
107        for (let i=0; i<10000; i++) {
108            const x = Math.random()*550;
109            const y = Math.random()*550;
110            ctx.paint.setColor(randomColorTwo(CanvasKit, 1, 2));
111            ctx.canvas.drawRect(CanvasKit.LTRBRect(x, y, x+50, y+50), ctx.paint);
112        }
113    },
114    teardown: function(CanvasKit, ctx) {
115        ctx.paint.delete();
116    },
117    perfKey: 'canvas_drawRect',
118});
119
120tests.push({
121    description: "Draw 10K colored rects with malloc'd rect",
122    setup: function(CanvasKit, ctx) {
123        ctx.canvas = ctx.surface.getCanvas();
124
125        ctx.paint = new CanvasKit.Paint();
126        ctx.paint.setAntiAlias(true);
127        ctx.paint.setStyle(CanvasKit.PaintStyle.Fill);
128        ctx.rect = CanvasKit.Malloc(Float32Array, 4);
129    },
130    test: function(CanvasKit, ctx) {
131        for (let i=0; i<10000; i++) {
132            ctx.paint.setColor(randomColorTwo(CanvasKit, 1, 2));
133            const ta = ctx.rect.toTypedArray();
134            ta[0] = Math.random()*550; // x
135            ta[1] = Math.random()*550; // y
136            ta[2] = ta[0] + 50;
137            ta[3] = ta[1] + 50;
138            ctx.canvas.drawRect(ta, ctx.paint);
139        }
140    },
141    teardown: function(CanvasKit, ctx) {
142        ctx.paint.delete();
143        CanvasKit.Free(ctx.rect);
144    },
145    perfKey: 'canvas_drawRect_malloc',
146});
147
148tests.push({
149    description: 'Draw 10K colored rects using 4 float API',
150    setup: function(CanvasKit, ctx) {
151        ctx.canvas = ctx.surface.getCanvas();
152
153        ctx.paint = new CanvasKit.Paint();
154        ctx.paint.setAntiAlias(true);
155        ctx.paint.setStyle(CanvasKit.PaintStyle.Fill);
156    },
157    test: function(CanvasKit, ctx) {
158        for (let i=0; i<10000; i++) {
159            const x = Math.random()*550;
160            const y = Math.random()*550;
161            ctx.paint.setColor(randomColorTwo(CanvasKit, 1, 2));
162            ctx.canvas.drawRect4f(x, y, x+50, y+50, ctx.paint);
163        }
164    },
165    teardown: function(CanvasKit, ctx) {
166        ctx.paint.delete();
167    },
168    perfKey: 'canvas_drawRect4f',
169});
170
171tests.push({
172    description: 'Compute tonal colors',
173    setup: function(CanvasKit, ctx) {},
174    test: function(CanvasKit, ctx) {
175        for (let i = 0; i < 10; i++) {
176            const input = {
177                ambient: randomColor(CanvasKit),
178                spot: randomColor(CanvasKit),
179            };
180            const out = CanvasKit.computeTonalColors(input);
181            if (out.spot[2] > 10 || out.ambient[3] > 10) {
182                // Something to make sure v8 can't optimize away the return value
183                throw 'not possible';
184            }
185        }
186    },
187    teardown: function(CanvasKit, ctx) {},
188    perfKey: 'computeTonalColors',
189});
190
191tests.push({
192    description: 'Get and set the color to a paint',
193    setup: function(CanvasKit, ctx) {
194        ctx.paint = new CanvasKit.Paint();
195    },
196    test: function(CanvasKit, ctx) {
197        for (let i = 0; i < 10; i++) {
198            ctx.paint.setColor(randomColor(CanvasKit));
199            const color = ctx.paint.getColor();
200            if (color[3] > 4) {
201                // Something to make sure v8 can't optimize away the return value
202                throw 'not possible';
203            }
204        }
205    },
206    teardown: function(CanvasKit, ctx) {
207        ctx.paint.delete();
208    },
209    perfKey: 'paint_setColor_getColor',
210});
211
212tests.push({
213    description: 'Set the color to a paint by components',
214    setup: function(CanvasKit, ctx) {
215        ctx.paint = new CanvasKit.Paint();
216    },
217    test: function(CanvasKit, ctx) {
218        const r = Math.random();
219        const g = Math.random();
220        const b = Math.random();
221        const a = Math.random();
222        for (let i = 0; i < 10000; i++) {
223            ctx.paint.setColorComponents(r, g, b, a);
224        }
225    },
226    teardown: function(CanvasKit, ctx) {
227        ctx.paint.delete();
228    },
229    perfKey: 'paint_setColorComponents',
230});
231
232tests.push({
233    description: 'Draw a shadow with tonal colors',
234    setup: function(CanvasKit, ctx) {
235        ctx.canvas = ctx.surface.getCanvas();
236
237        ctx.input = {
238            ambient: CanvasKit.Color4f(0.2, 0.1, 0.3, 0.5),
239            spot: CanvasKit.Color4f(0.8, 0.8, 0.9, 0.9),
240        };
241        ctx.lightRadius = 30;
242        ctx.flags = 0;
243        ctx.lightPos = [250,150,300];
244        ctx.zPlaneParams = [0,0,1];
245        ctx.path = starPath(CanvasKit);
246    },
247    test: function(CanvasKit, ctx) {
248        const out = CanvasKit.computeTonalColors(ctx.input);
249        ctx.canvas.drawShadow(ctx.path, ctx.zPlaneParams, ctx.lightPos, ctx.lightRadius,
250                              out.ambient, out.spot, ctx.flags);
251    },
252    teardown: function(CanvasKit, ctx) {},
253    perfKey: 'canvas_drawShadow',
254});
255
256tests.push({
257    description: 'Draw a gradient with an array of 10K colors',
258    setup: function(CanvasKit, ctx) {
259        ctx.canvas = ctx.surface.getCanvas();
260    },
261    test: function(CanvasKit, ctx) {
262        ctx.canvas.clear(CanvasKit.WHITE);
263
264        const num = 10000;
265        colors = Array(num);
266        positions = Array(num);
267        // Create an array of colors spaced evenly along the 0..1 range of positions.
268        for (let i=0; i<num; i++) {
269            colors[i] = randomColorTwo(CanvasKit, 2, 3);
270            positions[i] = i/num;
271        }
272        // make a gradient from those colors
273        const shader = CanvasKit.Shader.MakeRadialGradient(
274            [300, 300], 50, // center, radius
275            colors, positions,
276            CanvasKit.TileMode.Mirror,
277        );
278        // Fill the canvas using the gradient shader.
279        const paint = new CanvasKit.Paint();
280        paint.setStyle(CanvasKit.PaintStyle.Fill);
281        paint.setShader(shader);
282        ctx.canvas.drawPaint(paint);
283
284        shader.delete();
285        paint.delete();
286    },
287    teardown: function(CanvasKit, ctx) {},
288    perfKey: 'canvas_drawHugeGradient',
289});
290
291tests.push({
292    description: 'Draw a png image',
293    setup: async function(CanvasKit, ctx) {
294        ctx.canvas = ctx.surface.getCanvas();
295        ctx.paint = new CanvasKit.Paint();
296        ctx.img = CanvasKit.MakeImageFromEncoded(ctx.files['test_512x512.png']);
297        ctx.frame = 0;
298    },
299    test: function(CanvasKit, ctx) {
300        ctx.canvas.clear(CanvasKit.WHITE);
301        // Make the image to move so you can see visually that the test is running.
302        ctx.canvas.drawImage(ctx.img, ctx.frame, ctx.frame, ctx.paint);
303        ctx.frame++;
304    },
305    teardown: function(CanvasKit, ctx) {
306        ctx.img.delete();
307        ctx.paint.delete();
308    },
309    perfKey: 'canvas_drawPngImage',
310});
311
312
313function htmlImageElementToDataURL(htmlImageElement) {
314    const canvas = document.createElement('canvas');
315    canvas.height = htmlImageElement.height;
316    canvas.width = htmlImageElement.width;
317    const ctx = canvas.getContext('2d')
318    ctx.drawImage(htmlImageElement, 0, 0);
319    return canvas.toDataURL();
320}
321
322// This for loop generates two perf cases for each test image. One uses browser APIs
323// to decode an image, and the other uses codecs included in the CanvasKit wasm to decode an
324// image. wasm codec Image decoding is faster (50 microseconds vs 20000 microseconds), but
325// including codecs in wasm increases the size of the CanvasKit wasm binary.
326for (const testImageFilename of ['test_64x64.png', 'test_512x512.png', 'test_1500x959.jpg']) {
327    const htmlImageElement = new Image();
328    htmlImageElementLoadPromise = new Promise((resolve) =>
329        htmlImageElement.addEventListener('load', resolve));
330    // Create a data url of the image so that load and decode time can be measured
331    // while ignoring the time of getting the image from disk / the network.
332    imageDataURLPromise = htmlImageElementLoadPromise.then(() =>
333        htmlImageElementToDataURL(htmlImageElement));
334    htmlImageElement.src = `/static/assets/${testImageFilename}`;
335
336    tests.push({
337        description: 'Decode an image using HTMLImageElement and Canvas2D',
338        setup: async function(CanvasKit, ctx) {
339            ctx.imageDataURL = await imageDataURLPromise;
340        },
341        test: async function(CanvasKit, ctx) {
342            const image = new Image();
343            // Testing showed that waiting for the load event is faster than waiting for
344            // image.decode().
345            // Despite the name, both of them would decode the image, it was loaded in setup.
346            // HTMLImageElement.decode() reference:
347            // https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decode
348            const promise = new Promise((resolve) => image.addEventListener('load', resolve));
349            image.src = ctx.imageDataURL;
350            await promise;
351            const img = await CanvasKit.MakeImageFromCanvasImageSource(image);
352            img.delete();
353        },
354        teardown: function(CanvasKit, ctx) {},
355        perfKey: `canvas_${testImageFilename}_HTMLImageElementDecoding`,
356    });
357
358    tests.push({
359        description: 'Decode an image using codecs in wasm',
360        setup: function(CanvasKit, ctx) {},
361        test: function(CanvasKit, ctx) {
362            const img = CanvasKit.MakeImageFromEncoded(ctx.files[testImageFilename]);
363            img.delete();
364        },
365        teardown: function(CanvasKit, ctx) {},
366        perfKey: '`canvas_${testImageFilename}_wasmImageDecoding`',
367    });
368}
369
370// 3x3 matrix ops
371tests.push({
372    description: 'Multiply 3x3 matrices together',
373    setup: function(CanvasKit, ctx) {
374        ctx.first = CanvasKit.Matrix.rotated(Math.PI/2, 10, 20);
375        ctx.second = CanvasKit.Matrix.scaled(1, 2, 3, 4);
376    },
377    test: function(CanvasKit, ctx) {
378        ctx.result = CanvasKit.Matrix.multiply(ctx.first, ctx.second);
379        if (ctx.result.length === 18) {
380            throw 'this is here to keep the result from being optimized away';
381        }
382    },
383    teardown: function(CanvasKit, ctx) {},
384    perfKey: 'skmatrix_multiply',
385});
386
387tests.push({
388    description: 'Transform a point using a matrix (mapPoint)',
389    setup: function(CanvasKit, ctx) {
390        ctx.matr = CanvasKit.Matrix.multiply(
391            CanvasKit.Matrix.rotated(Math.PI/2, 10, 20),
392            CanvasKit.Matrix.scaled(1, 2, 3, 4),
393        ); // make an arbitrary, but interesting matrix
394    },
395    test: function(CanvasKit, ctx) {
396        for (let i = 0; i < 30; i++) {
397            const pt = CanvasKit.Matrix.mapPoints(ctx.matr, [i, i]);
398            if (pt.length === 18) {
399                throw 'this is here to keep pt from being optimized away';
400            }
401        }
402    },
403    teardown: function(CanvasKit, ctx) {},
404    perfKey: 'skmatrix_transformPoint',
405});
406
407tests.push({
408    description: 'Invert a 3x3 matrix',
409    setup: function(CanvasKit, ctx) {
410        ctx.matr = CanvasKit.Matrix.multiply(
411            CanvasKit.Matrix.rotated(Math.PI/2, 10, 20),
412            CanvasKit.Matrix.scaled(1, 2, 3, 4),
413        );
414    },
415    test: function(CanvasKit, ctx) {
416        ctx.result = CanvasKit.Matrix.invert(ctx.matr);
417        if (ctx.result.length === 18) {
418            throw 'this is here to keep the result from being optimized away';
419        }
420    },
421    teardown: function(CanvasKit, ctx) {},
422    perfKey: 'skmatrix_invert',
423});
424
425tests.push({
426    description: 'Create a shader from a 3x3 matrix',
427    setup: function(CanvasKit, ctx) {
428        ctx.matr = CanvasKit.Matrix.multiply(
429            CanvasKit.Matrix.rotated(Math.PI/2, 10, 20),
430            CanvasKit.Matrix.scaled(1, 2, 3, 4),
431        );
432    },
433    test: function(CanvasKit, ctx) {
434        const shader = CanvasKit.Shader.MakeSweepGradient(
435            100, 100,
436            [CanvasKit.GREEN, CanvasKit.BLUE],
437            [0.0, 1.0],
438            CanvasKit.TileMode.Clamp,
439            ctx.matr);
440        shader.delete();
441    },
442    teardown: function(CanvasKit, ctx) {},
443    perfKey: 'skmatrix_makeShader',
444});
445
446tests.push({
447    description: 'Concat 3x3 matrix on a canvas',
448    setup: function(CanvasKit, ctx) {
449        ctx.canvas = new CanvasKit.Canvas();
450        ctx.matr = CanvasKit.Matrix.multiply(
451            CanvasKit.Matrix.rotated(Math.PI/2, 10, 20),
452            CanvasKit.Matrix.scaled(1, 2, 3, 4),
453        );
454    },
455    test: function(CanvasKit, ctx) {
456        ctx.canvas.concat(ctx.matr);
457    },
458    teardown: function(CanvasKit, ctx) {
459        ctx.canvas.delete();
460    },
461    perfKey: 'skmatrix_concat',
462});
463
464// 4x4 matrix operations
465tests.push({
466    description: 'Multiply 4x4 matrices together',
467    setup: function(CanvasKit, ctx) {
468        ctx.first = CanvasKit.M44.rotated([10, 20, 30], Math.PI/2);
469        ctx.second = CanvasKit.M44.scaled([1, 2, 3]);
470    },
471    test: function(CanvasKit, ctx) {
472        ctx.result = CanvasKit.M44.multiply(ctx.first, ctx.second);
473        if (ctx.result.length === 18) {
474            throw 'this is here to keep the result from being optimized away';
475        }
476    },
477    teardown: function(CanvasKit, ctx) {},
478    perfKey: 'skm44_multiply',
479});
480
481tests.push({
482    description: 'Invert a 4x4 matrix',
483    setup: function(CanvasKit, ctx) {
484        ctx.matr = CanvasKit.M44.multiply(
485            CanvasKit.M44.rotated([10, 20, 30], Math.PI/2),
486            CanvasKit.M44.scaled([1, 2, 3]),
487        );
488    },
489    test: function(CanvasKit, ctx) {
490        const result = CanvasKit.M44.invert(ctx.matr);
491        if (result.length === 18) {
492            throw 'this is here to keep the result from being optimized away';
493        }
494    },
495    teardown: function(CanvasKit, ctx) {},
496    perfKey: 'skm44_invert',
497});
498
499tests.push({
500    description: 'Concat 4x4 matrix on a canvas',
501    setup: function(CanvasKit, ctx) {
502        ctx.canvas = new CanvasKit.Canvas();
503        ctx.matr = CanvasKit.M44.multiply(
504            CanvasKit.M44.rotated([10, 20, 30], Math.PI/2),
505            CanvasKit.M44.scaled([1, 2, 3]),
506        );
507    },
508    test: function(CanvasKit, ctx) {
509        ctx.canvas.concat(ctx.matr);
510    },
511    teardown: function(CanvasKit, ctx) {
512        ctx.canvas.delete();
513    },
514    perfKey: 'skm44_concat',
515});
516
517// DOMMatrix operations
518tests.push({
519    description: 'Multiply DOM matrices together',
520    setup: function(CanvasKit, ctx) {
521        ctx.first = new DOMMatrix().translate(10, 20).rotate(90).translate(-10, -20);
522        ctx.second = new DOMMatrix().translate(3, 4).scale(1, 2).translate(-3, -4);
523    },
524    test: function(CanvasKit, ctx) {
525        const result = ctx.first.multiply(ctx.second);
526        if (result.length === 18) {
527            throw 'this is here to keep the result from being optimized away';
528        }
529    },
530    teardown: function(CanvasKit, ctx) {},
531    perfKey: 'dommatrix_multiply',
532});
533
534tests.push({
535    description: 'Transform a point using a matrix (transformPoint)',
536    setup: function(CanvasKit, ctx) {
537        ctx.matr = new DOMMatrix().translate(10, 20).rotate(90).translate(-10, -20)
538            .multiply(new DOMMatrix().translate(3, 4).scale(1, 2).translate(-3, -4));
539
540        ctx.reusablePt = new DOMPoint(0, 0)
541    },
542    test: function(CanvasKit, ctx) {
543        for (let i = 0; i < 30; i++) {
544            ctx.reusablePt.X = i; ctx.reusablePt.Y = i;
545            const pt = ctx.matr.transformPoint(ctx.reusablePt);
546            if (pt.length === 18) {
547                throw 'this is here to keep pt from being optimized away';
548            }
549        }
550    },
551    teardown: function(CanvasKit, ctx) {},
552    perfKey: 'dommatrix_transformPoint',
553});
554
555tests.push({
556    description: 'Invert a DOM matrix',
557    setup: function(CanvasKit, ctx) {
558        ctx.matr = new DOMMatrix().translate(10, 20).rotate(90).translate(-10, -20)
559            .multiply(new DOMMatrix().translate(3, 4).scale(1, 2).translate(-3, -4));
560    },
561    test: function(CanvasKit, ctx) {
562        const inverted = ctx.matr.inverse();
563        if (inverted.length === 18) {
564            throw 'this is here to keep the result from being optimized away';
565        }
566    },
567    teardown: function(CanvasKit, ctx) {},
568    perfKey: 'dommatrix_invert',
569});
570
571tests.push({
572    description: 'make a shader from a DOMMatrix',
573    setup: function(CanvasKit, ctx) {
574        ctx.matr = new DOMMatrix().translate(10, 20).rotate(90).translate(-10, -20)
575            .multiply(new DOMMatrix().translate(3, 4).scale(1, 2).translate(-3, -4));
576    },
577    test: function(CanvasKit, ctx) {
578        const shader = CanvasKit.Shader.MakeSweepGradient(
579            100, 100,
580            [CanvasKit.GREEN, CanvasKit.BLUE],
581            [0.0, 1.0],
582            CanvasKit.TileMode.Clamp,
583            ctx.matr);
584        shader.delete();
585    },
586    teardown: function(CanvasKit, ctx) {},
587    perfKey: 'dommatrix_makeShader',
588});
589
590// Tests the layout and drawing of a paragraph with hundreds of words.
591// In the second variant, the colors change every frame
592// In the third variant, the font size cycles between three sizes.
593// In the fourth variant, the layout width changes.
594// in the fifth variant, all of those properties change at the same time.
595for (const variant of ['static', 'color_changing', 'size_changing', 'layout_changing', 'everything']) {
596    tests.push({
597        description: `Layout and draw a ${variant} paragraph`,
598        setup: function(CanvasKit, ctx) {
599            ctx.canvas = ctx.surface.getCanvas();
600            ctx.fontMgr = CanvasKit.FontMgr.FromData([ctx.files['Roboto-Regular.ttf']]);
601            ctx.paraStyle = new CanvasKit.ParagraphStyle({
602                textStyle: {
603                        color: CanvasKit.WHITE,
604                        fontFamilies: ['Roboto'],
605                        fontSize: 11,
606                    },
607                textAlign: CanvasKit.TextAlign.Left,
608            });
609            ctx.frame = 0;
610            ctx.text = "annap sap sa ladipidapidi rapadip sam dim dap dim dap do raka dip da da badip badip badipidipidipadisuten din dab do ".repeat(40);
611        },
612        test: function(CanvasKit, ctx) {
613            ctx.canvas.clear(CanvasKit.BLACK);
614            const builder = CanvasKit.ParagraphBuilder.Make(ctx.paraStyle, ctx.fontMgr);
615            let pos = 0;
616            let color = CanvasKit.WHITE;
617            while (pos < ctx.text.length) {
618                let size = 11;
619                if (variant === 'size_changing' || variant === 'everything') {
620                    // the bigger this modulo, the more work it takes to fill the glyph cache
621                    size += ctx.frame % 4;
622                }
623                if (variant === 'color_changing' || variant === 'everything') {
624                    color = randomColorTwo(CanvasKit, 0, 1);
625                }
626                builder.pushStyle(CanvasKit.TextStyle({
627                    color: color,
628                    fontFamilies: ['Roboto'],
629                    fontSize: size,
630                    fontStyle: {
631                        weight: CanvasKit.FontWeight.Bold,
632                    },
633                }));
634                const len = Math.floor(Math.random()*5+2);
635                builder.addText(ctx.text.slice(pos, pos+len));
636                builder.pop();
637                pos += len;
638            }
639            const paragraph = builder.build();
640            let w = 0;
641            const base_width = 520;
642            const varying_width_modulo = 70;
643            if (variant === 'layout_changing' || variant === 'everything') {
644                w = ctx.frame % varying_width_modulo;
645            }
646            paragraph.layout(base_width + w); // width in pixels to use when wrapping text
647            ctx.canvas.drawParagraph(paragraph, 10, 10);
648
649            ctx.frame++;
650            builder.delete();
651            paragraph.delete();
652        },
653        teardown: function(CanvasKit, ctx) {
654            ctx.fontMgr.delete();
655        },
656        perfKey: 'canvas_drawParagraph_'+variant,
657    });
658}
659
660tests.push({
661    description: 'Draw a path with a blur mask',
662    setup: function(CanvasKit, ctx) {
663        ctx.canvas = ctx.surface.getCanvas();
664        ctx.paint = new CanvasKit.Paint();
665        ctx.paint.setAntiAlias(true);
666        ctx.paint.setStyle(CanvasKit.PaintStyle.Fill);
667        ctx.paint.setColor(CanvasKit.Color4f(0.1, 0.7, 0.0, 1.0));
668        ctx.path = starPath(CanvasKit);
669        ctx.frame = 0;
670    },
671    test: function(CanvasKit, ctx) {
672        const sigma = 0.1 + (ctx.frame/10);
673        const blurMask = CanvasKit.MaskFilter.MakeBlur(
674            CanvasKit.BlurStyle.Normal, sigma, true);
675        ctx.paint.setMaskFilter(blurMask);
676        ctx.canvas.drawPath(ctx.path, ctx.paint);
677        blurMask.delete();
678        ctx.frame++;
679    },
680    teardown: function(CanvasKit, ctx) {
681        ctx.paint.delete();
682        ctx.path.delete();
683    },
684    perfKey: 'canvas_blur_mask_filter',
685});
686
687for (const variant of ['ttf', 'woff', 'woff2']) {
688    tests.push({
689        description: `Get glyphIDs from a ${variant} font`,
690        setup: function (CanvasKit, ctx) {
691            const robotoData = ctx.files['Roboto-Regular.' + variant];
692            ctx.robotoFace = CanvasKit.Typeface.MakeFreeTypeFaceFromData(robotoData);
693            if (!ctx.robotoFace) {
694                throw 'could not load ' + variant;
695            }
696            ctx.robotoFont = new CanvasKit.Font(ctx.robotoFace, 20);
697            ctx.testGlyphID = 1;
698        },
699        test: function (CanvasKit, ctx) {
700            // We get one glyph ID at a time to force cache misses and require Skia to
701            // perhaps re-access the font. See skbug.com/12112 for example.
702            const output = new Uint16Array(1);
703            for (let i = ctx.testGlyphID; i < ctx.testGlyphID+100; i++) {
704                ctx.robotoFont.getGlyphIDs(String.fromCodePoint(i), 1, output);
705            }
706            ctx.testGlyphID += 100;
707        },
708        teardown: function (CanvasKit, ctx) {
709            ctx.robotoFace.delete();
710            ctx.robotoFont.delete();
711        },
712        perfKey: 'font_getGlyphIDs_' + variant,
713    });
714}
715