blob: 8388c6778dfc67bfc23bd28cf44bd2c68bed1ffe [file] [log] [blame]
Kaido Kertb1089432024-03-18 19:46:49 -07001describe('Path Behavior', () => {
2 let container;
Xiaoming Shi73dfa202020-03-12 11:31:35 -07003
Kaido Kertb1089432024-03-18 19:46:49 -07004 beforeEach(async () => {
5 await LoadCanvasKit;
6 container = document.createElement('div');
Xiaoming Shi73dfa202020-03-12 11:31:35 -07007 container.innerHTML = `
8 <canvas width=600 height=600 id=test></canvas>
9 <canvas width=600 height=600 id=report></canvas>`;
Kaido Kertb1089432024-03-18 19:46:49 -070010 document.body.appendChild(container);
Xiaoming Shi73dfa202020-03-12 11:31:35 -070011 });
12
Kaido Kertb1089432024-03-18 19:46:49 -070013 afterEach(() => {
14 document.body.removeChild(container);
Xiaoming Shi73dfa202020-03-12 11:31:35 -070015 });
16
Kaido Kertb1089432024-03-18 19:46:49 -070017 gm('path_api_example', (canvas) => {
18 const paint = new CanvasKit.Paint();
19 paint.setStrokeWidth(1.0);
20 paint.setAntiAlias(true);
21 paint.setColor(CanvasKit.Color(0, 0, 0, 1.0));
22 paint.setStyle(CanvasKit.PaintStyle.Stroke);
Xiaoming Shi73dfa202020-03-12 11:31:35 -070023
Kaido Kertb1089432024-03-18 19:46:49 -070024 const path = new CanvasKit.Path();
25 path.moveTo(20, 5);
26 path.lineTo(30, 20);
27 path.lineTo(40, 10);
28 path.lineTo(50, 20);
29 path.lineTo(60, 0);
30 path.lineTo(20, 5);
Xiaoming Shi73dfa202020-03-12 11:31:35 -070031
Kaido Kertb1089432024-03-18 19:46:49 -070032 path.moveTo(20, 80);
33 path.cubicTo(90, 10, 160, 150, 190, 10);
Xiaoming Shi73dfa202020-03-12 11:31:35 -070034
Kaido Kertb1089432024-03-18 19:46:49 -070035 path.moveTo(36, 148);
36 path.quadTo(66, 188, 120, 136);
37 path.lineTo(36, 148);
Xiaoming Shi73dfa202020-03-12 11:31:35 -070038
Kaido Kertb1089432024-03-18 19:46:49 -070039 path.moveTo(150, 180);
40 path.arcToTangent(150, 100, 50, 200, 20);
41 path.lineTo(160, 160);
Xiaoming Shi73dfa202020-03-12 11:31:35 -070042
Kaido Kertb1089432024-03-18 19:46:49 -070043 path.moveTo(20, 120);
44 path.lineTo(20, 120);
Xiaoming Shi73dfa202020-03-12 11:31:35 -070045
Kaido Kertb1089432024-03-18 19:46:49 -070046 path.transform([2, 0, 0,
47 0, 2, 0,
48 0, 0, 1 ]);
Xiaoming Shi73dfa202020-03-12 11:31:35 -070049
Kaido Kertb1089432024-03-18 19:46:49 -070050 canvas.drawPath(path, paint);
Xiaoming Shi73dfa202020-03-12 11:31:35 -070051
Kaido Kertb1089432024-03-18 19:46:49 -070052 const rrect = CanvasKit.RRectXY([100, 10, 140, 62], 10, 4);
Xiaoming Shi73dfa202020-03-12 11:31:35 -070053
Kaido Kertb1089432024-03-18 19:46:49 -070054 const rrectPath = new CanvasKit.Path().addRRect(rrect, true);
Xiaoming Shi73dfa202020-03-12 11:31:35 -070055
Kaido Kertb1089432024-03-18 19:46:49 -070056 canvas.drawPath(rrectPath, paint);
Xiaoming Shi73dfa202020-03-12 11:31:35 -070057
Kaido Kertb1089432024-03-18 19:46:49 -070058 rrectPath.delete();
59 path.delete();
60 paint.delete();
Xiaoming Shi73dfa202020-03-12 11:31:35 -070061 // See PathKit for more tests, since they share implementation
62 });
63
Kaido Kertb1089432024-03-18 19:46:49 -070064 it('can create a path from an SVG string', () => {
65 //.This is a parallelogram from
66 // https://upload.wikimedia.org/wikipedia/commons/e/e7/Simple_parallelogram.svg
67 const path = CanvasKit.Path.MakeFromSVGString(
68 'M 205,5 L 795,5 L 595,295 L 5,295 L 205,5 z');
Xiaoming Shi73dfa202020-03-12 11:31:35 -070069
Kaido Kertb1089432024-03-18 19:46:49 -070070 const cmds = path.toCmds();
71 expect(cmds).toBeTruthy();
72 // 1 move, 4 lines, 1 close
73 // each element in cmds is an array, with index 0 being the verb, and the rest being args
74 expect(cmds).toEqual(Float32Array.of(
75 CanvasKit.MOVE_VERB, 205, 5,
76 CanvasKit.LINE_VERB, 795, 5,
77 CanvasKit.LINE_VERB, 595, 295,
78 CanvasKit.LINE_VERB, 5, 295,
79 CanvasKit.LINE_VERB, 205, 5,
80 CanvasKit.CLOSE_VERB));
81 path.delete();
Xiaoming Shi73dfa202020-03-12 11:31:35 -070082 });
83
Kaido Kertb1089432024-03-18 19:46:49 -070084 it('can create a path by combining two other paths', () => {
85 // Get the intersection of two overlapping squares and verify that it is the smaller square.
86 const pathOne = new CanvasKit.Path();
87 pathOne.addRect([10, 10, 20, 20]);
Xiaoming Shi73dfa202020-03-12 11:31:35 -070088
Kaido Kertb1089432024-03-18 19:46:49 -070089 const pathTwo = new CanvasKit.Path();
90 pathTwo.addRect([15, 15, 30, 30]);
91
92 const path = CanvasKit.Path.MakeFromOp(pathOne, pathTwo, CanvasKit.PathOp.Intersect);
93 const cmds = path.toCmds();
94 expect(cmds).toBeTruthy();
95 expect(cmds).toEqual(Float32Array.of(
96 CanvasKit.MOVE_VERB, 15, 15,
97 CanvasKit.LINE_VERB, 20, 15,
98 CanvasKit.LINE_VERB, 20, 20,
99 CanvasKit.LINE_VERB, 15, 20,
100 CanvasKit.CLOSE_VERB));
101 path.delete();
102 pathOne.delete();
103 pathTwo.delete();
Xiaoming Shi73dfa202020-03-12 11:31:35 -0700104 });
105
Kaido Kertb1089432024-03-18 19:46:49 -0700106 it('can create an SVG string from a path', () => {
107 const cmds = [CanvasKit.MOVE_VERB, 205, 5,
108 CanvasKit.LINE_VERB, 795, 5,
109 CanvasKit.LINE_VERB, 595, 295,
110 CanvasKit.LINE_VERB, 5, 295,
111 CanvasKit.LINE_VERB, 205, 5,
112 CanvasKit.CLOSE_VERB];
113 const path = CanvasKit.Path.MakeFromCmds(cmds);
Xiaoming Shi73dfa202020-03-12 11:31:35 -0700114
Kaido Kertb1089432024-03-18 19:46:49 -0700115 const svgStr = path.toSVGString();
116 // We output it in terse form, which is different than Wikipedia's version
117 expect(svgStr).toEqual('M205 5L795 5L595 295L5 295L205 5Z');
118 path.delete();
119 });
Xiaoming Shi73dfa202020-03-12 11:31:35 -0700120
Kaido Kertb1089432024-03-18 19:46:49 -0700121 it('can create a path with malloced verbs, points, weights', () => {
122 const mVerbs = CanvasKit.Malloc(Uint8Array, 6);
123 const mPoints = CanvasKit.Malloc(Float32Array, 18);
124 const mWeights = CanvasKit.Malloc(Float32Array, 1);
125 mVerbs.toTypedArray().set([CanvasKit.MOVE_VERB, CanvasKit.LINE_VERB,
126 CanvasKit.QUAD_VERB, CanvasKit.CONIC_VERB, CanvasKit.CUBIC_VERB, CanvasKit.CLOSE_VERB
127 ]);
Xiaoming Shi73dfa202020-03-12 11:31:35 -0700128
Kaido Kertb1089432024-03-18 19:46:49 -0700129 mPoints.toTypedArray().set([
130 1,2, // moveTo
131 3,4, // lineTo
132 5,6,7,8, // quadTo
133 9,10,11,12, // conicTo
134 13,14,15,16,17,18, // cubicTo
135 ]);
Xiaoming Shi73dfa202020-03-12 11:31:35 -0700136
Kaido Kertb1089432024-03-18 19:46:49 -0700137 mWeights.toTypedArray().set([117]);
138
139 let path = CanvasKit.Path.MakeFromVerbsPointsWeights(mVerbs, mPoints, mWeights);
140
141 let cmds = path.toCmds();
142 expect(cmds).toEqual(Float32Array.of(
143 CanvasKit.MOVE_VERB, 1, 2,
144 CanvasKit.LINE_VERB, 3, 4,
145 CanvasKit.QUAD_VERB, 5, 6, 7, 8,
146 CanvasKit.CONIC_VERB, 9, 10, 11, 12, 117,
147 CanvasKit.CUBIC_VERB, 13, 14, 15, 16, 17, 18,
148 CanvasKit.CLOSE_VERB,
149 ));
150 path.delete();
151
152 // If given insufficient points, it stops early (but doesn't read out of bounds).
153 path = CanvasKit.Path.MakeFromVerbsPointsWeights(mVerbs, mPoints.subarray(0, 10), mWeights);
154
155 cmds = path.toCmds();
156 expect(cmds).toEqual(Float32Array.of(
157 CanvasKit.MOVE_VERB, 1, 2,
158 CanvasKit.LINE_VERB, 3, 4,
159 CanvasKit.QUAD_VERB, 5, 6, 7, 8,
160 ));
161 path.delete();
162 CanvasKit.Free(mVerbs);
163 CanvasKit.Free(mPoints);
164 CanvasKit.Free(mWeights);
165 });
166
167 it('can create and update a path with verbs and points (no weights)', () => {
168 const path = CanvasKit.Path.MakeFromVerbsPointsWeights(
169 [CanvasKit.MOVE_VERB, CanvasKit.LINE_VERB],
170 [1,2, 3,4]);
171 let cmds = path.toCmds();
172 expect(cmds).toEqual(Float32Array.of(
173 CanvasKit.MOVE_VERB, 1, 2,
174 CanvasKit.LINE_VERB, 3, 4
175 ));
176
177 path.addVerbsPointsWeights(
178 [CanvasKit.QUAD_VERB, CanvasKit.CLOSE_VERB],
179 [5,6,7,8],
180 );
181
182 cmds = path.toCmds();
183 expect(cmds).toEqual(Float32Array.of(
184 CanvasKit.MOVE_VERB, 1, 2,
185 CanvasKit.LINE_VERB, 3, 4,
186 CanvasKit.QUAD_VERB, 5, 6, 7, 8,
187 CanvasKit.CLOSE_VERB
188 ));
189 path.delete();
190 });
191
192
193 it('can add points to a path in bulk', () => {
194 const mVerbs = CanvasKit.Malloc(Uint8Array, 6);
195 const mPoints = CanvasKit.Malloc(Float32Array, 18);
196 const mWeights = CanvasKit.Malloc(Float32Array, 1);
197 mVerbs.toTypedArray().set([CanvasKit.MOVE_VERB, CanvasKit.LINE_VERB,
198 CanvasKit.QUAD_VERB, CanvasKit.CONIC_VERB, CanvasKit.CUBIC_VERB, CanvasKit.CLOSE_VERB
199 ]);
200
201 mPoints.toTypedArray().set([
202 1,2, // moveTo
203 3,4, // lineTo
204 5,6,7,8, // quadTo
205 9,10,11,12, // conicTo
206 13,14,15,16,17,18, // cubicTo
207 ]);
208
209 mWeights.toTypedArray().set([117]);
210
211 const path = new CanvasKit.Path();
212 path.lineTo(77, 88);
213 path.addVerbsPointsWeights(mVerbs, mPoints, mWeights);
214
215 let cmds = path.toCmds();
216 expect(cmds).toEqual(Float32Array.of(
217 CanvasKit.MOVE_VERB, 0, 0,
218 CanvasKit.LINE_VERB, 77, 88,
219 CanvasKit.MOVE_VERB, 1, 2,
220 CanvasKit.LINE_VERB, 3, 4,
221 CanvasKit.QUAD_VERB, 5, 6, 7, 8,
222 CanvasKit.CONIC_VERB, 9, 10, 11, 12, 117,
223 CanvasKit.CUBIC_VERB, 13, 14, 15, 16, 17, 18,
224 CanvasKit.CLOSE_VERB,
225 ));
226
227 path.rewind();
228 cmds = path.toCmds();
229 expect(cmds).toEqual(new Float32Array(0));
230
231 path.delete();
232 CanvasKit.Free(mVerbs);
233 CanvasKit.Free(mPoints);
234 CanvasKit.Free(mWeights);
235 });
236
237 it('can retrieve points from a path', () => {
238 const path = new CanvasKit.Path();
239 path.addRect([10, 15, 20, 25]);
240
241 let pt = path.getPoint(0);
242 expect(pt[0]).toEqual(10);
243 expect(pt[1]).toEqual(15);
244
245 path.getPoint(2, pt);
246 expect(pt[0]).toEqual(20);
247 expect(pt[1]).toEqual(25);
248
249 path.getPoint(1000, pt); // off the end returns (0, 0) as per the docs.
250 expect(pt[0]).toEqual(0);
251 expect(pt[1]).toEqual(0);
252
253 path.delete();
254 });
255
256 gm('offset_path', (canvas) => {
257 const path = starPath(CanvasKit);
258
259 const paint = new CanvasKit.Paint();
260 paint.setStyle(CanvasKit.PaintStyle.Stroke);
261 paint.setStrokeWidth(5.0);
262 paint.setAntiAlias(true);
263 paint.setColor(CanvasKit.BLACK);
264
265 canvas.clear(CanvasKit.WHITE);
266
267 canvas.drawPath(path, paint);
268 path.offset(80, 40);
269 canvas.drawPath(path, paint);
270
271 path.delete();
272 paint.delete();
273 });
274
275 gm('oval_path', (canvas) => {
276 const paint = new CanvasKit.Paint();
277
278 paint.setStyle(CanvasKit.PaintStyle.Stroke);
279 paint.setStrokeWidth(5.0);
280 paint.setAntiAlias(true);
281 paint.setColor(CanvasKit.BLACK);
282
283 canvas.clear(CanvasKit.WHITE);
284
285 const path = new CanvasKit.Path();
286 path.moveTo(5, 5);
287 path.lineTo(10, 120);
288 path.addOval(CanvasKit.LTRBRect(10, 20, 100, 200), false, 3);
289 path.lineTo(300, 300);
290
291 canvas.drawPath(path, paint);
292
293 path.delete();
294 paint.delete();
295 });
296
297 gm('bounds_path', (canvas) => {
298 const paint = new CanvasKit.Paint();
299
300 paint.setStyle(CanvasKit.PaintStyle.Stroke);
301 paint.setStrokeWidth(5.0);
302 paint.setAntiAlias(true);
303 paint.setColor(CanvasKit.BLACK);
304
305 canvas.clear(CanvasKit.WHITE);
306
307 const path = new CanvasKit.Path();
308 // Arbitrary points to make an interesting curve.
309 path.moveTo(97, 225);
310 path.cubicTo(20, 400, 404, 75, 243, 271);
311
312 canvas.drawPath(path, paint);
313
314 const bounds = new Float32Array(4);
315 path.getBounds(bounds);
316
317 paint.setColor(CanvasKit.BLUE);
318 paint.setStrokeWidth(3.0);
319 canvas.drawRect(bounds, paint);
320
321 path.computeTightBounds(bounds);
322 paint.setColor(CanvasKit.RED);
323 paint.setStrokeWidth(3.0);
324 canvas.drawRect(bounds, paint);
325
326 path.delete();
327 paint.delete();
328 });
329
330 gm('arcto_path', (canvas) => {
331 const paint = new CanvasKit.Paint();
332
333 paint.setStyle(CanvasKit.PaintStyle.Stroke);
334 paint.setStrokeWidth(5.0);
335 paint.setAntiAlias(true);
336 paint.setColor(CanvasKit.BLACK);
337
338 canvas.clear(CanvasKit.WHITE);
339
340 const path = new CanvasKit.Path();
341
342 // - x1, y1, x2, y2, radius
343 path.arcToTangent(40, 0, 40, 40, 40);
344 // - oval (as Rect), startAngle, sweepAngle, forceMoveTo
345 path.arcToOval(CanvasKit.LTRBRect(90, 10, 120, 200), 30, 300, true);
346 // - rx, ry, xAxisRotate, useSmallArc, isCCW, x, y
347 path.moveTo(5, 105);
348 path.arcToRotated(24, 24, 45, true, false, 82, 156);
349
350 canvas.drawPath(path, paint);
351
352 path.delete();
353 paint.delete();
354 });
355
356 gm('path_relative', (canvas) => {
357 const paint = new CanvasKit.Paint();
358 paint.setStrokeWidth(1.0);
359 paint.setAntiAlias(true);
360 paint.setColor(CanvasKit.Color(0, 0, 0, 1.0));
361 paint.setStyle(CanvasKit.PaintStyle.Stroke);
362
363 const path = new CanvasKit.Path();
364 path.rMoveTo(20, 5)
365 .rLineTo(10, 15) // 30, 20
366 .rLineTo(10, -5); // 40, 10
367 path.rLineTo(10, 10); // 50, 20
368 path.rLineTo(10, -20); // 60, 0
369 path.rLineTo(-40, 5); // 20, 5
370
371 path.moveTo(20, 80)
372 .rCubicTo(70, -70, 140, 70, 170, -70); // 90, 10, 160, 150, 190, 10
373
374 path.moveTo(36, 148)
375 .rQuadTo(30, 40, 84, -12) // 66, 188, 120, 136
376 .lineTo(36, 148);
377
378 path.moveTo(150, 180)
379 .rArcTo(24, 24, 45, true, false, -68, -24); // 82, 156
380 path.lineTo(160, 160);
381
382 canvas.drawPath(path, paint);
383
384 path.delete();
385 paint.delete();
386 });
387
388 it('can measure the contours of a path', () => {
389 const path = new CanvasKit.Path();
390 path.moveTo(10, 10)
391 .lineTo(40, 50); // should be length 50 because of the 3/4/5 triangle rule
392
393 path.moveTo(80, 0)
394 .lineTo(80, 10)
395 .lineTo(100, 5)
396 .lineTo(80, 0);
397
398 const meas = new CanvasKit.ContourMeasureIter(path, false, 1);
399 let cont = meas.next();
400 expect(cont).toBeTruthy();
401
402 expect(cont.length()).toBeCloseTo(50.0, 3);
403 const pt = cont.getPosTan(28.7); // arbitrary point
404 expect(pt[0]).toBeCloseTo(27.22, 3); // x
405 expect(pt[1]).toBeCloseTo(32.96, 3); // y
406 expect(pt[2]).toBeCloseTo(0.6, 3); // dy
407 expect(pt[3]).toBeCloseTo(0.8, 3); // dy
408
409 pt.set([-1, -1, -1, -1]); // fill with sentinel values.
410 cont.getPosTan(28.7, pt); // arbitrary point again, passing in an array to copy into.
411 expect(pt[0]).toBeCloseTo(27.22, 3); // x
412 expect(pt[1]).toBeCloseTo(32.96, 3); // y
413 expect(pt[2]).toBeCloseTo(0.6, 3); // dy
414 expect(pt[3]).toBeCloseTo(0.8, 3); // dy
415
416 const subpath = cont.getSegment(20, 40, true); // make sure this doesn't crash
417
418 cont.delete();
419 cont = meas.next();
420 expect(cont).toBeTruthy();
421 expect(cont.length()).toBeCloseTo(51.231, 3);
422
423 cont.delete();
424 expect(meas.next()).toBeFalsy();
425
426 meas.delete();
427 path.delete();
428 });
429
430 gm('drawpoly_path', (canvas) => {
431 const paint = new CanvasKit.Paint();
432 paint.setStrokeWidth(1.0);
433 paint.setAntiAlias(true);
434 paint.setColor(CanvasKit.Color(0, 0, 0, 1.0));
435 paint.setStyle(CanvasKit.PaintStyle.Stroke);
436
437 const points = [5, 5, 30, 20, 55, 5, 55, 50, 30, 30, 5, 50];
438
439 const pointsObj = CanvasKit.Malloc(Float32Array, 6 * 2);
440 const mPoints = pointsObj.toTypedArray();
441 mPoints.set([105, 105, 130, 120, 155, 105, 155, 150, 130, 130, 105, 150]);
442
443 const path = new CanvasKit.Path();
444 path.addPoly(points, true)
445 .moveTo(100, 0)
446 .addPoly(mPoints, true);
447
448 canvas.drawPath(path, paint);
449 CanvasKit.Free(pointsObj);
450
451 path.delete();
452 paint.delete();
453 });
454
455 // Test trim, adding paths to paths, and a bunch of other path methods.
456 gm('trim_path', (canvas) => {
457 canvas.clear(CanvasKit.WHITE);
458
459 const paint = new CanvasKit.Paint();
460 paint.setStrokeWidth(1.0);
461 paint.setAntiAlias(true);
462 paint.setColor(CanvasKit.Color(0, 0, 0, 1.0));
463 paint.setStyle(CanvasKit.PaintStyle.Stroke);
464
465 const arcpath = new CanvasKit.Path();
466 arcpath.arc(400, 400, 100, 0, -90, false) // x, y, radius, startAngle, endAngle, ccw
467 .dash(3, 1, 0)
468 .conicTo(10, 20, 30, 40, 5)
469 .rConicTo(60, 70, 80, 90, 5)
470 .trim(0.2, 1, false);
471
472 const path = new CanvasKit.Path();
473 path.addArc(CanvasKit.LTRBRect(10, 20, 100, 200), 30, 300)
474 .addRect(CanvasKit.LTRBRect(200, 200, 300, 300)) // test single arg, default cw
475 .addRect(CanvasKit.LTRBRect(240, 240, 260, 260), true) // test two arg, true means ccw
476 .addRect([260, 260, 290, 290], true) // test five arg, true means ccw
477 .addRRect([300, 10, 500, 290, // Rect in LTRB order
478 60, 60, 60, 60, 60, 60, 60, 60], // all radii are the same
479 false) // ccw
480 .addRRect(CanvasKit.RRectXY([350, 60, 450, 240], 20, 80), true) // Rect, rx, ry, ccw
481 .addPath(arcpath)
482 .transform(0.54, -0.84, 390.35,
483 0.84, 0.54, -114.53,
484 0, 0, 1);
485
486 canvas.drawPath(path, paint);
487
488 path.delete();
489 paint.delete();
490 });
491
492 gm('winding_example', (canvas) => {
493 // Inspired by https://fiddle.skia.org/c/@Path_FillType_a
494 const path = new CanvasKit.Path();
495 // Draw overlapping rects on top
496 path.addRect(CanvasKit.LTRBRect(10, 10, 30, 30), false);
497 path.addRect(CanvasKit.LTRBRect(20, 20, 40, 40), false);
498 // Draw overlapping rects on bottom, with different direction lines.
499 path.addRect(CanvasKit.LTRBRect(10, 60, 30, 80), false);
500 path.addRect(CanvasKit.LTRBRect(20, 70, 40, 90), true);
501
502 expect(path.getFillType()).toEqual(CanvasKit.FillType.Winding);
503
504 // Draw the two rectangles on the left side.
505 const paint = new CanvasKit.Paint();
506 paint.setStyle(CanvasKit.PaintStyle.Stroke);
507 canvas.drawPath(path, paint);
508
509 const clipRect = CanvasKit.LTRBRect(0, 0, 51, 100);
510 paint.setStyle(CanvasKit.PaintStyle.Fill);
511
512 for (const fillType of [CanvasKit.FillType.Winding, CanvasKit.FillType.EvenOdd]) {
513 canvas.translate(51, 0);
514 canvas.save();
515 canvas.clipRect(clipRect, CanvasKit.ClipOp.Intersect, false);
516 path.setFillType(fillType);
Xiaoming Shi73dfa202020-03-12 11:31:35 -0700517 canvas.drawPath(path, paint);
Kaido Kertb1089432024-03-18 19:46:49 -0700518 canvas.restore();
519 }
Xiaoming Shi73dfa202020-03-12 11:31:35 -0700520
Kaido Kertb1089432024-03-18 19:46:49 -0700521 path.delete();
522 paint.delete();
Xiaoming Shi73dfa202020-03-12 11:31:35 -0700523 });
524
Kaido Kertb1089432024-03-18 19:46:49 -0700525 gm('as_winding', (canvas) => {
526 const evenOddPath = new CanvasKit.Path();
527 // Draw overlapping rects
528 evenOddPath.addRect(CanvasKit.LTRBRect(10, 10, 70, 70), false);
529 evenOddPath.addRect(CanvasKit.LTRBRect(30, 30, 50, 50), false);
530 evenOddPath.setFillType(CanvasKit.FillType.EvenOdd);
Xiaoming Shi73dfa202020-03-12 11:31:35 -0700531
Kaido Kertb1089432024-03-18 19:46:49 -0700532 const evenOddCmds = evenOddPath.toCmds();
533 expect(evenOddCmds).toEqual(Float32Array.of(
534 CanvasKit.MOVE_VERB, 10, 10,
535 CanvasKit.LINE_VERB, 70, 10,
536 CanvasKit.LINE_VERB, 70, 70,
537 CanvasKit.LINE_VERB, 10, 70,
538 CanvasKit.CLOSE_VERB,
539 CanvasKit.MOVE_VERB, 30, 30, // This contour is drawn
540 CanvasKit.LINE_VERB, 50, 30, // clockwise, as specified.
541 CanvasKit.LINE_VERB, 50, 50,
542 CanvasKit.LINE_VERB, 30, 50,
543 CanvasKit.CLOSE_VERB
544 ));
Xiaoming Shi73dfa202020-03-12 11:31:35 -0700545
Kaido Kertb1089432024-03-18 19:46:49 -0700546 const windingPath = evenOddPath.makeAsWinding();
Xiaoming Shi73dfa202020-03-12 11:31:35 -0700547
Kaido Kertb1089432024-03-18 19:46:49 -0700548 expect(windingPath.getFillType()).toBe(CanvasKit.FillType.Winding);
549 const windingCmds = windingPath.toCmds();
550 expect(windingCmds).toEqual(Float32Array.of(
551 CanvasKit.MOVE_VERB, 10, 10,
552 CanvasKit.LINE_VERB, 70, 10,
553 CanvasKit.LINE_VERB, 70, 70,
554 CanvasKit.LINE_VERB, 10, 70,
555 CanvasKit.CLOSE_VERB,
556 CanvasKit.MOVE_VERB, 30, 50, // This contour has been
557 CanvasKit.LINE_VERB, 50, 50, // re-drawn counter-clockwise
558 CanvasKit.LINE_VERB, 50, 30, // so that it covers the same
559 CanvasKit.LINE_VERB, 30, 30, // area, but with the winding fill type.
560 CanvasKit.CLOSE_VERB
561 ));
Xiaoming Shi73dfa202020-03-12 11:31:35 -0700562
Kaido Kertb1089432024-03-18 19:46:49 -0700563 const paint = new CanvasKit.Paint();
564 paint.setStyle(CanvasKit.PaintStyle.Fill);
565 const font = new CanvasKit.Font(null, 20);
Xiaoming Shi73dfa202020-03-12 11:31:35 -0700566
Kaido Kertb1089432024-03-18 19:46:49 -0700567 canvas.drawText('Original path (even odd)', 5, 20, paint, font);
568 canvas.translate(0, 50);
569 canvas.drawPath(evenOddPath, paint);
Xiaoming Shi73dfa202020-03-12 11:31:35 -0700570
Kaido Kertb1089432024-03-18 19:46:49 -0700571 canvas.translate(300, 0);
572 canvas.drawPath(windingPath, paint);
Xiaoming Shi73dfa202020-03-12 11:31:35 -0700573
Kaido Kertb1089432024-03-18 19:46:49 -0700574 canvas.translate(0, -50);
575 canvas.drawText('makeAsWinding path', 5, 20, paint, font);
Xiaoming Shi73dfa202020-03-12 11:31:35 -0700576
Kaido Kertb1089432024-03-18 19:46:49 -0700577 evenOddPath.delete();
578 windingPath.delete();
Kaido Kertf9bc0132023-12-04 11:52:41 -0800579 });
Xiaoming Shi73dfa202020-03-12 11:31:35 -0700580});