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