1// Adds JS functions to augment the CanvasKit interface. 2// For example, if there is a wrapper around the C++ call or logic to allow 3// chaining, it should go here. 4 5// CanvasKit.onRuntimeInitialized is called after the WASM library has loaded. 6// Anything that modifies an exposed class (e.g. SkPath) should be set 7// after onRuntimeInitialized, otherwise, it can happen outside of that scope. 8CanvasKit.onRuntimeInitialized = function() { 9 // All calls to 'this' need to go in externs.js so closure doesn't minify them away. 10 11 // Add some helpers for matrices. This is ported from SkMatrix.cpp 12 // to save complexity and overhead of going back and forth between 13 // C++ and JS layers. 14 // I would have liked to use something like DOMMatrix, except it 15 // isn't widely supported (would need polyfills) and it doesn't 16 // have a mapPoints() function (which could maybe be tacked on here). 17 // If DOMMatrix catches on, it would be worth re-considering this usage. 18 CanvasKit.SkMatrix = {}; 19 function sdot(a, b, c, d, e, f) { 20 e = e || 0; 21 f = f || 0; 22 return a * b + c * d + e * f; 23 } 24 25 CanvasKit.SkMatrix.identity = function() { 26 return [ 27 1, 0, 0, 28 0, 1, 0, 29 0, 0, 1, 30 ]; 31 }; 32 33 // Return the inverse (if it exists) of this matrix. 34 // Otherwise, return the identity. 35 CanvasKit.SkMatrix.invert = function(m) { 36 var det = m[0]*m[4]*m[8] + m[1]*m[5]*m[6] + m[2]*m[3]*m[7] 37 - m[2]*m[4]*m[6] - m[1]*m[3]*m[8] - m[0]*m[5]*m[7]; 38 if (!det) { 39 SkDebug('Warning, uninvertible matrix'); 40 return CanvasKit.SkMatrix.identity(); 41 } 42 return [ 43 (m[4]*m[8] - m[5]*m[7])/det, (m[2]*m[7] - m[1]*m[8])/det, (m[1]*m[5] - m[2]*m[4])/det, 44 (m[5]*m[6] - m[3]*m[8])/det, (m[0]*m[8] - m[2]*m[6])/det, (m[2]*m[3] - m[0]*m[5])/det, 45 (m[3]*m[7] - m[4]*m[6])/det, (m[1]*m[6] - m[0]*m[7])/det, (m[0]*m[4] - m[1]*m[3])/det, 46 ]; 47 }; 48 49 // Maps the given points according to the passed in matrix. 50 // Results are done in place. 51 // See SkMatrix.h::mapPoints for the docs on the math. 52 CanvasKit.SkMatrix.mapPoints = function(matrix, ptArr) { 53 if (ptArr.length % 2) { 54 throw 'mapPoints requires an even length arr'; 55 } 56 for (var i = 0; i < ptArr.length; i+=2) { 57 var x = ptArr[i], y = ptArr[i+1]; 58 // Gx+Hy+I 59 var denom = matrix[6]*x + matrix[7]*y + matrix[8]; 60 // Ax+By+C 61 var xTrans = matrix[0]*x + matrix[1]*y + matrix[2]; 62 // Dx+Ey+F 63 var yTrans = matrix[3]*x + matrix[4]*y + matrix[5]; 64 ptArr[i] = xTrans/denom; 65 ptArr[i+1] = yTrans/denom; 66 } 67 return ptArr; 68 }; 69 70 CanvasKit.SkMatrix.multiply = function(m1, m2) { 71 var result = [0,0,0, 0,0,0, 0,0,0]; 72 for (var r = 0; r < 3; r++) { 73 for (var c = 0; c < 3; c++) { 74 // m1 and m2 are 1D arrays pretending to be 2D arrays 75 result[3*r + c] = sdot(m1[3*r + 0], m2[3*0 + c], 76 m1[3*r + 1], m2[3*1 + c], 77 m1[3*r + 2], m2[3*2 + c]); 78 } 79 } 80 return result; 81 } 82 83 // Return a matrix representing a rotation by n radians. 84 // px, py optionally say which point the rotation should be around 85 // with the default being (0, 0); 86 CanvasKit.SkMatrix.rotated = function(radians, px, py) { 87 px = px || 0; 88 py = py || 0; 89 var sinV = Math.sin(radians); 90 var cosV = Math.cos(radians); 91 return [ 92 cosV, -sinV, sdot( sinV, py, 1 - cosV, px), 93 sinV, cosV, sdot(-sinV, px, 1 - cosV, py), 94 0, 0, 1, 95 ]; 96 }; 97 98 CanvasKit.SkMatrix.scaled = function(sx, sy, px, py) { 99 px = px || 0; 100 py = py || 0; 101 return [ 102 sx, 0, px - sx * px, 103 0, sy, py - sy * py, 104 0, 0, 1, 105 ]; 106 }; 107 108 CanvasKit.SkMatrix.skewed = function(kx, ky, px, py) { 109 px = px || 0; 110 py = py || 0; 111 return [ 112 1, kx, -kx * px, 113 ky, 1, -ky * py, 114 0, 0, 1, 115 ]; 116 }; 117 118 CanvasKit.SkMatrix.translated = function(dx, dy) { 119 return [ 120 1, 0, dx, 121 0, 1, dy, 122 0, 0, 1, 123 ]; 124 }; 125 126 CanvasKit.SkPath.prototype.addArc = function(oval, startAngle, sweepAngle) { 127 // see arc() for the HTMLCanvas version 128 // note input angles are degrees. 129 this._addArc(oval, startAngle, sweepAngle); 130 return this; 131 }; 132 133 CanvasKit.SkPath.prototype.addPath = function() { 134 // Takes 1, 2, 7, or 10 required args, where the first arg is always the path. 135 // The last arg is optional and chooses between add or extend mode. 136 // The options for the remaining args are: 137 // - an array of 6 or 9 parameters (perspective is optional) 138 // - the 9 parameters of a full matrix or 139 // the 6 non-perspective params of a matrix. 140 var args = Array.prototype.slice.call(arguments); 141 var path = args[0]; 142 var extend = false; 143 if (typeof args[args.length-1] === "boolean") { 144 extend = args.pop(); 145 } 146 if (args.length === 1) { 147 // Add path, unchanged. Use identity matrix 148 this._addPath(path, 1, 0, 0, 149 0, 1, 0, 150 0, 0, 1, 151 extend); 152 } else if (args.length === 2) { 153 // User provided the 9 params of a full matrix as an array. 154 var a = args[1]; 155 this._addPath(path, a[0], a[1], a[2], 156 a[3], a[4], a[5], 157 a[6] || 0, a[7] || 0, a[8] || 1, 158 extend); 159 } else if (args.length === 7 || args.length === 10) { 160 // User provided the 9 params of a (full) matrix directly. 161 // (or just the 6 non perspective ones) 162 // These are in the same order as what Skia expects. 163 var a = args; 164 this._addPath(path, a[1], a[2], a[3], 165 a[4], a[5], a[6], 166 a[7] || 0, a[8] || 0, a[9] || 1, 167 extend); 168 } else { 169 SkDebug('addPath expected to take 1, 2, 7, or 10 required args. Got ' + args.length); 170 return null; 171 } 172 return this; 173 }; 174 175 CanvasKit.SkPath.prototype.addRect = function() { 176 // Takes 1, 2, 4 or 5 args 177 // - SkRect 178 // - SkRect, isCCW 179 // - left, top, right, bottom 180 // - left, top, right, bottom, isCCW 181 if (arguments.length === 1 || arguments.length === 2) { 182 var r = arguments[0]; 183 var ccw = arguments[1] || false; 184 this._addRect(r.fLeft, r.fTop, r.fRight, r.fBottom, ccw); 185 } else if (arguments.length === 4 || arguments.length === 5) { 186 var a = arguments; 187 this._addRect(a[0], a[1], a[2], a[3], a[4] || false); 188 } else { 189 SkDebug('addRect expected to take 1, 2, 4, or 5 args. Got ' + arguments.length); 190 return null; 191 } 192 return this; 193 }; 194 195 CanvasKit.SkPath.prototype.addRoundRect = function() { 196 // Takes 3, 4, 6 or 7 args 197 // - SkRect, radii, ccw 198 // - SkRect, rx, ry, ccw 199 // - left, top, right, bottom, radii, ccw 200 // - left, top, right, bottom, rx, ry, ccw 201 var args = arguments; 202 if (args.length === 3 || args.length === 6) { 203 var radii = args[args.length-2]; 204 } else if (args.length === 6 || args.length === 7){ 205 // duplicate the given (rx, ry) pairs for each corner. 206 var rx = args[args.length-3]; 207 var ry = args[args.length-2]; 208 var radii = [rx, ry, rx, ry, rx, ry, rx, ry]; 209 } else { 210 SkDebug('addRoundRect expected to take 3, 4, 6, or 7 args. Got ' + args.length); 211 return null; 212 } 213 if (radii.length !== 8) { 214 SkDebug('addRoundRect needs 8 radii provided. Got ' + radii.length); 215 return null; 216 } 217 var rptr = copy1dArray(radii, CanvasKit.HEAPF32); 218 if (args.length === 3 || args.length === 4) { 219 var r = args[0]; 220 var ccw = args[args.length - 1]; 221 this._addRoundRect(r.fLeft, r.fTop, r.fRight, r.fBottom, rptr, ccw); 222 } else if (args.length === 6 || args.length === 7) { 223 var a = args; 224 this._addRoundRect(a[0], a[1], a[2], a[3], rptr, ccw); 225 } 226 CanvasKit._free(rptr); 227 return this; 228 }; 229 230 CanvasKit.SkPath.prototype.arc = function(x, y, radius, startAngle, endAngle, ccw) { 231 // emulates the HTMLCanvas behavior. See addArc() for the SkPath version. 232 // Note input angles are radians. 233 var bounds = CanvasKit.LTRBRect(x-radius, y-radius, x+radius, y+radius); 234 var sweep = radiansToDegrees(endAngle - startAngle) - (360 * !!ccw); 235 var temp = new CanvasKit.SkPath(); 236 temp.addArc(bounds, radiansToDegrees(startAngle), sweep); 237 this.addPath(temp, true); 238 temp.delete(); 239 return this; 240 }; 241 242 CanvasKit.SkPath.prototype.arcTo = function() { 243 // takes 4, 5 or 7 args 244 // - 5 x1, y1, x2, y2, radius 245 // - 4 oval (as Rect), startAngle, sweepAngle, forceMoveTo 246 // - 7 x1, y1, x2, y2, startAngle, sweepAngle, forceMoveTo 247 var args = arguments; 248 if (args.length === 5) { 249 this._arcTo(args[0], args[1], args[2], args[3], args[4]); 250 } else if (args.length === 4) { 251 this._arcTo(args[0], args[1], args[2], args[3]); 252 } else if (args.length === 7) { 253 this._arcTo(CanvasKit.LTRBRect(args[0], args[1], args[2], args[3]), 254 args[4], args[5], args[6]); 255 } else { 256 throw 'Invalid args for arcTo. Expected 4, 5, or 7, got '+ args.length; 257 } 258 259 return this; 260 }; 261 262 CanvasKit.SkPath.prototype.close = function() { 263 this._close(); 264 return this; 265 }; 266 267 CanvasKit.SkPath.prototype.conicTo = function(x1, y1, x2, y2, w) { 268 this._conicTo(x1, y1, x2, y2, w); 269 return this; 270 }; 271 272 CanvasKit.SkPath.prototype.cubicTo = function(cp1x, cp1y, cp2x, cp2y, x, y) { 273 this._cubicTo(cp1x, cp1y, cp2x, cp2y, x, y); 274 return this; 275 }; 276 277 CanvasKit.SkPath.prototype.dash = function(on, off, phase) { 278 if (this._dash(on, off, phase)) { 279 return this; 280 } 281 return null; 282 }; 283 284 CanvasKit.SkPath.prototype.lineTo = function(x, y) { 285 this._lineTo(x, y); 286 return this; 287 }; 288 289 CanvasKit.SkPath.prototype.moveTo = function(x, y) { 290 this._moveTo(x, y); 291 return this; 292 }; 293 294 CanvasKit.SkPath.prototype.op = function(otherPath, op) { 295 if (this._op(otherPath, op)) { 296 return this; 297 } 298 return null; 299 }; 300 301 CanvasKit.SkPath.prototype.quadTo = function(cpx, cpy, x, y) { 302 this._quadTo(cpx, cpy, x, y); 303 return this; 304 }; 305 306 CanvasKit.SkPath.prototype.simplify = function() { 307 if (this._simplify()) { 308 return this; 309 } 310 return null; 311 }; 312 313 CanvasKit.SkPath.prototype.stroke = function(opts) { 314 // Fill out any missing values with the default values. 315 /** 316 * See externs.js for this definition 317 * @type {StrokeOpts} 318 */ 319 opts = opts || {}; 320 opts.width = opts.width || 1; 321 opts.miter_limit = opts.miter_limit || 4; 322 opts.cap = opts.cap || CanvasKit.StrokeCap.Butt; 323 opts.join = opts.join || CanvasKit.StrokeJoin.Miter; 324 opts.precision = opts.precision || 1; 325 if (this._stroke(opts)) { 326 return this; 327 } 328 return null; 329 }; 330 331 CanvasKit.SkPath.prototype.transform = function() { 332 // Takes 1 or 9 args 333 if (arguments.length === 1) { 334 // argument 1 should be a 6 or 9 element array. 335 var a = arguments[0]; 336 this._transform(a[0], a[1], a[2], 337 a[3], a[4], a[5], 338 a[6] || 0, a[7] || 0, a[8] || 1); 339 } else if (arguments.length === 6 || arguments.length === 9) { 340 // these arguments are the 6 or 9 members of the matrix 341 var a = arguments; 342 this._transform(a[0], a[1], a[2], 343 a[3], a[4], a[5], 344 a[6] || 0, a[7] || 0, a[8] || 1); 345 } else { 346 throw 'transform expected to take 1 or 9 arguments. Got ' + arguments.length; 347 } 348 return this; 349 }; 350 // isComplement is optional, defaults to false 351 CanvasKit.SkPath.prototype.trim = function(startT, stopT, isComplement) { 352 if (this._trim(startT, stopT, !!isComplement)) { 353 return this; 354 } 355 return null; 356 }; 357 358 // bones should be a 3d array. 359 // Each bone is a 3x2 transformation matrix in column major order: 360 // | scaleX skewX transX | 361 // | skewY scaleY transY | 362 // and bones is an array of those matrices. 363 // Returns a copy of this (SkVertices) with the bones applied. 364 CanvasKit.SkVertices.prototype.applyBones = function(bones) { 365 var bPtr = copy3dArray(bones, CanvasKit.HEAPF32); 366 var vert = this._applyBones(bPtr, bones.length); 367 CanvasKit._free(bPtr); 368 return vert; 369 } 370 371 CanvasKit.SkImage.prototype.encodeToData = function() { 372 if (!arguments.length) { 373 return this._encodeToData(); 374 } 375 376 if (arguments.length === 2) { 377 var a = arguments; 378 return this._encodeToDataWithFormat(a[0], a[1]); 379 } 380 381 throw 'encodeToData expected to take 0 or 2 arguments. Got ' + arguments.length; 382 } 383 384 CanvasKit.SkImage.prototype.makeShader = function(xTileMode, yTileMode, localMatrix) { 385 if (localMatrix) { 386 // Add perspective args if not provided. 387 if (localMatrix.length === 6) { 388 localMatrix.push(0, 0, 1); 389 } 390 return this._makeShader(xTileMode, yTileMode, localMatrix); 391 } else { 392 return this._makeShader(xTileMode, yTileMode); 393 } 394 } 395 396 CanvasKit.SkImage.prototype.readPixels = function(imageInfo, srcX, srcY) { 397 var rowBytes; 398 switch (imageInfo.colorType){ 399 case CanvasKit.ColorType.RGBA_8888: 400 rowBytes = imageInfo.width * 4; // 1 byte per channel == 4 bytes per pixel in 8888 401 break; 402 case CanvasKit.ColorType.RGBA_F32: 403 rowBytes = imageInfo.width * 16; // 4 bytes per channel == 16 bytes per pixel in F32 404 break; 405 default: 406 SkDebug("Colortype not yet supported"); 407 return; 408 } 409 var pBytes = rowBytes * imageInfo.height; 410 var pPtr = CanvasKit._malloc(pBytes); 411 412 if (!this._readPixels(imageInfo, pPtr, rowBytes, srcX, srcY)) { 413 SkDebug("Could not read pixels with the given inputs"); 414 return null; 415 } 416 417 // Put those pixels into a typed array of the right format and then 418 // make a copy with slice() that we can return. 419 var retVal = null; 420 switch (imageInfo.colorType){ 421 case CanvasKit.ColorType.RGBA_8888: 422 retVal = new Uint8Array(CanvasKit.buffer, pPtr, pBytes).slice(); 423 break; 424 case CanvasKit.ColorType.RGBA_F32: 425 retVal = new Float32Array(CanvasKit.buffer, pPtr, pBytes).slice(); 426 break; 427 } 428 429 // Free the allocated pixels in the WASM memory 430 CanvasKit._free(pPtr); 431 return retVal; 432 433 } 434 435 // atlas is an SkImage, e.g. from CanvasKit.MakeImageFromEncoded 436 // srcRects and dstXforms should be CanvasKit.SkRectBuilder and CanvasKit.RSXFormBuilder 437 // or just arrays of floats in groups of 4. 438 // colors, if provided, should be a CanvasKit.SkColorBuilder or array of SkColor 439 // (from CanvasKit.Color) 440 CanvasKit.SkCanvas.prototype.drawAtlas = function(atlas, srcRects, dstXforms, paint, 441 /*optional*/ blendMode, colors) { 442 if (!atlas || !paint || !srcRects || !dstXforms) { 443 SkDebug('Doing nothing since missing a required input'); 444 return; 445 } 446 if (srcRects.length !== dstXforms.length || (colors && colors.length !== dstXforms.length)) { 447 SkDebug('Doing nothing since input arrays length mismatches'); 448 } 449 if (!blendMode) { 450 blendMode = CanvasKit.BlendMode.SrcOver; 451 } 452 453 var srcRectPtr; 454 if (srcRects.build) { 455 srcRectPtr = srcRects.build(); 456 } else { 457 srcRectPtr = copy1dArray(srcRects, CanvasKit.HEAPF32); 458 } 459 460 var dstXformPtr; 461 if (dstXforms.build) { 462 dstXformPtr = dstXforms.build(); 463 } else { 464 dstXformPtr = copy1dArray(dstXforms, CanvasKit.HEAPF32); 465 } 466 467 var colorPtr = 0; // enscriptem doesn't like undefined for nullptr 468 if (colors) { 469 if (colors.build) { 470 colorPtr = colors.build(); 471 } else { 472 colorPtr = copy1dArray(colors, CanvasKit.HEAPU32); 473 } 474 } 475 476 this._drawAtlas(atlas, dstXformPtr, srcRectPtr, colorPtr, dstXforms.length, 477 blendMode, paint); 478 479 if (srcRectPtr && !srcRects.build) { 480 CanvasKit._free(srcRectPtr); 481 } 482 if (dstXformPtr && !dstXforms.build) { 483 CanvasKit._free(dstXformPtr); 484 } 485 if (colorPtr && !colors.build) { 486 CanvasKit._free(colorPtr); 487 } 488 489 } 490 491 // str can be either a text string or a ShapedText object 492 CanvasKit.SkCanvas.prototype.drawText = function(str, x, y, paint, font) { 493 if (typeof str === 'string') { 494 // lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten 495 // JS. See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8 496 // Add 1 for null terminator 497 var strLen = lengthBytesUTF8(str) + 1; 498 var strPtr = CanvasKit._malloc(strLen); 499 500 stringToUTF8(str, strPtr, strLen); 501 this._drawSimpleText(strPtr, strLen, x, y, font, paint); 502 } else { 503 this._drawShapedText(str, x, y, paint); 504 } 505 } 506 507 // returns Uint8Array 508 CanvasKit.SkCanvas.prototype.readPixels = function(x, y, w, h, alphaType, 509 colorType, dstRowBytes) { 510 // supply defaults (which are compatible with HTMLCanvas's getImageData) 511 alphaType = alphaType || CanvasKit.AlphaType.Unpremul; 512 colorType = colorType || CanvasKit.ColorType.RGBA_8888; 513 dstRowBytes = dstRowBytes || (4 * w); 514 515 var len = h * dstRowBytes 516 var pptr = CanvasKit._malloc(len); 517 var ok = this._readPixels({ 518 'width': w, 519 'height': h, 520 'colorType': colorType, 521 'alphaType': alphaType, 522 }, pptr, dstRowBytes, x, y); 523 if (!ok) { 524 CanvasKit._free(pptr); 525 return null; 526 } 527 528 // The first typed array is just a view into memory. Because we will 529 // be free-ing that, we call slice to make a persistent copy. 530 var pixels = new Uint8Array(CanvasKit.HEAPU8.buffer, pptr, len).slice(); 531 CanvasKit._free(pptr); 532 return pixels; 533 } 534 535 // pixels is a TypedArray. No matter the input size, it will be treated as 536 // a Uint8Array (essentially, a byte array). 537 CanvasKit.SkCanvas.prototype.writePixels = function(pixels, srcWidth, srcHeight, 538 destX, destY, alphaType, colorType) { 539 if (pixels.byteLength % (srcWidth * srcHeight)) { 540 throw 'pixels length must be a multiple of the srcWidth * srcHeight'; 541 } 542 var bytesPerPixel = pixels.byteLength / (srcWidth * srcHeight); 543 // supply defaults (which are compatible with HTMLCanvas's putImageData) 544 alphaType = alphaType || CanvasKit.AlphaType.Unpremul; 545 colorType = colorType || CanvasKit.ColorType.RGBA_8888; 546 var srcRowBytes = bytesPerPixel * srcWidth; 547 548 var pptr = CanvasKit._malloc(pixels.byteLength); 549 CanvasKit.HEAPU8.set(pixels, pptr); 550 551 var ok = this._writePixels({ 552 'width': srcWidth, 553 'height': srcHeight, 554 'colorType': colorType, 555 'alphaType': alphaType, 556 }, pptr, srcRowBytes, destX, destY); 557 558 CanvasKit._free(pptr); 559 return ok; 560 } 561 562 // Returns an array of the widths of the glyphs in this string. 563 CanvasKit.SkFont.prototype.getWidths = function(str) { 564 // add 1 for null terminator 565 var codePoints = str.length + 1; 566 // lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten 567 // JS. See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8 568 // Add 1 for null terminator 569 var strBytes = lengthBytesUTF8(str) + 1; 570 var strPtr = CanvasKit._malloc(strBytes); 571 stringToUTF8(str, strPtr, strBytes); 572 573 var bytesPerFloat = 4; 574 // allocate widths == numCodePoints 575 var widthPtr = CanvasKit._malloc(codePoints * bytesPerFloat); 576 if (!this._getWidths(strPtr, strBytes, codePoints, widthPtr)) { 577 SkDebug('Could not compute widths'); 578 CanvasKit._free(strPtr); 579 CanvasKit._free(widthPtr); 580 return null; 581 } 582 // reminder, this shouldn't copy the data, just is a nice way to 583 // wrap 4 bytes together into a float. 584 var widths = new Float32Array(CanvasKit.buffer, widthPtr, codePoints); 585 // This copies the data so we can free the CanvasKit memory 586 var retVal = Array.from(widths); 587 CanvasKit._free(strPtr); 588 CanvasKit._free(widthPtr); 589 return retVal; 590 } 591 592 // fontData should be an arrayBuffer 593 CanvasKit.SkFontMgr.prototype.MakeTypefaceFromData = function(fontData) { 594 var data = new Uint8Array(fontData); 595 596 var fptr = CanvasKit._malloc(data.byteLength); 597 CanvasKit.HEAPU8.set(data, fptr); 598 var font = this._makeTypefaceFromData(fptr, data.byteLength); 599 if (!font) { 600 SkDebug('Could not decode font data'); 601 // We do not need to free the data since the C++ will do that for us 602 // when the font is deleted (or fails to decode); 603 return null; 604 } 605 return font; 606 } 607 608 // The serialized format of an SkPicture (informally called an "skp"), is not something 609 // that clients should ever rely on. It is useful when filing bug reports, but that's 610 // about it. The format may change at anytime and no promises are made for backwards 611 // or forward compatibility. 612 CanvasKit.SkPicture.prototype.DEBUGONLY_saveAsFile = function(skpName) { 613 var data = this.DEBUGONLY_serialize(); 614 if (!data) { 615 SkDebug('Could not serialize to skpicture.'); 616 return; 617 } 618 var bytes = CanvasKit.getSkDataBytes(data); 619 saveBytesToFile(bytes, skpName); 620 data.delete(); 621 } 622 623 CanvasKit.SkSurface.prototype.captureFrameAsSkPicture = function(drawFrame) { 624 // Set up SkPictureRecorder 625 var spr = new CanvasKit.SkPictureRecorder(); 626 var canvas = spr.beginRecording( 627 CanvasKit.LTRBRect(0, 0, this.width(), this.height())); 628 drawFrame(canvas); 629 var pic = spr.finishRecordingAsPicture(); 630 spr.delete(); 631 // TODO: do we need to clean up the memory for canvas? 632 // If we delete it here, saveAsFile doesn't work correctly. 633 return pic; 634 } 635 636 CanvasKit.SkSurface.prototype.requestAnimationFrame = function(callback, dirtyRect) { 637 if (!this._cached_canvas) { 638 this._cached_canvas = this.getCanvas(); 639 } 640 window.requestAnimationFrame(function() { 641 if (this._context !== undefined) { 642 CanvasKit.setCurrentContext(this._context); 643 } 644 645 callback(this._cached_canvas); 646 647 this.flush(); 648 }.bind(this)); 649 } 650 651 CanvasKit.SkTextBlob.MakeOnPath = function(str, path, font, initialOffset) { 652 if (!str || !str.length) { 653 SkDebug('ignoring 0 length string'); 654 return; 655 } 656 if (!path || !path.countPoints()) { 657 SkDebug('ignoring empty path'); 658 return; 659 } 660 if (path.countPoints() === 1) { 661 SkDebug('path has 1 point, returning normal textblob'); 662 return this.MakeFromText(str, font); 663 } 664 665 if (!initialOffset) { 666 initialOffset = 0; 667 } 668 669 var widths = font.getWidths(str); 670 671 var rsx = new CanvasKit.RSXFormBuilder(); 672 var meas = new CanvasKit.SkPathMeasure(path, false, 1); 673 var dist = initialOffset; 674 for (var i = 0; i < str.length; i++) { 675 var width = widths[i]; 676 dist += width/2; 677 if (dist > meas.getLength()) { 678 // jump to next contour 679 if (!meas.nextContour()) { 680 // We have come to the end of the path - terminate the string 681 // right here. 682 str = str.substring(0, i); 683 break; 684 } 685 dist = width/2; 686 } 687 688 // Gives us the (x, y) coordinates as well as the cos/sin of the tangent 689 // line at that position. 690 var xycs = meas.getPosTan(dist); 691 var cx = xycs[0]; 692 var cy = xycs[1]; 693 var cosT = xycs[2]; 694 var sinT = xycs[3]; 695 696 var adjustedX = cx - (width/2 * cosT); 697 var adjustedY = cy - (width/2 * sinT); 698 699 rsx.push(cosT, sinT, adjustedX, adjustedY); 700 dist += width/2; 701 } 702 var retVal = this.MakeFromRSXform(str, rsx, font); 703 rsx.delete(); 704 meas.delete(); 705 return retVal; 706 } 707 708 CanvasKit.SkTextBlob.MakeFromRSXform = function(str, rsxBuilder, font) { 709 // lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten 710 // JS. See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8 711 // Add 1 for null terminator 712 var strLen = lengthBytesUTF8(str) + 1; 713 var strPtr = CanvasKit._malloc(strLen); 714 // Add 1 for the null terminator. 715 stringToUTF8(str, strPtr, strLen); 716 var rptr = rsxBuilder.build(); 717 718 var blob = CanvasKit.SkTextBlob._MakeFromRSXform(strPtr, strLen - 1, 719 rptr, font, CanvasKit.TextEncoding.UTF8); 720 if (!blob) { 721 SkDebug('Could not make textblob from string "' + str + '"'); 722 return null; 723 } 724 725 var origDelete = blob.delete.bind(blob); 726 blob.delete = function() { 727 CanvasKit._free(strPtr); 728 origDelete(); 729 } 730 return blob; 731 } 732 733 CanvasKit.SkTextBlob.MakeFromText = function(str, font) { 734 // lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten 735 // JS. See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8 736 // Add 1 for null terminator 737 var strLen = lengthBytesUTF8(str) + 1; 738 var strPtr = CanvasKit._malloc(strLen); 739 // Add 1 for the null terminator. 740 stringToUTF8(str, strPtr, strLen); 741 742 var blob = CanvasKit.SkTextBlob._MakeFromText(strPtr, strLen - 1, font, CanvasKit.TextEncoding.UTF8); 743 if (!blob) { 744 SkDebug('Could not make textblob from string "' + str + '"'); 745 return null; 746 } 747 748 var origDelete = blob.delete.bind(blob); 749 blob.delete = function() { 750 CanvasKit._free(strPtr); 751 origDelete(); 752 } 753 return blob; 754 } 755 756 // Run through the JS files that are added at compile time. 757 if (CanvasKit._extraInitializations) { 758 CanvasKit._extraInitializations.forEach(function(init) { 759 init(); 760 }); 761 } 762}; // end CanvasKit.onRuntimeInitialized, that is, anything changing prototypes or dynamic. 763 764CanvasKit.LTRBRect = function(l, t, r, b) { 765 return { 766 fLeft: l, 767 fTop: t, 768 fRight: r, 769 fBottom: b, 770 }; 771} 772 773CanvasKit.XYWHRect = function(x, y, w, h) { 774 return { 775 fLeft: x, 776 fTop: y, 777 fRight: x+w, 778 fBottom: y+h, 779 }; 780} 781 782CanvasKit.MakePathFromCmds = function(cmds) { 783 var ptrLen = loadCmdsTypedArray(cmds); 784 var path = CanvasKit._MakePathFromCmds(ptrLen[0], ptrLen[1]); 785 CanvasKit._free(ptrLen[0]); 786 return path; 787} 788 789CanvasKit.MakeSkDashPathEffect = function(intervals, phase) { 790 if (!phase) { 791 phase = 0; 792 } 793 if (!intervals.length || intervals.length % 2 === 1) { 794 throw 'Intervals array must have even length'; 795 } 796 var ptr = copy1dArray(intervals, CanvasKit.HEAPF32); 797 var dpe = CanvasKit._MakeSkDashPathEffect(ptr, intervals.length, phase); 798 CanvasKit._free(ptr); 799 return dpe; 800} 801 802// data is a TypedArray or ArrayBuffer e.g. from fetch().then(resp.arrayBuffer()) 803CanvasKit.MakeImageFromEncoded = function(data) { 804 data = new Uint8Array(data); 805 806 var iptr = CanvasKit._malloc(data.byteLength); 807 CanvasKit.HEAPU8.set(data, iptr); 808 var img = CanvasKit._decodeImage(iptr, data.byteLength); 809 if (!img) { 810 SkDebug('Could not decode image'); 811 return null; 812 } 813 return img; 814} 815 816// pixels is a Uint8Array 817CanvasKit.MakeImage = function(pixels, width, height, alphaType, colorType) { 818 var bytesPerPixel = pixels.byteLength / (width * height); 819 var info = { 820 'width': width, 821 'height': height, 822 'alphaType': alphaType, 823 'colorType': colorType, 824 }; 825 var pptr = CanvasKit._malloc(pixels.byteLength); 826 CanvasKit.HEAPU8.set(pixels, pptr); 827 // No need to _free iptr, Image takes it with SkData::MakeFromMalloc 828 829 return CanvasKit._MakeImage(info, pptr, pixels.byteLength, width * bytesPerPixel); 830} 831 832CanvasKit.MakeLinearGradientShader = function(start, end, colors, pos, mode, localMatrix, flags) { 833 var colorPtr = copy1dArray(colors, CanvasKit.HEAPU32); 834 var posPtr = copy1dArray(pos, CanvasKit.HEAPF32); 835 flags = flags || 0; 836 837 if (localMatrix) { 838 // Add perspective args if not provided. 839 if (localMatrix.length === 6) { 840 localMatrix.push(0, 0, 1); 841 } 842 var lgs = CanvasKit._MakeLinearGradientShader(start, end, colorPtr, posPtr, 843 colors.length, mode, flags, localMatrix); 844 } else { 845 var lgs = CanvasKit._MakeLinearGradientShader(start, end, colorPtr, posPtr, 846 colors.length, mode, flags); 847 } 848 849 CanvasKit._free(colorPtr); 850 CanvasKit._free(posPtr); 851 return lgs; 852} 853 854CanvasKit.MakeRadialGradientShader = function(center, radius, colors, pos, mode, localMatrix, flags) { 855 var colorPtr = copy1dArray(colors, CanvasKit.HEAPU32); 856 var posPtr = copy1dArray(pos, CanvasKit.HEAPF32); 857 flags = flags || 0; 858 859 if (localMatrix) { 860 // Add perspective args if not provided. 861 if (localMatrix.length === 6) { 862 localMatrix.push(0, 0, 1); 863 } 864 var rgs = CanvasKit._MakeRadialGradientShader(center, radius, colorPtr, posPtr, 865 colors.length, mode, flags, localMatrix); 866 } else { 867 var rgs = CanvasKit._MakeRadialGradientShader(center, radius, colorPtr, posPtr, 868 colors.length, mode, flags); 869 } 870 871 CanvasKit._free(colorPtr); 872 CanvasKit._free(posPtr); 873 return rgs; 874} 875 876CanvasKit.MakeTwoPointConicalGradientShader = function(start, startRadius, end, endRadius, 877 colors, pos, mode, localMatrix, flags) { 878 var colorPtr = copy1dArray(colors, CanvasKit.HEAPU32); 879 var posPtr = copy1dArray(pos, CanvasKit.HEAPF32); 880 flags = flags || 0; 881 882 if (localMatrix) { 883 // Add perspective args if not provided. 884 if (localMatrix.length === 6) { 885 localMatrix.push(0, 0, 1); 886 } 887 var rgs = CanvasKit._MakeTwoPointConicalGradientShader( 888 start, startRadius, end, endRadius, 889 colorPtr, posPtr, colors.length, mode, flags, localMatrix); 890 } else { 891 var rgs = CanvasKit._MakeTwoPointConicalGradientShader( 892 start, startRadius, end, endRadius, 893 colorPtr, posPtr, colors.length, mode, flags); 894 } 895 896 CanvasKit._free(colorPtr); 897 CanvasKit._free(posPtr); 898 return rgs; 899} 900 901CanvasKit.MakeSkVertices = function(mode, positions, textureCoordinates, colors, 902 boneIndices, boneWeights, indices, isVolatile) { 903 // Default isVolitile to true if not set 904 isVolatile = isVolatile === undefined ? true : isVolatile; 905 var idxCount = (indices && indices.length) || 0; 906 907 var flags = 0; 908 // These flags are from SkVertices.h and should be kept in sync with those. 909 if (textureCoordinates && textureCoordinates.length) { 910 flags |= (1 << 0); 911 } 912 if (colors && colors.length) { 913 flags |= (1 << 1); 914 } 915 if (boneIndices && boneIndices.length) { 916 flags |= (1 << 2); 917 } 918 if (!isVolatile) { 919 flags |= (1 << 3); 920 } 921 922 var builder = new CanvasKit._SkVerticesBuilder(mode, positions.length, idxCount, flags); 923 924 copy2dArray(positions, CanvasKit.HEAPF32, builder.positions()); 925 if (builder.texCoords()) { 926 copy2dArray(textureCoordinates, CanvasKit.HEAPF32, builder.texCoords()); 927 } 928 if (builder.colors()) { 929 copy1dArray(colors, CanvasKit.HEAPU32, builder.colors()); 930 } 931 if (builder.boneIndices()) { 932 copy2dArray(boneIndices, CanvasKit.HEAP32, builder.boneIndices()); 933 } 934 if (builder.boneWeights()) { 935 copy2dArray(boneWeights, CanvasKit.HEAPF32, builder.boneWeights()); 936 } 937 if (builder.indices()) { 938 copy1dArray(indices, CanvasKit.HEAPU16, builder.indices()); 939 } 940 941 var idxCount = (indices && indices.length) || 0; 942 // Create the vertices, which owns the memory that the builder had allocated. 943 return builder.detach(); 944}; 945