1describe('Core canvas behavior', () => { 2 let container; 3 4 beforeEach(async () => { 5 await LoadCanvasKit; 6 container = document.createElement('div'); 7 container.innerHTML = ` 8 <canvas width=600 height=600 id=test></canvas> 9 <canvas width=600 height=600 id=report></canvas>`; 10 document.body.appendChild(container); 11 }); 12 13 afterEach(() => { 14 document.body.removeChild(container); 15 }); 16 17 gm('picture_test', (canvas) => { 18 const spr = new CanvasKit.PictureRecorder(); 19 const rcanvas = spr.beginRecording( 20 CanvasKit.LTRBRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT)); 21 const paint = new CanvasKit.Paint(); 22 paint.setStrokeWidth(2.0); 23 paint.setAntiAlias(true); 24 paint.setColor(CanvasKit.Color(0, 0, 0, 1.0)); 25 paint.setStyle(CanvasKit.PaintStyle.Stroke); 26 27 rcanvas.drawRRect(CanvasKit.RRectXY([5, 35, 45, 80], 15, 10), paint); 28 29 const font = new CanvasKit.Font(null, 20); 30 rcanvas.drawText('this picture has a round rect', 5, 100, paint, font); 31 const pic = spr.finishRecordingAsPicture(); 32 spr.delete(); 33 paint.delete(); 34 35 canvas.drawPicture(pic); 36 37 const bytes = pic.serialize(); 38 expect(bytes).toBeTruthy(); 39 40 pic.delete(); 41 }); 42 43 const uIntColorToCanvasKitColor = (c) => { 44 return CanvasKit.Color( 45 (c >> 16) & 0xFF, 46 (c >> 8) & 0xFF, 47 (c >> 0) & 0xFF, 48 ((c >> 24) & 0xFF) / 255 49 ); 50 }; 51 52 it('can compute tonal colors', () => { 53 const input = { 54 ambient: CanvasKit.BLUE, 55 spot: CanvasKit.RED, 56 }; 57 const out = CanvasKit.computeTonalColors(input); 58 expect(new Float32Array(out.ambient)).toEqual(CanvasKit.BLACK); 59 const expectedSpot = [0.173, 0, 0, 0.969]; 60 expect(out.spot.length).toEqual(4); 61 expect(out.spot[0]).toBeCloseTo(expectedSpot[0], 3); 62 expect(out.spot[1]).toBeCloseTo(expectedSpot[1], 3); 63 expect(out.spot[2]).toBeCloseTo(expectedSpot[2], 3); 64 expect(out.spot[3]).toBeCloseTo(expectedSpot[3], 3); 65 }); 66 67 it('can compute tonal colors with malloced values', () => { 68 const ambientColor = CanvasKit.Malloc(Float32Array, 4); 69 ambientColor.toTypedArray().set(CanvasKit.BLUE); 70 const spotColor = CanvasKit.Malloc(Float32Array, 4); 71 spotColor.toTypedArray().set(CanvasKit.RED); 72 const input = { 73 ambient: ambientColor, 74 spot: spotColor, 75 }; 76 const out = CanvasKit.computeTonalColors(input); 77 expect(new Float32Array(out.ambient)).toEqual(CanvasKit.BLACK); 78 const expectedSpot = [0.173, 0, 0, 0.969]; 79 expect(out.spot.length).toEqual(4); 80 expect(out.spot[0]).toBeCloseTo(expectedSpot[0], 3); 81 expect(out.spot[1]).toBeCloseTo(expectedSpot[1], 3); 82 expect(out.spot[2]).toBeCloseTo(expectedSpot[2], 3); 83 expect(out.spot[3]).toBeCloseTo(expectedSpot[3], 3); 84 }); 85 86 // This helper is used for all the MakeImageFromEncoded tests. 87 // TODO(kjlubick): rewrite this and callers to use gm 88 function decodeAndDrawSingleFrameImage(imgName, goldName, done) { 89 const imgPromise = fetch(imgName) 90 .then((response) => response.arrayBuffer()); 91 Promise.all([imgPromise, LoadCanvasKit]).then((values) => { 92 const imgData = values[0]; 93 expect(imgData).toBeTruthy(); 94 catchException(done, () => { 95 let img = CanvasKit.MakeImageFromEncoded(imgData); 96 expect(img).toBeTruthy(); 97 const surface = CanvasKit.MakeCanvasSurface('test'); 98 expect(surface).toBeTruthy('Could not make surface'); 99 if (!surface) { 100 done(); 101 return; 102 } 103 const canvas = surface.getCanvas(); 104 let paint = new CanvasKit.Paint(); 105 canvas.drawImage(img, 0, 0, paint); 106 107 paint.delete(); 108 img.delete(); 109 110 reportSurface(surface, goldName, done); 111 })(); 112 }); 113 } 114 115 it('can decode and draw a png', (done) => { 116 decodeAndDrawSingleFrameImage('/assets/mandrill_512.png', 'drawImage_png', done); 117 }); 118 119 it('can decode and draw a jpg', (done) => { 120 decodeAndDrawSingleFrameImage('/assets/mandrill_h1v1.jpg', 'drawImage_jpg', done); 121 }); 122 123 it('can decode and draw a (still) gif', (done) => { 124 decodeAndDrawSingleFrameImage('/assets/flightAnim.gif', 'drawImage_gif', done); 125 }); 126 127 it('can decode and draw a still webp', (done) => { 128 decodeAndDrawSingleFrameImage('/assets/color_wheel.webp', 'drawImage_webp', done); 129 }); 130 131 it('can readPixels from an Image', (done) => { 132 const imgPromise = fetch('/assets/mandrill_512.png') 133 .then((response) => response.arrayBuffer()); 134 Promise.all([imgPromise, LoadCanvasKit]).then((values) => { 135 const imgData = values[0]; 136 expect(imgData).toBeTruthy(); 137 catchException(done, () => { 138 let img = CanvasKit.MakeImageFromEncoded(imgData); 139 expect(img).toBeTruthy(); 140 const imageInfo = { 141 alphaType: CanvasKit.AlphaType.Unpremul, 142 colorType: CanvasKit.ColorType.RGBA_8888, 143 colorSpace: CanvasKit.ColorSpace.SRGB, 144 width: img.width(), 145 height: img.height(), 146 }; 147 const rowBytes = 4 * img.width(); 148 149 const pixels = img.readPixels(0, 0, imageInfo); 150 // We know the image is 512 by 512 pixels in size, each pixel 151 // requires 4 bytes (R, G, B, A). 152 expect(pixels.length).toEqual(512 * 512 * 4); 153 154 // Make enough space for a 5x5 8888 surface (4 bytes for R, G, B, A) 155 const rdsData = CanvasKit.Malloc(Uint8Array, 512 * 5*512 * 4); 156 const pixels2 = rdsData.toTypedArray(); 157 pixels2[0] = 127; // sentinel value, should be overwritten by readPixels. 158 img.readPixels(0, 0, imageInfo, rdsData, rowBytes); 159 expect(rdsData.toTypedArray()[0]).toEqual(pixels[0]); 160 161 img.delete(); 162 CanvasKit.Free(rdsData); 163 done(); 164 })(); 165 }); 166 }); 167 168 gm('drawDrawable_animated_gif', (canvas, fetchedByteBuffers) => { 169 let aImg = CanvasKit.MakeAnimatedImageFromEncoded(fetchedByteBuffers[0]); 170 expect(aImg).toBeTruthy(); 171 expect(aImg.getRepetitionCount()).toEqual(-1); // infinite loop 172 expect(aImg.width()).toEqual(320); 173 expect(aImg.height()).toEqual(240); 174 expect(aImg.getFrameCount()).toEqual(60); 175 176 const drawCurrentFrame = function(x, y) { 177 let img = aImg.makeImageAtCurrentFrame(); 178 canvas.drawImage(img, x, y, null); 179 img.delete(); 180 } 181 182 drawCurrentFrame(0, 0); 183 184 let c = aImg.decodeNextFrame(); 185 expect(c).not.toEqual(-1); 186 drawCurrentFrame(300, 0); 187 for(let i = 0; i < 10; i++) { 188 c = aImg.decodeNextFrame(); 189 expect(c).not.toEqual(-1); 190 } 191 drawCurrentFrame(0, 300); 192 for(let i = 0; i < 10; i++) { 193 c = aImg.decodeNextFrame(); 194 expect(c).not.toEqual(-1); 195 } 196 drawCurrentFrame(300, 300); 197 198 aImg.delete(); 199 }, '/assets/flightAnim.gif'); 200 201 gm('exif_orientation', (canvas, fetchedByteBuffers) => { 202 canvas.clear(CanvasKit.WHITE); 203 const paint = new CanvasKit.Paint(); 204 const font = new CanvasKit.Font(null, 14); 205 canvas.drawText('The following heart should be rotated 90 CCW due to exif.', 206 5, 25, paint, font); 207 208 // TODO(kjlubick) it would be nice to also to test MakeAnimatedImageFromEncoded but 209 // I could not create a sample animated image that worked. 210 const img = CanvasKit.MakeImageFromEncoded(fetchedByteBuffers[0]); 211 expect(img).toBeTruthy(); 212 canvas.drawImage(img, 5, 35, null); 213 214 img.delete(); 215 paint.delete(); 216 font.delete(); 217 }, '/assets/exif_rotated_heart.jpg'); 218 219 gm('1x4_from_scratch', (canvas) => { 220 canvas.clear(CanvasKit.WHITE); 221 const paint = new CanvasKit.Paint(); 222 223 // This creates and draws an Image that is 1 pixel wide, 4 pixels tall with 224 // the colors listed below. 225 const pixels = Uint8Array.from([ 226 255, 0, 0, 255, // opaque red 227 0, 255, 0, 255, // opaque green 228 0, 0, 255, 255, // opaque blue 229 255, 0, 255, 100, // transparent purple 230 ]); 231 const img = CanvasKit.MakeImage({ 232 'width': 1, 233 'height': 4, 234 'alphaType': CanvasKit.AlphaType.Unpremul, 235 'colorType': CanvasKit.ColorType.RGBA_8888, 236 'colorSpace': CanvasKit.ColorSpace.SRGB 237 }, pixels, 4); 238 canvas.drawImage(img, 1, 1, paint); 239 240 const info = img.getImageInfo(); 241 expect(info).toEqual({ 242 'width': 1, 243 'height': 4, 244 'alphaType': CanvasKit.AlphaType.Unpremul, 245 'colorType': CanvasKit.ColorType.RGBA_8888, 246 }); 247 const cs = img.getColorSpace(); 248 expect(CanvasKit.ColorSpace.Equals(cs, CanvasKit.ColorSpace.SRGB)).toBeTruthy(); 249 250 cs.delete(); 251 img.delete(); 252 paint.delete(); 253 }); 254 255 gm('draw_atlas_with_builders', (canvas, fetchedByteBuffers) => { 256 const atlas = CanvasKit.MakeImageFromEncoded(fetchedByteBuffers[0]); 257 expect(atlas).toBeTruthy(); 258 canvas.clear(CanvasKit.WHITE); 259 260 const paint = new CanvasKit.Paint(); 261 paint.setColor(CanvasKit.Color(0, 0, 0, 0.8)); 262 263 // Allocate space for 4 rectangles. 264 const srcs = CanvasKit.Malloc(Float32Array, 16); 265 srcs.toTypedArray().set([ 266 0, 0, 256, 256, // LTRB 267 256, 0, 512, 256, 268 0, 256, 256, 512, 269 256, 256, 512, 512 270 ]); 271 272 // Allocate space for 4 RSXForms. 273 const dsts = CanvasKit.Malloc(Float32Array, 16); 274 dsts.toTypedArray().set([ 275 0.5, 0, 20, 20, // scos, ssin, tx, ty 276 0.5, 0, 300, 20, 277 0.5, 0, 20, 300, 278 0.5, 0, 300, 300 279 ]); 280 281 // Allocate space for 4 colors. 282 const colors = new CanvasKit.Malloc(Uint32Array, 4); 283 colors.toTypedArray().set([ 284 CanvasKit.ColorAsInt( 85, 170, 10, 128), // light green 285 CanvasKit.ColorAsInt( 51, 51, 191, 128), // light blue 286 CanvasKit.ColorAsInt( 0, 0, 0, 128), 287 CanvasKit.ColorAsInt(256, 256, 256, 128), 288 ]); 289 290 canvas.drawAtlas(atlas, srcs, dsts, paint, CanvasKit.BlendMode.Modulate, colors); 291 292 atlas.delete(); 293 CanvasKit.Free(srcs); 294 CanvasKit.Free(dsts); 295 CanvasKit.Free(colors); 296 paint.delete(); 297 }, '/assets/mandrill_512.png'); 298 299 gm('draw_atlas_with_arrays', (canvas, fetchedByteBuffers) => { 300 const atlas = CanvasKit.MakeImageFromEncoded(fetchedByteBuffers[0]); 301 expect(atlas).toBeTruthy(); 302 canvas.clear(CanvasKit.WHITE); 303 304 const paint = new CanvasKit.Paint(); 305 paint.setColor(CanvasKit.Color(0, 0, 0, 0.8)); 306 307 const srcs = [ 308 0, 0, 8, 8, 309 8, 0, 16, 8, 310 0, 8, 8, 16, 311 8, 8, 16, 16, 312 ]; 313 314 const dsts = [ 315 10, 0, 0, 0, 316 10, 0, 100, 0, 317 10, 0, 0, 100, 318 10, 0, 100, 100, 319 ]; 320 321 const colors = Uint32Array.of( 322 CanvasKit.ColorAsInt( 85, 170, 10, 128), // light green 323 CanvasKit.ColorAsInt( 51, 51, 191, 128), // light blue 324 CanvasKit.ColorAsInt( 0, 0, 0, 128), 325 CanvasKit.ColorAsInt(255, 255, 255, 128), 326 ); 327 328 // sampling for each of the 4 instances 329 const sampling = [ 330 null, 331 {B: 0, C: 0.5}, 332 {filter: CanvasKit.FilterMode.Nearest, mipmap: CanvasKit.MipmapMode.None}, 333 {filter: CanvasKit.FilterMode.Linear, mipmap: CanvasKit.MipmapMode.Nearest}, 334 ]; 335 336 // positioning for each of the 4 instances 337 const offset = [ 338 [0, 0], [256, 0], [0, 256], [256, 256] 339 ]; 340 341 canvas.translate(20, 20); 342 for (let i = 0; i < 4; ++i) { 343 canvas.save(); 344 canvas.translate(offset[i][0], offset[i][1]); 345 canvas.drawAtlas(atlas, srcs, dsts, paint, CanvasKit.BlendMode.SrcOver, colors, 346 sampling[i]); 347 canvas.restore(); 348 } 349 350 atlas.delete(); 351 paint.delete(); 352 }, '/assets/mandrill_16.png'); 353 354 gm('draw_patch', (canvas, fetchedByteBuffers) => { 355 const image = CanvasKit.MakeImageFromEncoded(fetchedByteBuffers[0]); 356 expect(image).toBeTruthy(); 357 canvas.clear(CanvasKit.WHITE); 358 359 const paint = new CanvasKit.Paint(); 360 const shader = image.makeShaderOptions(CanvasKit.TileMode.Clamp, 361 CanvasKit.TileMode.Clamp, 362 CanvasKit.FilterMode.Linear, 363 CanvasKit.MipmapMode.None); 364 const cubics = [0,0, 80,50, 160,50, 365 240,0, 200,80, 200,160, 366 240,240, 160,160, 80,240, 367 0,240, 50,160, 0,80]; 368 const colors = [CanvasKit.RED, CanvasKit.BLUE, CanvasKit.YELLOW, CanvasKit.CYAN]; 369 const texs = [0,0, 16,0, 16,16, 0,16]; 370 371 const params = [ 372 [ 0, 0, colors, null, null, null], 373 [256, 0, null, texs, shader, null], 374 [ 0, 256, colors, texs, shader, null], 375 [256, 256, colors, texs, shader, CanvasKit.BlendMode.Screen], 376 ]; 377 for (const p of params) { 378 paint.setShader(p[4]); 379 canvas.save(); 380 canvas.translate(p[0], p[1]); 381 canvas.drawPatch(cubics, p[2], p[3], p[5], paint); 382 canvas.restore(); 383 } 384 paint.delete(); 385 }, '/assets/mandrill_16.png'); 386 387 gm('draw_glyphs', (canvas, fetchedByteBuffers) => { 388 canvas.clear(CanvasKit.WHITE); 389 390 const paint = new CanvasKit.Paint(); 391 const font = new CanvasKit.Font(null, 24); 392 paint.setAntiAlias(true); 393 394 const DIM = 16; // row/col count for the grid 395 const GAP = 32; // spacing between each glyph 396 const glyphs = new Uint16Array(256); 397 const positions = new Float32Array(256*2); 398 for (let i = 0; i < 256; ++i) { 399 glyphs[i] = i; 400 positions[2*i+0] = (i%DIM) * GAP; 401 positions[2*i+1] = Math.round(i/DIM) * GAP; 402 } 403 canvas.drawGlyphs(glyphs, positions, 16, 20, font, paint); 404 405 font.delete(); 406 paint.delete(); 407 }); 408 409 gm('image_decoding_methods', async (canvas) => { 410 canvas.clear(CanvasKit.WHITE); 411 412 const IMAGE_FILE_PATHS = [ 413 '/assets/brickwork-texture.jpg', 414 '/assets/mandrill_512.png', 415 '/assets/color_wheel.gif' 416 ]; 417 418 let row = 1; 419 // Test 4 different methods of decoding an image for each of the three images in 420 // IMAGE_FILE_PATHS. 421 // Resulting Images are drawn to visually show that all methods decode correctly. 422 for (const imageFilePath of IMAGE_FILE_PATHS) { 423 const response = await fetch(imageFilePath); 424 const arrayBuffer = await response.arrayBuffer(); 425 // response.blob() is preferable when you don't need both a Blob *and* an ArrayBuffer. 426 const blob = new Blob([ arrayBuffer ]); 427 428 // Method 1 - decode TypedArray using wasm codecs: 429 const skImage1 = CanvasKit.MakeImageFromEncoded(arrayBuffer); 430 431 // Method 2 (slower and does not work in Safari) decode using ImageBitmap: 432 const imageBitmap = await createImageBitmap(blob); 433 // Testing showed that transferring an ImageBitmap to a canvas using the 'bitmaprenderer' 434 // context and passing that canvas to CanvasKit.MakeImageFromCanvasImageSource() is 435 // marginally faster than passing ImageBitmap to 436 // CanvasKit.MakeImageFromCanvasImageSource() directly. 437 const canvasBitmapElement = document.createElement('canvas'); 438 canvasBitmapElement.width = imageBitmap.width; 439 canvasBitmapElement.height = imageBitmap.height; 440 const ctxBitmap = canvasBitmapElement.getContext('bitmaprenderer'); 441 ctxBitmap.transferFromImageBitmap(imageBitmap); 442 const skImage2 = CanvasKit.MakeImageFromCanvasImageSource(canvasBitmapElement); 443 444 // Method 3 (slowest) decode using HTMLImageElement directly: 445 const image = new Image(); 446 // Testing showed that waiting for a load event is faster than waiting on image.decode() 447 // HTMLImageElement.decode() reference: https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decode 448 const promise1 = new Promise((resolve) => image.addEventListener('load', resolve)); 449 image.src = imageFilePath; 450 await promise1; 451 const skImage3 = CanvasKit.MakeImageFromCanvasImageSource(image); 452 453 // Method 4 (roundabout, but works if all you have is a Blob) decode from Blob using 454 // HTMLImageElement: 455 const imageObjectUrl = URL.createObjectURL( blob ); 456 const image2 = new Image(); 457 const promise2 = new Promise((resolve) => image2.addEventListener('load', resolve)); 458 image2.src = imageObjectUrl; 459 await promise2; 460 const skImage4 = CanvasKit.MakeImageFromCanvasImageSource(image2); 461 462 // Draw decoded images 463 const sourceRect = CanvasKit.XYWHRect(0, 0, 150, 150); 464 canvas.drawImageRect(skImage1, sourceRect, CanvasKit.XYWHRect(0, row * 100, 90, 90), null, false); 465 canvas.drawImageRect(skImage2, sourceRect, CanvasKit.XYWHRect(100, row * 100, 90, 90), null, false); 466 canvas.drawImageRect(skImage3, sourceRect, CanvasKit.XYWHRect(200, row * 100, 90, 90), null, false); 467 canvas.drawImageRect(skImage4, sourceRect, CanvasKit.XYWHRect(300, row * 100, 90, 90), null, false); 468 469 row++; 470 } 471 // Label images with the method used to decode them 472 const paint = new CanvasKit.Paint(); 473 const textFont = new CanvasKit.Font(null, 7); 474 canvas.drawText('WASM Decoding', 0, 90, paint, textFont); 475 canvas.drawText('ImageBitmap Decoding', 100, 90, paint, textFont); 476 canvas.drawText('HTMLImageEl Decoding', 200, 90, paint, textFont); 477 canvas.drawText('Blob Decoding', 300, 90, paint, textFont); 478 }); 479 480 gm('sweep_gradient', (canvas) => { 481 const paint = new CanvasKit.Paint(); 482 const shader = CanvasKit.Shader.MakeSweepGradient( 483 100, 100, // X, Y coordinates 484 [CanvasKit.GREEN, CanvasKit.BLUE], 485 [0.0, 1.0], 486 CanvasKit.TileMode.Clamp, 487 ); 488 expect(shader).toBeTruthy('Could not make shader'); 489 490 paint.setShader(shader); 491 canvas.drawPaint(paint); 492 493 paint.delete(); 494 shader.delete(); 495 }); 496 497 // TODO(kjlubick): There's a lot of shared code between the gradient gms 498 // It would be best to deduplicate that in a nice DAMP way. 499 // Inspired by https://fiddle.skia.org/c/b29ce50a341510784ac7d5281586d076 500 gm('linear_gradients', (canvas) => { 501 canvas.clear(CanvasKit.WHITE); 502 canvas.scale(2, 2); 503 const strokePaint = new CanvasKit.Paint(); 504 strokePaint.setStyle(CanvasKit.PaintStyle.Stroke); 505 strokePaint.setColor(CanvasKit.BLACK); 506 507 const paint = new CanvasKit.Paint(); 508 paint.setStyle(CanvasKit.PaintStyle.Fill); 509 const transparentGreen = CanvasKit.Color(0, 255, 255, 0); 510 511 const lgs = CanvasKit.Shader.MakeLinearGradient( 512 [0, 0], [50, 100], // start and stop points 513 [transparentGreen, CanvasKit.BLUE, CanvasKit.RED], 514 [0, 0.65, 1.0], 515 CanvasKit.TileMode.Mirror 516 ); 517 paint.setShader(lgs); 518 let r = CanvasKit.LTRBRect(0, 0, 100, 100); 519 canvas.drawRect(r, paint); 520 canvas.drawRect(r, strokePaint); 521 522 const lgsPremul = CanvasKit.Shader.MakeLinearGradient( 523 [100, 0], [150, 100], // start and stop points 524 Uint32Array.of( 525 CanvasKit.ColorAsInt(0, 255, 255, 0), 526 CanvasKit.ColorAsInt(0, 0, 255, 255), 527 CanvasKit.ColorAsInt(255, 0, 0, 255)), 528 [0, 0.65, 1.0], 529 CanvasKit.TileMode.Mirror, 530 null, // no local matrix 531 1 // interpolate colors in premul 532 ); 533 paint.setShader(lgsPremul); 534 r = CanvasKit.LTRBRect(100, 0, 200, 100); 535 canvas.drawRect(r, paint); 536 canvas.drawRect(r, strokePaint); 537 538 const lgs45 = CanvasKit.Shader.MakeLinearGradient( 539 [0, 100], [50, 200], // start and stop points 540 Float32Array.of(...transparentGreen, ...CanvasKit.BLUE, ...CanvasKit.RED), 541 [0, 0.65, 1.0], 542 CanvasKit.TileMode.Mirror, 543 CanvasKit.Matrix.rotated(Math.PI/4, 0, 100), 544 ); 545 paint.setShader(lgs45); 546 r = CanvasKit.LTRBRect(0, 100, 100, 200); 547 canvas.drawRect(r, paint); 548 canvas.drawRect(r, strokePaint); 549 550 // malloc'd color array 551 const colors = CanvasKit.Malloc(Float32Array, 12); 552 const typedColorsArray = colors.toTypedArray(); 553 typedColorsArray.set(transparentGreen, 0); 554 typedColorsArray.set(CanvasKit.BLUE, 4); 555 typedColorsArray.set(CanvasKit.RED, 8); 556 const lgs45Premul = CanvasKit.Shader.MakeLinearGradient( 557 [100, 100], [150, 200], // start and stop points 558 typedColorsArray, 559 [0, 0.65, 1.0], 560 CanvasKit.TileMode.Mirror, 561 CanvasKit.Matrix.rotated(Math.PI/4, 100, 100), 562 1 // interpolate colors in premul 563 ); 564 CanvasKit.Free(colors); 565 paint.setShader(lgs45Premul); 566 r = CanvasKit.LTRBRect(100, 100, 200, 200); 567 canvas.drawRect(r, paint); 568 canvas.drawRect(r, strokePaint); 569 570 lgs.delete(); 571 lgs45.delete(); 572 lgsPremul.delete(); 573 lgs45Premul.delete(); 574 strokePaint.delete(); 575 paint.delete(); 576 }); 577 578 gm('radial_gradients', (canvas) => { 579 canvas.clear(CanvasKit.WHITE); 580 canvas.scale(2, 2); 581 const strokePaint = new CanvasKit.Paint(); 582 strokePaint.setStyle(CanvasKit.PaintStyle.Stroke); 583 strokePaint.setColor(CanvasKit.BLACK); 584 585 const paint = new CanvasKit.Paint(); 586 paint.setStyle(CanvasKit.PaintStyle.Fill); 587 const transparentGreen = CanvasKit.Color(0, 255, 255, 0); 588 589 const rgs = CanvasKit.Shader.MakeRadialGradient( 590 [50, 50], 50, // center, radius 591 [transparentGreen, CanvasKit.BLUE, CanvasKit.RED], 592 [0, 0.65, 1.0], 593 CanvasKit.TileMode.Mirror 594 ); 595 paint.setShader(rgs); 596 let r = CanvasKit.LTRBRect(0, 0, 100, 100); 597 canvas.drawRect(r, paint); 598 canvas.drawRect(r, strokePaint); 599 600 const rgsPremul = CanvasKit.Shader.MakeRadialGradient( 601 [150, 50], 50, // center, radius 602 [transparentGreen, CanvasKit.BLUE, CanvasKit.RED], 603 [0, 0.65, 1.0], 604 CanvasKit.TileMode.Mirror, 605 null, // no local matrix 606 1 // interpolate colors in premul 607 ); 608 paint.setShader(rgsPremul); 609 r = CanvasKit.LTRBRect(100, 0, 200, 100); 610 canvas.drawRect(r, paint); 611 canvas.drawRect(r, strokePaint); 612 613 const rgsSkew = CanvasKit.Shader.MakeRadialGradient( 614 [50, 150], 50, // center, radius 615 [transparentGreen, CanvasKit.BLUE, CanvasKit.RED], 616 [0, 0.65, 1.0], 617 CanvasKit.TileMode.Mirror, 618 CanvasKit.Matrix.skewed(0.5, 0, 100, 100), 619 null, // color space 620 ); 621 paint.setShader(rgsSkew); 622 r = CanvasKit.LTRBRect(0, 100, 100, 200); 623 canvas.drawRect(r, paint); 624 canvas.drawRect(r, strokePaint); 625 626 const rgsSkewPremul = CanvasKit.Shader.MakeRadialGradient( 627 [150, 150], 50, // center, radius 628 [transparentGreen, CanvasKit.BLUE, CanvasKit.RED], 629 [0, 0.65, 1.0], 630 CanvasKit.TileMode.Mirror, 631 CanvasKit.Matrix.skewed(0.5, 0, 100, 100), 632 1, // interpolate colors in premul 633 null, // color space 634 ); 635 paint.setShader(rgsSkewPremul); 636 r = CanvasKit.LTRBRect(100, 100, 200, 200); 637 canvas.drawRect(r, paint); 638 canvas.drawRect(r, strokePaint); 639 640 rgs.delete(); 641 rgsPremul.delete(); 642 rgsSkew.delete(); 643 rgsSkewPremul.delete(); 644 strokePaint.delete(); 645 paint.delete(); 646 }); 647 648 gm('conical_gradients', (canvas) => { 649 canvas.clear(CanvasKit.WHITE); 650 canvas.scale(2, 2); 651 const strokePaint = new CanvasKit.Paint(); 652 strokePaint.setStyle(CanvasKit.PaintStyle.Stroke); 653 strokePaint.setColor(CanvasKit.BLACK); 654 655 const paint = new CanvasKit.Paint(); 656 paint.setStyle(CanvasKit.PaintStyle.Fill); 657 paint.setAntiAlias(true); 658 const transparentGreen = CanvasKit.Color(0, 255, 255, 0); 659 660 const cgs = CanvasKit.Shader.MakeTwoPointConicalGradient( 661 [80, 10], 15, // start, radius 662 [10, 110], 60, // end, radius 663 [transparentGreen, CanvasKit.BLUE, CanvasKit.RED], 664 [0, 0.65, 1.0], 665 CanvasKit.TileMode.Mirror, 666 null, // no local matrix 667 ); 668 paint.setShader(cgs); 669 let r = CanvasKit.LTRBRect(0, 0, 100, 100); 670 canvas.drawRect(r, paint); 671 canvas.drawRect(r, strokePaint); 672 673 const cgsPremul = CanvasKit.Shader.MakeTwoPointConicalGradient( 674 [180, 10], 15, // start, radius 675 [110, 110], 60, // end, radius 676 [transparentGreen, CanvasKit.BLUE, CanvasKit.RED], 677 [0, 0.65, 1.0], 678 CanvasKit.TileMode.Mirror, 679 null, // no local matrix 680 1, // interpolate colors in premul 681 null, // color space 682 ); 683 paint.setShader(cgsPremul); 684 r = CanvasKit.LTRBRect(100, 0, 200, 100); 685 canvas.drawRect(r, paint); 686 canvas.drawRect(r, strokePaint); 687 688 const cgs45 = CanvasKit.Shader.MakeTwoPointConicalGradient( 689 [80, 110], 15, // start, radius 690 [10, 210], 60, // end, radius 691 [transparentGreen, CanvasKit.BLUE, CanvasKit.RED], 692 [0, 0.65, 1.0], 693 CanvasKit.TileMode.Mirror, 694 CanvasKit.Matrix.rotated(Math.PI/4, 0, 100), 695 null, // color space 696 ); 697 paint.setShader(cgs45); 698 r = CanvasKit.LTRBRect(0, 100, 100, 200); 699 canvas.drawRect(r, paint); 700 canvas.drawRect(r, strokePaint); 701 702 const cgs45Premul = CanvasKit.Shader.MakeTwoPointConicalGradient( 703 [180, 110], 15, // start, radius 704 [110, 210], 60, // end, radius 705 [transparentGreen, CanvasKit.BLUE, CanvasKit.RED], 706 [0, 0.65, 1.0], 707 CanvasKit.TileMode.Mirror, 708 CanvasKit.Matrix.rotated(Math.PI/4, 100, 100), 709 1, // interpolate colors in premul 710 null, // color space 711 ); 712 paint.setShader(cgs45Premul); 713 r = CanvasKit.LTRBRect(100, 100, 200, 200); 714 canvas.drawRect(r, paint); 715 canvas.drawRect(r, strokePaint); 716 717 cgs.delete(); 718 cgsPremul.delete(); 719 cgs45.delete(); 720 strokePaint.delete(); 721 paint.delete(); 722 }); 723 724 gm('blur_filters', (canvas) => { 725 const pathUL = starPath(CanvasKit, 100, 100, 80); 726 const pathBR = starPath(CanvasKit, 400, 300, 80); 727 const paint = new CanvasKit.Paint(); 728 const textFont = new CanvasKit.Font(null, 24); 729 730 canvas.drawText('Above: MaskFilter', 20, 220, paint, textFont); 731 canvas.drawText('Right: ImageFilter', 20, 260, paint, textFont); 732 733 paint.setColor(CanvasKit.BLUE); 734 735 const blurMask = CanvasKit.MaskFilter.MakeBlur(CanvasKit.BlurStyle.Normal, 5, true); 736 paint.setMaskFilter(blurMask); 737 canvas.drawPath(pathUL, paint); 738 739 const blurIF = CanvasKit.ImageFilter.MakeBlur(8, 1, CanvasKit.TileMode.Decal, null); 740 paint.setImageFilter(blurIF); 741 canvas.drawPath(pathBR, paint); 742 743 pathUL.delete(); 744 pathBR.delete(); 745 paint.delete(); 746 blurMask.delete(); 747 blurIF.delete(); 748 textFont.delete(); 749 }); 750 751 gm('combined_filters', (canvas, fetchedByteBuffers) => { 752 const img = CanvasKit.MakeImageFromEncoded(fetchedByteBuffers[0]); 753 expect(img).toBeTruthy(); 754 755 canvas.clear(CanvasKit.WHITE); 756 const paint = new CanvasKit.Paint(); 757 paint.setAntiAlias(true); 758 paint.setColor(CanvasKit.Color(0, 255, 0, 1.0)); 759 const redCF = CanvasKit.ColorFilter.MakeBlend( 760 CanvasKit.Color(255, 0, 0, 0.1), CanvasKit.BlendMode.SrcOver); 761 const redIF = CanvasKit.ImageFilter.MakeColorFilter(redCF, null); 762 const blurIF = CanvasKit.ImageFilter.MakeBlur(8, 0.2, CanvasKit.TileMode.Decal, null); 763 const combined = CanvasKit.ImageFilter.MakeCompose(redIF, blurIF); 764 765 // rotate 10 degrees centered on 200, 200 766 const m = CanvasKit.Matrix.rotated(Math.PI/18, 200, 200); 767 const filtering = { filter: CanvasKit.FilterMode.Linear }; 768 const rotated = CanvasKit.ImageFilter.MakeMatrixTransform(m, filtering, combined); 769 paint.setImageFilter(rotated); 770 771 //canvas.rotate(10, 200, 200); 772 canvas.drawImage(img, 0, 0, paint); 773 canvas.drawRect(CanvasKit.LTRBRect(5, 35, 45, 80), paint); 774 775 paint.delete(); 776 redIF.delete(); 777 redCF.delete(); 778 blurIF.delete(); 779 combined.delete(); 780 rotated.delete(); 781 img.delete(); 782 }, '/assets/mandrill_512.png'); 783 784 gm('animated_filters', (canvas, fetchedByteBuffers) => { 785 const img = CanvasKit.MakeAnimatedImageFromEncoded(fetchedByteBuffers[0]); 786 expect(img).toBeTruthy(); 787 img.decodeNextFrame(); 788 img.decodeNextFrame(); 789 canvas.clear(CanvasKit.WHITE); 790 const paint = new CanvasKit.Paint(); 791 paint.setAntiAlias(true); 792 paint.setColor(CanvasKit.Color(0, 255, 0, 1.0)); 793 const redCF = CanvasKit.ColorFilter.MakeBlend( 794 CanvasKit.Color(255, 0, 0, 0.1), CanvasKit.BlendMode.SrcOver); 795 const redIF = CanvasKit.ImageFilter.MakeColorFilter(redCF, null); 796 const blurIF = CanvasKit.ImageFilter.MakeBlur(8, 0.2, CanvasKit.TileMode.Decal, null); 797 const combined = CanvasKit.ImageFilter.MakeCompose(redIF, blurIF); 798 paint.setImageFilter(combined); 799 800 const frame = img.makeImageAtCurrentFrame(); 801 canvas.drawImage(frame, 100, 50, paint); 802 803 paint.delete(); 804 redIF.delete(); 805 redCF.delete(); 806 blurIF.delete(); 807 combined.delete(); 808 frame.delete(); 809 img.delete(); 810 }, '/assets/flightAnim.gif'); 811 812 gm('drawImageVariants', (canvas, fetchedByteBuffers) => { 813 const img = CanvasKit.MakeImageFromEncoded(fetchedByteBuffers[0]); 814 expect(img).toBeTruthy(); 815 816 canvas.clear(CanvasKit.WHITE); 817 canvas.scale(2, 2); 818 const paint = new CanvasKit.Paint(); 819 const clipTo = (x, y) => { 820 canvas.save(); 821 canvas.clipRect(CanvasKit.XYWHRect(x, y, 128, 128), CanvasKit.ClipOp.Intersect); 822 }; 823 824 clipTo(0, 0); 825 canvas.drawImage(img, 0, 0, paint); 826 canvas.restore(); 827 828 clipTo(128, 0); 829 canvas.drawImageCubic(img, 128, 0, 1/3, 1/3, null); 830 canvas.restore(); 831 832 clipTo(0, 128); 833 canvas.drawImageOptions(img, 0, 128, CanvasKit.FilterMode.Linear, CanvasKit.MipmapMode.None, null); 834 canvas.restore(); 835 836 const mipImg = img.makeCopyWithDefaultMipmaps(); 837 clipTo(128, 128); 838 canvas.drawImageOptions(mipImg, 128, 128, 839 CanvasKit.FilterMode.Nearest, CanvasKit.MipmapMode.Nearest, null); 840 canvas.restore(); 841 842 paint.delete(); 843 mipImg.delete(); 844 img.delete(); 845 }, '/assets/mandrill_512.png'); 846 847 gm('drawImageRectVariants', (canvas, fetchedByteBuffers) => { 848 const img = CanvasKit.MakeImageFromEncoded(fetchedByteBuffers[0]); 849 expect(img).toBeTruthy(); 850 851 canvas.clear(CanvasKit.WHITE); 852 const paint = new CanvasKit.Paint(); 853 const src = CanvasKit.XYWHRect(100, 100, 128, 128); 854 canvas.drawImageRect(img, src, CanvasKit.XYWHRect(0, 0, 256, 256), paint); 855 canvas.drawImageRectCubic(img, src, CanvasKit.XYWHRect(256, 0, 256, 256), 1/3, 1/3); 856 canvas.drawImageRectOptions(img, src, CanvasKit.XYWHRect(0, 256, 256, 256), 857 CanvasKit.FilterMode.Linear, CanvasKit.MipmapMode.None); 858 const mipImg = img.makeCopyWithDefaultMipmaps(); 859 canvas.drawImageRectOptions(mipImg, src, CanvasKit.XYWHRect(256, 256, 256, 256), 860 CanvasKit.FilterMode.Nearest, CanvasKit.MipmapMode.Nearest); 861 862 paint.delete(); 863 mipImg.delete(); 864 img.delete(); 865 }, '/assets/mandrill_512.png'); 866 867 gm('drawImage_skp', (canvas, fetchedByteBuffers) => { 868 const pic = CanvasKit.MakePicture(fetchedByteBuffers[0]); 869 canvas.clear(CanvasKit.TRANSPARENT); 870 canvas.drawPicture(pic); 871 // The asset below can be re-downloaded from 872 // https://fiddle.skia.org/c/cbb8dee39e9f1576cd97c2d504db8eee 873 }, '/assets/red_line.skp'); 874 875 it('can draw once using drawOnce utility method', (done) => { 876 const surface = CanvasKit.MakeCanvasSurface('test'); 877 expect(surface).toBeTruthy('Could not make surface'); 878 if (!surface) { 879 done(); 880 return; 881 } 882 883 const drawFrame = (canvas) => { 884 const paint = new CanvasKit.Paint(); 885 paint.setStrokeWidth(1.0); 886 paint.setAntiAlias(true); 887 paint.setColor(CanvasKit.Color(0, 0, 0, 1.0)); 888 paint.setStyle(CanvasKit.PaintStyle.Stroke); 889 const path = new CanvasKit.Path(); 890 path.moveTo(20, 5); 891 path.lineTo(30, 20); 892 path.lineTo(40, 10); 893 canvas.drawPath(path, paint); 894 path.delete(); 895 paint.delete(); 896 // surface hasn't been flushed yet (nor should we call flush 897 // ourselves), so reportSurface would likely be blank if we 898 // were to call it. 899 done(); 900 }; 901 surface.drawOnce(drawFrame); 902 // Reminder: drawOnce is async. In this test, we are just making 903 // sure the drawOnce function is there and doesn't crash, so we can 904 // just call done() when the frame is rendered. 905 }); 906 907 it('can draw client-supplied dirty rects', (done) => { 908 // dirty rects are only honored by software (CPU) canvases today. 909 const surface = CanvasKit.MakeSWCanvasSurface('test'); 910 expect(surface).toBeTruthy('Could not make surface'); 911 if (!surface) { 912 done(); 913 return; 914 } 915 916 const drawFrame = (canvas) => { 917 const paint = new CanvasKit.Paint(); 918 paint.setStrokeWidth(1.0); 919 paint.setAntiAlias(true); 920 paint.setColor(CanvasKit.Color(0, 0, 0, 1.0)); 921 paint.setStyle(CanvasKit.PaintStyle.Stroke); 922 const path = new CanvasKit.Path(); 923 path.moveTo(20, 5); 924 path.lineTo(30, 20); 925 path.lineTo(40, 10); 926 canvas.drawPath(path, paint); 927 path.delete(); 928 paint.delete(); 929 done(); 930 }; 931 const dirtyRect = CanvasKit.XYWHRect(10, 10, 15, 15); 932 surface.drawOnce(drawFrame, dirtyRect); 933 // We simply ensure that passing a dirty rect doesn't crash. 934 }); 935 936 it('can use DecodeCache APIs', () => { 937 const initialLimit = CanvasKit.getDecodeCacheLimitBytes(); 938 expect(initialLimit).toBeGreaterThan(1024 * 1024); 939 940 const newLimit = 42 * 1024 * 1024; 941 CanvasKit.setDecodeCacheLimitBytes(newLimit); 942 expect(CanvasKit.getDecodeCacheLimitBytes()).toEqual(newLimit); 943 944 // We cannot make any assumptions about this value, 945 // so we just make sure it doesn't crash. 946 CanvasKit.getDecodeCacheUsedBytes(); 947 }); 948 949 gm('combined_shaders', (canvas) => { 950 const rShader = CanvasKit.Shader.Color(CanvasKit.Color(255, 0, 0, 1.0)); // deprecated 951 const gShader = CanvasKit.Shader.MakeColor(CanvasKit.Color(0, 255, 0, 0.6)); 952 953 const rgShader = CanvasKit.Shader.MakeBlend(CanvasKit.BlendMode.SrcOver, rShader, gShader); 954 955 const p = new CanvasKit.Paint(); 956 p.setShader(rgShader); 957 canvas.drawPaint(p); 958 959 rShader.delete(); 960 gShader.delete(); 961 rgShader.delete(); 962 p.delete(); 963 }); 964 965 it('exports consts correctly', () => { 966 expect(CanvasKit.TRANSPARENT).toEqual(Float32Array.of(0, 0, 0, 0)); 967 expect(CanvasKit.RED).toEqual(Float32Array.of(1, 0, 0, 1)); 968 969 expect(CanvasKit.QUAD_VERB).toEqual(2); 970 expect(CanvasKit.CONIC_VERB).toEqual(3); 971 972 expect(CanvasKit.SaveLayerInitWithPrevious).toEqual(4); 973 expect(CanvasKit.SaveLayerF16ColorType).toEqual(16); 974 }); 975 976 it('can set color on a paint and get it as four floats', () => { 977 const paint = new CanvasKit.Paint(); 978 paint.setColor(CanvasKit.Color4f(3.3, 2.2, 1.1, 0.5)); 979 expect(paint.getColor()).toEqual(Float32Array.of(3.3, 2.2, 1.1, 0.5)); 980 981 paint.setColorComponents(0.5, 0.6, 0.7, 0.8); 982 expect(paint.getColor()).toEqual(Float32Array.of(0.5, 0.6, 0.7, 0.8)); 983 984 paint.setColorInt(CanvasKit.ColorAsInt(50, 100, 150, 200)); 985 let color = paint.getColor(); 986 expect(color.length).toEqual(4); 987 expect(color[0]).toBeCloseTo(50/255, 5); // Red 988 expect(color[1]).toBeCloseTo(100/255, 5); // Green 989 expect(color[2]).toBeCloseTo(150/255, 5); // Blue 990 expect(color[3]).toBeCloseTo(200/255, 5); // Alpha 991 992 paint.setColorInt(0xFF000000); 993 expect(paint.getColor()).toEqual(Float32Array.of(0, 0, 0, 1.0)); 994 }); 995 996 gm('draw shadow', (canvas) => { 997 const lightRadius = 20; 998 const lightPos = [500,500,20]; 999 const zPlaneParams = [0,0,1]; 1000 const path = starPath(CanvasKit); 1001 const textFont = new CanvasKit.Font(null, 24); 1002 const textPaint = new CanvasKit.Paint(); 1003 1004 canvas.drawShadow(path, zPlaneParams, lightPos, lightRadius, 1005 CanvasKit.BLACK, CanvasKit.MAGENTA, 0); 1006 canvas.drawText('Default Flags', 5, 250, textPaint, textFont); 1007 1008 let bounds = CanvasKit.getShadowLocalBounds(CanvasKit.Matrix.identity(), 1009 path, zPlaneParams, lightPos, lightRadius, 0); 1010 expectTypedArraysToEqual(bounds, Float32Array.of(-3.64462, -12.67541, 245.50, 242.59164)); 1011 1012 bounds = CanvasKit.getShadowLocalBounds(CanvasKit.M44.identity(), 1013 path, zPlaneParams, lightPos, lightRadius, 0); 1014 expectTypedArraysToEqual(bounds, Float32Array.of(-3.64462, -12.67541, 245.50, 242.59164)); 1015 1016 // Test that the APIs accept Malloc objs and the Malloced typearray 1017 const mZPlane = CanvasKit.Malloc(Float32Array, 3); 1018 mZPlane.toTypedArray().set(zPlaneParams); 1019 const mLight = CanvasKit.Malloc(Float32Array, 3); 1020 const lightTA = mLight.toTypedArray(); 1021 lightTA.set(lightPos); 1022 1023 canvas.translate(250, 250); 1024 canvas.drawShadow(path, mZPlane, lightTA, lightRadius, 1025 CanvasKit.BLACK, CanvasKit.MAGENTA, 1026 CanvasKit.ShadowTransparentOccluder | CanvasKit.ShadowGeometricOnly | CanvasKit.ShadowDirectionalLight); 1027 canvas.drawText('All Flags', 5, 250, textPaint, textFont); 1028 1029 const outBounds = new Float32Array(4); 1030 CanvasKit.getShadowLocalBounds(CanvasKit.Matrix.rotated(Math.PI / 6), 1031 path, mZPlane, mLight, lightRadius, 1032 CanvasKit.ShadowTransparentOccluder | CanvasKit.ShadowGeometricOnly | CanvasKit.ShadowDirectionalLight, 1033 outBounds); 1034 expectTypedArraysToEqual(outBounds, Float32Array.of(-31.6630249, -15.24227, 245.5, 252.94101)); 1035 1036 CanvasKit.Free(mZPlane); 1037 CanvasKit.Free(mLight); 1038 1039 path.delete(); 1040 textFont.delete(); 1041 textPaint.delete(); 1042 }); 1043 1044 gm('fractal_noise_shader', (canvas) => { 1045 const shader = CanvasKit.Shader.MakeFractalNoise(0.1, 0.05, 2, 0, 0, 0); 1046 const paint = new CanvasKit.Paint(); 1047 paint.setColor(CanvasKit.BLACK); 1048 paint.setShader(shader); 1049 canvas.drawPaint(paint); 1050 paint.delete(); 1051 shader.delete(); 1052 }); 1053 1054 gm('turbulance_shader', (canvas) => { 1055 const shader = CanvasKit.Shader.MakeTurbulence(0.1, 0.05, 2, 117, 0, 0); 1056 const paint = new CanvasKit.Paint(); 1057 paint.setColor(CanvasKit.BLACK); 1058 paint.setShader(shader); 1059 canvas.drawPaint(paint); 1060 paint.delete(); 1061 shader.delete(); 1062 }); 1063 1064 gm('fractal_noise_tiled_shader', (canvas) => { 1065 const shader = CanvasKit.Shader.MakeFractalNoise(0.1, 0.05, 2, 0, 80, 80); 1066 const paint = new CanvasKit.Paint(); 1067 paint.setColor(CanvasKit.BLACK); 1068 paint.setShader(shader); 1069 canvas.drawPaint(paint); 1070 paint.delete(); 1071 shader.delete(); 1072 }); 1073 1074 gm('turbulance_tiled_shader', (canvas) => { 1075 const shader = CanvasKit.Shader.MakeTurbulence(0.1, 0.05, 2, 117, 80, 80); 1076 const paint = new CanvasKit.Paint(); 1077 paint.setColor(CanvasKit.BLACK); 1078 paint.setShader(shader); 1079 canvas.drawPaint(paint); 1080 paint.delete(); 1081 shader.delete(); 1082 }); 1083 1084 describe('ColorSpace Support', () => { 1085 it('Can create an SRGB 8888 surface', () => { 1086 const colorSpace = CanvasKit.ColorSpace.SRGB; 1087 const surface = CanvasKit.MakeCanvasSurface('test', CanvasKit.ColorSpace.SRGB); 1088 expect(surface).toBeTruthy('Could not make surface'); 1089 let info = surface.imageInfo(); 1090 expect(info.alphaType).toEqual(CanvasKit.AlphaType.Unpremul); 1091 expect(info.colorType).toEqual(CanvasKit.ColorType.RGBA_8888); 1092 expect(CanvasKit.ColorSpace.Equals(info.colorSpace, colorSpace)) 1093 .toBeTruthy("Surface not created with correct color space."); 1094 1095 const mObj = CanvasKit.Malloc(Uint8Array, CANVAS_WIDTH * CANVAS_HEIGHT * 4); 1096 mObj.toTypedArray()[0] = 127; // sentinel value. Should be overwritten by readPixels. 1097 const canvas = surface.getCanvas(); 1098 canvas.clear(CanvasKit.TRANSPARENT); 1099 const pixels = canvas.readPixels(0, 0, { 1100 width: CANVAS_WIDTH, 1101 height: CANVAS_HEIGHT, 1102 colorType: CanvasKit.ColorType.RGBA_8888, 1103 alphaType: CanvasKit.AlphaType.Unpremul, 1104 colorSpace: colorSpace 1105 }, mObj, 4 * CANVAS_WIDTH); 1106 expect(pixels).toBeTruthy('Could not read pixels from surface'); 1107 expect(pixels[0] !== 127).toBeTruthy(); 1108 expect(pixels[0]).toEqual(mObj.toTypedArray()[0]); 1109 CanvasKit.Free(mObj); 1110 surface.delete(); 1111 }); 1112 it('Can create a Display P3 surface', () => { 1113 const colorSpace = CanvasKit.ColorSpace.DISPLAY_P3; 1114 const surface = CanvasKit.MakeCanvasSurface('test', CanvasKit.ColorSpace.DISPLAY_P3); 1115 expect(surface).toBeTruthy('Could not make surface'); 1116 if (!surface.reportBackendTypeIsGPU()) { 1117 console.log('Not expecting color space support in cpu backed suface.'); 1118 return; 1119 } 1120 let info = surface.imageInfo(); 1121 expect(info.alphaType).toEqual(CanvasKit.AlphaType.Unpremul); 1122 expect(info.colorType).toEqual(CanvasKit.ColorType.RGBA_F16); 1123 expect(CanvasKit.ColorSpace.Equals(info.colorSpace, colorSpace)) 1124 .toBeTruthy("Surface not created with correct color space."); 1125 1126 const pixels = surface.getCanvas().readPixels(0, 0, { 1127 width: CANVAS_WIDTH, 1128 height: CANVAS_HEIGHT, 1129 colorType: CanvasKit.ColorType.RGBA_F16, 1130 alphaType: CanvasKit.AlphaType.Unpremul, 1131 colorSpace: colorSpace 1132 }); 1133 expect(pixels).toBeTruthy('Could not read pixels from surface'); 1134 }); 1135 it('Can create an Adobe RGB surface', () => { 1136 const colorSpace = CanvasKit.ColorSpace.ADOBE_RGB; 1137 const surface = CanvasKit.MakeCanvasSurface('test', CanvasKit.ColorSpace.ADOBE_RGB); 1138 expect(surface).toBeTruthy('Could not make surface'); 1139 if (!surface.reportBackendTypeIsGPU()) { 1140 console.log('Not expecting color space support in cpu backed surface.'); 1141 return; 1142 } 1143 let info = surface.imageInfo(); 1144 expect(info.alphaType).toEqual(CanvasKit.AlphaType.Unpremul); 1145 expect(info.colorType).toEqual(CanvasKit.ColorType.RGBA_F16); 1146 expect(CanvasKit.ColorSpace.Equals(info.colorSpace, colorSpace)) 1147 .toBeTruthy("Surface not created with correct color space."); 1148 1149 const pixels = surface.getCanvas().readPixels(0, 0, { 1150 width: CANVAS_WIDTH, 1151 height: CANVAS_HEIGHT, 1152 colorType: CanvasKit.ColorType.RGBA_F16, 1153 alphaType: CanvasKit.AlphaType.Unpremul, 1154 colorSpace: colorSpace 1155 }); 1156 expect(pixels).toBeTruthy('Could not read pixels from surface'); 1157 }); 1158 1159 it('combine draws from several color spaces', () => { 1160 const surface = CanvasKit.MakeCanvasSurface('test', CanvasKit.ColorSpace.ADOBE_RGB); 1161 expect(surface).toBeTruthy('Could not make surface'); 1162 if (!surface.reportBackendTypeIsGPU()) { 1163 console.log('Not expecting color space support in cpu backed suface.'); 1164 return; 1165 } 1166 const canvas = surface.getCanvas(); 1167 1168 let paint = new CanvasKit.Paint(); 1169 paint.setColor(CanvasKit.RED, CanvasKit.ColorSpace.ADOBE_RGB); 1170 canvas.drawPaint(paint); 1171 paint.setColor(CanvasKit.RED, CanvasKit.ColorSpace.DISPLAY_P3); // 93.7 in adobeRGB 1172 canvas.drawRect(CanvasKit.LTRBRect(200, 0, 400, 600), paint); 1173 paint.setColor(CanvasKit.RED, CanvasKit.ColorSpace.SRGB); // 85.9 in adobeRGB 1174 canvas.drawRect(CanvasKit.LTRBRect(400, 0, 600, 600), paint); 1175 1176 // this test paints three bands of red, each the maximum red that a color space supports. 1177 // They are each represented by skia by some color in the Adobe RGB space of the surface, 1178 // as floats between 0 and 1. 1179 1180 // TODO(nifong) readpixels and verify correctness after f32 readpixels bug is fixed 1181 }); 1182 }); // end describe('ColorSpace Support') 1183 1184 describe('DOMMatrix support', () => { 1185 gm('sweep_gradient_dommatrix', (canvas) => { 1186 const paint = new CanvasKit.Paint(); 1187 const shader = CanvasKit.Shader.MakeSweepGradient( 1188 100, 100, // x y coordinates 1189 [CanvasKit.GREEN, CanvasKit.BLUE], 1190 [0.0, 1.0], 1191 CanvasKit.TileMode.Clamp, 1192 new DOMMatrix().translate(-10, 100), 1193 ); 1194 expect(shader).toBeTruthy('Could not make shader'); 1195 if (!shader) { 1196 return; 1197 } 1198 1199 paint.setShader(shader); 1200 canvas.drawPaint(paint); 1201 1202 paint.delete(); 1203 shader.delete(); 1204 }); 1205 1206 const radiansToDegrees = (rad) => { 1207 return (rad / Math.PI) * 180; 1208 }; 1209 1210 // this should draw the same as concat_with4x4_canvas 1211 gm('concat_dommatrix', (canvas) => { 1212 const path = starPath(CanvasKit, CANVAS_WIDTH/2, CANVAS_HEIGHT/2); 1213 const paint = new CanvasKit.Paint(); 1214 paint.setAntiAlias(true); 1215 canvas.clear(CanvasKit.WHITE); 1216 canvas.concat(new DOMMatrix().translate(CANVAS_WIDTH/2, 0, 0)); 1217 canvas.concat(new DOMMatrix().rotateAxisAngle(1, 0, 0, radiansToDegrees(Math.PI/3))); 1218 canvas.concat(new DOMMatrix().rotateAxisAngle(0, 1, 0, radiansToDegrees(Math.PI/4))); 1219 canvas.concat(new DOMMatrix().rotateAxisAngle(0, 0, 1, radiansToDegrees(Math.PI/16))); 1220 canvas.concat(new DOMMatrix().translate(-CANVAS_WIDTH/2, 0, 0)); 1221 1222 const localMatrix = canvas.getLocalToDevice(); 1223 expect4x4MatricesToMatch([ 1224 0.693519, -0.137949, 0.707106, 91.944030, 1225 0.698150, 0.370924, -0.612372, -209.445297, 1226 -0.177806, 0.918359, 0.353553, 53.342029, 1227 0 , 0 , 0 , 1 ], localMatrix); 1228 1229 // Draw some stripes to help the eye detect the turn 1230 const stripeWidth = 10; 1231 paint.setColor(CanvasKit.BLACK); 1232 for (let i = 0; i < CANVAS_WIDTH; i += 2*stripeWidth) { 1233 canvas.drawRect(CanvasKit.LTRBRect(i, 0, i + stripeWidth, CANVAS_HEIGHT), paint); 1234 } 1235 1236 paint.setColor(CanvasKit.YELLOW); 1237 canvas.drawPath(path, paint); 1238 paint.delete(); 1239 path.delete(); 1240 }); 1241 }); // end describe('DOMMatrix support') 1242 1243 it('can call subarray on a Malloced object', () => { 1244 const mThings = CanvasKit.Malloc(Float32Array, 6); 1245 mThings.toTypedArray().set([4, 5, 6, 7, 8, 9]); 1246 expectTypedArraysToEqual(Float32Array.of(4, 5, 6, 7, 8, 9), mThings.toTypedArray()); 1247 expectTypedArraysToEqual(Float32Array.of(4, 5, 6, 7, 8, 9), mThings.subarray(0)); 1248 expectTypedArraysToEqual(Float32Array.of(7, 8, 9), mThings.subarray(3)); 1249 expectTypedArraysToEqual(Float32Array.of(7), mThings.subarray(3, 4)); 1250 expectTypedArraysToEqual(Float32Array.of(7, 8), mThings.subarray(3, 5)); 1251 1252 // mutations on the subarray affect the entire array (because they are backed by the 1253 // same memory) 1254 mThings.subarray(3)[0] = 100.5; 1255 expectTypedArraysToEqual(Float32Array.of(4, 5, 6, 100.5, 8, 9), mThings.toTypedArray()); 1256 CanvasKit.Free(mThings); 1257 }); 1258 1259 function expectTypedArraysToEqual(expected, actual) { 1260 expect(expected.constructor.name).toEqual(actual.constructor.name); 1261 expect(expected.length).toEqual(actual.length); 1262 for (let i = 0; i < expected.length; i++) { 1263 expect(expected[i]).toBeCloseTo(actual[i], 5, `element ${i}`); 1264 } 1265 } 1266 1267 it('can create a RasterDirectSurface', () => { 1268 // Make enough space for a 5x5 8888 surface (4 bytes for R, G, B, A) 1269 const rdsData = CanvasKit.Malloc(Uint8Array, 5 * 5 * 4); 1270 const surface = CanvasKit.MakeRasterDirectSurface({ 1271 'width': 5, 1272 'height': 5, 1273 'colorType': CanvasKit.ColorType.RGBA_8888, 1274 'alphaType': CanvasKit.AlphaType.Premul, 1275 'colorSpace': CanvasKit.ColorSpace.SRGB, 1276 }, rdsData, 5 * 4); 1277 1278 surface.getCanvas().clear(CanvasKit.Color(200, 100, 0, 0.8)); 1279 const pixels = rdsData.toTypedArray(); 1280 // Check that the first pixels colors are right. 1281 expect(pixels[0]).toEqual(160); // red (premul, 0.8 * 200) 1282 expect(pixels[1]).toEqual(80); // green (premul, 0.8 * 100) 1283 expect(pixels[2]).toEqual(0); // blue (premul, not that it matters) 1284 expect(pixels[3]).toEqual(204); // alpha (0.8 * 255) 1285 surface.delete(); 1286 CanvasKit.Free(rdsData); 1287 }); 1288 1289 gm('makeImageFromTextureSource_TypedArray', (canvas, _, surface) => { 1290 if (!CanvasKit.gpu) { 1291 return; 1292 } 1293 // This creates and draws an Unpremul Image that is 1 pixel wide, 4 pixels tall with 1294 // the colors listed below. 1295 const pixels = Uint8Array.from([ 1296 255, 0, 0, 255, // opaque red 1297 0, 255, 0, 255, // opaque green 1298 0, 0, 255, 255, // opaque blue 1299 255, 0, 255, 100, // transparent purple 1300 ]); 1301 const img = surface.makeImageFromTextureSource(pixels, { 1302 'width': 1, 1303 'height': 4, 1304 'alphaType': CanvasKit.AlphaType.Unpremul, 1305 'colorType': CanvasKit.ColorType.RGBA_8888, 1306 }); 1307 canvas.drawImage(img, 1, 1, null); 1308 1309 const info = img.getImageInfo(); 1310 expect(info).toEqual({ 1311 'width': 1, 1312 'height': 4, 1313 'alphaType': CanvasKit.AlphaType.Unpremul, 1314 'colorType': CanvasKit.ColorType.RGBA_8888, 1315 }); 1316 const cs = img.getColorSpace(); 1317 expect(CanvasKit.ColorSpace.Equals(cs, CanvasKit.ColorSpace.SRGB)).toBeTruthy(); 1318 1319 cs.delete(); 1320 img.delete(); 1321 }); 1322 1323 gm('makeImageFromTextureSource_PremulTypedArray', (canvas, _, surface) => { 1324 if (!CanvasKit.gpu) { 1325 return; 1326 } 1327 // This creates and draws an Unpremul Image that is 1 pixel wide, 4 pixels tall with 1328 // the colors listed below. 1329 const pixels = Uint8Array.from([ 1330 255, 0, 0, 255, // opaque red 1331 0, 255, 0, 255, // opaque green 1332 0, 0, 255, 255, // opaque blue 1333 100, 0, 100, 100, // transparent purple 1334 ]); 1335 const img = surface.makeImageFromTextureSource(pixels, { 1336 'width': 1, 1337 'height': 4, 1338 'alphaType': CanvasKit.AlphaType.Premul, 1339 'colorType': CanvasKit.ColorType.RGBA_8888, 1340 }); 1341 canvas.drawImage(img, 1, 1, null); 1342 1343 const info = img.getImageInfo(); 1344 expect(info).toEqual({ 1345 'width': 1, 1346 'height': 4, 1347 'alphaType': CanvasKit.AlphaType.Premul, 1348 'colorType': CanvasKit.ColorType.RGBA_8888, 1349 }); 1350 img.delete(); 1351 }); 1352 1353 gm('makeImageFromTextureSource_imgElement', (canvas, _, surface) => { 1354 if (!CanvasKit.gpu) { 1355 return; 1356 } 1357 // This makes an offscreen <img> with the provided source. 1358 const imageEle = new Image(); 1359 imageEle.src = '/assets/mandrill_512.png'; 1360 1361 // We need to wait until the image is loaded before the texture can use it. For good 1362 // measure, we also wait for it to be decoded. 1363 return imageEle.decode().then(() => { 1364 const img = surface.makeImageFromTextureSource(imageEle); 1365 canvas.drawImage(img, 0, 0, null); 1366 1367 const info = img.getImageInfo(); 1368 expect(info).toEqual({ 1369 'width': 512, // width and height should be derived from the image. 1370 'height': 512, 1371 'alphaType': CanvasKit.AlphaType.Unpremul, 1372 'colorType': CanvasKit.ColorType.RGBA_8888, 1373 }); 1374 img.delete(); 1375 }); 1376 }); 1377 1378 gm('MakeLazyImageFromTextureSource_imgElement', (canvas) => { 1379 if (!CanvasKit.gpu) { 1380 return; 1381 } 1382 // This makes an offscreen <img> with the provided source. 1383 const imageEle = new Image(); 1384 imageEle.src = '/assets/mandrill_512.png'; 1385 1386 // We need to wait until the image is loaded before the texture can use it. For good 1387 // measure, we also wait for it to be decoded. 1388 return imageEle.decode().then(() => { 1389 const img = CanvasKit.MakeLazyImageFromTextureSource(imageEle); 1390 canvas.drawImage(img, 5, 5, null); 1391 1392 const info = img.getImageInfo(); 1393 expect(info).toEqual({ 1394 'width': 512, // width and height should be derived from the image. 1395 'height': 512, 1396 'alphaType': CanvasKit.AlphaType.Unpremul, 1397 'colorType': CanvasKit.ColorType.RGBA_8888, 1398 }); 1399 img.delete(); 1400 }); 1401 }); 1402 1403 gm('MakeLazyImageFromTextureSource_imageInfo', (canvas) => { 1404 if (!CanvasKit.gpu) { 1405 return; 1406 } 1407 // This makes an offscreen <img> with the provided source. 1408 const imageEle = new Image(); 1409 imageEle.src = '/assets/mandrill_512.png'; 1410 1411 // We need to wait until the image is loaded before the texture can use it. For good 1412 // measure, we also wait for it to be decoded. 1413 return imageEle.decode().then(() => { 1414 const img = CanvasKit.MakeLazyImageFromTextureSource(imageEle, { 1415 'width': 400, 1416 'height': 400, 1417 'alphaType': CanvasKit.AlphaType.Premul, 1418 'colorType': CanvasKit.ColorType.RGBA_8888, 1419 }); 1420 canvas.drawImage(img, 20, 20, null); 1421 1422 img.delete(); 1423 }); 1424 }); 1425}); 1426