describe('CanvasKit\'s Canvas Behavior', function() { let container = document.createElement('div'); document.body.appendChild(container); const CANVAS_WIDTH = 600; const CANVAS_HEIGHT = 600; beforeEach(function() { container.innerHTML = ` `; }); afterEach(function() { container.innerHTML = ''; }); it('can draw directly to a canvas', function(done) { LoadCanvasKit.then(catchException(done, () => { // This is taken from example.html const surface = CanvasKit.MakeCanvasSurface('test'); expect(surface).toBeTruthy('Could not make surface') if (!surface) { done(); return; } const canvas = surface.getCanvas(); const paint = new CanvasKit.SkPaint(); paint.setStrokeWidth(2.0); paint.setAntiAlias(true); paint.setColor(CanvasKit.Color(0, 0, 0, 1.0)); paint.setStyle(CanvasKit.PaintStyle.Stroke); canvas.drawLine(3, 10, 30, 15, paint); canvas.drawRoundRect(CanvasKit.LTRBRect(5, 35, 45, 80), 15, 10, paint); canvas.drawOval(CanvasKit.LTRBRect(5, 35, 45, 80), paint); canvas.drawArc(CanvasKit.LTRBRect(55, 35, 95, 80), 15, 270, true, paint); const font = new CanvasKit.SkFont(null, 20); canvas.drawText('this is ascii text', 5, 100, paint, font); const blob = CanvasKit.SkTextBlob.MakeFromText('Unicode chars 💩 é É ص', font); canvas.drawTextBlob(blob, 5, 130, paint); surface.flush(); font.delete(); blob.delete(); paint.delete(); reportSurface(surface, 'canvas_api_example', done); })); // See canvas2d for more API tests }); it('can apply an effect and draw text', function(done) { LoadCanvasKit.then(catchException(done, () => { const surface = CanvasKit.MakeCanvasSurface('test'); expect(surface).toBeTruthy('Could not make surface') if (!surface) { done(); return; } const canvas = surface.getCanvas(); const path = starPath(CanvasKit); const paint = new CanvasKit.SkPaint(); const textPaint = new CanvasKit.SkPaint(); textPaint.setColor(CanvasKit.Color(40, 0, 0, 1.0)); textPaint.setAntiAlias(true); const textFont = new CanvasKit.SkFont(null, 30); const dpe = CanvasKit.SkPathEffect.MakeDash([15, 5, 5, 10], 1); paint.setPathEffect(dpe); paint.setStyle(CanvasKit.PaintStyle.Stroke); paint.setStrokeWidth(5.0); paint.setAntiAlias(true); paint.setColor(CanvasKit.Color(66, 129, 164, 1.0)); canvas.clear(CanvasKit.Color(255, 255, 255, 1.0)); canvas.drawPath(path, paint); canvas.drawText('This is text', 10, 280, textPaint, textFont); surface.flush(); dpe.delete(); path.delete(); paint.delete(); textFont.delete(); textPaint.delete(); reportSurface(surface, 'effect_and_text_example', done); })); }); it('returns the depth of the save state stack', function(done) { LoadCanvasKit.then(catchException(done, () => { const surface = CanvasKit.MakeCanvasSurface('test'); expect(surface).toBeTruthy('Could not make surface') if (!surface) { done(); return; } const canvas = surface.getCanvas(); expect(canvas.getSaveCount()).toEqual(1); canvas.save(); canvas.save(); canvas.restore(); canvas.save(); canvas.save(); expect(canvas.getSaveCount()).toEqual(4); // does nothing, by the SkCanvas API canvas.restoreToCount(500); expect(canvas.getSaveCount()).toEqual(4); canvas.restore(); expect(canvas.getSaveCount()).toEqual(3); canvas.save(); canvas.restoreToCount(2); expect(canvas.getSaveCount()).toEqual(2); surface.delete(); done(); })); }); it('draws circles', function(done) { LoadCanvasKit.then(catchException(done, () => { const surface = CanvasKit.MakeCanvasSurface('test'); expect(surface).toBeTruthy('Could not make surface') if (!surface) { done(); return; } const canvas = surface.getCanvas(); const path = starPath(CanvasKit); const paint = new CanvasKit.SkPaint(); paint.setStyle(CanvasKit.PaintStyle.Stroke); paint.setStrokeWidth(5.0); paint.setAntiAlias(true); paint.setColor(CanvasKit.CYAN); canvas.clear(CanvasKit.WHITE); canvas.drawCircle(30, 50, 15, paint); paint.setStyle(CanvasKit.PaintStyle.Fill); paint.setColor(CanvasKit.RED); canvas.drawCircle(130, 80, 60, paint); canvas.drawCircle(20, 150, 60, paint); surface.flush(); path.delete(); paint.delete(); reportSurface(surface, 'circle_canvas', done); })); }); it('draws simple rrects', function(done) { LoadCanvasKit.then(catchException(done, () => { const surface = CanvasKit.MakeCanvasSurface('test'); expect(surface).toBeTruthy('Could not make surface') if (!surface) { done(); return; } const canvas = surface.getCanvas(); const path = starPath(CanvasKit); const paint = new CanvasKit.SkPaint(); paint.setStyle(CanvasKit.PaintStyle.Stroke); paint.setStrokeWidth(3.0); paint.setAntiAlias(true); paint.setColor(CanvasKit.BLACK); canvas.clear(CanvasKit.WHITE); canvas.drawRRect(CanvasKit.RRectXY( CanvasKit.LTRBRect(10, 10, 50, 50), 5, 10), paint); canvas.drawRRect(CanvasKit.RRectXY( CanvasKit.LTRBRect(60, 10, 110, 50), 10, 5), paint); canvas.drawRRect(CanvasKit.RRectXY( CanvasKit.LTRBRect(10, 60, 210, 260), 0, 30), paint); canvas.drawRRect(CanvasKit.RRectXY( CanvasKit.LTRBRect(50, 90, 160, 210), 30, 30), paint); surface.flush(); path.delete(); paint.delete(); reportSurface(surface, 'rrect_canvas', done); })); }); it('draws complex rrects', function(done) { LoadCanvasKit.then(catchException(done, () => { const surface = CanvasKit.MakeCanvasSurface('test'); expect(surface).toBeTruthy('Could not make surface') if (!surface) { done(); return; } const canvas = surface.getCanvas(); const path = starPath(CanvasKit); const paint = new CanvasKit.SkPaint(); paint.setStyle(CanvasKit.PaintStyle.Stroke); paint.setStrokeWidth(3.0); paint.setAntiAlias(true); paint.setColor(CanvasKit.BLACK); canvas.clear(CanvasKit.WHITE); canvas.drawRRect({ rect: CanvasKit.LTRBRect(10, 10, 210, 210), rx1: 10, // top left corner, going clockwise ry1: 30, rx2: 30, ry2: 10, rx3: 50, ry3: 75, rx4: 120, ry4: 120, }, paint); surface.flush(); path.delete(); paint.delete(); reportSurface(surface, 'rrect_8corners_canvas', done); })); }); it('draws between two rrects', function(done) { LoadCanvasKit.then(catchException(done, () => { const surface = CanvasKit.MakeCanvasSurface('test'); expect(surface).toBeTruthy('Could not make surface') if (!surface) { done(); return; } const canvas = surface.getCanvas(); const path = starPath(CanvasKit); const paint = new CanvasKit.SkPaint(); paint.setStyle(CanvasKit.PaintStyle.Fill); paint.setStrokeWidth(3.0); paint.setAntiAlias(true); paint.setColor(CanvasKit.BLACK); canvas.clear(CanvasKit.WHITE); const outer = CanvasKit.RRectXY(CanvasKit.LTRBRect(10, 60, 210, 260), 10, 5); const inner = CanvasKit.RRectXY(CanvasKit.LTRBRect(50, 90, 160, 210), 30, 30); canvas.drawDRRect(outer, inner, paint); surface.flush(); path.delete(); paint.delete(); reportSurface(surface, 'drawDRRect_canvas', done); })); }); it('draws with color filters', function(done) { LoadCanvasKit.then(catchException(done, () => { const surface = CanvasKit.MakeCanvasSurface('test'); expect(surface).toBeTruthy('Could not make surface') if (!surface) { done(); return; } const canvas = surface.getCanvas(); const paint = new CanvasKit.SkPaint(); const blue = CanvasKit.SkColorFilter.MakeBlend( CanvasKit.BLUE, CanvasKit.BlendMode.SrcIn); const red = CanvasKit.SkColorFilter.MakeBlend( CanvasKit.Color(255, 0, 0, 0.8), CanvasKit.BlendMode.SrcOver); const lerp = CanvasKit.SkColorFilter.MakeLerp(0.6, red, blue); paint.setStyle(CanvasKit.PaintStyle.Fill); paint.setAntiAlias(true); canvas.clear(CanvasKit.Color(230, 230, 230)); paint.setColorFilter(blue) canvas.drawRect(CanvasKit.LTRBRect(10, 10, 60, 60), paint); paint.setColorFilter(lerp) canvas.drawRect(CanvasKit.LTRBRect(50, 10, 100, 60), paint); paint.setColorFilter(red) canvas.drawRect(CanvasKit.LTRBRect(90, 10, 140, 60), paint); const r = CanvasKit.SkColorMatrix.rotated(0, .707, -.707); const b = CanvasKit.SkColorMatrix.rotated(2, .5, .866); const s = CanvasKit.SkColorMatrix.scaled(0.9, 1.5, 0.8, 0.8); let cm = CanvasKit.SkColorMatrix.concat(r, s); cm = CanvasKit.SkColorMatrix.concat(cm, b); CanvasKit.SkColorMatrix.postTranslate(cm, 20, 0, -10, 0); const mat = CanvasKit.SkColorFilter.MakeMatrix(cm); const final = CanvasKit.SkColorFilter.MakeCompose(mat, lerp); paint.setColorFilter(final) canvas.drawRect(CanvasKit.LTRBRect(10, 70, 140, 120), paint); surface.flush(); paint.delete(); blue.delete(); red.delete(); lerp.delete(); final.delete(); reportSurface(surface, 'colorfilters_canvas', done); })); }); it('can use Malloc to save a copy', function(done) { LoadCanvasKit.then(catchException(done, () => { const surface = CanvasKit.MakeCanvasSurface('test'); expect(surface).toBeTruthy('Could not make surface') if (!surface) { done(); return; } const canvas = surface.getCanvas(); const paint = new CanvasKit.SkPaint(); const src = [ 0.8, 0.45, 2, 0, 20, 0.53, -0.918, 0.566, 0, 0, 0.53, -0.918, -0.566, 0, -10, 0, 0, 0, 0.8, 0, ] const cm = new CanvasKit.Malloc(Float32Array, 20); for (i in src) { cm[i] = src[i]; } const final = CanvasKit.SkColorFilter.MakeMatrix(cm); paint.setColorFilter(final) canvas.drawRect(CanvasKit.LTRBRect(10, 70, 140, 120), paint); surface.flush() paint.delete(); final.delete(); reportSurface(surface, 'colorfilters_malloc_canvas', done); })); }); it('can clip using rrect and path', function(done) { LoadCanvasKit.then(catchException(done, () => { const surface = CanvasKit.MakeCanvasSurface('test'); expect(surface).toBeTruthy('Could not make surface') if (!surface) { done(); return; } const canvas = surface.getCanvas(); const path = starPath(CanvasKit); const paint = new CanvasKit.SkPaint(); paint.setColor(CanvasKit.BLUE); const rrect = CanvasKit.RRectXY(CanvasKit.LTRBRect(300, 300, 500, 500), 40, 40); canvas.save(); // draw magenta around the outside edge of an rrect. canvas.clipRRect(rrect, CanvasKit.ClipOp.Difference, true); canvas.drawColor(CanvasKit.Color(250, 30, 240, 0.9), CanvasKit.BlendMode.SrcOver); canvas.restore(); // draw grey inside of a star pattern, then the blue star on top canvas.clipPath(path, CanvasKit.ClipOp.Intersect, false); canvas.drawColor(CanvasKit.Color(200, 200, 200, 1.0), CanvasKit.BlendMode.SrcOver); canvas.drawPath(path, paint); surface.flush(); path.delete(); reportSurface(surface, 'clips_canvas', done); })); }); // inspired by https://fiddle.skia.org/c/feb2a08bb09ede5309678d6a0ab3f981 it('can save layers with rect and paint', function(done) { LoadCanvasKit.then(catchException(done, () => { const surface = CanvasKit.MakeCanvasSurface('test'); expect(surface).toBeTruthy('Could not make surface') if (!surface) { done(); return; } const canvas = surface.getCanvas(); canvas.clear(CanvasKit.WHITE); const redPaint = new CanvasKit.SkPaint(); redPaint.setColor(CanvasKit.RED); const solidBluePaint = new CanvasKit.SkPaint(); solidBluePaint.setColor(CanvasKit.BLUE); const thirtyBluePaint = new CanvasKit.SkPaint(); thirtyBluePaint.setColor(CanvasKit.BLUE); thirtyBluePaint.setAlphaf(0.3); const alpha = new CanvasKit.SkPaint(); alpha.setAlphaf(0.3); // Draw 4 solid red rectangles on the 0th layer. canvas.drawRect(CanvasKit.LTRBRect(10, 10, 60, 60), redPaint); canvas.drawRect(CanvasKit.LTRBRect(150, 10, 200, 60), redPaint); canvas.drawRect(CanvasKit.LTRBRect(10, 70, 60, 120), redPaint); canvas.drawRect(CanvasKit.LTRBRect(150, 70, 200, 120), redPaint); // Draw 2 blue rectangles that overlap. One is solid, the other // is 30% transparent. We should see purple from the right one, // the left one overlaps the red because it is opaque. canvas.drawRect(CanvasKit.LTRBRect(30, 10, 80, 60), solidBluePaint); canvas.drawRect(CanvasKit.LTRBRect(170, 10, 220, 60), thirtyBluePaint); // Save a new layer. When the 1st layer gets merged onto the // 0th layer (i.e. when restore() is called), it will use the provided // paint to do so. The provided paint is set to have 30% opacity, but // it could also have things set like blend modes or image filters. // The rectangle is just a hint, so I've set it to be the area that // we actually draw in before restore is called. It could also be omitted. canvas.saveLayer(CanvasKit.LTRBRect(10, 10, 220, 180), alpha); // Draw the same blue overlapping rectangles as before. Notice in the // final output, we have two different shades of purple instead of the // solid blue overwriting the red. This proves the opacity was applied. canvas.drawRect(CanvasKit.LTRBRect(30, 70, 80, 120), solidBluePaint); canvas.drawRect(CanvasKit.LTRBRect(170, 70, 220, 120), thirtyBluePaint); // We draw two more sets of overlapping red and blue rectangles. Notice // the solid blue overwrites the red. This proves that the opacity from // the alpha paint isn't available when the drawing happens - it only // matters when restore() is called. canvas.drawRect(CanvasKit.LTRBRect(10, 130, 60, 180), redPaint); canvas.drawRect(CanvasKit.LTRBRect(30, 130, 80, 180), solidBluePaint); canvas.drawRect(CanvasKit.LTRBRect(150, 130, 200, 180), redPaint); canvas.drawRect(CanvasKit.LTRBRect(170, 130, 220, 180), thirtyBluePaint); canvas.restore(); surface.flush(); redPaint.delete(); solidBluePaint.delete(); thirtyBluePaint.delete(); alpha.delete(); reportSurface(surface, 'savelayer_rect_paint_canvas', done); })); }); it('can save layers with just paint', function(done) { LoadCanvasKit.then(catchException(done, () => { const surface = CanvasKit.MakeCanvasSurface('test'); expect(surface).toBeTruthy('Could not make surface') if (!surface) { done(); return; } const canvas = surface.getCanvas(); canvas.clear(CanvasKit.WHITE); const redPaint = new CanvasKit.SkPaint(); redPaint.setColor(CanvasKit.RED); const solidBluePaint = new CanvasKit.SkPaint(); solidBluePaint.setColor(CanvasKit.BLUE); const thirtyBluePaint = new CanvasKit.SkPaint(); thirtyBluePaint.setColor(CanvasKit.BLUE); thirtyBluePaint.setAlphaf(0.3); const alpha = new CanvasKit.SkPaint(); alpha.setAlphaf(0.3); // Draw 4 solid red rectangles on the 0th layer. canvas.drawRect(CanvasKit.LTRBRect(10, 10, 60, 60), redPaint); canvas.drawRect(CanvasKit.LTRBRect(150, 10, 200, 60), redPaint); canvas.drawRect(CanvasKit.LTRBRect(10, 70, 60, 120), redPaint); canvas.drawRect(CanvasKit.LTRBRect(150, 70, 200, 120), redPaint); // Draw 2 blue rectangles that overlap. One is solid, the other // is 30% transparent. We should see purple from the right one, // the left one overlaps the red because it is opaque. canvas.drawRect(CanvasKit.LTRBRect(30, 10, 80, 60), solidBluePaint); canvas.drawRect(CanvasKit.LTRBRect(170, 10, 220, 60), thirtyBluePaint); // Save a new layer. When the 1st layer gets merged onto the // 0th layer (i.e. when restore() is called), it will use the provided // paint to do so. The provided paint is set to have 30% opacity, but // it could also have things set like blend modes or image filters. canvas.saveLayer(alpha); // Draw the same blue overlapping rectangles as before. Notice in the // final output, we have two different shades of purple instead of the // solid blue overwriting the red. This proves the opacity was applied. canvas.drawRect(CanvasKit.LTRBRect(30, 70, 80, 120), solidBluePaint); canvas.drawRect(CanvasKit.LTRBRect(170, 70, 220, 120), thirtyBluePaint); // We draw two more sets of overlapping red and blue rectangles. Notice // the solid blue overwrites the red. This proves that the opacity from // the alpha paint isn't available when the drawing happens - it only // matters when restore() is called. canvas.drawRect(CanvasKit.LTRBRect(10, 130, 60, 180), redPaint); canvas.drawRect(CanvasKit.LTRBRect(30, 130, 80, 180), solidBluePaint); canvas.drawRect(CanvasKit.LTRBRect(150, 130, 200, 180), redPaint); canvas.drawRect(CanvasKit.LTRBRect(170, 130, 220, 180), thirtyBluePaint); canvas.restore(); surface.flush(); redPaint.delete(); solidBluePaint.delete(); thirtyBluePaint.delete(); alpha.delete(); reportSurface(surface, 'savelayer_paint_canvas', done); })); }); it('can save layer with SaveLayerRec-like things', function(done) { LoadCanvasKit.then(catchException(done, () => { const surface = CanvasKit.MakeCanvasSurface('test'); expect(surface).toBeTruthy('Could not make surface') if (!surface) { done(); return; } const canvas = surface.getCanvas(); // Note: fiddle.skia.org quietly draws a white background before doing // other things, which is noticed in cases like this where we use saveLayer // with the rec struct. canvas.clear(CanvasKit.WHITE); canvas.scale(8, 8); const redPaint = new CanvasKit.SkPaint(); redPaint.setColor(CanvasKit.RED); redPaint.setAntiAlias(true); canvas.drawCircle(21, 21, 8, redPaint); const bluePaint = new CanvasKit.SkPaint(); bluePaint.setColor(CanvasKit.BLUE); canvas.drawCircle(31, 21, 8, bluePaint); const blurIF = CanvasKit.SkImageFilter.MakeBlur(8, 0.2, CanvasKit.TileMode.Decal, null); const count = canvas.saveLayer(null, blurIF, 0); expect(count).toEqual(1); canvas.scale(1/4, 1/4); canvas.drawCircle(125, 85, 8, redPaint); canvas.restore(); surface.flush(); blurIF.delete(); redPaint.delete(); bluePaint.delete(); reportSurface(surface, 'savelayerrec_canvas', done); })); }); it('can drawPoints', function(done) { LoadCanvasKit.then(catchException(done, () => { const surface = CanvasKit.MakeCanvasSurface('test'); expect(surface).toBeTruthy('Could not make surface') if (!surface) { done(); return; } const canvas = surface.getCanvas(); const paint = new CanvasKit.SkPaint(); paint.setAntiAlias(true); paint.setStyle(CanvasKit.PaintStyle.Stroke); paint.setStrokeWidth(10); paint.setColor(CanvasKit.Color(153, 204, 162, 0.82)); const points = [[32, 16], [48, 48], [16, 32]]; const caps = [CanvasKit.StrokeCap.Round, CanvasKit.StrokeCap.Square, CanvasKit.StrokeCap.Butt]; const joins = [CanvasKit.StrokeJoin.Round, CanvasKit.StrokeJoin.Miter, CanvasKit.StrokeJoin.Bevel]; const modes = [CanvasKit.PointMode.Points, CanvasKit.PointMode.Lines, CanvasKit.PointMode.Polygon]; for (let i = 0; i < caps.length; i++) { paint.setStrokeCap(caps[i]); paint.setStrokeJoin(joins[i]); for (const m of modes) { canvas.drawPoints(m, points, paint); canvas.translate(64, 0); } // Try with the malloc approach. Note that the drawPoints // will free the pointer when done. const mPoints = CanvasKit.Malloc(Float32Array, 3*2); mPoints.set([32, 16, 48, 48, 16, 32]); canvas.drawPoints(CanvasKit.PointMode.Polygon, mPoints, paint); canvas.translate(-192, 64); } surface.flush(); paint.delete(); reportSurface(surface, 'drawpoints_canvas', done); })); }); it('can stretch an image with drawImageNine', function(done) { const imgPromise = fetch('/assets/mandrill_512.png') .then((response) => response.arrayBuffer()); Promise.all([imgPromise, LoadCanvasKit]).then((values) => { const pngData = values[0]; expect(pngData).toBeTruthy(); catchException(done, () => { let img = CanvasKit.MakeImageFromEncoded(pngData); expect(img).toBeTruthy(); const surface = CanvasKit.MakeCanvasSurface('test'); expect(surface).toBeTruthy('Could not make surface') if (!surface) { done(); return; } const canvas = surface.getCanvas(); canvas.clear(CanvasKit.WHITE); const paint = new CanvasKit.SkPaint(); canvas.drawImageNine(img, { fLeft: 40, fTop: 40, fRight: 400, fBottom: 300, }, CanvasKit.LTRBRect(5, 5, 300, 650), paint); surface.flush(); paint.delete(); img.delete(); reportSurface(surface, 'drawImageNine_canvas', done); })(); }); }); it('can draw a triangle mesh with SkVertices', function(done) { LoadCanvasKit.then(catchException(done, () => { const surface = CanvasKit.MakeCanvasSurface('test'); expect(surface).toBeTruthy('Could not make surface') if (!surface) { done(); return; } const canvas = surface.getCanvas(); const paint = new CanvasKit.SkPaint(); paint.setAntiAlias(true); const points = [[ 0, 0 ], [ 250, 0 ], [ 100, 100 ], [ 0, 250 ]]; const colors = [CanvasKit.RED, CanvasKit.BLUE, CanvasKit.YELLOW, CanvasKit.CYAN]; const vertices = CanvasKit.MakeSkVertices(CanvasKit.VertexMode.TriangleFan, points, null /*textureCoordinates*/, colors, false /*isVolatile*/); const bounds = vertices.bounds(); expect(bounds.fLeft).toEqual(0); expect(bounds.fTop).toEqual(0); expect(bounds.fRight).toEqual(250); expect(bounds.fBottom).toEqual(250); canvas.drawVertices(vertices, CanvasKit.BlendMode.Src, paint); vertices.delete(); surface.flush(); reportSurface(surface, 'drawvertices_canvas', done); })); }); it('can draw a textured triangle mesh with SkVertices', function(done) { const imgPromise = fetch('/assets/brickwork-texture.jpg') .then((response) => response.arrayBuffer()); Promise.all([imgPromise, LoadCanvasKit]).then((values) => { const imgData = values[0]; expect(imgData).toBeTruthy(); const img = CanvasKit.MakeImageFromEncoded(imgData); const surface = CanvasKit.MakeCanvasSurface('test'); expect(surface).toBeTruthy('Could not make surface') if (!surface) { done(); return; } const canvas = surface.getCanvas(); const paint = new CanvasKit.SkPaint(); paint.setAntiAlias(true); const points = [ [ 70, 170 ], [ 40, 90 ], [ 130, 150 ], [ 100, 50 ], [ 225, 150 ], [ 225, 60 ], [ 310, 180 ], [ 330, 100 ] ]; const textureCoordinates = [ [ 0, 240 ], [ 0, 0 ], [ 80, 240 ], [ 80, 0 ], [ 160, 240 ], [ 160, 0 ], [ 240, 240 ], [ 240, 0 ] ]; const vertices = CanvasKit.MakeSkVertices(CanvasKit.VertexMode.TrianglesStrip, points, textureCoordinates, null /* colors */, false /*isVolatile*/); const shader = img.makeShader(CanvasKit.TileMode.Repeat, CanvasKit.TileMode.Mirror); paint.setShader(shader); canvas.drawVertices(vertices, CanvasKit.BlendMode.Src, paint); vertices.delete(); surface.flush(); reportSurface(surface, 'drawvertices_texture_canvas', done); }); }); });