1 /*
2 * Copyright 2019 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #ifndef WasmCommon_DEFINED
9 #define WasmCommon_DEFINED
10
11 #include <emscripten.h>
12 #include <emscripten/bind.h>
13 #include "include/core/SkColor.h"
14 #include "include/core/SkSpan.h"
15 #include "include/private/SkMalloc.h"
16
17 using namespace emscripten;
18
19 // Self-documenting types
20 using JSArray = emscripten::val;
21 using JSObject = emscripten::val;
22 using JSString = emscripten::val;
23 using SkPathOrNull = emscripten::val;
24 using TypedArray = emscripten::val;
25 using Uint8Array = emscripten::val;
26 using Uint16Array = emscripten::val;
27 using Uint32Array = emscripten::val;
28 using Float32Array = emscripten::val;
29
30 // If we are using C++ and EMSCRIPTEN_BINDINGS, we can't have primitive pointers in our function
31 // type signatures. (this gives an error message like "Cannot call foo due to unbound
32 // types Pi, Pf"). But, we can just pretend they are numbers and cast them to be pointers and
33 // the compiler is happy.
34 // These types refer to the TypedArray that the JS interface wrote into or will read out of.
35 // This doesn't stop us from using these as different types; e.g. a float* can be treated as an
36 // SkPoint* in some APIs.
37 using WASMPointerF32 = uintptr_t;
38 using WASMPointerU8 = uintptr_t;
39 using WASMPointerU16 = uintptr_t;
40 using WASMPointerU32 = uintptr_t;
41 using WASMPointer = uintptr_t;
42
43 #define SPECIALIZE_JSARRAYTYPE(type, name) \
44 template <> struct JSArrayType<type> { \
45 static constexpr const char* const gName = name; \
46 }
47
48 template <typename T> struct JSArrayType {};
49
50 SPECIALIZE_JSARRAYTYPE( int8_t, "Int8Array");
51 SPECIALIZE_JSARRAYTYPE(uint8_t, "Uint8Array");
52 SPECIALIZE_JSARRAYTYPE( int16_t, "Int16Array");
53 SPECIALIZE_JSARRAYTYPE(uint16_t, "Uint16Array");
54 SPECIALIZE_JSARRAYTYPE( int32_t, "Int32Array");
55 SPECIALIZE_JSARRAYTYPE(uint32_t, "Uint32Array");
56 SPECIALIZE_JSARRAYTYPE(float, "Float32Array");
57
58 #undef SPECIALIZE_JSARRAYTYPE
59
60 /**
61 * Create a typed-array (in the JS heap) and initialize it with the provided
62 * data (from the wasm heap).
63 */
MakeTypedArray(int count,const T src[])64 template <typename T> TypedArray MakeTypedArray(int count, const T src[]) {
65 emscripten::val length = emscripten::val(count);
66 emscripten::val jarray = emscripten::val::global(JSArrayType<T>::gName).new_(count);
67 jarray.call<void>("set", val(typed_memory_view(count, src)));
68 return jarray;
69 }
70
71 /**
72 * Gives read access to a JSArray
73 *
74 * We explicitly use malloc/free (not new/delete) so this can be used with allocations from the JS
75 * side (ala CanvasKit.Malloc).
76 */
77 template <typename T> class JSSpan {
78 public:
79 // Note: Use of this constructor is 5-20x slower than manually copying the data on the JS side
80 // and sending over a pointer, length, and boolean for the other constructor.
JSSpan(JSArray src)81 JSSpan(JSArray src) {
82 const size_t len = src["length"].as<size_t>();
83 T* data;
84
85 // If the buffer was allocated via CanvasKit' Malloc, we can peek directly at it!
86 if (src["_ck"].isTrue()) {
87 fOwned = false;
88 data = reinterpret_cast<T*>(src["byteOffset"].as<size_t>());
89 } else {
90 fOwned = true;
91 data = static_cast<T*>(sk_malloc_throw(len, sizeof(T)));
92
93 // now actually copy into 'data'
94 if (src.instanceof(emscripten::val::global(JSArrayType<T>::gName))) {
95 auto dst_view = emscripten::val(typed_memory_view(len, data));
96 dst_view.call<void>("set", src);
97 } else {
98 for (size_t i = 0; i < len; ++i) {
99 data[i] = src[i].as<T>();
100 }
101 }
102 }
103 fSpan = SkSpan(data, len);
104 }
105
JSSpan(WASMPointer ptr,size_t len,bool takeOwnership)106 JSSpan(WASMPointer ptr, size_t len, bool takeOwnership): fOwned(takeOwnership) {
107 fSpan = SkSpan(reinterpret_cast<T*>(ptr), len);
108 }
109
~JSSpan()110 ~JSSpan() {
111 if (fOwned) {
112 sk_free(fSpan.data());
113 }
114 }
115
data()116 const T* data() const { return fSpan.data(); }
size()117 size_t size() const { return fSpan.size(); }
118
119 private:
120 SkSpan<T> fSpan;
121 bool fOwned;
122 };
123
124 #endif
125