facebetter 1.5.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -27
- package/dist/facebetter.esm.js +886 -566
- package/dist/facebetter.esm.js.map +1 -1
- package/dist/facebetter.js +905 -571
- package/dist/facebetter.js.map +1 -1
- package/package.json +8 -3
package/dist/facebetter.js
CHANGED
|
@@ -4,238 +4,312 @@
|
|
|
4
4
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Facebetter = {}));
|
|
5
5
|
})(this, (function (exports) { 'use strict';
|
|
6
6
|
|
|
7
|
-
/**
|
|
8
|
-
* Facebetter Error Classes
|
|
9
|
-
* Platform-agnostic error handling
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Facebetter error class
|
|
14
|
-
*/
|
|
15
|
-
class FacebetterError extends Error {
|
|
16
|
-
constructor(message, code = -1) {
|
|
17
|
-
super(message);
|
|
18
|
-
this.name = 'FacebetterError';
|
|
19
|
-
this.code = code;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Checks if a result code indicates success
|
|
25
|
-
* @param {number} result - Result code from WASM function
|
|
26
|
-
* @throws {FacebetterError} If result indicates failure
|
|
27
|
-
*/
|
|
28
|
-
function checkResult(result, errorMessage = 'Operation failed') {
|
|
29
|
-
if (result !== 0) {
|
|
30
|
-
throw new FacebetterError(`${errorMessage} (error code: ${result})`, result);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
7
|
/**
|
|
35
8
|
* Engine Configuration
|
|
36
9
|
* Platform-agnostic configuration class
|
|
37
10
|
*/
|
|
38
11
|
|
|
39
|
-
|
|
40
12
|
/**
|
|
41
13
|
* Engine configuration class
|
|
42
|
-
*
|
|
14
|
+
* Analogous to EngineConfig in C++ / Java / Objective-C, with Web-only auth fields.
|
|
43
15
|
*/
|
|
44
16
|
class EngineConfig {
|
|
45
17
|
/**
|
|
46
|
-
*
|
|
47
|
-
* @param {
|
|
48
|
-
*
|
|
49
|
-
* @param {string} [config.
|
|
50
|
-
*
|
|
18
|
+
* @param {Object} config
|
|
19
|
+
* @param {string} [config.licenseToken] Compact JWS, or the raw `{success, token}`
|
|
20
|
+
* HTTP response body from your auth server.
|
|
21
|
+
* @param {string} [config.authProxyUrl] Production: forward the WASM auth
|
|
22
|
+
* challenge to your server, which holds app_id/app_key and calls Cloudflare.
|
|
23
|
+
* @param {Function} [config.fetchAuthResponse] Custom auth hook with signature
|
|
24
|
+
* `async (challenge) => string|object`. For local debugging you can use
|
|
25
|
+
* `createDirectAuthFetcher` to hit Cloudflare directly — never ship keys in
|
|
26
|
+
* a production frontend bundle.
|
|
27
|
+
* @param {boolean} [config.externalContext] Kept for cross-platform alignment;
|
|
28
|
+
* unused on Web/WASM today.
|
|
51
29
|
*/
|
|
52
30
|
constructor(config = {}) {
|
|
53
|
-
this.
|
|
54
|
-
this.
|
|
55
|
-
this.
|
|
31
|
+
this.licenseToken = config.licenseToken || null;
|
|
32
|
+
this.authProxyUrl = config.authProxyUrl || null;
|
|
33
|
+
this.fetchAuthResponse = config.fetchAuthResponse || null;
|
|
56
34
|
this.resourcePath = '/resource.fbd';
|
|
57
35
|
/**
|
|
58
36
|
* Whether to use an external GL context (native platforms only).
|
|
59
|
-
*
|
|
60
|
-
* but it does not take effect in the current implementation.
|
|
37
|
+
* On Web/WASM this field is kept for config-shape alignment and has no effect.
|
|
61
38
|
*/
|
|
62
39
|
this.externalContext = !!config.externalContext;
|
|
63
40
|
}
|
|
64
41
|
|
|
65
|
-
/**
|
|
66
|
-
* Validates the configuration
|
|
67
|
-
* @returns {boolean} True if valid
|
|
68
|
-
*/
|
|
69
42
|
isValid() {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return
|
|
43
|
+
if (this.licenseToken && typeof this.licenseToken === 'string' &&
|
|
44
|
+
this.licenseToken.trim() !== '') {
|
|
45
|
+
return true;
|
|
73
46
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
47
|
+
if (typeof this.fetchAuthResponse === 'function') {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
if (this.authProxyUrl && typeof this.authProxyUrl === 'string' &&
|
|
51
|
+
this.authProxyUrl.trim() !== '') {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
77
55
|
}
|
|
78
56
|
|
|
79
|
-
/**
|
|
80
|
-
* Returns a string representation of the config
|
|
81
|
-
* @returns {string} String representation
|
|
82
|
-
*/
|
|
83
57
|
toString() {
|
|
84
|
-
|
|
58
|
+
const mode = this.licenseToken
|
|
59
|
+
? 'licenseToken'
|
|
60
|
+
: this.fetchAuthResponse
|
|
61
|
+
? 'fetchAuthResponse'
|
|
62
|
+
: this.authProxyUrl
|
|
63
|
+
? 'authProxyUrl'
|
|
64
|
+
: 'invalid';
|
|
65
|
+
return `EngineConfig{mode='${mode}'}`;
|
|
85
66
|
}
|
|
86
67
|
}
|
|
87
68
|
|
|
88
69
|
/**
|
|
89
|
-
* Facebetter
|
|
90
|
-
* Platform-agnostic
|
|
70
|
+
* Facebetter Error Classes
|
|
71
|
+
* Platform-agnostic error handling
|
|
91
72
|
*/
|
|
92
73
|
|
|
93
74
|
/**
|
|
94
|
-
*
|
|
75
|
+
* Facebetter error class
|
|
95
76
|
*/
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
Sticker: 6 // 2D/3D sticker
|
|
104
|
-
};
|
|
77
|
+
class FacebetterError extends Error {
|
|
78
|
+
constructor(message, code = -1) {
|
|
79
|
+
super(message);
|
|
80
|
+
this.name = 'FacebetterError';
|
|
81
|
+
this.code = code;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
105
84
|
|
|
106
85
|
/**
|
|
107
|
-
*
|
|
108
|
-
*
|
|
86
|
+
* Checks if a result code indicates success
|
|
87
|
+
* @param {number} result - Result code from WASM function
|
|
88
|
+
* @throws {FacebetterError} If result indicates failure
|
|
109
89
|
*/
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
};
|
|
90
|
+
function checkResult(result, errorMessage = 'Operation failed') {
|
|
91
|
+
if (result !== 0) {
|
|
92
|
+
throw new FacebetterError(`${errorMessage} (error code: ${result})`, result);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
116
95
|
|
|
117
96
|
/**
|
|
118
|
-
*
|
|
119
|
-
*
|
|
97
|
+
* Facebetter Constants and Enums
|
|
98
|
+
* Platform-agnostic constants. Ordinals match facebetter::beauty_params.
|
|
120
99
|
*/
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
Chin: 6, // Chin length adjustment (0.0: none, 1.0: maximum)
|
|
129
|
-
NoseSlimming: 7, // Nose slimming (0.0: none, 1.0: maximum)
|
|
130
|
-
EyeSize: 8, // Eye size (0.0: normal, 1.0: maximum)
|
|
131
|
-
EyeDistance: 9 // Eye distance (0.0: normal, 1.0: maximum)
|
|
100
|
+
|
|
101
|
+
const WhiteningStyle$1 = {
|
|
102
|
+
ColdWhite: 0, // Cool white
|
|
103
|
+
PinkWhite: 1, // Pink white
|
|
104
|
+
WarmWhite: 2, // Warm white
|
|
105
|
+
Wheat: 3, // Light wheat / olive
|
|
106
|
+
Tan: 4 // Tan / bronzed
|
|
132
107
|
};
|
|
133
108
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const MakeupParam$1 = {
|
|
139
|
-
Lipstick: 0, // Lipstick intensity (0.0: none, 1.0: maximum)
|
|
140
|
-
Blush: 1 // Blush intensity (0.0: none, 1.0: maximum)
|
|
109
|
+
const SmoothingStyle$1 = {
|
|
110
|
+
Natural: 0, // Natural: keeps pores
|
|
111
|
+
Texture: 1, // Cleaner skin while retaining texture
|
|
112
|
+
Smooth: 2 // Creamy / porcelain finish
|
|
141
113
|
};
|
|
142
114
|
|
|
143
115
|
/**
|
|
144
|
-
*
|
|
116
|
+
* Face reshape parameters.
|
|
117
|
+
* Values match facebetter::beauty_params::Reshape.
|
|
118
|
+
* Intensity range is [-1.0, 1.0]; 0 is off. Comments list + / - directions.
|
|
145
119
|
*/
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
120
|
+
const Reshape$1 = {
|
|
121
|
+
FaceThin: 0, // +slim face / -fuller cheeks
|
|
122
|
+
FaceVShape: 1, // +V-shape jaw / -square jaw
|
|
123
|
+
FaceNarrow: 2, // +narrow face / -wider face
|
|
124
|
+
FaceShort: 3, // +shorter face / -longer face
|
|
125
|
+
Cheekbone: 4, // +slim cheekbones / -wider cheekbones
|
|
126
|
+
Jawbone: 5, // +slim jaw / -wider jaw
|
|
127
|
+
Chin: 6, // +longer chin / -shorter chin
|
|
128
|
+
NoseSlim: 7, // +slimmer nose / -wider nose
|
|
129
|
+
EyeSize: 8, // +larger eyes / -smaller eyes
|
|
130
|
+
EyeDistance: 9, // +wider eye spacing / -closer eyes
|
|
131
|
+
FaceSmall: 10, // +smaller face / -larger face
|
|
132
|
+
Forehead: 11, // +fuller forehead / -lower forehead
|
|
133
|
+
NoseLong: 12, // +longer nose / -shorter nose
|
|
134
|
+
Philtrum: 13, // +shorter philtrum / -longer philtrum
|
|
135
|
+
MouthSize: 14, // +larger mouth / -smaller mouth
|
|
136
|
+
MouthPosition: 15, // +mouth lower / -mouth higher
|
|
137
|
+
MouthSmile: 16, // +smile lift / -droop corners
|
|
138
|
+
LipThickness: 17, // +thicker lips / -thinner lips
|
|
139
|
+
EyeRound: 18, // +rounder eyes / -narrower eyes
|
|
140
|
+
EyePosition: 19, // +eyes lower / -eyes higher
|
|
141
|
+
EyeAngle: 20, // +outer corner up / -outer corner down
|
|
142
|
+
EyeCornerOpen: 21, // +open eye corners / -close eye corners
|
|
143
|
+
LowerEyelid: 22, // +lower eyelid down / -lift lower eyelid
|
|
144
|
+
BrowPosition: 23, // +brows higher / -brows lower
|
|
145
|
+
BrowDistance: 24, // +wider brow spacing / -closer brows
|
|
146
|
+
BrowThickness: 25 // +thicker brows / -thinner brows
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const EngineEventCode$1 = {
|
|
150
|
+
LicenseValidationSuccess: 0,
|
|
151
|
+
LicenseValidationFailed: 1,
|
|
152
|
+
InitializationComplete: 100,
|
|
153
|
+
InitializationFailed: 101
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const LipstickColor$1 = {
|
|
157
|
+
Rouge: 0, // Classic rose
|
|
158
|
+
RetroRed: 1, // Retro red
|
|
159
|
+
Peach: 2, // Peach
|
|
160
|
+
CoralOrange: 3, // Coral orange
|
|
161
|
+
GentlePink: 4, // Soft pink
|
|
162
|
+
VitalityOrange: 5 // Bright orange
|
|
150
163
|
};
|
|
151
164
|
|
|
152
|
-
/**
|
|
153
|
-
* Blush style types
|
|
154
|
-
*/
|
|
155
165
|
const BlushStyle$1 = {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
166
|
+
SunKissed: 0, // Sun-kissed sweep across cheekbones and bridge
|
|
167
|
+
Igari: 1, // Flushed band across the nose bridge
|
|
168
|
+
Soft: 2, // Soft dual cheeks
|
|
169
|
+
Apple: 3, // Round apple cheeks
|
|
170
|
+
Classic: 4, // Classic two spots
|
|
171
|
+
Doll: 5, // Cheeks + nose tip
|
|
172
|
+
Rose: 6 // Rose dual cheeks
|
|
159
173
|
};
|
|
160
174
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
Smoothness: 2, // Edge smoothness (0.0 - 1.0)
|
|
168
|
-
Desaturation: 3 // Spill desaturation (0.0 - 1.0)
|
|
175
|
+
const BlushColor$1 = {
|
|
176
|
+
CoralPink: 0, // Coral pink
|
|
177
|
+
DustyRose: 1, // Dusty rose
|
|
178
|
+
VividRed: 2, // Vivid red
|
|
179
|
+
Berry: 3, // Berry
|
|
180
|
+
SunsetOrange: 4 // Sunset orange
|
|
169
181
|
};
|
|
170
182
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
183
|
+
const ContourStyle$1 = {
|
|
184
|
+
Natural: 0, // Soft everyday contour
|
|
185
|
+
Sculpt: 1, // Deeper sculpted contour
|
|
186
|
+
Glow: 2, // Highlight-focused
|
|
187
|
+
Slim: 3, // Slim cheekbones
|
|
188
|
+
Nose: 4, // Nose bridge lift
|
|
189
|
+
Glam: 5 // Spot highlights
|
|
178
190
|
};
|
|
179
191
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
192
|
+
const EyeShadowStyle$1 = {
|
|
193
|
+
Soft: 0, // Soft wash
|
|
194
|
+
Crease: 1, // Crease deepen
|
|
195
|
+
Smoky: 2, // Smoky surround
|
|
196
|
+
Halo: 3, // Halo around the eye
|
|
197
|
+
Glow: 4, // Outer-corner glow
|
|
198
|
+
Drama: 5, // Full dramatic cover
|
|
199
|
+
Warm: 6 // Warm-tone wash
|
|
186
200
|
};
|
|
187
201
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
Horizontal: 1, // Mirror horizontally (e.g. front camera selfie)
|
|
194
|
-
Vertical: 2, // Mirror vertically
|
|
195
|
-
Both: 3 // Mirror both axes
|
|
202
|
+
const EyeShadowColor$1 = {
|
|
203
|
+
Plum: 0, // Plum / mauve (default)
|
|
204
|
+
Brown: 1, // Warm brown
|
|
205
|
+
Gold: 2, // Soft gold
|
|
206
|
+
Pink: 3 // Dusty pink
|
|
196
207
|
};
|
|
197
208
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
* @param {number} [options.mode] - Background mode (use BackgroundMode enum)
|
|
207
|
-
* @param {ImageData|HTMLImageElement|HTMLCanvasElement} [options.backgroundImage] - Background image (required when mode is Image)
|
|
208
|
-
*/
|
|
209
|
-
constructor(options = {}) {
|
|
210
|
-
this.mode = options.mode !== undefined ? options.mode : BackgroundMode$1.None;
|
|
211
|
-
this.backgroundImage = options.backgroundImage || null;
|
|
212
|
-
}
|
|
209
|
+
const EyeLinerStyle$1 = {
|
|
210
|
+
Classic: 0, // Full liner with winged tip
|
|
211
|
+
Flick: 1, // Extended outer flick
|
|
212
|
+
CatEye: 2, // Short cat-eye lift
|
|
213
|
+
Natural: 3, // Thin soft natural line
|
|
214
|
+
Bold: 4, // Bold cover
|
|
215
|
+
Soft: 5 // Soft feathered wing
|
|
216
|
+
};
|
|
213
217
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
218
|
+
const EyeLinerColor$1 = {
|
|
219
|
+
Burgundy: 0, // Burgundy brown
|
|
220
|
+
Plum: 1, // Deep plum
|
|
221
|
+
Chocolate: 2, // Chocolate brown
|
|
222
|
+
Coffee: 3, // Near-black coffee
|
|
223
|
+
Mauve: 4 // Mauve grey
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const EyebrowStyle$1 = {
|
|
227
|
+
Natural: 0, // Natural arch with hair strokes
|
|
228
|
+
Soft: 1, // Soft powdery thick brow
|
|
229
|
+
Feathered: 2, // Feathered hair strokes
|
|
230
|
+
Mist: 3, // Light powder mist
|
|
231
|
+
Arched: 4, // Classic high arch
|
|
232
|
+
Powder: 5, // Dense powder fill
|
|
233
|
+
Wild: 6, // Wispy upper edge
|
|
234
|
+
Full: 7, // Full balanced brow
|
|
235
|
+
Straight: 8 // Straight low arch
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const EyebrowColor$1 = {
|
|
239
|
+
DarkBrown: 0, // Dark brown (default)
|
|
240
|
+
Black: 1, // Black
|
|
241
|
+
SoftBrown: 2 // Soft brown
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const EyelashStyle$1 = {
|
|
245
|
+
Classic: 0, // Balanced clusters + lower lashes
|
|
246
|
+
Manga: 1, // Bold spiked clusters
|
|
247
|
+
Winged: 2, // Extended outer corner
|
|
248
|
+
Wispy: 3, // Fluffy lifted tips
|
|
249
|
+
Clustered: 4, // Distinct clusters
|
|
250
|
+
Doll: 5 // Short doll lashes
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const EyelashColor$1 = {
|
|
254
|
+
Black: 0, // Near-black (default)
|
|
255
|
+
Brown: 1, // Soft brown
|
|
256
|
+
SoftBlack: 2 // Slightly softer black
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const PupilColor$1 = {
|
|
260
|
+
Hazel: 0, // Amber / honey brown
|
|
261
|
+
Ice: 1, // Icy blue
|
|
262
|
+
Mocha: 2, // Dark brown with sparkle
|
|
263
|
+
Olive: 3, // Forest green
|
|
264
|
+
Gloss: 4, // Glossy dark brown
|
|
265
|
+
Moss: 5, // Muted olive
|
|
266
|
+
Sand: 6, // Sandy beige brown
|
|
267
|
+
Glow: 7, // Lower-iris crescent highlight
|
|
268
|
+
Slate: 8 // Cool blue-grey
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const ChromaKeyColor$1 = {
|
|
272
|
+
Green: 0,
|
|
273
|
+
Blue: 1,
|
|
274
|
+
Red: 2
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
/** Frame type for processing (affects temporal smoothing / tracking). */
|
|
278
|
+
const FrameType$1 = {
|
|
279
|
+
Image: 0, // Still image
|
|
280
|
+
Video: 1 // Continuous video stream
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
/** Mirror mode applied to the input before processing. */
|
|
284
|
+
const MirrorMode$1 = {
|
|
285
|
+
None: 0, // No mirror
|
|
286
|
+
Horizontal: 1, // Horizontal (e.g. front-camera selfie)
|
|
287
|
+
Vertical: 2, // Vertical
|
|
288
|
+
Both: 3 // Both axes
|
|
224
289
|
};
|
|
225
290
|
|
|
226
291
|
var constants = /*#__PURE__*/Object.freeze({
|
|
227
292
|
__proto__: null,
|
|
228
|
-
|
|
229
|
-
BasicParam: BasicParam$1,
|
|
230
|
-
BeautyType: BeautyType$1,
|
|
293
|
+
BlushColor: BlushColor$1,
|
|
231
294
|
BlushStyle: BlushStyle$1,
|
|
232
|
-
|
|
295
|
+
ChromaKeyColor: ChromaKeyColor$1,
|
|
296
|
+
ContourStyle: ContourStyle$1,
|
|
297
|
+
EngineEventCode: EngineEventCode$1,
|
|
298
|
+
EyeLinerColor: EyeLinerColor$1,
|
|
299
|
+
EyeLinerStyle: EyeLinerStyle$1,
|
|
300
|
+
EyeShadowColor: EyeShadowColor$1,
|
|
301
|
+
EyeShadowStyle: EyeShadowStyle$1,
|
|
302
|
+
EyebrowColor: EyebrowColor$1,
|
|
303
|
+
EyebrowStyle: EyebrowStyle$1,
|
|
304
|
+
EyelashColor: EyelashColor$1,
|
|
305
|
+
EyelashStyle: EyelashStyle$1,
|
|
233
306
|
FrameType: FrameType$1,
|
|
234
|
-
|
|
235
|
-
MakeupParam: MakeupParam$1,
|
|
307
|
+
LipstickColor: LipstickColor$1,
|
|
236
308
|
MirrorMode: MirrorMode$1,
|
|
237
|
-
|
|
238
|
-
|
|
309
|
+
PupilColor: PupilColor$1,
|
|
310
|
+
Reshape: Reshape$1,
|
|
311
|
+
SmoothingStyle: SmoothingStyle$1,
|
|
312
|
+
WhiteningStyle: WhiteningStyle$1
|
|
239
313
|
});
|
|
240
314
|
|
|
241
315
|
/**
|
|
@@ -272,17 +346,28 @@
|
|
|
272
346
|
console.warn(formatLogLine('warning', message));
|
|
273
347
|
}
|
|
274
348
|
|
|
275
|
-
/** @param {string} message */
|
|
276
|
-
function logError(message) {
|
|
277
|
-
console.error(formatLogLine('error', message));
|
|
278
|
-
}
|
|
279
|
-
|
|
280
349
|
/**
|
|
281
350
|
* Beauty Effect Engine Core
|
|
282
351
|
* Platform-agnostic engine implementation with dependency injection
|
|
283
352
|
*/
|
|
284
353
|
|
|
285
354
|
|
|
355
|
+
/** Byte layouts of fb_engine_config_t / fb_log_config_t on wasm32. */
|
|
356
|
+
const FB_ENGINE_CONFIG_SIZE = 28;
|
|
357
|
+
const FB_LOG_CONFIG_SIZE = 16;
|
|
358
|
+
|
|
359
|
+
function allocUtf8(Module, text) {
|
|
360
|
+
const value = text || '';
|
|
361
|
+
const len = Module.lengthBytesUTF8(value) + 1;
|
|
362
|
+
const ptr = Module._malloc(len);
|
|
363
|
+
Module.stringToUTF8(value, ptr, len);
|
|
364
|
+
return ptr;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function writeI32(Module, ptr, index, value) {
|
|
368
|
+
Module.HEAP32[(ptr >> 2) + index] = value;
|
|
369
|
+
}
|
|
370
|
+
|
|
286
371
|
/**
|
|
287
372
|
* Beauty effect engine class
|
|
288
373
|
* Provides high-level API for face beauty effects processing
|
|
@@ -297,9 +382,8 @@
|
|
|
297
382
|
* @param {Function} platformAPI.getWasmModule - Get WASM module instance
|
|
298
383
|
* @param {Function} platformAPI.getWasmBuffer - Get WASM memory buffer
|
|
299
384
|
* @param {Function} platformAPI.toImageData - Convert to ImageData
|
|
300
|
-
* @param {Function} platformAPI.
|
|
301
|
-
* @param {Function} [platformAPI.
|
|
302
|
-
* @param {Function} [platformAPI.cleanupGPUPixelCanvas] - Cleanup GPU canvas (browser only)
|
|
385
|
+
* @param {Function} [platformAPI.ensureFacebetterCanvas] - Ensure GPU canvas exists (browser only)
|
|
386
|
+
* @param {Function} [platformAPI.cleanupFacebetterCanvas] - Cleanup GPU canvas (browser only)
|
|
303
387
|
*/
|
|
304
388
|
constructor(config, platformAPI) {
|
|
305
389
|
if (!config) {
|
|
@@ -325,9 +409,9 @@
|
|
|
325
409
|
}
|
|
326
410
|
|
|
327
411
|
this.config = engineConfig;
|
|
328
|
-
this.
|
|
329
|
-
this.
|
|
330
|
-
this.
|
|
412
|
+
this.licenseToken = engineConfig.licenseToken;
|
|
413
|
+
this.authProxyUrl = engineConfig.authProxyUrl;
|
|
414
|
+
this.fetchAuthResponse = engineConfig.fetchAuthResponse;
|
|
331
415
|
this.resourcePath = '/resource.fbd';
|
|
332
416
|
this.enginePtr = null;
|
|
333
417
|
this.initialized = false;
|
|
@@ -345,18 +429,18 @@
|
|
|
345
429
|
this._getWasmModule = platformAPI.getWasmModule;
|
|
346
430
|
this._getWasmBuffer = platformAPI.getWasmBuffer;
|
|
347
431
|
this._toImageData = platformAPI.toImageData;
|
|
348
|
-
this.
|
|
349
|
-
this.
|
|
432
|
+
this._ensureFacebetterCanvas = platformAPI.ensureFacebetterCanvas;
|
|
433
|
+
this._cleanupFacebetterCanvas = platformAPI.cleanupFacebetterCanvas;
|
|
350
434
|
|
|
351
435
|
// Browser-specific state
|
|
352
|
-
this.
|
|
353
|
-
this.
|
|
436
|
+
this._facebetterCanvas = null;
|
|
437
|
+
this._createdFacebetterCanvas = false;
|
|
354
438
|
this._offscreenCanvas = null;
|
|
355
439
|
this._offscreenCtx = null;
|
|
356
440
|
|
|
357
|
-
// Automatically create
|
|
358
|
-
if (this.
|
|
359
|
-
this.
|
|
441
|
+
// Automatically create facebetter_canvas if it doesn't exist (browser only)
|
|
442
|
+
if (this._ensureFacebetterCanvas) {
|
|
443
|
+
this._ensureFacebetterCanvas();
|
|
360
444
|
}
|
|
361
445
|
|
|
362
446
|
// Start loading WASM module immediately in constructor
|
|
@@ -453,6 +537,10 @@
|
|
|
453
537
|
if (!Module.onReportUsage) {
|
|
454
538
|
Module.onReportUsage = async (payloadJson) => {
|
|
455
539
|
try {
|
|
540
|
+
const payload = JSON.parse(payloadJson);
|
|
541
|
+
if (!payload.app_id || !payload.hmac_signature) {
|
|
542
|
+
return true;
|
|
543
|
+
}
|
|
456
544
|
const url = 'https://facebetter.pixpark.net/facebetter/v1/report';
|
|
457
545
|
const response = await fetch(url, {
|
|
458
546
|
method: 'POST',
|
|
@@ -468,48 +556,97 @@
|
|
|
468
556
|
};
|
|
469
557
|
}
|
|
470
558
|
|
|
471
|
-
//
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
559
|
+
// Web v2 auth: never accept appId/appKey in the browser.
|
|
560
|
+
// - licenseToken: pre-fetched JWS or `{success, token}` body
|
|
561
|
+
// - authProxyUrl: production — forward the challenge to your server
|
|
562
|
+
// - fetchAuthResponse: custom hook; locally use createDirectAuthFetcher
|
|
563
|
+
if (!this.licenseToken) {
|
|
564
|
+
if (typeof this.fetchAuthResponse === 'function') {
|
|
565
|
+
Module.onOnlineAuth = async (payloadJson) => {
|
|
566
|
+
try {
|
|
567
|
+
const challenge = JSON.parse(payloadJson);
|
|
568
|
+
const result = await this.fetchAuthResponse(challenge);
|
|
569
|
+
if (result && typeof result === 'object' && typeof result.response === 'string') {
|
|
570
|
+
return { response: result.response };
|
|
571
|
+
}
|
|
572
|
+
if (typeof result === 'string') {
|
|
573
|
+
return result;
|
|
574
|
+
}
|
|
575
|
+
if (result && typeof result === 'object') {
|
|
576
|
+
return JSON.stringify(result);
|
|
577
|
+
}
|
|
486
578
|
return '';
|
|
579
|
+
} catch (err) {
|
|
580
|
+
logWarn(
|
|
581
|
+
`Online license: fetchAuthResponse error${err?.message ? `: ${err.message}` : ''}`
|
|
582
|
+
);
|
|
583
|
+
return JSON.stringify({
|
|
584
|
+
success: false,
|
|
585
|
+
error: 'auth_fetch_failed',
|
|
586
|
+
message: err?.message || 'auth fetch failed',
|
|
587
|
+
});
|
|
487
588
|
}
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
589
|
+
};
|
|
590
|
+
} else if (this.authProxyUrl) {
|
|
591
|
+
Module.onOnlineAuth = async (payloadJson) => {
|
|
592
|
+
logInfo('Online license: proxying auth challenge');
|
|
593
|
+
try {
|
|
594
|
+
const response = await fetch(this.authProxyUrl, {
|
|
595
|
+
method: 'POST',
|
|
596
|
+
headers: { 'Content-Type': 'application/json' },
|
|
597
|
+
body: payloadJson,
|
|
598
|
+
});
|
|
599
|
+
if (!response.ok) {
|
|
600
|
+
logWarn(`Online license: proxy HTTP ${response.status}`);
|
|
601
|
+
return '';
|
|
602
|
+
}
|
|
603
|
+
const text = await response.text();
|
|
604
|
+
logInfo('Online license: proxy response ok');
|
|
605
|
+
return text;
|
|
606
|
+
} catch (err) {
|
|
607
|
+
logWarn(
|
|
608
|
+
`Online license: proxy error${err?.message ? `: ${err.message}` : ''}`
|
|
609
|
+
);
|
|
610
|
+
return '';
|
|
611
|
+
}
|
|
612
|
+
};
|
|
613
|
+
} else {
|
|
614
|
+
throw new FacebetterError(
|
|
615
|
+
'Web auth requires licenseToken, authProxyUrl, or fetchAuthResponse.',
|
|
616
|
+
'LICENSE_ERROR'
|
|
617
|
+
);
|
|
618
|
+
}
|
|
498
619
|
}
|
|
499
620
|
|
|
500
|
-
//
|
|
501
|
-
const
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
);
|
|
621
|
+
// C ABI still has app_id/app_key slots; leave them empty on Web.
|
|
622
|
+
const appIdPtr = allocUtf8(Module, '');
|
|
623
|
+
const appKeyPtr = allocUtf8(Module, '');
|
|
624
|
+
const licensePtr = allocUtf8(Module, this.licenseToken);
|
|
625
|
+
const resourcePtr = allocUtf8(Module, this.resourcePath);
|
|
626
|
+
const configPtr = Module._malloc(FB_ENGINE_CONFIG_SIZE);
|
|
627
|
+
writeI32(Module, configPtr, 0, appIdPtr);
|
|
628
|
+
writeI32(Module, configPtr, 1, appKeyPtr);
|
|
629
|
+
writeI32(Module, configPtr, 2, licensePtr);
|
|
630
|
+
writeI32(Module, configPtr, 3, resourcePtr);
|
|
631
|
+
writeI32(Module, configPtr, 4, 0);
|
|
632
|
+
writeI32(Module, configPtr, 5, this.config.externalContext ? 1 : 0);
|
|
633
|
+
writeI32(Module, configPtr, 6, 0);
|
|
634
|
+
|
|
635
|
+
let enginePtr = 0;
|
|
636
|
+
try {
|
|
637
|
+
enginePtr = Module.ccall(
|
|
638
|
+
'fb_engine_create',
|
|
639
|
+
'number',
|
|
640
|
+
['number'],
|
|
641
|
+
[configPtr]
|
|
642
|
+
);
|
|
643
|
+
} finally {
|
|
644
|
+
Module._free(configPtr);
|
|
645
|
+
Module._free(appIdPtr);
|
|
646
|
+
Module._free(appKeyPtr);
|
|
647
|
+
Module._free(licensePtr);
|
|
648
|
+
Module._free(resourcePtr);
|
|
649
|
+
}
|
|
513
650
|
|
|
514
651
|
if (!enginePtr) {
|
|
515
652
|
throw new FacebetterError(
|
|
@@ -557,7 +694,10 @@
|
|
|
557
694
|
this._callbackSharedBufferSize = 0;
|
|
558
695
|
}
|
|
559
696
|
|
|
560
|
-
Module.ccall('
|
|
697
|
+
Module.ccall('fb_engine_destroy', null, ['number'], [this.enginePtr]);
|
|
698
|
+
if (Module._engine_event_callbacks) {
|
|
699
|
+
delete Module._engine_event_callbacks[this.enginePtr];
|
|
700
|
+
}
|
|
561
701
|
this.enginePtr = null;
|
|
562
702
|
this.initialized = false;
|
|
563
703
|
this.bufferSize = 0;
|
|
@@ -569,15 +709,15 @@
|
|
|
569
709
|
this._offscreenCtx = null;
|
|
570
710
|
}
|
|
571
711
|
|
|
572
|
-
// Clean up
|
|
573
|
-
if (this.
|
|
574
|
-
this.
|
|
712
|
+
// Clean up facebetter_canvas if we created it
|
|
713
|
+
if (this._cleanupFacebetterCanvas) {
|
|
714
|
+
this._cleanupFacebetterCanvas();
|
|
575
715
|
}
|
|
576
716
|
}
|
|
577
717
|
|
|
578
718
|
/**
|
|
579
719
|
* Sets log configuration
|
|
580
|
-
* Can be called before init() since
|
|
720
|
+
* Can be called before init() since fb_set_log_config is a global function
|
|
581
721
|
* @param {Object} config - Log configuration
|
|
582
722
|
* @param {boolean} config.consoleEnabled - Enable console logging
|
|
583
723
|
* @param {boolean} config.fileEnabled - Enable file logging
|
|
@@ -590,406 +730,415 @@
|
|
|
590
730
|
await this._wasmLoadPromise;
|
|
591
731
|
const Module = this._getWasmModule();
|
|
592
732
|
|
|
593
|
-
const
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
733
|
+
const fileNamePtr = allocUtf8(Module, config.fileName);
|
|
734
|
+
const configPtr = Module._malloc(FB_LOG_CONFIG_SIZE);
|
|
735
|
+
writeI32(Module, configPtr, 0, config.consoleEnabled ? 1 : 0);
|
|
736
|
+
writeI32(Module, configPtr, 1, config.fileEnabled ? 1 : 0);
|
|
737
|
+
writeI32(Module, configPtr, 2, config.level || 0);
|
|
738
|
+
writeI32(Module, configPtr, 3, fileNamePtr);
|
|
739
|
+
|
|
740
|
+
let result;
|
|
741
|
+
try {
|
|
742
|
+
result = Module.ccall(
|
|
743
|
+
'fb_set_log_config',
|
|
744
|
+
'number',
|
|
745
|
+
['number'],
|
|
746
|
+
[configPtr]
|
|
747
|
+
);
|
|
748
|
+
} finally {
|
|
749
|
+
Module._free(configPtr);
|
|
750
|
+
Module._free(fileNamePtr);
|
|
751
|
+
}
|
|
604
752
|
|
|
605
753
|
checkResult(result, 'Failed to set log config');
|
|
606
754
|
}
|
|
607
755
|
|
|
608
756
|
/**
|
|
609
|
-
*
|
|
610
|
-
* -
|
|
611
|
-
* - setVirtualBackground
|
|
612
|
-
* - setFilter/setSticker
|
|
613
|
-
* Kept for compatibility and may be removed in a future release.
|
|
614
|
-
* @deprecated
|
|
615
|
-
* @param {number} beautyType - Beauty type (use BeautyType enum)
|
|
616
|
-
* @param {boolean} enabled - Enable or disable
|
|
757
|
+
* Sets skin smoothing intensity
|
|
758
|
+
* @param {number} value - Parameter value (0.0 - 1.0)
|
|
617
759
|
*/
|
|
618
|
-
|
|
760
|
+
setSmoothing(value) {
|
|
619
761
|
this._ensureInitialized();
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
const result = Module.ccall(
|
|
623
|
-
'SetBeautyTypeEnabled',
|
|
624
|
-
'number',
|
|
625
|
-
['number', 'number', 'number'],
|
|
626
|
-
[this.enginePtr, beautyType, enabled ? 1 : 0]
|
|
627
|
-
);
|
|
762
|
+
this._setIntensity('fb_set_smoothing', value);
|
|
763
|
+
}
|
|
628
764
|
|
|
629
|
-
|
|
765
|
+
/**
|
|
766
|
+
* Sets skin smoothing style
|
|
767
|
+
* @param {number} style - Style (use SmoothingStyle enum, e.g., SmoothingStyle.Natural)
|
|
768
|
+
*/
|
|
769
|
+
setSmoothingStyle(style) {
|
|
770
|
+
this._setMakeupStyle('fb_set_smoothing_style', style, 'Failed to set smoothing style');
|
|
630
771
|
}
|
|
631
772
|
|
|
632
773
|
/**
|
|
633
|
-
*
|
|
634
|
-
*
|
|
635
|
-
* Kept for compatibility and may be removed in a future release.
|
|
636
|
-
* @deprecated
|
|
637
|
-
* @param {number} beautyType - Beauty type (use BeautyType enum)
|
|
638
|
-
* @returns {boolean} True if enabled
|
|
774
|
+
* Sets skin whitening intensity
|
|
775
|
+
* @param {number} value - Parameter value (0.0 - 1.0)
|
|
639
776
|
*/
|
|
640
|
-
|
|
777
|
+
setWhitening(value) {
|
|
641
778
|
this._ensureInitialized();
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
const result = Module.ccall(
|
|
645
|
-
'IsBeautyTypeEnabled',
|
|
646
|
-
'number',
|
|
647
|
-
['number', 'number'],
|
|
648
|
-
[this.enginePtr, beautyType]
|
|
649
|
-
);
|
|
650
|
-
|
|
651
|
-
if (result === -1) {
|
|
652
|
-
throw new FacebetterError('Failed to check beauty type status');
|
|
653
|
-
}
|
|
779
|
+
this._setIntensity('fb_set_whitening', value);
|
|
780
|
+
}
|
|
654
781
|
|
|
655
|
-
|
|
782
|
+
/**
|
|
783
|
+
* Sets whitening style by swapping the custom LUT
|
|
784
|
+
* @param {number} style - Style (use WhiteningStyle enum, e.g., WhiteningStyle.ColdWhite)
|
|
785
|
+
*/
|
|
786
|
+
setWhiteningStyle(style) {
|
|
787
|
+
this._setMakeupStyle('fb_set_whitening_style', style, 'Failed to set whitening style');
|
|
656
788
|
}
|
|
657
789
|
|
|
658
790
|
/**
|
|
659
|
-
*
|
|
660
|
-
*
|
|
661
|
-
* be removed in a future release.
|
|
662
|
-
* @deprecated
|
|
791
|
+
* Sets image sharpening intensity
|
|
792
|
+
* @param {number} value - Parameter value (0.0 - 1.0)
|
|
663
793
|
*/
|
|
664
|
-
|
|
794
|
+
setSharpening(value) {
|
|
665
795
|
this._ensureInitialized();
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
const result = Module.ccall(
|
|
669
|
-
'DisableAllBeautyTypes',
|
|
670
|
-
'number',
|
|
671
|
-
['number'],
|
|
672
|
-
[this.enginePtr]
|
|
673
|
-
);
|
|
674
|
-
|
|
675
|
-
checkResult(result, 'Failed to disable all beauty types');
|
|
796
|
+
this._setIntensity('fb_set_sharpening', value);
|
|
676
797
|
}
|
|
677
798
|
|
|
678
799
|
/**
|
|
679
|
-
* Sets
|
|
680
|
-
* @param {number} param - Parameter (use BasicParam enum, e.g., BasicParam.Smoothing)
|
|
800
|
+
* Sets skin rosiness intensity
|
|
681
801
|
* @param {number} value - Parameter value (0.0 - 1.0)
|
|
682
802
|
*/
|
|
683
|
-
|
|
803
|
+
setRosiness(value) {
|
|
684
804
|
this._ensureInitialized();
|
|
685
|
-
this.
|
|
805
|
+
this._setIntensity('fb_set_rosiness', value);
|
|
686
806
|
}
|
|
687
807
|
|
|
688
808
|
/**
|
|
689
809
|
* Sets a reshape parameter
|
|
690
|
-
* @param {number} param - Parameter (use
|
|
691
|
-
* @param {number} value - Parameter value
|
|
810
|
+
* @param {number} param - Parameter (use Reshape enum, e.g., Reshape.FaceThin)
|
|
811
|
+
* @param {number} value - Parameter value in [-1.0, 1.0]. 0 is off.
|
|
692
812
|
*/
|
|
693
|
-
|
|
813
|
+
setReshape(param, value) {
|
|
694
814
|
this._ensureInitialized();
|
|
695
|
-
this._setBeautyParam('
|
|
815
|
+
this._setBeautyParam('fb_set_reshape', param, value);
|
|
696
816
|
}
|
|
697
817
|
|
|
698
818
|
/**
|
|
699
|
-
* Sets
|
|
700
|
-
* @param {number} param - Parameter (use MakeupParam enum, e.g., MakeupParam.Lipstick)
|
|
819
|
+
* Sets lipstick intensity
|
|
701
820
|
* @param {number} value - Parameter value (0.0 - 1.0)
|
|
702
821
|
*/
|
|
703
|
-
|
|
822
|
+
setLipstick(value) {
|
|
704
823
|
this._ensureInitialized();
|
|
705
|
-
this.
|
|
824
|
+
this._setIntensity('fb_set_lipstick', value);
|
|
706
825
|
}
|
|
707
826
|
|
|
708
827
|
/**
|
|
709
|
-
* Sets lipstick
|
|
710
|
-
* @param {number} style - Style (use
|
|
828
|
+
* Sets lipstick colour preset
|
|
829
|
+
* @param {number} style - Style (use LipstickColor enum, e.g., LipstickColor.Rouge)
|
|
711
830
|
*/
|
|
712
|
-
|
|
713
|
-
this.
|
|
714
|
-
const Module = this._getWasmModule();
|
|
715
|
-
const result = Module.ccall(
|
|
716
|
-
'SetLipstickStyle',
|
|
717
|
-
'number',
|
|
718
|
-
['number', 'number'],
|
|
719
|
-
[this.enginePtr, style]
|
|
720
|
-
);
|
|
721
|
-
checkResult(result, 'Failed to set lipstick style');
|
|
831
|
+
setLipstickColor(style) {
|
|
832
|
+
this._setMakeupStyle('fb_set_lipstick_color', style, 'Failed to set lipstick style');
|
|
722
833
|
}
|
|
723
834
|
|
|
724
835
|
/**
|
|
725
|
-
* Sets blush
|
|
726
|
-
* @param {number}
|
|
836
|
+
* Sets blush intensity
|
|
837
|
+
* @param {number} value - Parameter value (0.0 - 1.0)
|
|
727
838
|
*/
|
|
839
|
+
setBlush(value) {
|
|
840
|
+
this._ensureInitialized();
|
|
841
|
+
this._setIntensity('fb_set_blush', value);
|
|
842
|
+
}
|
|
843
|
+
|
|
728
844
|
setBlushStyle(style) {
|
|
845
|
+
this._setMakeupStyle('fb_set_blush_style', style, 'Failed to set blush style');
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
setBlushColor(color) {
|
|
849
|
+
this._setMakeupStyle('fb_set_blush_color', color, 'Failed to set blush color');
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
setContour(value) {
|
|
729
853
|
this._ensureInitialized();
|
|
730
|
-
|
|
731
|
-
const result = Module.ccall(
|
|
732
|
-
'SetBlushStyle',
|
|
733
|
-
'number',
|
|
734
|
-
['number', 'number'],
|
|
735
|
-
[this.enginePtr, style]
|
|
736
|
-
);
|
|
737
|
-
checkResult(result, 'Failed to set blush style');
|
|
854
|
+
this._setIntensity('fb_set_contour', value);
|
|
738
855
|
}
|
|
739
856
|
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
setChromaKeyParam(param, value) {
|
|
857
|
+
setContourStyle(style) {
|
|
858
|
+
this._setMakeupStyle('fb_set_contour_style', style, 'Failed to set contour style');
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
setEyeShadow(value) {
|
|
746
862
|
this._ensureInitialized();
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
this.
|
|
863
|
+
this._setIntensity('fb_set_eye_shadow', value);
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
setEyeShadowStyle(style) {
|
|
867
|
+
this._setMakeupStyle('fb_set_eye_shadow_style', style, 'Failed to set eyeshadow style');
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
setEyeShadowColor(style) {
|
|
871
|
+
this._setMakeupStyle('fb_set_eye_shadow_color', style, 'Failed to set eyeshadow style');
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
setEyeLiner(value) {
|
|
875
|
+
this._ensureInitialized();
|
|
876
|
+
this._setIntensity('fb_set_eye_liner', value);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
setEyeLinerStyle(style) {
|
|
880
|
+
this._setMakeupStyle('fb_set_eye_liner_style', style, 'Failed to set eyeliner style');
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
setEyeLinerColor(style) {
|
|
884
|
+
this._setMakeupStyle('fb_set_eye_liner_color', style, 'Failed to set eyeliner style');
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
setEyebrow(value) {
|
|
888
|
+
this._ensureInitialized();
|
|
889
|
+
this._setIntensity('fb_set_eyebrow', value);
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
setEyebrowStyle(style) {
|
|
893
|
+
this._setMakeupStyle('fb_set_eyebrow_style', style, 'Failed to set eyebrow style');
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
setEyebrowColor(style) {
|
|
897
|
+
this._setMakeupStyle('fb_set_eyebrow_color', style, 'Failed to set eyebrow style');
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
setEyelash(value) {
|
|
901
|
+
this._ensureInitialized();
|
|
902
|
+
this._setIntensity('fb_set_eyelash', value);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
setEyelashStyle(style) {
|
|
906
|
+
this._setMakeupStyle('fb_set_eyelash_style', style, 'Failed to set eyelash style');
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
setEyelashColor(style) {
|
|
910
|
+
this._setMakeupStyle('fb_set_eyelash_color', style, 'Failed to set eyelash style');
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
setPupil(value) {
|
|
914
|
+
this._ensureInitialized();
|
|
915
|
+
this._setIntensity('fb_set_pupil', value);
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
setPupilColor(style) {
|
|
919
|
+
this._setMakeupStyle('fb_set_pupil_color', style, 'Failed to set pupil style');
|
|
760
920
|
}
|
|
761
921
|
|
|
762
922
|
/**
|
|
763
|
-
*
|
|
764
|
-
*
|
|
923
|
+
* Uses chroma keying as the virtual-background mask.
|
|
924
|
+
* Fill is still controlled by setVirtualBackgroundBlur / setVirtualBackground.
|
|
925
|
+
* @param {number} color - Key colour (use ChromaKeyColor enum)
|
|
765
926
|
*/
|
|
766
|
-
|
|
767
|
-
this.
|
|
768
|
-
const Module = this._getWasmModule();
|
|
769
|
-
const result = Module.ccall(
|
|
770
|
-
'SetFilter',
|
|
771
|
-
'number',
|
|
772
|
-
['number', 'string'],
|
|
773
|
-
[this.enginePtr, filterId || '']
|
|
774
|
-
);
|
|
775
|
-
checkResult(result, 'Failed to set filter');
|
|
927
|
+
setChromaKey(color) {
|
|
928
|
+
this._setMakeupStyle('fb_set_chroma_key', color, 'Failed to set chroma key');
|
|
776
929
|
}
|
|
777
930
|
|
|
778
931
|
/**
|
|
779
|
-
*
|
|
780
|
-
* @param {number} intensity - Filter intensity (0.0 - 1.0)
|
|
932
|
+
* Turns off chroma keying and restores portrait-segmentation as the mask.
|
|
781
933
|
*/
|
|
782
|
-
|
|
934
|
+
clearChromaKey() {
|
|
783
935
|
this._ensureInitialized();
|
|
784
936
|
const Module = this._getWasmModule();
|
|
785
937
|
const result = Module.ccall(
|
|
786
|
-
'
|
|
938
|
+
'fb_clear_chroma_key',
|
|
787
939
|
'number',
|
|
788
|
-
['number'
|
|
789
|
-
[this.enginePtr
|
|
940
|
+
['number'],
|
|
941
|
+
[this.enginePtr]
|
|
790
942
|
);
|
|
791
|
-
checkResult(result, 'Failed to
|
|
943
|
+
checkResult(result, 'Failed to clear chroma key');
|
|
792
944
|
}
|
|
793
945
|
|
|
794
|
-
/**
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
946
|
+
/** How close a pixel must be to the key colour to be keyed out. [0.0, 1.0]. */
|
|
947
|
+
setChromaKeySimilarity(value) {
|
|
948
|
+
this._setChromaKeyFloat('fb_set_chroma_key_similarity', value);
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
/** Edge feather around the key. [0.0, 1.0]. */
|
|
952
|
+
setChromaKeySmoothness(value) {
|
|
953
|
+
this._setChromaKeyFloat('fb_set_chroma_key_smoothness', value);
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
/** Spill suppression on semi-transparent edges. [0.0, 1.0]. */
|
|
957
|
+
setChromaKeyDesaturation(value) {
|
|
958
|
+
this._setChromaKeyFloat('fb_set_chroma_key_desaturation', value);
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
_setChromaKeyFloat(cFuncName, value) {
|
|
799
962
|
this._ensureInitialized();
|
|
800
963
|
const Module = this._getWasmModule();
|
|
801
964
|
const result = Module.ccall(
|
|
802
|
-
|
|
965
|
+
cFuncName,
|
|
803
966
|
'number',
|
|
804
|
-
['number', '
|
|
805
|
-
[this.enginePtr,
|
|
967
|
+
['number', 'number'],
|
|
968
|
+
[this.enginePtr, value]
|
|
806
969
|
);
|
|
807
|
-
checkResult(result,
|
|
970
|
+
checkResult(result, `Failed to call ${cFuncName}`);
|
|
808
971
|
}
|
|
809
972
|
|
|
810
973
|
/**
|
|
811
|
-
*
|
|
812
|
-
* @param {string}
|
|
813
|
-
* @param {string|Uint8Array} resource - Path to .fbd file or Uint8Array data
|
|
974
|
+
* Applies a LUT filter from a file path or in-memory .fbd data.
|
|
975
|
+
* @param {string|Uint8Array} resource - Path to .fbd file or Uint8Array data.
|
|
814
976
|
*/
|
|
815
|
-
|
|
977
|
+
setFilter(resource) {
|
|
816
978
|
this._ensureInitialized();
|
|
817
979
|
const Module = this._getWasmModule();
|
|
818
980
|
|
|
819
981
|
if (typeof resource === 'string') {
|
|
982
|
+
if (!resource) {
|
|
983
|
+
throw new FacebetterError('Filter path must not be empty; use clearFilter()');
|
|
984
|
+
}
|
|
820
985
|
const result = Module.ccall(
|
|
821
|
-
'
|
|
986
|
+
'fb_set_filter',
|
|
822
987
|
'number',
|
|
823
|
-
['number', 'string'
|
|
824
|
-
[this.enginePtr,
|
|
988
|
+
['number', 'string'],
|
|
989
|
+
[this.enginePtr, resource]
|
|
825
990
|
);
|
|
826
|
-
checkResult(result,
|
|
827
|
-
|
|
991
|
+
checkResult(result, 'Failed to set filter from path');
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
if (resource instanceof Uint8Array) {
|
|
828
996
|
const dataPtr = Module._malloc(resource.length);
|
|
829
997
|
Module.HEAPU8.set(resource, dataPtr);
|
|
830
998
|
try {
|
|
831
999
|
const result = Module.ccall(
|
|
832
|
-
'
|
|
1000
|
+
'fb_set_filter_data',
|
|
833
1001
|
'number',
|
|
834
|
-
['number', '
|
|
835
|
-
[this.enginePtr,
|
|
1002
|
+
['number', 'number', 'number'],
|
|
1003
|
+
[this.enginePtr, dataPtr, resource.length]
|
|
836
1004
|
);
|
|
837
|
-
checkResult(result,
|
|
1005
|
+
checkResult(result, 'Failed to set filter from data');
|
|
838
1006
|
} finally {
|
|
839
1007
|
Module._free(dataPtr);
|
|
840
1008
|
}
|
|
841
|
-
|
|
842
|
-
throw new FacebetterError('Resource must be a string path or Uint8Array data');
|
|
1009
|
+
return;
|
|
843
1010
|
}
|
|
1011
|
+
|
|
1012
|
+
throw new FacebetterError('Filter resource must be a string path or Uint8Array');
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
/**
|
|
1016
|
+
* Clears the current LUT filter.
|
|
1017
|
+
*/
|
|
1018
|
+
clearFilter() {
|
|
1019
|
+
this._ensureInitialized();
|
|
1020
|
+
const Module = this._getWasmModule();
|
|
1021
|
+
const result = Module.ccall(
|
|
1022
|
+
'fb_clear_filter',
|
|
1023
|
+
'number',
|
|
1024
|
+
['number'],
|
|
1025
|
+
[this.enginePtr]
|
|
1026
|
+
);
|
|
1027
|
+
checkResult(result, 'Failed to clear filter');
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
/**
|
|
1031
|
+
* Sets the intensity of the current filter
|
|
1032
|
+
* @param {number} intensity - Filter intensity (0.0 - 1.0)
|
|
1033
|
+
*/
|
|
1034
|
+
setFilterIntensity(intensity) {
|
|
1035
|
+
this._ensureInitialized();
|
|
1036
|
+
const Module = this._getWasmModule();
|
|
1037
|
+
const result = Module.ccall(
|
|
1038
|
+
'fb_set_filter_intensity',
|
|
1039
|
+
'number',
|
|
1040
|
+
['number', 'number'],
|
|
1041
|
+
[this.enginePtr, intensity]
|
|
1042
|
+
);
|
|
1043
|
+
checkResult(result, 'Failed to set filter intensity');
|
|
844
1044
|
}
|
|
845
1045
|
|
|
846
1046
|
/**
|
|
847
|
-
*
|
|
848
|
-
* @param {string}
|
|
849
|
-
* @param {string|Uint8Array} resource - Path to .fbd file or Uint8Array data
|
|
1047
|
+
* Applies a 2D sticker from a file path or in-memory .fbd data.
|
|
1048
|
+
* @param {string|Uint8Array} resource - Path to .fbd file or Uint8Array data.
|
|
850
1049
|
*/
|
|
851
|
-
|
|
1050
|
+
setSticker(resource) {
|
|
852
1051
|
this._ensureInitialized();
|
|
853
1052
|
const Module = this._getWasmModule();
|
|
854
1053
|
|
|
855
1054
|
if (typeof resource === 'string') {
|
|
1055
|
+
if (!resource) {
|
|
1056
|
+
throw new FacebetterError('Sticker path must not be empty; use clearSticker()');
|
|
1057
|
+
}
|
|
856
1058
|
const result = Module.ccall(
|
|
857
|
-
'
|
|
1059
|
+
'fb_set_sticker',
|
|
858
1060
|
'number',
|
|
859
|
-
['number', 'string'
|
|
860
|
-
[this.enginePtr,
|
|
1061
|
+
['number', 'string'],
|
|
1062
|
+
[this.enginePtr, resource]
|
|
861
1063
|
);
|
|
862
|
-
checkResult(result,
|
|
863
|
-
|
|
1064
|
+
checkResult(result, 'Failed to set sticker from path');
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
if (resource instanceof Uint8Array) {
|
|
864
1069
|
const dataPtr = Module._malloc(resource.length);
|
|
865
1070
|
Module.HEAPU8.set(resource, dataPtr);
|
|
866
1071
|
try {
|
|
867
1072
|
const result = Module.ccall(
|
|
868
|
-
'
|
|
1073
|
+
'fb_set_sticker_data',
|
|
869
1074
|
'number',
|
|
870
|
-
['number', '
|
|
871
|
-
[this.enginePtr,
|
|
1075
|
+
['number', 'number', 'number'],
|
|
1076
|
+
[this.enginePtr, dataPtr, resource.length]
|
|
872
1077
|
);
|
|
873
|
-
checkResult(result,
|
|
1078
|
+
checkResult(result, 'Failed to set sticker from data');
|
|
874
1079
|
} finally {
|
|
875
1080
|
Module._free(dataPtr);
|
|
876
1081
|
}
|
|
877
|
-
|
|
878
|
-
throw new FacebetterError('Resource must be a string path or Uint8Array data');
|
|
1082
|
+
return;
|
|
879
1083
|
}
|
|
1084
|
+
|
|
1085
|
+
throw new FacebetterError('Sticker resource must be a string path or Uint8Array');
|
|
880
1086
|
}
|
|
881
1087
|
|
|
882
1088
|
/**
|
|
883
|
-
*
|
|
884
|
-
* @param {string} filterId - Filter ID to unregister
|
|
1089
|
+
* Clears the current 2D sticker.
|
|
885
1090
|
*/
|
|
886
|
-
|
|
1091
|
+
clearSticker() {
|
|
887
1092
|
this._ensureInitialized();
|
|
888
1093
|
const Module = this._getWasmModule();
|
|
889
1094
|
const result = Module.ccall(
|
|
890
|
-
'
|
|
1095
|
+
'fb_clear_sticker',
|
|
891
1096
|
'number',
|
|
892
|
-
['number'
|
|
893
|
-
[this.enginePtr
|
|
1097
|
+
['number'],
|
|
1098
|
+
[this.enginePtr]
|
|
894
1099
|
);
|
|
895
|
-
checkResult(result,
|
|
1100
|
+
checkResult(result, 'Failed to clear sticker');
|
|
896
1101
|
}
|
|
897
1102
|
|
|
898
1103
|
/**
|
|
899
|
-
*
|
|
1104
|
+
* Internal method to set a single intensity parameter
|
|
1105
|
+
* @private
|
|
900
1106
|
*/
|
|
901
|
-
|
|
902
|
-
|
|
1107
|
+
_setIntensity(functionName, value) {
|
|
1108
|
+
if (value < 0 || value > 1) {
|
|
1109
|
+
throw new FacebetterError('Parameter value must be between 0.0 and 1.0');
|
|
1110
|
+
}
|
|
1111
|
+
|
|
903
1112
|
const Module = this._getWasmModule();
|
|
904
1113
|
const result = Module.ccall(
|
|
905
|
-
|
|
1114
|
+
functionName,
|
|
906
1115
|
'number',
|
|
907
|
-
['number'],
|
|
908
|
-
[this.enginePtr]
|
|
1116
|
+
['number', 'number'],
|
|
1117
|
+
[this.enginePtr, value]
|
|
909
1118
|
);
|
|
910
|
-
|
|
1119
|
+
|
|
1120
|
+
checkResult(result, `Failed to set beauty parameter`);
|
|
911
1121
|
}
|
|
912
1122
|
|
|
913
|
-
|
|
914
|
-
* Unregisters a specific sticker
|
|
915
|
-
* @param {string} stickerId - Sticker ID to unregister
|
|
916
|
-
*/
|
|
917
|
-
unregisterSticker(stickerId) {
|
|
1123
|
+
_setMakeupStyle(cFuncName, value, errorMessage) {
|
|
918
1124
|
this._ensureInitialized();
|
|
919
1125
|
const Module = this._getWasmModule();
|
|
920
1126
|
const result = Module.ccall(
|
|
921
|
-
|
|
1127
|
+
cFuncName,
|
|
922
1128
|
'number',
|
|
923
|
-
['number', '
|
|
924
|
-
[this.enginePtr,
|
|
1129
|
+
['number', 'number'],
|
|
1130
|
+
[this.enginePtr, value]
|
|
925
1131
|
);
|
|
926
|
-
checkResult(result,
|
|
1132
|
+
checkResult(result, errorMessage);
|
|
927
1133
|
}
|
|
928
1134
|
|
|
929
1135
|
/**
|
|
930
|
-
*
|
|
931
|
-
*/
|
|
932
|
-
unregisterAllStickers() {
|
|
933
|
-
this._ensureInitialized();
|
|
934
|
-
const Module = this._getWasmModule();
|
|
935
|
-
const result = Module.ccall(
|
|
936
|
-
'UnregisterAllStickers',
|
|
937
|
-
'number',
|
|
938
|
-
['number'],
|
|
939
|
-
[this.enginePtr]
|
|
940
|
-
);
|
|
941
|
-
checkResult(result, 'Failed to unregister all stickers');
|
|
942
|
-
}
|
|
943
|
-
|
|
944
|
-
/**
|
|
945
|
-
* Gets the list of registered filter IDs
|
|
946
|
-
* @returns {string[]} Array of registered filter IDs
|
|
947
|
-
*/
|
|
948
|
-
getRegisteredFilters() {
|
|
949
|
-
this._ensureInitialized();
|
|
950
|
-
const Module = this._getWasmModule();
|
|
951
|
-
const jsonStr = Module.ccall(
|
|
952
|
-
'GetRegisteredFilters',
|
|
953
|
-
'string',
|
|
954
|
-
['number'],
|
|
955
|
-
[this.enginePtr]
|
|
956
|
-
);
|
|
957
|
-
try {
|
|
958
|
-
return JSON.parse(jsonStr || '[]');
|
|
959
|
-
} catch (e) {
|
|
960
|
-
logError(`Failed to parse registered filters JSON: ${e}`);
|
|
961
|
-
return [];
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
/**
|
|
966
|
-
* Gets the list of registered sticker IDs
|
|
967
|
-
* @returns {string[]} Array of registered sticker IDs
|
|
968
|
-
*/
|
|
969
|
-
getRegisteredStickers() {
|
|
970
|
-
this._ensureInitialized();
|
|
971
|
-
const Module = this._getWasmModule();
|
|
972
|
-
const jsonStr = Module.ccall(
|
|
973
|
-
'GetRegisteredStickers',
|
|
974
|
-
'string',
|
|
975
|
-
['number'],
|
|
976
|
-
[this.enginePtr]
|
|
977
|
-
);
|
|
978
|
-
try {
|
|
979
|
-
return JSON.parse(jsonStr || '[]');
|
|
980
|
-
} catch (e) {
|
|
981
|
-
logError(`Failed to parse registered stickers JSON: ${e}`);
|
|
982
|
-
return [];
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
/**
|
|
987
|
-
* Internal method to set beauty parameters
|
|
1136
|
+
* Internal method to set beauty parameters
|
|
988
1137
|
* @private
|
|
989
1138
|
*/
|
|
990
1139
|
_setBeautyParam(functionName, param, value) {
|
|
991
|
-
if (value <
|
|
992
|
-
throw new FacebetterError('Parameter value must be between
|
|
1140
|
+
if (value < -1 || value > 1) {
|
|
1141
|
+
throw new FacebetterError('Parameter value must be between -1.0 and 1.0');
|
|
993
1142
|
}
|
|
994
1143
|
|
|
995
1144
|
const Module = this._getWasmModule();
|
|
@@ -1004,15 +1153,25 @@
|
|
|
1004
1153
|
}
|
|
1005
1154
|
|
|
1006
1155
|
/**
|
|
1007
|
-
* Sets engine callbacks (face landmarks
|
|
1156
|
+
* Sets engine callbacks (face landmarks and engine events)
|
|
1008
1157
|
* @param {Object} callbacks - Callback functions
|
|
1009
|
-
* @param {Function} callbacks.onFaceLandmarks - Callback for face landmarks detection
|
|
1158
|
+
* @param {Function} [callbacks.onFaceLandmarks] - Callback for face landmarks detection
|
|
1159
|
+
* @param {Function} [callbacks.onEngineEvent] - Callback for license / init events (code, message)
|
|
1010
1160
|
* @param {number} [callbacks.maxFaces=10] - Maximum number of faces to support (affects shared buffer size)
|
|
1011
1161
|
*/
|
|
1012
1162
|
setCallbacks(callbacks) {
|
|
1013
1163
|
this._ensureInitialized();
|
|
1014
1164
|
const Module = this._getWasmModule();
|
|
1015
|
-
|
|
1165
|
+
|
|
1166
|
+
if (!Module._engine_event_callbacks) {
|
|
1167
|
+
Module._engine_event_callbacks = {};
|
|
1168
|
+
}
|
|
1169
|
+
if (callbacks && typeof callbacks.onEngineEvent === 'function') {
|
|
1170
|
+
Module._engine_event_callbacks[this.enginePtr] = callbacks.onEngineEvent;
|
|
1171
|
+
} else {
|
|
1172
|
+
delete Module._engine_event_callbacks[this.enginePtr];
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1016
1175
|
// Initialize callback storage (if it doesn't exist)
|
|
1017
1176
|
if (!Module._face_landmarks_callbacks) {
|
|
1018
1177
|
Module._face_landmarks_callbacks = [];
|
|
@@ -1028,9 +1187,9 @@
|
|
|
1028
1187
|
|
|
1029
1188
|
// Pre-allocate shared memory
|
|
1030
1189
|
// Metadata: 2 ints (frame_number, face_count) = 8 bytes
|
|
1031
|
-
// Face data: maxFaces *
|
|
1190
|
+
// Face data: maxFaces * 342 floats * 4 bytes = maxFaces * 1368 bytes
|
|
1032
1191
|
const metadataSize = 2 * 4; // 2 ints
|
|
1033
|
-
const faceDataSize = maxFaces *
|
|
1192
|
+
const faceDataSize = maxFaces * 342 * 4; // 342 floats per face
|
|
1034
1193
|
sharedBufferSize = metadataSize + faceDataSize;
|
|
1035
1194
|
sharedBufferPtr = Module._malloc(sharedBufferSize);
|
|
1036
1195
|
|
|
@@ -1047,7 +1206,7 @@
|
|
|
1047
1206
|
const dataOffset = dataPtr / 4; // float is 4 bytes
|
|
1048
1207
|
|
|
1049
1208
|
const results = [];
|
|
1050
|
-
const FLOATS_PER_FACE =
|
|
1209
|
+
const FLOATS_PER_FACE = 342;
|
|
1051
1210
|
|
|
1052
1211
|
for (let i = 0; i < faceCount; i++) {
|
|
1053
1212
|
const faceOffset = dataOffset + i * FLOATS_PER_FACE;
|
|
@@ -1063,7 +1222,6 @@
|
|
|
1063
1222
|
|
|
1064
1223
|
// Read basic fields
|
|
1065
1224
|
const faceId = Math.round(heap[offset++]);
|
|
1066
|
-
const faceAction = Math.round(heap[offset++]);
|
|
1067
1225
|
const score = heap[offset++];
|
|
1068
1226
|
const pitch = heap[offset++];
|
|
1069
1227
|
const roll = heap[offset++];
|
|
@@ -1089,7 +1247,6 @@
|
|
|
1089
1247
|
key_points: keyPoints,
|
|
1090
1248
|
visibility,
|
|
1091
1249
|
face_id: faceId,
|
|
1092
|
-
face_action: faceAction,
|
|
1093
1250
|
score,
|
|
1094
1251
|
pitch,
|
|
1095
1252
|
roll,
|
|
@@ -1108,7 +1265,7 @@
|
|
|
1108
1265
|
|
|
1109
1266
|
// Call C API
|
|
1110
1267
|
const result = Module.ccall(
|
|
1111
|
-
'
|
|
1268
|
+
'fb_engine_set_wasm_callbacks',
|
|
1112
1269
|
'number',
|
|
1113
1270
|
['number', 'number', 'number', 'number'],
|
|
1114
1271
|
[
|
|
@@ -1137,55 +1294,81 @@
|
|
|
1137
1294
|
}
|
|
1138
1295
|
|
|
1139
1296
|
/**
|
|
1140
|
-
*
|
|
1141
|
-
*
|
|
1142
|
-
* @param {number} [
|
|
1143
|
-
* @param {ImageData|HTMLImageElement|HTMLCanvasElement} [options.backgroundImage] - Background image (required when mode is Image)
|
|
1297
|
+
* Enables virtual background blur.
|
|
1298
|
+
* Level is continuous in [0.0, 1.0] (downsample + mix, no shader rebuild).
|
|
1299
|
+
* @param {number} level - Blur strength in [0.0, 1.0]. 0 clears the virtual background.
|
|
1144
1300
|
*/
|
|
1145
|
-
|
|
1301
|
+
setVirtualBackgroundBlur(level) {
|
|
1146
1302
|
this._ensureInitialized();
|
|
1147
1303
|
const Module = this._getWasmModule();
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1304
|
+
const result = Module.ccall(
|
|
1305
|
+
'fb_set_virtual_background_blur',
|
|
1306
|
+
'number',
|
|
1307
|
+
['number', 'number'],
|
|
1308
|
+
[this.enginePtr, level]
|
|
1309
|
+
);
|
|
1310
|
+
checkResult(result, 'Failed to set virtual background blur');
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
/**
|
|
1314
|
+
* Replaces the background with an image file path or encoded png/jpg bytes.
|
|
1315
|
+
* @param {string|Uint8Array} resource - Image path or Uint8Array data.
|
|
1316
|
+
*/
|
|
1317
|
+
setVirtualBackground(resource) {
|
|
1318
|
+
this._ensureInitialized();
|
|
1319
|
+
const Module = this._getWasmModule();
|
|
1320
|
+
|
|
1321
|
+
if (typeof resource === 'string') {
|
|
1322
|
+
if (!resource) {
|
|
1323
|
+
throw new FacebetterError(
|
|
1324
|
+
'Virtual background path must not be empty; use clearVirtualBackground()'
|
|
1325
|
+
);
|
|
1165
1326
|
}
|
|
1166
|
-
|
|
1167
|
-
// Use unified SetVirtualBackground C interface (consistent with other platforms)
|
|
1168
1327
|
const result = Module.ccall(
|
|
1169
|
-
'
|
|
1328
|
+
'fb_set_virtual_background',
|
|
1170
1329
|
'number',
|
|
1171
|
-
['number', '
|
|
1172
|
-
[
|
|
1173
|
-
this.enginePtr,
|
|
1174
|
-
opts.mode,
|
|
1175
|
-
imageBufferPtr || 0, // If null, pass 0
|
|
1176
|
-
imageData ? imageData.width : 0,
|
|
1177
|
-
imageData ? imageData.height : 0,
|
|
1178
|
-
imageData ? imageData.width * 4 : 0
|
|
1179
|
-
]
|
|
1330
|
+
['number', 'string'],
|
|
1331
|
+
[this.enginePtr, resource]
|
|
1180
1332
|
);
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
}
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1333
|
+
checkResult(result, 'Failed to set virtual background from path');
|
|
1334
|
+
return;
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
if (resource instanceof Uint8Array) {
|
|
1338
|
+
const dataPtr = Module._malloc(resource.length);
|
|
1339
|
+
Module.HEAPU8.set(resource, dataPtr);
|
|
1340
|
+
try {
|
|
1341
|
+
const result = Module.ccall(
|
|
1342
|
+
'fb_set_virtual_background_data',
|
|
1343
|
+
'number',
|
|
1344
|
+
['number', 'number', 'number'],
|
|
1345
|
+
[this.enginePtr, dataPtr, resource.length]
|
|
1346
|
+
);
|
|
1347
|
+
checkResult(result, 'Failed to set virtual background from data');
|
|
1348
|
+
} finally {
|
|
1349
|
+
Module._free(dataPtr);
|
|
1187
1350
|
}
|
|
1351
|
+
return;
|
|
1188
1352
|
}
|
|
1353
|
+
|
|
1354
|
+
throw new FacebetterError(
|
|
1355
|
+
'setVirtualBackground expects a file path string or Uint8Array'
|
|
1356
|
+
);
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
/**
|
|
1360
|
+
* Clears virtual background (blur or image replacement).
|
|
1361
|
+
*/
|
|
1362
|
+
clearVirtualBackground() {
|
|
1363
|
+
this._ensureInitialized();
|
|
1364
|
+
const Module = this._getWasmModule();
|
|
1365
|
+
const result = Module.ccall(
|
|
1366
|
+
'fb_clear_virtual_background',
|
|
1367
|
+
'number',
|
|
1368
|
+
['number'],
|
|
1369
|
+
[this.enginePtr]
|
|
1370
|
+
);
|
|
1371
|
+
checkResult(result, 'Failed to clear virtual background');
|
|
1189
1372
|
}
|
|
1190
1373
|
|
|
1191
1374
|
/**
|
|
@@ -1193,12 +1376,12 @@
|
|
|
1193
1376
|
* When enabled, beauty effects will only be applied to detected skin areas.
|
|
1194
1377
|
* @param {boolean} enabled - True to enable skin-only beauty, false to apply to entire image.
|
|
1195
1378
|
*/
|
|
1196
|
-
|
|
1379
|
+
setBeautySkinOnly(enabled) {
|
|
1197
1380
|
this._ensureInitialized();
|
|
1198
1381
|
const Module = this._getWasmModule();
|
|
1199
1382
|
|
|
1200
1383
|
const result = Module.ccall(
|
|
1201
|
-
'
|
|
1384
|
+
'fb_set_beauty_skin_only',
|
|
1202
1385
|
'number',
|
|
1203
1386
|
['number', 'number'],
|
|
1204
1387
|
[this.enginePtr, enabled ? 1 : 0]
|
|
@@ -1207,6 +1390,35 @@
|
|
|
1207
1390
|
checkResult(result, 'Failed to set skin only beauty');
|
|
1208
1391
|
}
|
|
1209
1392
|
|
|
1393
|
+
/**
|
|
1394
|
+
* Gets engine performance statistics.
|
|
1395
|
+
* @returns {{fps: number, avgProcessTimeMs: number, sessionTimeS: number}}
|
|
1396
|
+
*/
|
|
1397
|
+
getStats() {
|
|
1398
|
+
this._ensureInitialized();
|
|
1399
|
+
const Module = this._getWasmModule();
|
|
1400
|
+
const ptr = Module._malloc(24);
|
|
1401
|
+
try {
|
|
1402
|
+
const result = Module.ccall(
|
|
1403
|
+
'fb_engine_get_stats',
|
|
1404
|
+
'number',
|
|
1405
|
+
['number', 'number'],
|
|
1406
|
+
[this.enginePtr, ptr]
|
|
1407
|
+
);
|
|
1408
|
+
checkResult(result, 'Failed to get stats');
|
|
1409
|
+
if ((ptr & 7) !== 0) {
|
|
1410
|
+
throw new FacebetterError('Unaligned stats buffer');
|
|
1411
|
+
}
|
|
1412
|
+
const base = ptr >> 3;
|
|
1413
|
+
return {
|
|
1414
|
+
fps: Module.HEAPF64[base],
|
|
1415
|
+
avgProcessTimeMs: Module.HEAPF64[base + 1],
|
|
1416
|
+
sessionTimeS: Module.HEAPF64[base + 2]
|
|
1417
|
+
};
|
|
1418
|
+
} finally {
|
|
1419
|
+
Module._free(ptr);
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1210
1422
|
|
|
1211
1423
|
/**
|
|
1212
1424
|
* Ensures buffers are allocated for the given dimensions
|
|
@@ -1260,7 +1472,7 @@
|
|
|
1260
1472
|
srcView.set(imageData.data);
|
|
1261
1473
|
|
|
1262
1474
|
const result = Module.ccall(
|
|
1263
|
-
'
|
|
1475
|
+
'fb_process_rgba',
|
|
1264
1476
|
'number',
|
|
1265
1477
|
['number', 'number', 'number', 'number', 'number', 'number', 'number', 'number'],
|
|
1266
1478
|
[
|
|
@@ -1321,7 +1533,7 @@
|
|
|
1321
1533
|
|
|
1322
1534
|
try {
|
|
1323
1535
|
const result = Module.ccall(
|
|
1324
|
-
'
|
|
1536
|
+
'fb_process_texture',
|
|
1325
1537
|
'number',
|
|
1326
1538
|
['number', 'number', 'number', 'number', 'number', 'number', 'number'],
|
|
1327
1539
|
[
|
|
@@ -1361,37 +1573,37 @@
|
|
|
1361
1573
|
}
|
|
1362
1574
|
|
|
1363
1575
|
/**
|
|
1364
|
-
* Ensures
|
|
1576
|
+
* Ensures facebetter_canvas exists in the DOM (browser only)
|
|
1365
1577
|
* This canvas is required by GPUPixel for WebGL context creation
|
|
1366
1578
|
* @private
|
|
1367
1579
|
*/
|
|
1368
|
-
|
|
1580
|
+
_ensureFacebetterCanvas() {
|
|
1369
1581
|
if (typeof document === 'undefined') {
|
|
1370
1582
|
return;
|
|
1371
1583
|
}
|
|
1372
1584
|
|
|
1373
|
-
let canvas = document.getElementById('
|
|
1585
|
+
let canvas = document.getElementById('facebetter_canvas');
|
|
1374
1586
|
if (!canvas) {
|
|
1375
1587
|
canvas = document.createElement('canvas');
|
|
1376
|
-
canvas.id = '
|
|
1588
|
+
canvas.id = 'facebetter_canvas';
|
|
1377
1589
|
canvas.style.display = 'none';
|
|
1378
1590
|
canvas.width = 1;
|
|
1379
1591
|
canvas.height = 1;
|
|
1380
1592
|
document.body.appendChild(canvas);
|
|
1381
|
-
this.
|
|
1593
|
+
this._createdFacebetterCanvas = true;
|
|
1382
1594
|
}
|
|
1383
|
-
this.
|
|
1595
|
+
this._facebetterCanvas = canvas;
|
|
1384
1596
|
}
|
|
1385
1597
|
|
|
1386
1598
|
/**
|
|
1387
|
-
* Cleans up
|
|
1599
|
+
* Cleans up facebetter_canvas if it was created by this engine instance (browser only)
|
|
1388
1600
|
* @private
|
|
1389
1601
|
*/
|
|
1390
|
-
|
|
1391
|
-
if (this.
|
|
1392
|
-
this.
|
|
1393
|
-
this.
|
|
1394
|
-
this.
|
|
1602
|
+
_cleanupFacebetterCanvas() {
|
|
1603
|
+
if (this._createdFacebetterCanvas && this._facebetterCanvas) {
|
|
1604
|
+
this._facebetterCanvas.remove();
|
|
1605
|
+
this._facebetterCanvas = null;
|
|
1606
|
+
this._createdFacebetterCanvas = false;
|
|
1395
1607
|
}
|
|
1396
1608
|
}
|
|
1397
1609
|
|
|
@@ -1420,6 +1632,102 @@
|
|
|
1420
1632
|
|
|
1421
1633
|
}
|
|
1422
1634
|
|
|
1635
|
+
/** Cloudflare v2 online auth URL. Proxy through your server in production; never ship keys in the frontend bundle. */
|
|
1636
|
+
const FACEBETTER_AUTH_URL =
|
|
1637
|
+
'https://facebetter.pixpark.net/facebetter/v2/auth';
|
|
1638
|
+
|
|
1639
|
+
function toHex(buffer) {
|
|
1640
|
+
return Array.from(new Uint8Array(buffer), (b) =>
|
|
1641
|
+
b.toString(16).padStart(2, '0')
|
|
1642
|
+
).join('');
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
async function hmacSha256Hex(key, message) {
|
|
1646
|
+
const encoder = new TextEncoder();
|
|
1647
|
+
const cryptoKey = await crypto.subtle.importKey(
|
|
1648
|
+
'raw',
|
|
1649
|
+
encoder.encode(key),
|
|
1650
|
+
{ name: 'HMAC', hash: 'SHA-256' },
|
|
1651
|
+
false,
|
|
1652
|
+
['sign']
|
|
1653
|
+
);
|
|
1654
|
+
const signature = await crypto.subtle.sign(
|
|
1655
|
+
'HMAC',
|
|
1656
|
+
cryptoKey,
|
|
1657
|
+
encoder.encode(message)
|
|
1658
|
+
);
|
|
1659
|
+
return toHex(signature);
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
/**
|
|
1663
|
+
* Call Cloudflare v2/auth directly with app_id / app_key.
|
|
1664
|
+
* Intended for local debugging, or environments where key leakage is not a risk.
|
|
1665
|
+
*
|
|
1666
|
+
* @param {Object} options
|
|
1667
|
+
* @param {string} options.appId
|
|
1668
|
+
* @param {string} options.appKey
|
|
1669
|
+
* @param {Object} options.challenge WASM challenge `{nonce, timestamp, platform, user_agent}`
|
|
1670
|
+
* @param {string} [options.authUrl]
|
|
1671
|
+
* @returns {Promise<string>} Raw HTTP body (`{success, token}` or an error envelope)
|
|
1672
|
+
*/
|
|
1673
|
+
async function requestFacebetterAuth({
|
|
1674
|
+
appId,
|
|
1675
|
+
appKey,
|
|
1676
|
+
challenge,
|
|
1677
|
+
authUrl = FACEBETTER_AUTH_URL,
|
|
1678
|
+
}) {
|
|
1679
|
+
const id = typeof appId === 'string' ? appId.trim() : '';
|
|
1680
|
+
const key = typeof appKey === 'string' ? appKey.trim() : '';
|
|
1681
|
+
if (!id || !key) {
|
|
1682
|
+
throw new FacebetterError(
|
|
1683
|
+
'requestFacebetterAuth requires appId and appKey',
|
|
1684
|
+
'LICENSE_ERROR'
|
|
1685
|
+
);
|
|
1686
|
+
}
|
|
1687
|
+
if (!challenge || typeof challenge !== 'object') {
|
|
1688
|
+
throw new FacebetterError(
|
|
1689
|
+
'requestFacebetterAuth requires a WASM auth challenge',
|
|
1690
|
+
'LICENSE_ERROR'
|
|
1691
|
+
);
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
const nonce = String(challenge.nonce || '');
|
|
1695
|
+
const timestamp = Number(challenge.timestamp);
|
|
1696
|
+
const platform = String(challenge.platform || 'web');
|
|
1697
|
+
const userAgent = String(challenge.user_agent || '');
|
|
1698
|
+
const payload = `v2|${id}|${timestamp}|${nonce}|${platform}`;
|
|
1699
|
+
const hmac = await hmacSha256Hex(key, payload);
|
|
1700
|
+
|
|
1701
|
+
const response = await fetch(authUrl, {
|
|
1702
|
+
method: 'POST',
|
|
1703
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1704
|
+
body: JSON.stringify({
|
|
1705
|
+
app_id: id,
|
|
1706
|
+
hmac_signature: hmac,
|
|
1707
|
+
timestamp,
|
|
1708
|
+
nonce,
|
|
1709
|
+
platform,
|
|
1710
|
+
user_agent: userAgent,
|
|
1711
|
+
}),
|
|
1712
|
+
});
|
|
1713
|
+
return response.text();
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
/**
|
|
1717
|
+
* Build an `EngineConfig.fetchAuthResponse` that talks to Cloudflare directly
|
|
1718
|
+
* (no customer proxy).
|
|
1719
|
+
*
|
|
1720
|
+
* @param {Object} options
|
|
1721
|
+
* @param {string} options.appId
|
|
1722
|
+
* @param {string} options.appKey
|
|
1723
|
+
* @param {string} [options.authUrl]
|
|
1724
|
+
* @returns {(challenge: Object) => Promise<string>}
|
|
1725
|
+
*/
|
|
1726
|
+
function createDirectAuthFetcher({ appId, appKey, authUrl } = {}) {
|
|
1727
|
+
return (challenge) =>
|
|
1728
|
+
requestFacebetterAuth({ appId, appKey, challenge, authUrl });
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1423
1731
|
/**
|
|
1424
1732
|
* Browser-specific WASM Module Loader
|
|
1425
1733
|
* Supports ESM format WASM modules (generated with MODULARIZE and EXPORT_ES6)
|
|
@@ -1684,15 +1992,15 @@
|
|
|
1684
1992
|
|
|
1685
1993
|
|
|
1686
1994
|
// Browser-specific GPU canvas management
|
|
1687
|
-
function
|
|
1995
|
+
function ensureFacebetterCanvas() {
|
|
1688
1996
|
if (typeof document === 'undefined') {
|
|
1689
1997
|
return;
|
|
1690
1998
|
}
|
|
1691
1999
|
|
|
1692
|
-
let canvas = document.getElementById('
|
|
2000
|
+
let canvas = document.getElementById('facebetter_canvas');
|
|
1693
2001
|
if (!canvas) {
|
|
1694
2002
|
canvas = document.createElement('canvas');
|
|
1695
|
-
canvas.id = '
|
|
2003
|
+
canvas.id = 'facebetter_canvas';
|
|
1696
2004
|
canvas.style.display = 'none';
|
|
1697
2005
|
canvas.width = 1;
|
|
1698
2006
|
canvas.height = 1;
|
|
@@ -1702,7 +2010,7 @@
|
|
|
1702
2010
|
}
|
|
1703
2011
|
}
|
|
1704
2012
|
|
|
1705
|
-
function
|
|
2013
|
+
function cleanupFacebetterCanvas() {
|
|
1706
2014
|
// Cleanup is handled by engine instance
|
|
1707
2015
|
}
|
|
1708
2016
|
|
|
@@ -1712,8 +2020,8 @@
|
|
|
1712
2020
|
getWasmModule: getWasmModule,
|
|
1713
2021
|
getWasmBuffer: getWasmBuffer,
|
|
1714
2022
|
toImageData: toImageData,
|
|
1715
|
-
|
|
1716
|
-
|
|
2023
|
+
ensureFacebetterCanvas,
|
|
2024
|
+
cleanupFacebetterCanvas
|
|
1717
2025
|
};
|
|
1718
2026
|
|
|
1719
2027
|
// Export factory function for UMD
|
|
@@ -1731,42 +2039,68 @@
|
|
|
1731
2039
|
}
|
|
1732
2040
|
|
|
1733
2041
|
// Export constants with proper names
|
|
1734
|
-
const
|
|
1735
|
-
const
|
|
1736
|
-
const
|
|
1737
|
-
const
|
|
1738
|
-
const LipstickStyle = LipstickStyle$1;
|
|
2042
|
+
const WhiteningStyle = WhiteningStyle$1;
|
|
2043
|
+
const SmoothingStyle = SmoothingStyle$1;
|
|
2044
|
+
const Reshape = Reshape$1;
|
|
2045
|
+
const LipstickColor = LipstickColor$1;
|
|
1739
2046
|
const BlushStyle = BlushStyle$1;
|
|
1740
|
-
const
|
|
2047
|
+
const BlushColor = BlushColor$1;
|
|
2048
|
+
const ContourStyle = ContourStyle$1;
|
|
2049
|
+
const EyeShadowStyle = EyeShadowStyle$1;
|
|
2050
|
+
const EyeShadowColor = EyeShadowColor$1;
|
|
2051
|
+
const EyeLinerStyle = EyeLinerStyle$1;
|
|
2052
|
+
const EyeLinerColor = EyeLinerColor$1;
|
|
2053
|
+
const EyebrowStyle = EyebrowStyle$1;
|
|
2054
|
+
const EyebrowColor = EyebrowColor$1;
|
|
2055
|
+
const EyelashStyle = EyelashStyle$1;
|
|
2056
|
+
const EyelashColor = EyelashColor$1;
|
|
2057
|
+
const PupilColor = PupilColor$1;
|
|
2058
|
+
const ChromaKeyColor = ChromaKeyColor$1;
|
|
1741
2059
|
const FrameType = FrameType$1;
|
|
1742
2060
|
const MirrorMode = MirrorMode$1;
|
|
1743
|
-
const
|
|
2061
|
+
const EngineEventCode = EngineEventCode$1;
|
|
1744
2062
|
|
|
1745
2063
|
// Default export
|
|
1746
2064
|
var index = {
|
|
1747
2065
|
BeautyEffectEngine: BrowserBeautyEffectEngine,
|
|
1748
2066
|
EngineConfig,
|
|
1749
2067
|
FacebetterError,
|
|
2068
|
+
FACEBETTER_AUTH_URL,
|
|
2069
|
+
createDirectAuthFetcher,
|
|
2070
|
+
requestFacebetterAuth,
|
|
1750
2071
|
...constants,
|
|
1751
2072
|
loadWasmModule: loadWasmModule
|
|
1752
2073
|
};
|
|
1753
2074
|
|
|
1754
|
-
exports.BackgroundMode = BackgroundMode;
|
|
1755
|
-
exports.BasicParam = BasicParam;
|
|
1756
2075
|
exports.BeautyEffectEngine = BrowserBeautyEffectEngine;
|
|
1757
|
-
exports.
|
|
2076
|
+
exports.BlushColor = BlushColor;
|
|
1758
2077
|
exports.BlushStyle = BlushStyle;
|
|
2078
|
+
exports.ChromaKeyColor = ChromaKeyColor;
|
|
2079
|
+
exports.ContourStyle = ContourStyle;
|
|
1759
2080
|
exports.EngineConfig = EngineConfig;
|
|
2081
|
+
exports.EngineEventCode = EngineEventCode;
|
|
2082
|
+
exports.EyeLinerColor = EyeLinerColor;
|
|
2083
|
+
exports.EyeLinerStyle = EyeLinerStyle;
|
|
2084
|
+
exports.EyeShadowColor = EyeShadowColor;
|
|
2085
|
+
exports.EyeShadowStyle = EyeShadowStyle;
|
|
2086
|
+
exports.EyebrowColor = EyebrowColor;
|
|
2087
|
+
exports.EyebrowStyle = EyebrowStyle;
|
|
2088
|
+
exports.EyelashColor = EyelashColor;
|
|
2089
|
+
exports.EyelashStyle = EyelashStyle;
|
|
2090
|
+
exports.FACEBETTER_AUTH_URL = FACEBETTER_AUTH_URL;
|
|
1760
2091
|
exports.FacebetterError = FacebetterError;
|
|
1761
2092
|
exports.FrameType = FrameType;
|
|
1762
|
-
exports.
|
|
1763
|
-
exports.MakeupParam = MakeupParam;
|
|
2093
|
+
exports.LipstickColor = LipstickColor;
|
|
1764
2094
|
exports.MirrorMode = MirrorMode;
|
|
1765
|
-
exports.
|
|
1766
|
-
exports.
|
|
2095
|
+
exports.PupilColor = PupilColor;
|
|
2096
|
+
exports.Reshape = Reshape;
|
|
2097
|
+
exports.SmoothingStyle = SmoothingStyle;
|
|
2098
|
+
exports.WhiteningStyle = WhiteningStyle;
|
|
1767
2099
|
exports.createBeautyEffectEngine = createBeautyEffectEngine;
|
|
2100
|
+
exports.createDirectAuthFetcher = createDirectAuthFetcher;
|
|
1768
2101
|
exports.default = index;
|
|
1769
2102
|
exports.loadWasmModule = loadWasmModule;
|
|
2103
|
+
exports.requestFacebetterAuth = requestFacebetterAuth;
|
|
1770
2104
|
|
|
1771
2105
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
1772
2106
|
|