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