1/* 2 * This file houses utilities for copying blocks of memory to and from 3 * the WASM heap. 4 */ 5 6/** 7 * Malloc returns a TypedArray backed by the C++ memory of the 8 * given length. It should only be used by advanced users who 9 * can manage memory and initialize values properly. When used 10 * correctly, it can save copying of data between JS and C++. 11 * When used incorrectly, it can lead to memory leaks. 12 * Any memory allocated by CanvasKit.Malloc needs to be released with CanvasKit.Free. 13 * 14 * const mObj = CanvasKit.Malloc(Float32Array, 20); 15 * Get a TypedArray view around the malloc'd memory (this does not copy anything). 16 * const ta = mObj.toTypedArray(); 17 * // store data into ta 18 * const cf = CanvasKit.ColorFilter.MakeMatrix(ta); // mObj could also be used. 19 * 20 * // eventually... 21 * CanvasKit.Free(mObj); 22 * 23 * @param {TypedArray} typedArray - constructor for the typedArray. 24 * @param {number} len - number of *elements* to store. 25 */ 26CanvasKit.Malloc = function(typedArray, len) { 27 var byteLen = len * typedArray.BYTES_PER_ELEMENT; 28 var ptr = CanvasKit._malloc(byteLen); 29 return { 30 '_ck': true, 31 'length': len, 32 'byteOffset': ptr, 33 typedArray: null, 34 'subarray': function(start, end) { 35 var sa = this['toTypedArray']().subarray(start, end); 36 sa['_ck'] = true; 37 return sa; 38 }, 39 'toTypedArray': function() { 40 // Check if the previously allocated array is still usable. 41 // If it's falsy, then we haven't created an array yet. 42 // If it's empty, then WASM resized memory and emptied the array. 43 if (this.typedArray && this.typedArray.length) { 44 return this.typedArray; 45 } 46 this.typedArray = new typedArray(CanvasKit.HEAPU8.buffer, ptr, len); 47 // add a marker that this was allocated in C++ land 48 this.typedArray['_ck'] = true; 49 return this.typedArray; 50 }, 51 }; 52}; 53 54/** 55 * Free frees the memory returned by Malloc. 56 * Any memory allocated by CanvasKit.Malloc needs to be released with CanvasKit.Free. 57 */ 58CanvasKit.Free = function(mallocObj) { 59 CanvasKit._free(mallocObj['byteOffset']); 60 mallocObj['byteOffset'] = nullptr; 61 // Set these to null to make sure the TypedArrays can be garbage collected. 62 mallocObj['toTypedArray'] = null; 63 mallocObj.typedArray = null; 64}; 65 66// This helper will free the given pointer unless the provided array is one 67// that was returned by CanvasKit.Malloc. 68function freeArraysThatAreNotMallocedByUsers(ptr, arr) { 69 if (arr && !arr['_ck']) { 70 CanvasKit._free(ptr); 71 } 72} 73 74// We define some "scratch" variables which will house both the pointer to 75// memory we allocate at startup as well as a Malloc object, which we can 76// use to get a TypedArray view of that memory. 77 78var _scratch3x3MatrixPtr = nullptr; 79var _scratch3x3Matrix; // the result from CanvasKit.Malloc 80 81var _scratch4x4MatrixPtr = nullptr; 82var _scratch4x4Matrix; 83 84var _scratchColorPtr = nullptr; 85var _scratchColor; 86 87var _scratchFourFloatsA; 88var _scratchFourFloatsAPtr = nullptr; 89 90var _scratchFourFloatsB; 91var _scratchFourFloatsBPtr = nullptr; 92 93var _scratchThreeFloatsA; 94var _scratchThreeFloatsAPtr = nullptr; 95 96var _scratchThreeFloatsB; 97var _scratchThreeFloatsBPtr = nullptr; 98 99var _scratchIRect; 100var _scratchIRectPtr = nullptr; 101 102var _scratchRRect; 103var _scratchRRectPtr = nullptr; 104 105var _scratchRRect2; 106var _scratchRRect2Ptr = nullptr; 107 108// arr can be a normal JS array or a TypedArray 109// dest is a string like 'HEAPU32' that specifies the type the src array 110// should be copied into. 111// ptr can be optionally provided if the memory was already allocated. 112// Callers should eventually free the data unless the C++ object owns the memory, 113// or the provided pointer is a scratch pointer or a user-malloced value. 114// see also freeArraysThatAreNotMallocedByUsers(). 115function copy1dArray(arr, dest, ptr) { 116 if (!arr || !arr.length) { 117 return nullptr; 118 } 119 // This was created with CanvasKit.Malloc, so it's already been copied. 120 if (arr['_ck']) { 121 return arr.byteOffset; 122 } 123 var bytesPerElement = CanvasKit[dest].BYTES_PER_ELEMENT; 124 if (!ptr) { 125 ptr = CanvasKit._malloc(arr.length * bytesPerElement); 126 } 127 // In c++ terms, the WASM heap is a uint8_t*, a long buffer/array of single 128 // byte elements. When we run _malloc, we always get an offset/pointer into 129 // that block of memory. 130 // CanvasKit exposes some different views to make it easier to work with 131 // different types. HEAPF32 for example, exposes it as a float* 132 // However, to make the ptr line up, we have to do some pointer arithmetic. 133 // Concretely, we need to convert ptr to go from an index into a 1-byte-wide 134 // buffer to an index into a 4-byte-wide buffer (in the case of HEAPF32) 135 // and thus we divide ptr by 4. 136 // It is important to make sure we are grabbing the freshest view of the 137 // memory possible because if we call _malloc and the heap needs to grow, 138 // the TypedArrayView will no longer be valid. 139 CanvasKit[dest].set(arr, ptr / bytesPerElement); 140 return ptr; 141} 142 143// Copies an array of colors to wasm, returning an object with the pointer 144// and info necessary to use the copied colors. 145// Accepts either a flat Float32Array, flat Uint32Array or Array of Float32Arrays. 146// If color is an object that was allocated with CanvasKit.Malloc, its pointer is 147// returned and no extra copy is performed. 148// TODO(nifong): have this accept color builders. 149function copyFlexibleColorArray(colors) { 150 var result = { 151 colorPtr: nullptr, 152 count: colors.length, 153 colorType: CanvasKit.ColorType.RGBA_F32, 154 }; 155 if (colors instanceof Float32Array) { 156 result.colorPtr = copy1dArray(colors, 'HEAPF32'); 157 result.count = colors.length / 4; 158 159 } else if (colors instanceof Uint32Array) { 160 result.colorPtr = copy1dArray(colors, 'HEAPU32'); 161 result.colorType = CanvasKit.ColorType.RGBA_8888; 162 163 } else if (colors instanceof Array) { 164 result.colorPtr = copyColorArray(colors); 165 } else { 166 throw('Invalid argument to copyFlexibleColorArray, Not a color array '+typeof(colors)); 167 } 168 return result; 169} 170 171function copyColorArray(arr) { 172 if (!arr || !arr.length) { 173 return nullptr; 174 } 175 // 4 floats per color, 4 bytes per float. 176 var ptr = CanvasKit._malloc(arr.length * 4 * 4); 177 178 var idx = 0; 179 var adjustedPtr = ptr / 4; // cast the byte pointer into a float pointer. 180 for (var r = 0; r < arr.length; r++) { 181 for (var c = 0; c < 4; c++) { 182 CanvasKit.HEAPF32[adjustedPtr + idx] = arr[r][c]; 183 idx++; 184 } 185 } 186 return ptr; 187} 188 189var defaultPerspective = Float32Array.of(0, 0, 1); 190 191// Copies the given DOMMatrix/Array/TypedArray to the CanvasKit heap and 192// returns a pointer to the memory. This memory is a float* of length 9. 193// If the passed in matrix is null/undefined, we return 0 (nullptr). The 194// returned pointer should NOT be freed, as it is either null or a scratch 195// pointer. 196function copy3x3MatrixToWasm(matr) { 197 if (!matr) { 198 return nullptr; 199 } 200 201 if (matr.length) { 202 if (matr.length === 6 || matr.length === 9) { 203 // matr should be an array or typed array. 204 copy1dArray(matr, 'HEAPF32', _scratch3x3MatrixPtr); 205 if (matr.length === 6) { 206 // Overwrite the last 3 floats with the default perspective. The divide 207 // by 4 casts the pointer into a float pointer. 208 CanvasKit.HEAPF32.set(defaultPerspective, 6 + _scratch3x3MatrixPtr / 4); 209 } 210 return _scratch3x3MatrixPtr; 211 } else if (matr.length === 16) { 212 // Downsample the 4x4 matrix into a 3x3 213 var wasm3x3Matrix = _scratch3x3Matrix['toTypedArray'](); 214 wasm3x3Matrix[0] = matr[0]; 215 wasm3x3Matrix[1] = matr[1]; 216 wasm3x3Matrix[2] = matr[3]; 217 218 wasm3x3Matrix[3] = matr[4]; 219 wasm3x3Matrix[4] = matr[5]; 220 wasm3x3Matrix[5] = matr[7]; 221 222 wasm3x3Matrix[6] = matr[12]; 223 wasm3x3Matrix[7] = matr[13]; 224 wasm3x3Matrix[8] = matr[15]; 225 return _scratch3x3MatrixPtr; 226 } 227 throw 'invalid matrix size'; 228 } 229 var wasm3x3Matrix = _scratch3x3Matrix['toTypedArray'](); 230 // Try as if it's a DOMMatrix. Reminder that DOMMatrix is column-major. 231 wasm3x3Matrix[0] = matr.m11; 232 wasm3x3Matrix[1] = matr.m21; 233 wasm3x3Matrix[2] = matr.m41; 234 235 wasm3x3Matrix[3] = matr.m12; 236 wasm3x3Matrix[4] = matr.m22; 237 wasm3x3Matrix[5] = matr.m42; 238 239 wasm3x3Matrix[6] = matr.m14; 240 wasm3x3Matrix[7] = matr.m24; 241 wasm3x3Matrix[8] = matr.m44; 242 return _scratch3x3MatrixPtr; 243} 244 245 246// Copies the given DOMMatrix/Array/TypedArray to the CanvasKit heap and 247// returns a pointer to the memory. This memory is a float* of length 16. 248// If the passed in matrix is null/undefined, we return 0 (nullptr). The 249// returned pointer should NOT be freed, as it is either null or a scratch 250// pointer. 251function copy4x4MatrixToWasm(matr) { 252 if (!matr) { 253 return nullptr; 254 } 255 var wasm4x4Matrix = _scratch4x4Matrix['toTypedArray'](); 256 if (matr.length) { 257 if (matr.length !== 16 && matr.length !== 6 && matr.length !== 9) { 258 throw 'invalid matrix size'; 259 } 260 if (matr.length === 16) { 261 // matr should be an array or typed array. 262 return copy1dArray(matr, 'HEAPF32', _scratch4x4MatrixPtr); 263 } 264 // Upscale the row-major 3x3 or 3x2 matrix into a 4x4 row-major matrix 265 // TODO(skbug.com/10108) This will need to change when we convert our 266 // JS 4x4 to be column-major. 267 // When upscaling, we need to overwrite the 3rd column and the 3rd row with 268 // 0s. It's easiest to just do that with a fill command. 269 wasm4x4Matrix.fill(0); 270 wasm4x4Matrix[0] = matr[0]; 271 wasm4x4Matrix[1] = matr[1]; 272 // skip col 2 273 wasm4x4Matrix[3] = matr[2]; 274 275 wasm4x4Matrix[4] = matr[3]; 276 wasm4x4Matrix[5] = matr[4]; 277 // skip col 2 278 wasm4x4Matrix[7] = matr[5]; 279 280 // skip row 2 281 282 wasm4x4Matrix[12] = matr[6]; 283 wasm4x4Matrix[13] = matr[7]; 284 // skip col 2 285 wasm4x4Matrix[15] = matr[8]; 286 287 if (matr.length === 6) { 288 // fix perspective for the 3x2 case (from above, they will be undefined). 289 wasm4x4Matrix[12]=0; 290 wasm4x4Matrix[13]=0; 291 wasm4x4Matrix[15]=1; 292 } 293 return _scratch4x4MatrixPtr; 294 } 295 // Try as if it's a DOMMatrix. Reminder that DOMMatrix is column-major. 296 wasm4x4Matrix[0] = matr.m11; 297 wasm4x4Matrix[1] = matr.m21; 298 wasm4x4Matrix[2] = matr.m31; 299 wasm4x4Matrix[3] = matr.m41; 300 301 wasm4x4Matrix[4] = matr.m12; 302 wasm4x4Matrix[5] = matr.m22; 303 wasm4x4Matrix[6] = matr.m32; 304 wasm4x4Matrix[7] = matr.m42; 305 306 wasm4x4Matrix[8] = matr.m13; 307 wasm4x4Matrix[9] = matr.m23; 308 wasm4x4Matrix[10] = matr.m33; 309 wasm4x4Matrix[11] = matr.m43; 310 311 wasm4x4Matrix[12] = matr.m14; 312 wasm4x4Matrix[13] = matr.m24; 313 wasm4x4Matrix[14] = matr.m34; 314 wasm4x4Matrix[15] = matr.m44; 315 return _scratch4x4MatrixPtr; 316} 317 318// copies a 4x4 matrix at the given pointer into a JS array. 319function copy4x4MatrixFromWasm(matrPtr) { 320 // read them out into an array. TODO(kjlubick): If we change Matrix to be 321 // typedArrays, then we should return a typed array here too. 322 var rv = new Array(16); 323 for (var i = 0; i < 16; i++) { 324 rv[i] = CanvasKit.HEAPF32[matrPtr/4 + i]; // divide by 4 to cast to float. 325 } 326 return rv; 327} 328 329// copies the given floats into the wasm heap as an SkColor4f. Unless a non-scratch pointer is 330// passed into ptr, callers do NOT need to free the returned pointer. 331function copyColorToWasm(color4f, ptr) { 332 return copy1dArray(color4f, 'HEAPF32', ptr || _scratchColorPtr); 333} 334 335// copies the given color into the wasm heap. Callers do not need to free the returned pointer. 336function copyColorComponentsToWasm(r, g, b, a) { 337 var colors = _scratchColor['toTypedArray'](); 338 colors[0] = r; 339 colors[1] = g; 340 colors[2] = b; 341 colors[3] = a; 342 return _scratchColorPtr; 343} 344 345// copies the given color into the wasm heap. Callers must free the returned pointer. 346function copyColorToWasmNoScratch(color4f) { 347 // TODO(kjlubick): accept 4 floats or int color 348 return copy1dArray(color4f, 'HEAPF32'); 349} 350 351// copies the four floats at the given pointer in a js Float32Array 352function copyColorFromWasm(colorPtr) { 353 var rv = new Float32Array(4); 354 for (var i = 0; i < 4; i++) { 355 rv[i] = CanvasKit.HEAPF32[colorPtr/4 + i]; // divide by 4 to cast to float. 356 } 357 return rv; 358} 359 360// copies the given floats into the wasm heap as an SkRect. Unless a non-scratch pointer is 361// passed into ptr, callers do NOT need to free the returned pointer. 362function copyRectToWasm(fourFloats, ptr) { 363 return copy1dArray(fourFloats, 'HEAPF32', ptr || _scratchFourFloatsAPtr); 364} 365 366// copies the given ints into the wasm heap as an SkIRect. Unless a non-scratch pointer is 367// passed into ptr, callers do NOT need to free the returned pointer. 368function copyIRectToWasm(fourInts, ptr) { 369 return copy1dArray(fourInts, 'HEAP32', ptr || _scratchIRectPtr); 370} 371 372// copies the given floats into the wasm heap as an SkRRect. Unless a non-scratch pointer is 373// passed into ptr, callers do NOT need to free the returned pointer. 374function copyRRectToWasm(twelveFloats, ptr) { 375 return copy1dArray(twelveFloats, 'HEAPF32', ptr || _scratchRRectPtr); 376}