1describe('Core canvas behavior', function() { 2 let container = document.createElement('div'); 3 document.body.appendChild(container); 4 const CANVAS_WIDTH = 600; 5 const CANVAS_HEIGHT = 600; 6 7 beforeEach(function() { 8 container.innerHTML = ` 9 <canvas width=600 height=600 id=test></canvas> 10 <canvas width=600 height=600 id=report></canvas>`; 11 }); 12 13 afterEach(function() { 14 container.innerHTML = ''; 15 }); 16 17 it('can draw an SkPicture', function(done) { 18 LoadCanvasKit.then(catchException(done, () => { 19 // This is taken from example.html 20 const surface = CanvasKit.MakeCanvasSurface('test'); 21 expect(surface).toBeTruthy('Could not make surface'); 22 if (!surface) { 23 done(); 24 return; 25 } 26 const spr = new CanvasKit.SkPictureRecorder(); 27 const rcanvas = spr.beginRecording( 28 CanvasKit.LTRBRect(0, 0, surface.width(), surface.height())); 29 const paint = new CanvasKit.SkPaint(); 30 paint.setStrokeWidth(2.0); 31 paint.setAntiAlias(true); 32 paint.setColor(CanvasKit.Color(0, 0, 0, 1.0)); 33 paint.setStyle(CanvasKit.PaintStyle.Stroke); 34 35 rcanvas.drawRoundRect(CanvasKit.LTRBRect(5, 35, 45, 80), 15, 10, paint); 36 37 const font = new CanvasKit.SkFont(null, 20); 38 rcanvas.drawText('this picture has a round rect', 5, 100, paint, font); 39 const pic = spr.finishRecordingAsPicture(); 40 spr.delete(); 41 42 43 const canvas = surface.getCanvas(); 44 canvas.drawPicture(pic); 45 46 reportSurface(surface, 'picture_test', done); 47 })); 48 }); 49 50 it('can compute tonal colors', function(done) { 51 LoadCanvasKit.then(catchException(done, () => { 52 const input = { 53 ambient: CanvasKit.BLUE, 54 spot: CanvasKit.RED, 55 }; 56 const out = CanvasKit.computeTonalColors(input); 57 58 expect(out.ambient).toEqual(CanvasKit.Color(0,0,0,1)); 59 60 const [r,g,b,a] = CanvasKit.getColorComponents(out.spot); 61 expect(r).toEqual(44); 62 expect(g).toEqual(0); 63 expect(b).toEqual(0); 64 expect(a).toBeCloseTo(0.969, 2); 65 done(); 66 })); 67 }); 68 69 // This helper is used for all the MakeImageFromEncoded tests. 70 function decodeAndDrawSingleFrameImage(imgName, goldName, done) { 71 const imgPromise = fetch(imgName) 72 .then((response) => response.arrayBuffer()); 73 Promise.all([imgPromise, LoadCanvasKit]).then((values) => { 74 const imgData = values[0]; 75 expect(imgData).toBeTruthy(); 76 catchException(done, () => { 77 let img = CanvasKit.MakeImageFromEncoded(imgData); 78 expect(img).toBeTruthy(); 79 const surface = CanvasKit.MakeCanvasSurface('test'); 80 expect(surface).toBeTruthy('Could not make surface'); 81 if (!surface) { 82 done(); 83 return; 84 } 85 const canvas = surface.getCanvas(); 86 let paint = new CanvasKit.SkPaint(); 87 canvas.drawImage(img, 0, 0, paint); 88 89 paint.delete(); 90 img.delete(); 91 92 reportSurface(surface, goldName, done); 93 })(); 94 }); 95 } 96 97 it('can decode and draw a png', function(done) { 98 decodeAndDrawSingleFrameImage('/assets/mandrill_512.png', 'drawImage_png', done); 99 }); 100 101 it('can decode and draw a jpg', function(done) { 102 decodeAndDrawSingleFrameImage('/assets/mandrill_h1v1.jpg', 'drawImage_jpg', done); 103 }); 104 105 it('can decode and draw a (still) gif', function(done) { 106 decodeAndDrawSingleFrameImage('/assets/flightAnim.gif', 'drawImage_gif', done); 107 }); 108 109 it('can decode and draw a still webp', function(done) { 110 decodeAndDrawSingleFrameImage('/assets/color_wheel.webp', 'drawImage_webp', done); 111 }); 112 113 it('can readPixels from an SkImage', function(done) { 114 const imgPromise = fetch('/assets/mandrill_512.png') 115 .then((response) => response.arrayBuffer()); 116 Promise.all([imgPromise, LoadCanvasKit]).then((values) => { 117 const imgData = values[0]; 118 expect(imgData).toBeTruthy(); 119 catchException(done, () => { 120 let img = CanvasKit.MakeImageFromEncoded(imgData); 121 expect(img).toBeTruthy(); 122 const imageInfo = { 123 alphaType: CanvasKit.AlphaType.Unpremul, 124 colorType: CanvasKit.ColorType.RGBA_8888, 125 width: img.width(), 126 height: img.height(), 127 }; 128 129 const pixels = img.readPixels(imageInfo, 0, 0); 130 // We know the image is 512 by 512 pixels in size, each pixel 131 // requires 4 bytes (R, G, B, A). 132 expect(pixels.length).toEqual(512 * 512 * 4); 133 134 img.delete(); 135 done(); 136 })(); 137 }); 138 }); 139 140 it('can decode and draw an animated gif', function(done) { 141 const imgPromise = fetch('/assets/flightAnim.gif') 142 .then((response) => response.arrayBuffer()); 143 Promise.all([imgPromise, LoadCanvasKit]).then((values) => { 144 const gifData = values[0]; 145 expect(gifData).toBeTruthy(); 146 catchException(done, () => { 147 let aImg = CanvasKit.MakeAnimatedImageFromEncoded(gifData); 148 expect(aImg).toBeTruthy(); 149 expect(aImg.getRepetitionCount()).toEqual(-1); // infinite loop 150 expect(aImg.width()).toEqual(320); 151 expect(aImg.height()).toEqual(240); 152 expect(aImg.getFrameCount()).toEqual(60); 153 154 const surface = CanvasKit.MakeCanvasSurface('test'); 155 expect(surface).toBeTruthy('Could not make surface'); 156 if (!surface) { 157 done(); 158 return; 159 } 160 const canvas = surface.getCanvas(); 161 canvas.drawAnimatedImage(aImg, 0, 0); 162 163 let c = aImg.decodeNextFrame(); 164 expect(c).not.toEqual(-1); 165 canvas.drawAnimatedImage(aImg, 300, 0); 166 for(let i = 0; i < 10; i++) { 167 c = aImg.decodeNextFrame(); 168 expect(c).not.toEqual(-1); 169 } 170 canvas.drawAnimatedImage(aImg, 0, 300); 171 for(let i = 0; i < 10; i++) { 172 c = aImg.decodeNextFrame(); 173 expect(c).not.toEqual(-1); 174 } 175 canvas.drawAnimatedImage(aImg, 300, 300); 176 177 aImg.delete(); 178 179 reportSurface(surface, 'drawDrawable_animated_gif', done); 180 })(); 181 }); 182 }); 183 184 it('can create an image "from scratch" by specifying pixels/colorInfo manually', function(done) { 185 LoadCanvasKit.then(catchException(done, () => { 186 const surface = CanvasKit.MakeCanvasSurface('test'); 187 expect(surface).toBeTruthy('Could not make surface'); 188 if (!surface) { 189 done(); 190 return; 191 } 192 const canvas = surface.getCanvas(); 193 canvas.clear(CanvasKit.WHITE); 194 const paint = new CanvasKit.SkPaint(); 195 196 // This creates and draws an SkImage that is 1 pixel wide, 4 pixels tall with 197 // the colors listed below. 198 const pixels = Uint8Array.from([ 199 255, 0, 0, 255, // opaque red 200 0, 255, 0, 255, // opaque green 201 0, 0, 255, 255, // opaque blue 202 255, 0, 255, 100, // transparent purple 203 ]); 204 const img = CanvasKit.MakeImage(pixels, 1, 4, CanvasKit.AlphaType.Unpremul, CanvasKit.ColorType.RGBA_8888); 205 canvas.drawImage(img, 1, 1, paint); 206 207 reportSurface(surface, '1x4_from_scratch', done); 208 })); 209 }); 210 211 it('can blur using ImageFilter or MaskFilter', function(done) { 212 LoadCanvasKit.then(catchException(done, () => { 213 const surface = CanvasKit.MakeCanvasSurface('test'); 214 expect(surface).toBeTruthy('Could not make surface'); 215 if (!surface) { 216 done(); 217 return; 218 } 219 const canvas = surface.getCanvas(); 220 const pathUL = starPath(CanvasKit, 100, 100, 80); 221 const pathBR = starPath(CanvasKit, 400, 300, 80); 222 const paint = new CanvasKit.SkPaint(); 223 const textFont = new CanvasKit.SkFont(null, 24); 224 225 canvas.drawText('Above: MaskFilter', 20, 220, paint, textFont); 226 canvas.drawText('Right: ImageFilter', 20, 260, paint, textFont); 227 228 paint.setColor(CanvasKit.BLUE); 229 230 const blurMask = CanvasKit.SkMaskFilter.MakeBlur(CanvasKit.BlurStyle.Normal, 5, true); 231 paint.setMaskFilter(blurMask); 232 canvas.drawPath(pathUL, paint); 233 234 const blurIF = CanvasKit.SkImageFilter.MakeBlur(8, 1, CanvasKit.TileMode.Decal, null); 235 paint.setImageFilter(blurIF); 236 canvas.drawPath(pathBR, paint); 237 238 surface.flush(); 239 240 pathUL.delete(); 241 pathBR.delete(); 242 paint.delete(); 243 blurMask.delete(); 244 blurIF.delete(); 245 textFont.delete(); 246 247 reportSurface(surface, 'blur_filters', done); 248 })); 249 }); 250 251 it('can use various image filters', function(done) { 252 const imgPromise = fetch('/assets/mandrill_512.png') 253 .then((response) => response.arrayBuffer()); 254 Promise.all([imgPromise, LoadCanvasKit]).then((values) => { 255 const pngData = values[0]; 256 expect(pngData).toBeTruthy(); 257 catchException(done, () => { 258 let img = CanvasKit.MakeImageFromEncoded(pngData); 259 expect(img).toBeTruthy(); 260 const surface = CanvasKit.MakeCanvasSurface('test'); 261 expect(surface).toBeTruthy('Could not make surface'); 262 if (!surface) { 263 done(); 264 return; 265 } 266 const canvas = surface.getCanvas(); 267 canvas.clear(CanvasKit.WHITE); 268 const paint = new CanvasKit.SkPaint(); 269 paint.setAntiAlias(true); 270 paint.setColor(CanvasKit.Color(0, 255, 0, 1.0)); 271 const redCF = CanvasKit.SkColorFilter.MakeBlend( 272 CanvasKit.Color(255, 0, 0, 0.1), CanvasKit.BlendMode.SrcOver); 273 const redIF = CanvasKit.SkImageFilter.MakeColorFilter(redCF, null); 274 const blurIF = CanvasKit.SkImageFilter.MakeBlur(8, 0.2, CanvasKit.TileMode.Decal, null); 275 const combined = CanvasKit.SkImageFilter.MakeCompose(redIF, blurIF); 276 277 // rotate 10 degrees centered on 200, 200 278 const m = CanvasKit.SkMatrix.rotated(Math.PI/18, 200, 200); 279 const rotated = CanvasKit.SkImageFilter.MakeMatrixTransform(m, CanvasKit.FilterQuality.Medium, combined); 280 paint.setImageFilter(rotated); 281 282 //canvas.rotate(10, 200, 200); 283 canvas.drawImage(img, 0, 0, paint); 284 canvas.drawRect(CanvasKit.LTRBRect(5, 35, 45, 80), paint); 285 286 surface.flush(); 287 288 paint.delete(); 289 redIF.delete(); 290 redCF.delete(); 291 blurIF.delete(); 292 combined.delete(); 293 rotated.delete(); 294 img.delete(); 295 296 reportSurface(surface, 'combined_filters', done); 297 })(); 298 }); 299 }); 300 301 it('can use various image filters on animated images', function(done) { 302 const imgPromise = fetch('/assets/flightAnim.gif') 303 .then((response) => response.arrayBuffer()); 304 Promise.all([imgPromise, LoadCanvasKit]).then((values) => { 305 const gifData = values[0]; 306 expect(gifData).toBeTruthy(); 307 catchException(done, () => { 308 let img = CanvasKit.MakeAnimatedImageFromEncoded(gifData); 309 expect(img).toBeTruthy(); 310 const surface = CanvasKit.MakeCanvasSurface('test'); 311 expect(surface).toBeTruthy('Could not make surface'); 312 if (!surface) { 313 done(); 314 return; 315 } 316 img.decodeNextFrame(); 317 img.decodeNextFrame(); 318 const canvas = surface.getCanvas(); 319 canvas.clear(CanvasKit.WHITE); 320 const paint = new CanvasKit.SkPaint(); 321 paint.setAntiAlias(true); 322 paint.setColor(CanvasKit.Color(0, 255, 0, 1.0)); 323 const redCF = CanvasKit.SkColorFilter.MakeBlend( 324 CanvasKit.Color(255, 0, 0, 0.1), CanvasKit.BlendMode.SrcOver); 325 const redIF = CanvasKit.SkImageFilter.MakeColorFilter(redCF, null); 326 const blurIF = CanvasKit.SkImageFilter.MakeBlur(8, 0.2, CanvasKit.TileMode.Decal, null); 327 const combined = CanvasKit.SkImageFilter.MakeCompose(redIF, blurIF); 328 paint.setImageFilter(combined); 329 330 const frame = img.getCurrentFrame(); 331 canvas.drawImage(frame, 100, 50, paint); 332 333 surface.flush(); 334 335 paint.delete(); 336 redIF.delete(); 337 redCF.delete(); 338 blurIF.delete(); 339 combined.delete(); 340 frame.delete(); 341 img.delete(); 342 343 reportSurface(surface, 'animated_filters', done); 344 })(); 345 }); 346 }); 347 348 it('can decode and draw an skp', function(done) { 349 const skpPromise = fetch('/assets/red_line.skp') 350 .then((response) => response.arrayBuffer()); 351 Promise.all([skpPromise, LoadCanvasKit]).then((values) => { 352 const skpData = values[0]; 353 expect(skpData).toBeTruthy(); 354 catchException(done, () => { 355 let pic = CanvasKit.MakeSkPicture(skpData); 356 expect(pic).toBeTruthy(); 357 const surface = CanvasKit.MakeCanvasSurface('test'); 358 expect(surface).toBeTruthy('Could not make surface'); 359 if (!surface) { 360 done(); 361 return; 362 } 363 const canvas = surface.getCanvas(); 364 canvas.clear(CanvasKit.TRANSPARENT); 365 canvas.drawPicture(pic); 366 367 reportSurface(surface, 'drawImage_skp', done); 368 })(); 369 }); 370 }); 371 372 it('can draw once using drawOnce utility method', function(done) { 373 LoadCanvasKit.then(catchException(done, () => { 374 const surface = CanvasKit.MakeCanvasSurface('test'); 375 expect(surface).toBeTruthy('Could not make surface'); 376 if (!surface) { 377 done(); 378 return; 379 } 380 381 function drawFrame(canvas) { 382 const paint = new CanvasKit.SkPaint(); 383 paint.setStrokeWidth(1.0); 384 paint.setAntiAlias(true); 385 paint.setColor(CanvasKit.Color(0, 0, 0, 1.0)); 386 paint.setStyle(CanvasKit.PaintStyle.Stroke); 387 const path = new CanvasKit.SkPath(); 388 path.moveTo(20, 5); 389 path.lineTo(30, 20); 390 path.lineTo(40, 10); 391 canvas.drawPath(path, paint); 392 path.delete(); 393 paint.delete(); 394 // surface hasn't been flushed yet (nor should we call flush 395 // ourselves), so reportSurface would likely be blank if we 396 // were to call it. 397 done(); 398 } 399 surface.drawOnce(drawFrame); 400 // Reminder: drawOnce is async. In this test, we are just making 401 // sure the drawOnce function is there and doesn't crash, so we can 402 // just call done() when the frame is rendered. 403 })); 404 }); 405 406 it('can use DecodeCache APIs', function(done) { 407 LoadCanvasKit.then(catchException(done, () => { 408 const initialLimit = CanvasKit.getDecodeCacheLimitBytes(); 409 expect(initialLimit).toBeGreaterThan(1024 * 1024); 410 411 const newLimit = 42 * 1024 * 1024; 412 CanvasKit.setDecodeCacheLimitBytes(newLimit); 413 expect(CanvasKit.getDecodeCacheLimitBytes()).toEqual(newLimit); 414 415 // We cannot make any assumptions about this value, 416 // so we just make sure it doesn't crash. 417 CanvasKit.getDecodeCacheUsedBytes(); 418 419 done(); 420 })); 421 }); 422 423 it('can combine shaders', function(done) { 424 LoadCanvasKit.then(catchException(done, () => { 425 const surface = CanvasKit.MakeCanvasSurface('test'); 426 expect(surface).toBeTruthy('Could not make surface'); 427 if (!surface) { 428 done(); 429 return; 430 } 431 const canvas = surface.getCanvas(); 432 433 const rShader = CanvasKit.SkShader.Color(CanvasKit.Color(255, 0, 0, 1.0)); 434 const gShader = CanvasKit.SkShader.Color(CanvasKit.Color(0, 255, 0, 0.6)); 435 const bShader = CanvasKit.SkShader.Color(CanvasKit.Color(0, 0, 255, 1.0)); 436 437 const rgShader = CanvasKit.SkShader.Blend(CanvasKit.BlendMode.SrcOver, rShader, gShader); 438 439 const p = new CanvasKit.SkPaint(); 440 p.setShader(rgShader); 441 canvas.drawPaint(p); 442 443 const gbShader = CanvasKit.SkShader.Lerp(0.5, gShader, bShader); 444 445 p.setShader(gbShader); 446 canvas.drawRect(CanvasKit.LTRBRect(5, 100, 300, 400), p); 447 448 449 reportSurface(surface, 'combined_shaders', done); 450 })); 451 }); 452 453 it('exports consts correctly', function(done) { 454 LoadCanvasKit.then(catchException(done, () => { 455 expect(CanvasKit.TRANSPARENT).toEqual(0); 456 expect(CanvasKit.RED).toEqual(4294901760); 457 458 expect(CanvasKit.QUAD_VERB).toEqual(2); 459 expect(CanvasKit.CONIC_VERB).toEqual(3); 460 461 expect(CanvasKit.SaveLayerInitWithPrevious).toEqual(4); 462 expect(CanvasKit.SaveLayerF16ColorType).toEqual(16); 463 464 done(); 465 })); 466 }); 467 468}); 469