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