| const onlytests = []; |
| const tests = []; |
| // In all tests, the canvas is 600 by 600 px. |
| // tests should NOT call ctx.surface.flush() |
| // flush is done by benchmark.js |
| |
| function randomColorTwo(CanvasKit, i, j) { |
| c = [1, 1, 1, 1]; |
| c[i] = Math.random(); |
| c[j] = Math.random(); |
| return CanvasKit.Color4f(...c); |
| } |
| |
| function randomColor(CanvasKit) { |
| return CanvasKit.Color4f(Math.random(), Math.random(), Math.random(), Math.random()); |
| } |
| |
| function starPath(CanvasKit, X=128, Y=128, R=116) { |
| const p = new CanvasKit.Path(); |
| p.moveTo(X + R, Y); |
| for (let i = 1; i < 8; i++) { |
| let a = 2.6927937 * i; |
| p.lineTo(X + R * Math.cos(a), Y + R * Math.sin(a)); |
| } |
| p.close(); |
| return p; |
| } |
| |
| tests.push({ |
| description: 'Draw 10K colored rect clips', |
| setup: function(CanvasKit, ctx) { |
| ctx.canvas = ctx.surface.getCanvas(); |
| }, |
| test: function(CanvasKit, ctx) { |
| // Draw a lot of colored squares. |
| for (let i=0; i<10000; i++) { |
| const x = Math.random()*550; |
| const y = Math.random()*550; |
| ctx.canvas.save(); |
| ctx.canvas.clipRect(CanvasKit.LTRBRect(x, y, x+50, y+50), |
| CanvasKit.ClipOp.Intersect, false); |
| ctx.canvas.drawColor(randomColorTwo(CanvasKit, 0, 1), CanvasKit.BlendMode.SrcOver); |
| ctx.canvas.restore(); |
| } |
| }, |
| teardown: function(CanvasKit, ctx) {}, |
| perfKey: 'canvas_drawColor', |
| }); |
| |
| tests.push({ |
| description: 'Draw 10K colored ellipses', |
| setup: function(CanvasKit, ctx) { |
| ctx.canvas = ctx.surface.getCanvas(); |
| |
| ctx.paint = new CanvasKit.Paint(); |
| ctx.paint.setAntiAlias(true); |
| ctx.paint.setStyle(CanvasKit.PaintStyle.Fill); |
| }, |
| test: function(CanvasKit, ctx) { |
| for (let i=0; i<10000; i++) { |
| const x = Math.random()*550; |
| const y = Math.random()*550; |
| ctx.paint.setColor(randomColorTwo(CanvasKit, 1, 2)); |
| ctx.canvas.drawOval(CanvasKit.LTRBRect(x, y, x+50, y+50), ctx.paint); |
| } |
| }, |
| teardown: function(CanvasKit, ctx) { |
| ctx.paint.delete(); |
| }, |
| perfKey: 'canvas_drawOval', |
| }); |
| |
| tests.push({ |
| description: 'Draw 10K colored roundRects', |
| setup: function(CanvasKit, ctx) { |
| ctx.canvas = ctx.surface.getCanvas(); |
| |
| ctx.paint = new CanvasKit.Paint(); |
| ctx.paint.setAntiAlias(true); |
| ctx.paint.setStyle(CanvasKit.PaintStyle.Fill); |
| }, |
| test: function(CanvasKit, ctx) { |
| for (let i=0; i<10000; i++) { |
| const x = Math.random()*550; |
| const y = Math.random()*550; |
| ctx.paint.setColor(randomColorTwo(CanvasKit, 0, 2)); |
| const rr = CanvasKit.RRectXY(CanvasKit.LTRBRect(x, y, x+50, y+50), 10, 10,); |
| ctx.canvas.drawRRect(rr, ctx.paint); |
| } |
| }, |
| teardown: function(CanvasKit, ctx) { |
| ctx.paint.delete(); |
| }, |
| perfKey: 'canvas_drawRRect', |
| }); |
| |
| tests.push({ |
| description: 'Draw 10K colored rects', |
| setup: function(CanvasKit, ctx) { |
| ctx.canvas = ctx.surface.getCanvas(); |
| |
| ctx.paint = new CanvasKit.Paint(); |
| ctx.paint.setAntiAlias(true); |
| ctx.paint.setStyle(CanvasKit.PaintStyle.Fill); |
| }, |
| test: function(CanvasKit, ctx) { |
| for (let i=0; i<10000; i++) { |
| const x = Math.random()*550; |
| const y = Math.random()*550; |
| ctx.paint.setColor(randomColorTwo(CanvasKit, 1, 2)); |
| ctx.canvas.drawRect(CanvasKit.LTRBRect(x, y, x+50, y+50), ctx.paint); |
| } |
| }, |
| teardown: function(CanvasKit, ctx) { |
| ctx.paint.delete(); |
| }, |
| perfKey: 'canvas_drawRect', |
| }); |
| |
| tests.push({ |
| description: "Draw 10K colored rects with malloc'd rect", |
| setup: function(CanvasKit, ctx) { |
| ctx.canvas = ctx.surface.getCanvas(); |
| |
| ctx.paint = new CanvasKit.Paint(); |
| ctx.paint.setAntiAlias(true); |
| ctx.paint.setStyle(CanvasKit.PaintStyle.Fill); |
| ctx.rect = CanvasKit.Malloc(Float32Array, 4); |
| }, |
| test: function(CanvasKit, ctx) { |
| for (let i=0; i<10000; i++) { |
| ctx.paint.setColor(randomColorTwo(CanvasKit, 1, 2)); |
| const ta = ctx.rect.toTypedArray(); |
| ta[0] = Math.random()*550; // x |
| ta[1] = Math.random()*550; // y |
| ta[2] = ta[0] + 50; |
| ta[3] = ta[1] + 50; |
| ctx.canvas.drawRect(ta, ctx.paint); |
| } |
| }, |
| teardown: function(CanvasKit, ctx) { |
| ctx.paint.delete(); |
| CanvasKit.Free(ctx.rect); |
| }, |
| perfKey: 'canvas_drawRect_malloc', |
| }); |
| |
| tests.push({ |
| description: 'Draw 10K colored rects using 4 float API', |
| setup: function(CanvasKit, ctx) { |
| ctx.canvas = ctx.surface.getCanvas(); |
| |
| ctx.paint = new CanvasKit.Paint(); |
| ctx.paint.setAntiAlias(true); |
| ctx.paint.setStyle(CanvasKit.PaintStyle.Fill); |
| }, |
| test: function(CanvasKit, ctx) { |
| for (let i=0; i<10000; i++) { |
| const x = Math.random()*550; |
| const y = Math.random()*550; |
| ctx.paint.setColor(randomColorTwo(CanvasKit, 1, 2)); |
| ctx.canvas.drawRect4f(x, y, x+50, y+50, ctx.paint); |
| } |
| }, |
| teardown: function(CanvasKit, ctx) { |
| ctx.paint.delete(); |
| }, |
| perfKey: 'canvas_drawRect4f', |
| }); |
| |
| tests.push({ |
| description: 'Compute tonal colors', |
| setup: function(CanvasKit, ctx) {}, |
| test: function(CanvasKit, ctx) { |
| for (let i = 0; i < 10; i++) { |
| const input = { |
| ambient: randomColor(CanvasKit), |
| spot: randomColor(CanvasKit), |
| }; |
| const out = CanvasKit.computeTonalColors(input); |
| if (out.spot[2] > 10 || out.ambient[3] > 10) { |
| // Something to make sure v8 can't optimize away the return value |
| throw 'not possible'; |
| } |
| } |
| }, |
| teardown: function(CanvasKit, ctx) {}, |
| perfKey: 'computeTonalColors', |
| }); |
| |
| tests.push({ |
| description: 'Get and set the color to a paint', |
| setup: function(CanvasKit, ctx) { |
| ctx.paint = new CanvasKit.Paint(); |
| }, |
| test: function(CanvasKit, ctx) { |
| for (let i = 0; i < 10; i++) { |
| ctx.paint.setColor(randomColor(CanvasKit)); |
| const color = ctx.paint.getColor(); |
| if (color[3] > 4) { |
| // Something to make sure v8 can't optimize away the return value |
| throw 'not possible'; |
| } |
| } |
| }, |
| teardown: function(CanvasKit, ctx) { |
| ctx.paint.delete(); |
| }, |
| perfKey: 'paint_setColor_getColor', |
| }); |
| |
| tests.push({ |
| description: 'Set the color to a paint by components', |
| setup: function(CanvasKit, ctx) { |
| ctx.paint = new CanvasKit.Paint(); |
| }, |
| test: function(CanvasKit, ctx) { |
| const r = Math.random(); |
| const g = Math.random(); |
| const b = Math.random(); |
| const a = Math.random(); |
| for (let i = 0; i < 10000; i++) { |
| ctx.paint.setColorComponents(r, g, b, a); |
| } |
| }, |
| teardown: function(CanvasKit, ctx) { |
| ctx.paint.delete(); |
| }, |
| perfKey: 'paint_setColorComponents', |
| }); |
| |
| tests.push({ |
| description: 'Draw a shadow with tonal colors', |
| setup: function(CanvasKit, ctx) { |
| ctx.canvas = ctx.surface.getCanvas(); |
| |
| ctx.input = { |
| ambient: CanvasKit.Color4f(0.2, 0.1, 0.3, 0.5), |
| spot: CanvasKit.Color4f(0.8, 0.8, 0.9, 0.9), |
| }; |
| ctx.lightRadius = 30; |
| ctx.flags = 0; |
| ctx.lightPos = [250,150,300]; |
| ctx.zPlaneParams = [0,0,1]; |
| ctx.path = starPath(CanvasKit); |
| }, |
| test: function(CanvasKit, ctx) { |
| const out = CanvasKit.computeTonalColors(ctx.input); |
| ctx.canvas.drawShadow(ctx.path, ctx.zPlaneParams, ctx.lightPos, ctx.lightRadius, |
| out.ambient, out.spot, ctx.flags); |
| }, |
| teardown: function(CanvasKit, ctx) {}, |
| perfKey: 'canvas_drawShadow', |
| }); |
| |
| tests.push({ |
| description: 'Draw a gradient with an array of 10K colors', |
| setup: function(CanvasKit, ctx) { |
| ctx.canvas = ctx.surface.getCanvas(); |
| }, |
| test: function(CanvasKit, ctx) { |
| ctx.canvas.clear(CanvasKit.WHITE); |
| |
| const num = 10000; |
| colors = Array(num); |
| positions = Array(num); |
| // Create an array of colors spaced evenly along the 0..1 range of positions. |
| for (let i=0; i<num; i++) { |
| colors[i] = randomColorTwo(CanvasKit, 2, 3); |
| positions[i] = i/num; |
| } |
| // make a gradient from those colors |
| const shader = CanvasKit.Shader.MakeRadialGradient( |
| [300, 300], 50, // center, radius |
| colors, positions, |
| CanvasKit.TileMode.Mirror, |
| ); |
| // Fill the canvas using the gradient shader. |
| const paint = new CanvasKit.Paint(); |
| paint.setStyle(CanvasKit.PaintStyle.Fill); |
| paint.setShader(shader); |
| ctx.canvas.drawPaint(paint); |
| |
| shader.delete(); |
| paint.delete(); |
| }, |
| teardown: function(CanvasKit, ctx) {}, |
| perfKey: 'canvas_drawHugeGradient', |
| }); |
| |
| tests.push({ |
| description: 'Draw a png image', |
| setup: async function(CanvasKit, ctx) { |
| ctx.canvas = ctx.surface.getCanvas(); |
| ctx.paint = new CanvasKit.Paint(); |
| ctx.img = CanvasKit.MakeImageFromEncoded(ctx.files['test_512x512.png']); |
| ctx.frame = 0; |
| }, |
| test: function(CanvasKit, ctx) { |
| ctx.canvas.clear(CanvasKit.WHITE); |
| // Make the image to move so you can see visually that the test is running. |
| ctx.canvas.drawImage(ctx.img, ctx.frame, ctx.frame, ctx.paint); |
| ctx.frame++; |
| }, |
| teardown: function(CanvasKit, ctx) { |
| ctx.img.delete(); |
| ctx.paint.delete(); |
| }, |
| perfKey: 'canvas_drawPngImage', |
| }); |
| |
| |
| function htmlImageElementToDataURL(htmlImageElement) { |
| const canvas = document.createElement('canvas'); |
| canvas.height = htmlImageElement.height; |
| canvas.width = htmlImageElement.width; |
| const ctx = canvas.getContext('2d') |
| ctx.drawImage(htmlImageElement, 0, 0); |
| return canvas.toDataURL(); |
| } |
| |
| // This for loop generates two perf cases for each test image. One uses browser APIs |
| // to decode an image, and the other uses codecs included in the CanvasKit wasm to decode an |
| // image. wasm codec Image decoding is faster (50 microseconds vs 20000 microseconds), but |
| // including codecs in wasm increases the size of the CanvasKit wasm binary. |
| for (const testImageFilename of ['test_64x64.png', 'test_512x512.png', 'test_1500x959.jpg']) { |
| const htmlImageElement = new Image(); |
| htmlImageElementLoadPromise = new Promise((resolve) => |
| htmlImageElement.addEventListener('load', resolve)); |
| // Create a data url of the image so that load and decode time can be measured |
| // while ignoring the time of getting the image from disk / the network. |
| imageDataURLPromise = htmlImageElementLoadPromise.then(() => |
| htmlImageElementToDataURL(htmlImageElement)); |
| htmlImageElement.src = `/static/assets/${testImageFilename}`; |
| |
| tests.push({ |
| description: 'Decode an image using HTMLImageElement and Canvas2D', |
| setup: async function(CanvasKit, ctx) { |
| ctx.imageDataURL = await imageDataURLPromise; |
| }, |
| test: async function(CanvasKit, ctx) { |
| const image = new Image(); |
| // Testing showed that waiting for the load event is faster than waiting for |
| // image.decode(). |
| // Despite the name, both of them would decode the image, it was loaded in setup. |
| // HTMLImageElement.decode() reference: |
| // https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decode |
| const promise = new Promise((resolve) => image.addEventListener('load', resolve)); |
| image.src = ctx.imageDataURL; |
| await promise; |
| const img = await CanvasKit.MakeImageFromCanvasImageSource(image); |
| img.delete(); |
| }, |
| teardown: function(CanvasKit, ctx) {}, |
| perfKey: `canvas_${testImageFilename}_HTMLImageElementDecoding`, |
| }); |
| |
| tests.push({ |
| description: 'Decode an image using codecs in wasm', |
| setup: function(CanvasKit, ctx) {}, |
| test: function(CanvasKit, ctx) { |
| const img = CanvasKit.MakeImageFromEncoded(ctx.files[testImageFilename]); |
| img.delete(); |
| }, |
| teardown: function(CanvasKit, ctx) {}, |
| perfKey: '`canvas_${testImageFilename}_wasmImageDecoding`', |
| }); |
| } |
| |
| // 3x3 matrix ops |
| tests.push({ |
| description: 'Multiply 3x3 matrices together', |
| setup: function(CanvasKit, ctx) { |
| ctx.first = CanvasKit.Matrix.rotated(Math.PI/2, 10, 20); |
| ctx.second = CanvasKit.Matrix.scaled(1, 2, 3, 4); |
| }, |
| test: function(CanvasKit, ctx) { |
| ctx.result = CanvasKit.Matrix.multiply(ctx.first, ctx.second); |
| if (ctx.result.length === 18) { |
| throw 'this is here to keep the result from being optimized away'; |
| } |
| }, |
| teardown: function(CanvasKit, ctx) {}, |
| perfKey: 'skmatrix_multiply', |
| }); |
| |
| tests.push({ |
| description: 'Transform a point using a matrix (mapPoint)', |
| setup: function(CanvasKit, ctx) { |
| ctx.matr = CanvasKit.Matrix.multiply( |
| CanvasKit.Matrix.rotated(Math.PI/2, 10, 20), |
| CanvasKit.Matrix.scaled(1, 2, 3, 4), |
| ); // make an arbitrary, but interesting matrix |
| }, |
| test: function(CanvasKit, ctx) { |
| for (let i = 0; i < 30; i++) { |
| const pt = CanvasKit.Matrix.mapPoints(ctx.matr, [i, i]); |
| if (pt.length === 18) { |
| throw 'this is here to keep pt from being optimized away'; |
| } |
| } |
| }, |
| teardown: function(CanvasKit, ctx) {}, |
| perfKey: 'skmatrix_transformPoint', |
| }); |
| |
| tests.push({ |
| description: 'Invert a 3x3 matrix', |
| setup: function(CanvasKit, ctx) { |
| ctx.matr = CanvasKit.Matrix.multiply( |
| CanvasKit.Matrix.rotated(Math.PI/2, 10, 20), |
| CanvasKit.Matrix.scaled(1, 2, 3, 4), |
| ); |
| }, |
| test: function(CanvasKit, ctx) { |
| ctx.result = CanvasKit.Matrix.invert(ctx.matr); |
| if (ctx.result.length === 18) { |
| throw 'this is here to keep the result from being optimized away'; |
| } |
| }, |
| teardown: function(CanvasKit, ctx) {}, |
| perfKey: 'skmatrix_invert', |
| }); |
| |
| tests.push({ |
| description: 'Create a shader from a 3x3 matrix', |
| setup: function(CanvasKit, ctx) { |
| ctx.matr = CanvasKit.Matrix.multiply( |
| CanvasKit.Matrix.rotated(Math.PI/2, 10, 20), |
| CanvasKit.Matrix.scaled(1, 2, 3, 4), |
| ); |
| }, |
| test: function(CanvasKit, ctx) { |
| const shader = CanvasKit.Shader.MakeSweepGradient( |
| 100, 100, |
| [CanvasKit.GREEN, CanvasKit.BLUE], |
| [0.0, 1.0], |
| CanvasKit.TileMode.Clamp, |
| ctx.matr); |
| shader.delete(); |
| }, |
| teardown: function(CanvasKit, ctx) {}, |
| perfKey: 'skmatrix_makeShader', |
| }); |
| |
| tests.push({ |
| description: 'Concat 3x3 matrix on a canvas', |
| setup: function(CanvasKit, ctx) { |
| ctx.canvas = new CanvasKit.Canvas(); |
| ctx.matr = CanvasKit.Matrix.multiply( |
| CanvasKit.Matrix.rotated(Math.PI/2, 10, 20), |
| CanvasKit.Matrix.scaled(1, 2, 3, 4), |
| ); |
| }, |
| test: function(CanvasKit, ctx) { |
| ctx.canvas.concat(ctx.matr); |
| }, |
| teardown: function(CanvasKit, ctx) { |
| ctx.canvas.delete(); |
| }, |
| perfKey: 'skmatrix_concat', |
| }); |
| |
| // 4x4 matrix operations |
| tests.push({ |
| description: 'Multiply 4x4 matrices together', |
| setup: function(CanvasKit, ctx) { |
| ctx.first = CanvasKit.M44.rotated([10, 20, 30], Math.PI/2); |
| ctx.second = CanvasKit.M44.scaled([1, 2, 3]); |
| }, |
| test: function(CanvasKit, ctx) { |
| ctx.result = CanvasKit.M44.multiply(ctx.first, ctx.second); |
| if (ctx.result.length === 18) { |
| throw 'this is here to keep the result from being optimized away'; |
| } |
| }, |
| teardown: function(CanvasKit, ctx) {}, |
| perfKey: 'skm44_multiply', |
| }); |
| |
| tests.push({ |
| description: 'Invert a 4x4 matrix', |
| setup: function(CanvasKit, ctx) { |
| ctx.matr = CanvasKit.M44.multiply( |
| CanvasKit.M44.rotated([10, 20, 30], Math.PI/2), |
| CanvasKit.M44.scaled([1, 2, 3]), |
| ); |
| }, |
| test: function(CanvasKit, ctx) { |
| const result = CanvasKit.M44.invert(ctx.matr); |
| if (result.length === 18) { |
| throw 'this is here to keep the result from being optimized away'; |
| } |
| }, |
| teardown: function(CanvasKit, ctx) {}, |
| perfKey: 'skm44_invert', |
| }); |
| |
| tests.push({ |
| description: 'Concat 4x4 matrix on a canvas', |
| setup: function(CanvasKit, ctx) { |
| ctx.canvas = new CanvasKit.Canvas(); |
| ctx.matr = CanvasKit.M44.multiply( |
| CanvasKit.M44.rotated([10, 20, 30], Math.PI/2), |
| CanvasKit.M44.scaled([1, 2, 3]), |
| ); |
| }, |
| test: function(CanvasKit, ctx) { |
| ctx.canvas.concat(ctx.matr); |
| }, |
| teardown: function(CanvasKit, ctx) { |
| ctx.canvas.delete(); |
| }, |
| perfKey: 'skm44_concat', |
| }); |
| |
| // DOMMatrix operations |
| tests.push({ |
| description: 'Multiply DOM matrices together', |
| setup: function(CanvasKit, ctx) { |
| ctx.first = new DOMMatrix().translate(10, 20).rotate(90).translate(-10, -20); |
| ctx.second = new DOMMatrix().translate(3, 4).scale(1, 2).translate(-3, -4); |
| }, |
| test: function(CanvasKit, ctx) { |
| const result = ctx.first.multiply(ctx.second); |
| if (result.length === 18) { |
| throw 'this is here to keep the result from being optimized away'; |
| } |
| }, |
| teardown: function(CanvasKit, ctx) {}, |
| perfKey: 'dommatrix_multiply', |
| }); |
| |
| tests.push({ |
| description: 'Transform a point using a matrix (transformPoint)', |
| setup: function(CanvasKit, ctx) { |
| ctx.matr = new DOMMatrix().translate(10, 20).rotate(90).translate(-10, -20) |
| .multiply(new DOMMatrix().translate(3, 4).scale(1, 2).translate(-3, -4)); |
| |
| ctx.reusablePt = new DOMPoint(0, 0) |
| }, |
| test: function(CanvasKit, ctx) { |
| for (let i = 0; i < 30; i++) { |
| ctx.reusablePt.X = i; ctx.reusablePt.Y = i; |
| const pt = ctx.matr.transformPoint(ctx.reusablePt); |
| if (pt.length === 18) { |
| throw 'this is here to keep pt from being optimized away'; |
| } |
| } |
| }, |
| teardown: function(CanvasKit, ctx) {}, |
| perfKey: 'dommatrix_transformPoint', |
| }); |
| |
| tests.push({ |
| description: 'Invert a DOM matrix', |
| setup: function(CanvasKit, ctx) { |
| ctx.matr = new DOMMatrix().translate(10, 20).rotate(90).translate(-10, -20) |
| .multiply(new DOMMatrix().translate(3, 4).scale(1, 2).translate(-3, -4)); |
| }, |
| test: function(CanvasKit, ctx) { |
| const inverted = ctx.matr.inverse(); |
| if (inverted.length === 18) { |
| throw 'this is here to keep the result from being optimized away'; |
| } |
| }, |
| teardown: function(CanvasKit, ctx) {}, |
| perfKey: 'dommatrix_invert', |
| }); |
| |
| tests.push({ |
| description: 'make a shader from a DOMMatrix', |
| setup: function(CanvasKit, ctx) { |
| ctx.matr = new DOMMatrix().translate(10, 20).rotate(90).translate(-10, -20) |
| .multiply(new DOMMatrix().translate(3, 4).scale(1, 2).translate(-3, -4)); |
| }, |
| test: function(CanvasKit, ctx) { |
| const shader = CanvasKit.Shader.MakeSweepGradient( |
| 100, 100, |
| [CanvasKit.GREEN, CanvasKit.BLUE], |
| [0.0, 1.0], |
| CanvasKit.TileMode.Clamp, |
| ctx.matr); |
| shader.delete(); |
| }, |
| teardown: function(CanvasKit, ctx) {}, |
| perfKey: 'dommatrix_makeShader', |
| }); |
| |
| // Tests the layout and drawing of a paragraph with hundreds of words. |
| // In the second variant, the colors change every frame |
| // In the third variant, the font size cycles between three sizes. |
| // In the fourth variant, the layout width changes. |
| // in the fifth variant, all of those properties change at the same time. |
| for (const variant of ['static', 'color_changing', 'size_changing', 'layout_changing', 'everything']) { |
| tests.push({ |
| description: `Layout and draw a ${variant} paragraph`, |
| setup: function(CanvasKit, ctx) { |
| ctx.canvas = ctx.surface.getCanvas(); |
| ctx.fontMgr = CanvasKit.FontMgr.FromData([ctx.files['Roboto-Regular.ttf']]); |
| ctx.paraStyle = new CanvasKit.ParagraphStyle({ |
| textStyle: { |
| color: CanvasKit.WHITE, |
| fontFamilies: ['Roboto'], |
| fontSize: 11, |
| }, |
| textAlign: CanvasKit.TextAlign.Left, |
| }); |
| ctx.frame = 0; |
| 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); |
| }, |
| test: function(CanvasKit, ctx) { |
| ctx.canvas.clear(CanvasKit.BLACK); |
| const builder = CanvasKit.ParagraphBuilder.Make(ctx.paraStyle, ctx.fontMgr); |
| let pos = 0; |
| let color = CanvasKit.WHITE; |
| while (pos < ctx.text.length) { |
| let size = 11; |
| if (variant === 'size_changing' || variant === 'everything') { |
| // the bigger this modulo, the more work it takes to fill the glyph cache |
| size += ctx.frame % 4; |
| } |
| if (variant === 'color_changing' || variant === 'everything') { |
| color = randomColorTwo(CanvasKit, 0, 1); |
| } |
| builder.pushStyle(CanvasKit.TextStyle({ |
| color: color, |
| fontFamilies: ['Roboto'], |
| fontSize: size, |
| fontStyle: { |
| weight: CanvasKit.FontWeight.Bold, |
| }, |
| })); |
| const len = Math.floor(Math.random()*5+2); |
| builder.addText(ctx.text.slice(pos, pos+len)); |
| builder.pop(); |
| pos += len; |
| } |
| const paragraph = builder.build(); |
| let w = 0; |
| const base_width = 520; |
| const varying_width_modulo = 70; |
| if (variant === 'layout_changing' || variant === 'everything') { |
| w = ctx.frame % varying_width_modulo; |
| } |
| paragraph.layout(base_width + w); // width in pixels to use when wrapping text |
| ctx.canvas.drawParagraph(paragraph, 10, 10); |
| |
| ctx.frame++; |
| builder.delete(); |
| paragraph.delete(); |
| }, |
| teardown: function(CanvasKit, ctx) { |
| ctx.fontMgr.delete(); |
| }, |
| perfKey: 'canvas_drawParagraph_'+variant, |
| }); |
| } |
| |
| tests.push({ |
| description: 'Draw a path with a blur mask', |
| setup: function(CanvasKit, ctx) { |
| ctx.canvas = ctx.surface.getCanvas(); |
| ctx.paint = new CanvasKit.Paint(); |
| ctx.paint.setAntiAlias(true); |
| ctx.paint.setStyle(CanvasKit.PaintStyle.Fill); |
| ctx.paint.setColor(CanvasKit.Color4f(0.1, 0.7, 0.0, 1.0)); |
| ctx.path = starPath(CanvasKit); |
| ctx.frame = 0; |
| }, |
| test: function(CanvasKit, ctx) { |
| const sigma = 0.1 + (ctx.frame/10); |
| const blurMask = CanvasKit.MaskFilter.MakeBlur( |
| CanvasKit.BlurStyle.Normal, sigma, true); |
| ctx.paint.setMaskFilter(blurMask); |
| ctx.canvas.drawPath(ctx.path, ctx.paint); |
| blurMask.delete(); |
| ctx.frame++; |
| }, |
| teardown: function(CanvasKit, ctx) { |
| ctx.paint.delete(); |
| ctx.path.delete(); |
| }, |
| perfKey: 'canvas_blur_mask_filter', |
| }); |
| |
| for (const variant of ['ttf', 'woff', 'woff2']) { |
| tests.push({ |
| description: `Get glyphIDs from a ${variant} font`, |
| setup: function (CanvasKit, ctx) { |
| const robotoData = ctx.files['Roboto-Regular.' + variant]; |
| ctx.robotoFace = CanvasKit.Typeface.MakeFreeTypeFaceFromData(robotoData); |
| if (!ctx.robotoFace) { |
| throw 'could not load ' + variant; |
| } |
| ctx.robotoFont = new CanvasKit.Font(ctx.robotoFace, 20); |
| ctx.testGlyphID = 1; |
| }, |
| test: function (CanvasKit, ctx) { |
| // We get one glyph ID at a time to force cache misses and require Skia to |
| // perhaps re-access the font. See skbug.com/12112 for example. |
| const output = new Uint16Array(1); |
| for (let i = ctx.testGlyphID; i < ctx.testGlyphID+100; i++) { |
| ctx.robotoFont.getGlyphIDs(String.fromCodePoint(i), 1, output); |
| } |
| ctx.testGlyphID += 100; |
| }, |
| teardown: function (CanvasKit, ctx) { |
| ctx.robotoFace.delete(); |
| ctx.robotoFont.delete(); |
| }, |
| perfKey: 'font_getGlyphIDs_' + variant, |
| }); |
| } |