Home | History | Annotate | Download | only in canvaskit
      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.
      8 CanvasKit.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   // str can be either a text string or a ShapedText object
    385   CanvasKit.SkCanvas.prototype.drawText = function(str, x, y, paint, font) {
    386     if (typeof str === 'string') {
    387       // lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten
    388       // JS.  See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8
    389       // Add 1 for null terminator
    390       var strLen = lengthBytesUTF8(str) + 1;
    391       var strPtr = CanvasKit._malloc(strLen);
    392 
    393       stringToUTF8(str, strPtr, strLen);
    394       this._drawSimpleText(strPtr, strLen, x, y, font, paint);
    395     } else {
    396       this._drawShapedText(str, x, y, paint);
    397     }
    398   }
    399 
    400   // returns Uint8Array
    401   CanvasKit.SkCanvas.prototype.readPixels = function(x, y, w, h, alphaType,
    402                                                      colorType, dstRowBytes) {
    403     // supply defaults (which are compatible with HTMLCanvas's getImageData)
    404     alphaType = alphaType || CanvasKit.AlphaType.Unpremul;
    405     colorType = colorType || CanvasKit.ColorType.RGBA_8888;
    406     dstRowBytes = dstRowBytes || (4 * w);
    407 
    408     var len = h * dstRowBytes
    409     var pptr = CanvasKit._malloc(len);
    410     var ok = this._readPixels({
    411       'width': w,
    412       'height': h,
    413       'colorType': colorType,
    414       'alphaType': alphaType,
    415     }, pptr, dstRowBytes, x, y);
    416     if (!ok) {
    417       CanvasKit._free(pptr);
    418       return null;
    419     }
    420 
    421     // The first typed array is just a view into memory. Because we will
    422     // be free-ing that, we call slice to make a persistent copy.
    423     var pixels = new Uint8Array(CanvasKit.HEAPU8.buffer, pptr, len).slice();
    424     CanvasKit._free(pptr);
    425     return pixels;
    426   }
    427 
    428   // pixels is a TypedArray. No matter the input size, it will be treated as
    429   // a Uint8Array (essentially, a byte array).
    430   CanvasKit.SkCanvas.prototype.writePixels = function(pixels, srcWidth, srcHeight,
    431                                                       destX, destY, alphaType, colorType) {
    432     if (pixels.byteLength % (srcWidth * srcHeight)) {
    433       throw 'pixels length must be a multiple of the srcWidth * srcHeight';
    434     }
    435     var bytesPerPixel = pixels.byteLength / (srcWidth * srcHeight);
    436     // supply defaults (which are compatible with HTMLCanvas's putImageData)
    437     alphaType = alphaType || CanvasKit.AlphaType.Unpremul;
    438     colorType = colorType || CanvasKit.ColorType.RGBA_8888;
    439     var srcRowBytes = bytesPerPixel * srcWidth;
    440 
    441     var pptr = CanvasKit._malloc(pixels.byteLength);
    442     CanvasKit.HEAPU8.set(pixels, pptr);
    443 
    444     var ok = this._writePixels({
    445       'width': srcWidth,
    446       'height': srcHeight,
    447       'colorType': colorType,
    448       'alphaType': alphaType,
    449     }, pptr, srcRowBytes, destX, destY);
    450 
    451     CanvasKit._free(pptr);
    452     return ok;
    453   }
    454 
    455   // fontData should be an arrayBuffer
    456   CanvasKit.SkFontMgr.prototype.MakeTypefaceFromData = function(fontData) {
    457     var data = new Uint8Array(fontData);
    458 
    459     var fptr = CanvasKit._malloc(data.byteLength);
    460     CanvasKit.HEAPU8.set(data, fptr);
    461     var font = this._makeTypefaceFromData(fptr, data.byteLength);
    462     if (!font) {
    463       SkDebug('Could not decode font data');
    464       // We do not need to free the data since the C++ will do that for us
    465       // when the font is deleted (or fails to decode);
    466       return null;
    467     }
    468     return font;
    469   }
    470 
    471   CanvasKit.SkTextBlob.MakeFromText = function(str, font) {
    472     // lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten
    473     // JS.  See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8
    474     // Add 1 for null terminator
    475     var strLen = lengthBytesUTF8(str) + 1;
    476     var strPtr = CanvasKit._malloc(strLen);
    477     // Add 1 for the null terminator.
    478     stringToUTF8(str, strPtr, strLen);
    479 
    480     var blob = CanvasKit.SkTextBlob._MakeFromText(strPtr, strLen - 1, font, CanvasKit.TextEncoding.UTF8);
    481     if (!blob) {
    482       SkDebug('Could not make textblob from string "' + str + '"');
    483       return null;
    484     }
    485 
    486     var origDelete = blob.delete.bind(blob);
    487     blob.delete = function() {
    488       CanvasKit._free(strPtr);
    489       origDelete();
    490     }
    491     return blob;
    492   }
    493 
    494   // Run through the JS files that are added at compile time.
    495   if (CanvasKit._extraInitializations) {
    496     CanvasKit._extraInitializations.forEach(function(init) {
    497       init();
    498     });
    499   }
    500 }; // end CanvasKit.onRuntimeInitialized, that is, anything changing prototypes or dynamic.
    501 
    502 CanvasKit.LTRBRect = function(l, t, r, b) {
    503   return {
    504     fLeft: l,
    505     fTop: t,
    506     fRight: r,
    507     fBottom: b,
    508   };
    509 }
    510 
    511 CanvasKit.XYWHRect = function(x, y, w, h) {
    512   return {
    513     fLeft: x,
    514     fTop: y,
    515     fRight: x+w,
    516     fBottom: y+h,
    517   };
    518 }
    519 
    520 CanvasKit.MakePathFromCmds = function(cmds) {
    521   var ptrLen = loadCmdsTypedArray(cmds);
    522   var path = CanvasKit._MakePathFromCmds(ptrLen[0], ptrLen[1]);
    523   CanvasKit._free(ptrLen[0]);
    524   return path;
    525 }
    526 
    527 CanvasKit.MakeSkDashPathEffect = function(intervals, phase) {
    528   if (!phase) {
    529     phase = 0;
    530   }
    531   if (!intervals.length || intervals.length % 2 === 1) {
    532     throw 'Intervals array must have even length';
    533   }
    534   var ptr = copy1dArray(intervals, CanvasKit.HEAPF32);
    535   var dpe = CanvasKit._MakeSkDashPathEffect(ptr, intervals.length, phase);
    536   CanvasKit._free(ptr);
    537   return dpe;
    538 }
    539 
    540 // data is a TypedArray or ArrayBuffer e.g. from fetch().then(resp.arrayBuffer())
    541 CanvasKit.MakeImageFromEncoded = function(data) {
    542   data = new Uint8Array(data);
    543 
    544   var iptr = CanvasKit._malloc(data.byteLength);
    545   CanvasKit.HEAPU8.set(data, iptr);
    546   var img = CanvasKit._decodeImage(iptr, data.byteLength);
    547   if (!img) {
    548     SkDebug('Could not decode image');
    549     return null;
    550   }
    551   return img;
    552 }
    553 
    554 // imgData is an SkImage, e.g. from MakeImageFromEncoded or SkSurface.makeImageSnapshot
    555 CanvasKit.MakeImageShader = function(img, xTileMode, yTileMode, clampUnpremul, localMatrix) {
    556   if (!img) {
    557     return null;
    558   }
    559   clampUnpremul = clampUnpremul || false;
    560   if (localMatrix) {
    561     // Add perspective args if not provided.
    562     if (localMatrix.length === 6) {
    563       localMatrix.push(0, 0, 1);
    564     }
    565     return CanvasKit._MakeImageShader(img, xTileMode, yTileMode, clampUnpremul, localMatrix);
    566   } else {
    567     return CanvasKit._MakeImageShader(img, xTileMode, yTileMode, clampUnpremul);
    568   }
    569 }
    570 
    571 // pixels is a Uint8Array
    572 CanvasKit.MakeImage = function(pixels, width, height, alphaType, colorType) {
    573   var bytesPerPixel = pixels.byteLength / (width * height);
    574   var info = {
    575     'width': width,
    576     'height': height,
    577     'alphaType': alphaType,
    578     'colorType': colorType,
    579   };
    580   var pptr = CanvasKit._malloc(pixels.byteLength);
    581   CanvasKit.HEAPU8.set(pixels, pptr);
    582   // No need to _free iptr, Image takes it with SkData::MakeFromMalloc
    583 
    584   return CanvasKit._MakeImage(info, pptr, pixels.byteLength, width * bytesPerPixel);
    585 }
    586 
    587 CanvasKit.MakeLinearGradientShader = function(start, end, colors, pos, mode, localMatrix, flags) {
    588   var colorPtr = copy1dArray(colors, CanvasKit.HEAP32);
    589   var posPtr =   copy1dArray(pos,    CanvasKit.HEAPF32);
    590   flags = flags || 0;
    591 
    592   if (localMatrix) {
    593     // Add perspective args if not provided.
    594     if (localMatrix.length === 6) {
    595       localMatrix.push(0, 0, 1);
    596     }
    597     var lgs = CanvasKit._MakeLinearGradientShader(start, end, colorPtr, posPtr,
    598                                                   colors.length, mode, flags, localMatrix);
    599   } else {
    600     var lgs = CanvasKit._MakeLinearGradientShader(start, end, colorPtr, posPtr,
    601                                                   colors.length, mode, flags);
    602   }
    603 
    604   CanvasKit._free(colorPtr);
    605   CanvasKit._free(posPtr);
    606   return lgs;
    607 }
    608 
    609 CanvasKit.MakeRadialGradientShader = function(center, radius, colors, pos, mode, localMatrix, flags) {
    610   var colorPtr = copy1dArray(colors, CanvasKit.HEAP32);
    611   var posPtr =   copy1dArray(pos,    CanvasKit.HEAPF32);
    612   flags = flags || 0;
    613 
    614   if (localMatrix) {
    615     // Add perspective args if not provided.
    616     if (localMatrix.length === 6) {
    617       localMatrix.push(0, 0, 1);
    618     }
    619     var rgs = CanvasKit._MakeRadialGradientShader(center, radius, colorPtr, posPtr,
    620                                                   colors.length, mode, flags, localMatrix);
    621   } else {
    622     var rgs = CanvasKit._MakeRadialGradientShader(center, radius, colorPtr, posPtr,
    623                                                   colors.length, mode, flags);
    624   }
    625 
    626   CanvasKit._free(colorPtr);
    627   CanvasKit._free(posPtr);
    628   return rgs;
    629 }
    630 
    631 CanvasKit.MakeTwoPointConicalGradientShader = function(start, startRadius, end, endRadius,
    632                                                        colors, pos, mode, localMatrix, flags) {
    633   var colorPtr = copy1dArray(colors, CanvasKit.HEAP32);
    634   var posPtr =   copy1dArray(pos,    CanvasKit.HEAPF32);
    635   flags = flags || 0;
    636 
    637   if (localMatrix) {
    638     // Add perspective args if not provided.
    639     if (localMatrix.length === 6) {
    640       localMatrix.push(0, 0, 1);
    641     }
    642     var rgs = CanvasKit._MakeTwoPointConicalGradientShader(
    643                         start, startRadius, end, endRadius,
    644                         colorPtr, posPtr, colors.length, mode, flags, localMatrix);
    645   } else {
    646     var rgs = CanvasKit._MakeTwoPointConicalGradientShader(
    647                         start, startRadius, end, endRadius,
    648                         colorPtr, posPtr, colors.length, mode, flags);
    649   }
    650 
    651   CanvasKit._free(colorPtr);
    652   CanvasKit._free(posPtr);
    653   return rgs;
    654 }
    655 
    656 CanvasKit.MakeSkVertices = function(mode, positions, textureCoordinates, colors,
    657                                     boneIndices, boneWeights, indices, isVolatile) {
    658   var positionPtr = copy2dArray(positions,          CanvasKit.HEAPF32);
    659   var texPtr =      copy2dArray(textureCoordinates, CanvasKit.HEAPF32);
    660   // Since we write the colors to memory as signed integers (JSColor), we can
    661   // read them out on the other side as unsigned ints (SkColor) just fine
    662   // - it's effectively casting.
    663   var colorPtr =    copy1dArray(colors,             CanvasKit.HEAP32);
    664 
    665   var boneIdxPtr =  copy2dArray(boneIndices,        CanvasKit.HEAP32);
    666   var boneWtPtr  =  copy2dArray(boneWeights,        CanvasKit.HEAPF32);
    667   var idxPtr =      copy1dArray(indices,            CanvasKit.HEAPU16);
    668 
    669   // Default isVolitile to true if not set
    670   isVolatile = isVolatile === undefined ? true : isVolatile;
    671 
    672   var idxCount = (indices && indices.length) || 0;
    673   // _MakeVertices will copy all the values in, so we are free to release
    674   // the memory after.
    675   var vertices = CanvasKit._MakeSkVertices(mode, positions.length, positionPtr,
    676                                            texPtr, colorPtr, boneIdxPtr, boneWtPtr,
    677                                            idxCount, idxPtr, isVolatile);
    678   positionPtr && CanvasKit._free(positionPtr);
    679   texPtr && CanvasKit._free(texPtr);
    680   colorPtr && CanvasKit._free(colorPtr);
    681   idxPtr && CanvasKit._free(idxPtr);
    682   boneIdxPtr && CanvasKit._free(boneIdxPtr);
    683   boneWtPtr && CanvasKit._free(boneWtPtr);
    684   return vertices;
    685 };