1// The increased timeout is especially needed with larger binaries
2// like in the debug/gpu build
3jasmine.DEFAULT_TIMEOUT_INTERVAL = 20000;
4
5describe('CanvasKit\'s Path Behavior', function() {
6    // Note, don't try to print the CanvasKit object - it can cause Karma/Jasmine to lock up.
7    var CanvasKit = null;
8    const LoadCanvasKit = new Promise(function(resolve, reject) {
9        if (CanvasKit) {
10            resolve();
11        } else {
12            CanvasKitInit({
13                locateFile: (file) => '/canvaskit/'+file,
14            }).ready().then((_CanvasKit) => {
15                CanvasKit = _CanvasKit;
16                resolve();
17            });
18        }
19    });
20
21    let container = document.createElement('div');
22    document.body.appendChild(container);
23    const CANVAS_WIDTH = 600;
24    const CANVAS_HEIGHT = 600;
25
26    beforeEach(function() {
27        container.innerHTML = `
28            <canvas width=600 height=600 id=test></canvas>
29            <canvas width=600 height=600 id=report></canvas>`;
30    });
31
32    afterEach(function() {
33        container.innerHTML = '';
34    });
35
36    function reportSurface(surface, testname, done) {
37        // In docker, the webgl canvas is blank, but the surface has the pixel
38        // data. So, we copy it out and draw it to a normal canvas to take a picture.
39        // To be consistent across CPU and GPU, we just do it for all configurations
40        // (even though the CPU canvas shows up after flush just fine).
41        let pixels = surface.getCanvas().readPixels(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
42        pixels = new Uint8ClampedArray(pixels.buffer);
43        var imageData = new ImageData(pixels, CANVAS_WIDTH, CANVAS_HEIGHT);
44
45        let reportingCanvas =  document.getElementById('report');
46        reportingCanvas.getContext('2d').putImageData(imageData, 0, 0);
47        reportCanvas(reportingCanvas, testname).then(() => {
48            done();
49        }).catch(reportError(done));
50    }
51
52    it('can draw a path', function(done) {
53        LoadCanvasKit.then(catchException(done, () => {
54            // This is taken from example.html
55            const surface = CanvasKit.MakeCanvasSurface('test');
56            expect(surface).toBeTruthy('Could not make surface')
57            if (!surface) {
58                done();
59                return;
60            }
61            const canvas = surface.getCanvas();
62            const paint = new CanvasKit.SkPaint();
63            paint.setStrokeWidth(1.0);
64            paint.setAntiAlias(true);
65            paint.setColor(CanvasKit.Color(0, 0, 0, 1.0));
66            paint.setStyle(CanvasKit.PaintStyle.Stroke);
67
68            const path = new CanvasKit.SkPath();
69            path.moveTo(20, 5);
70            path.lineTo(30, 20);
71            path.lineTo(40, 10);
72            path.lineTo(50, 20);
73            path.lineTo(60, 0);
74            path.lineTo(20, 5);
75
76            path.moveTo(20, 80);
77            path.cubicTo(90, 10, 160, 150, 190, 10);
78
79            path.moveTo(36, 148);
80            path.quadTo(66, 188, 120, 136);
81            path.lineTo(36, 148);
82
83            path.moveTo(150, 180);
84            path.arcTo(150, 100, 50, 200, 20);
85            path.lineTo(160, 160);
86
87            path.moveTo(20, 120);
88            path.lineTo(20, 120);
89
90            path.transform([2, 0, 0,
91                            0, 2, 0,
92                            0, 0, 1 ])
93
94            canvas.drawPath(path, paint);
95
96            let rrect = new CanvasKit.SkPath()
97                               .addRoundRect(100, 10, 140, 62,
98                                             10, 4, true);
99
100            canvas.drawPath(rrect, paint);
101            rrect.delete();
102
103            surface.flush();
104
105            path.delete();
106            paint.delete();
107
108            reportSurface(surface, 'path_api_example', done);
109        }));
110        // See PathKit for more tests, since they share implementation
111    });
112
113    it('can draw directly to a canvas', function(done) {
114        LoadCanvasKit.then(catchException(done, () => {
115            // This is taken from example.html
116            const surface = CanvasKit.MakeCanvasSurface('test');
117            expect(surface).toBeTruthy('Could not make surface')
118            if (!surface) {
119                done();
120                return;
121            }
122            const canvas = surface.getCanvas();
123            const paint = new CanvasKit.SkPaint();
124            paint.setStrokeWidth(2.0);
125            paint.setAntiAlias(true);
126            paint.setColor(CanvasKit.Color(0, 0, 0, 1.0));
127            paint.setStyle(CanvasKit.PaintStyle.Stroke);
128
129            canvas.drawLine(3, 10, 30, 15, paint);
130            canvas.drawRoundRect(CanvasKit.LTRBRect(5, 35, 45, 80), 15, 10, paint);
131
132            canvas.drawOval(CanvasKit.LTRBRect(5, 35, 45, 80), paint);
133
134            canvas.drawArc(CanvasKit.LTRBRect(55, 35, 95, 80), 15, 270, true, paint);
135
136            const font = new CanvasKit.SkFont(null, 20);
137            canvas.drawText('this is ascii text', 5, 100, font, paint);
138
139            const blob = CanvasKit.SkTextBlob.MakeFromText('Unicode chars �� é É ص', font);
140            canvas.drawTextBlob(blob, 5, 130, paint);
141
142            surface.flush();
143            font.delete();
144            blob.delete();
145            paint.delete();
146
147            reportSurface(surface, 'canvas_api_example', done);
148        }));
149        // See canvas2d for more API tests
150    });
151
152    function starPath(CanvasKit, X=128, Y=128, R=116) {
153        let p = new CanvasKit.SkPath();
154        p.moveTo(X + R, Y);
155        for (let i = 1; i < 8; i++) {
156          let a = 2.6927937 * i;
157          p.lineTo(X + R * Math.cos(a), Y + R * Math.sin(a));
158        }
159        return p;
160      }
161
162    it('can apply an effect and draw text', function(done) {
163        LoadCanvasKit.then(catchException(done, () => {
164            const surface = CanvasKit.MakeCanvasSurface('test');
165            expect(surface).toBeTruthy('Could not make surface')
166            if (!surface) {
167                done();
168                return;
169            }
170            const canvas = surface.getCanvas();
171            const path = starPath(CanvasKit);
172
173            const paint = new CanvasKit.SkPaint();
174
175            const textPaint = new CanvasKit.SkPaint();
176            textPaint.setColor(CanvasKit.Color(40, 0, 0, 1.0));
177            textPaint.setAntiAlias(true);
178
179            const textFont = new CanvasKit.SkFont(null, 30);
180
181            const dpe = CanvasKit.MakeSkDashPathEffect([15, 5, 5, 10], 1);
182
183            paint.setPathEffect(dpe);
184            paint.setStyle(CanvasKit.PaintStyle.Stroke);
185            paint.setStrokeWidth(5.0);
186            paint.setAntiAlias(true);
187            paint.setColor(CanvasKit.Color(66, 129, 164, 1.0));
188
189            canvas.clear(CanvasKit.Color(255, 255, 255, 1.0));
190
191            canvas.drawPath(path, paint);
192            canvas.drawText('This is text', 10, 280, textFont, textPaint);
193            surface.flush();
194            dpe.delete();
195            path.delete();
196
197            reportSurface(surface, 'effect_and_text_example', done);
198        }));
199    });
200
201    it('can create a path from an SVG string', function(done) {
202        LoadCanvasKit.then(catchException(done, () => {
203            //.This is a parallelagram from
204            // https://upload.wikimedia.org/wikipedia/commons/e/e7/Simple_parallelogram.svg
205            let path = CanvasKit.MakePathFromSVGString('M 205,5 L 795,5 L 595,295 L 5,295 L 205,5 z');
206
207            let cmds = path.toCmds();
208            expect(cmds).toBeTruthy();
209            // 1 move, 4 lines, 1 close
210            // each element in cmds is an array, with index 0 being the verb, and the rest being args
211            expect(cmds.length).toBe(6);
212            expect(cmds).toEqual([[CanvasKit.MOVE_VERB, 205, 5],
213                                  [CanvasKit.LINE_VERB, 795, 5],
214                                  [CanvasKit.LINE_VERB, 595, 295],
215                                  [CanvasKit.LINE_VERB, 5, 295],
216                                  [CanvasKit.LINE_VERB, 205, 5],
217                                  [CanvasKit.CLOSE_VERB]]);
218            path.delete();
219            done();
220        }));
221    });
222
223     it('can create an SVG string from a path', function(done) {
224        LoadCanvasKit.then(catchException(done, () => {
225            let cmds = [[CanvasKit.MOVE_VERB, 205, 5],
226                       [CanvasKit.LINE_VERB, 795, 5],
227                       [CanvasKit.LINE_VERB, 595, 295],
228                       [CanvasKit.LINE_VERB, 5, 295],
229                       [CanvasKit.LINE_VERB, 205, 5],
230                       [CanvasKit.CLOSE_VERB]];
231            let path = CanvasKit.MakePathFromCmds(cmds);
232
233            let svgStr = path.toSVGString();
234            // We output it in terse form, which is different than Wikipedia's version
235            expect(svgStr).toEqual('M205 5L795 5L595 295L5 295L205 5Z');
236            path.delete();
237            done();
238        }));
239    });
240});
241