facebetter 1.0.14 → 1.1.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 +157 -0
- package/dist/facebetter.esm.js +349 -85
- package/dist/facebetter.esm.js.map +1 -1
- package/dist/facebetter.js +348 -85
- package/dist/facebetter.js.map +1 -1
- package/package.json +2 -2
package/dist/facebetter.js
CHANGED
|
@@ -54,6 +54,12 @@
|
|
|
54
54
|
this.appKey = config.appKey || null;
|
|
55
55
|
this.licenseJson = config.licenseJson || null;
|
|
56
56
|
this.resourcePath = '/facebetter/resource.bundle';
|
|
57
|
+
/**
|
|
58
|
+
* Whether to use an external GL context (native platforms only).
|
|
59
|
+
* In Web/WASM environments, this field is kept for configuration structure alignment,
|
|
60
|
+
* but it does not take effect in the current implementation.
|
|
61
|
+
*/
|
|
62
|
+
this.externalContext = !!config.externalContext;
|
|
57
63
|
}
|
|
58
64
|
|
|
59
65
|
/**
|
|
@@ -61,11 +67,11 @@
|
|
|
61
67
|
* @returns {boolean} True if valid
|
|
62
68
|
*/
|
|
63
69
|
isValid() {
|
|
64
|
-
//
|
|
70
|
+
// If licenseJson is provided, use it for validation
|
|
65
71
|
if (this.licenseJson) {
|
|
66
72
|
return typeof this.licenseJson === 'string' && this.licenseJson.trim() !== '';
|
|
67
73
|
}
|
|
68
|
-
//
|
|
74
|
+
// Otherwise appId and appKey are required
|
|
69
75
|
return this.appId && typeof this.appId === 'string' && this.appId.trim() !== '' &&
|
|
70
76
|
this.appKey && typeof this.appKey === 'string' && this.appKey.trim() !== '';
|
|
71
77
|
}
|
|
@@ -88,61 +94,75 @@
|
|
|
88
94
|
* Beauty type enumeration
|
|
89
95
|
*/
|
|
90
96
|
const BeautyType$1 = {
|
|
91
|
-
Basic: 0,
|
|
92
|
-
Reshape: 1,
|
|
93
|
-
Makeup: 2,
|
|
94
|
-
VirtualBackground: 3
|
|
97
|
+
Basic: 0, // Basic beauty (smoothing, whitening, etc.)
|
|
98
|
+
Reshape: 1, // Face reshaping (face thinning, big eyes, etc.)
|
|
99
|
+
Makeup: 2, // Makeup effects (lipstick, blush, etc.)
|
|
100
|
+
VirtualBackground: 3, // Virtual background (blur, image replacement)
|
|
101
|
+
ChromaKey: 4 // Chroma key (green screen removal)
|
|
95
102
|
};
|
|
96
103
|
|
|
97
104
|
/**
|
|
98
105
|
* Basic beauty parameter enumeration
|
|
106
|
+
* All values should be in range [0.0, 1.0]
|
|
99
107
|
*/
|
|
100
108
|
const BasicParam$1 = {
|
|
101
|
-
Smoothing: 0, //
|
|
102
|
-
Sharpening: 1,
|
|
103
|
-
Whitening: 2, //
|
|
104
|
-
Rosiness: 3 //
|
|
109
|
+
Smoothing: 0, // Skin smoothing (0.0: none, 1.0: maximum)
|
|
110
|
+
Sharpening: 1, // Image sharpening (0.0: none, 1.0: maximum)
|
|
111
|
+
Whitening: 2, // Skin whitening (0.0: none, 1.0: maximum)
|
|
112
|
+
Rosiness: 3 // Skin rosiness (0.0: none, 1.0: maximum)
|
|
105
113
|
};
|
|
106
114
|
|
|
107
115
|
/**
|
|
108
116
|
* Face reshape parameter enumeration
|
|
117
|
+
* All values should be in range [0.0, 1.0]
|
|
109
118
|
*/
|
|
110
119
|
const ReshapeParam$1 = {
|
|
111
|
-
|
|
112
|
-
FaceVShape: 1,
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
Cheekbone: 4,
|
|
116
|
-
Jawbone: 5,
|
|
117
|
-
Chin: 6,
|
|
118
|
-
|
|
119
|
-
EyeSize: 8,
|
|
120
|
-
EyeDistance: 9
|
|
120
|
+
FaceThinning: 0, // Face thinning (0.0: none, 1.0: maximum)
|
|
121
|
+
FaceVShape: 1, // V-shape face (0.0: none, 1.0: maximum)
|
|
122
|
+
FaceNarrowing: 2, // Face narrowing (0.0: none, 1.0: maximum)
|
|
123
|
+
FaceShortening: 3,// Face shortening (0.0: none, 1.0: maximum)
|
|
124
|
+
Cheekbone: 4, // Cheekbone slimming (0.0: none, 1.0: maximum)
|
|
125
|
+
Jawbone: 5, // Jawbone slimming (0.0: none, 1.0: maximum)
|
|
126
|
+
Chin: 6, // Chin length adjustment (0.0: none, 1.0: maximum)
|
|
127
|
+
NoseSlimming: 7, // Nose slimming (0.0: none, 1.0: maximum)
|
|
128
|
+
EyeSize: 8, // Eye size (0.0: normal, 1.0: maximum)
|
|
129
|
+
EyeDistance: 9 // Eye distance (0.0: normal, 1.0: maximum)
|
|
121
130
|
};
|
|
122
131
|
|
|
123
132
|
/**
|
|
124
133
|
* Makeup parameter enumeration
|
|
134
|
+
* All values should be in range [0.0, 1.0]
|
|
125
135
|
*/
|
|
126
136
|
const MakeupParam$1 = {
|
|
127
|
-
Lipstick: 0, //
|
|
128
|
-
Blush: 1 //
|
|
137
|
+
Lipstick: 0, // Lipstick intensity (0.0: none, 1.0: maximum)
|
|
138
|
+
Blush: 1 // Blush intensity (0.0: none, 1.0: maximum)
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Chroma Key parameter enumeration
|
|
143
|
+
*/
|
|
144
|
+
const ChromaKeyParam = {
|
|
145
|
+
KeyColor: 0, // Key color (0.0: Green, 1.0: Blue, 2.0: Red)
|
|
146
|
+
Similarity: 1, // Color similarity (0.0 - 1.0)
|
|
147
|
+
Smoothness: 2, // Edge smoothness (0.0 - 1.0)
|
|
148
|
+
Desaturation: 3 // Spill desaturation (0.0 - 1.0)
|
|
129
149
|
};
|
|
130
150
|
|
|
131
151
|
/**
|
|
132
152
|
* Background mode enumeration
|
|
133
153
|
*/
|
|
134
154
|
const BackgroundMode$1 = {
|
|
135
|
-
None: 0, //
|
|
136
|
-
Blur: 1, //
|
|
137
|
-
Image: 2 //
|
|
155
|
+
None: 0, // No background processing
|
|
156
|
+
Blur: 1, // Blurred background
|
|
157
|
+
Image: 2 // Background image replacement
|
|
138
158
|
};
|
|
139
159
|
|
|
140
160
|
/**
|
|
141
|
-
*
|
|
161
|
+
* Frame type enumeration
|
|
142
162
|
*/
|
|
143
|
-
const
|
|
144
|
-
Image: 0, //
|
|
145
|
-
Video: 1 //
|
|
163
|
+
const FrameType$1 = {
|
|
164
|
+
Image: 0, // Image mode
|
|
165
|
+
Video: 1 // Video mode
|
|
146
166
|
};
|
|
147
167
|
|
|
148
168
|
/**
|
|
@@ -178,8 +198,9 @@
|
|
|
178
198
|
BackgroundMode: BackgroundMode$1,
|
|
179
199
|
BasicParam: BasicParam$1,
|
|
180
200
|
BeautyType: BeautyType$1,
|
|
201
|
+
ChromaKeyParam: ChromaKeyParam,
|
|
202
|
+
FrameType: FrameType$1,
|
|
181
203
|
MakeupParam: MakeupParam$1,
|
|
182
|
-
ProcessMode: ProcessMode$1,
|
|
183
204
|
ReshapeParam: ReshapeParam$1,
|
|
184
205
|
VirtualBackgroundOptions: VirtualBackgroundOptions$1
|
|
185
206
|
});
|
|
@@ -217,7 +238,7 @@
|
|
|
217
238
|
throw new FacebetterError('Platform API is required');
|
|
218
239
|
}
|
|
219
240
|
|
|
220
|
-
//
|
|
241
|
+
// If an EngineConfig instance is passed, use it directly; otherwise create a new EngineConfig
|
|
221
242
|
let engineConfig;
|
|
222
243
|
if (config instanceof EngineConfig) {
|
|
223
244
|
engineConfig = config;
|
|
@@ -242,6 +263,10 @@
|
|
|
242
263
|
this.dstBufferPtr = null;
|
|
243
264
|
this.bufferSize = 0;
|
|
244
265
|
|
|
266
|
+
// Callback related state
|
|
267
|
+
this._callbackSharedBufferPtr = null;
|
|
268
|
+
this._callbackSharedBufferSize = 0;
|
|
269
|
+
|
|
245
270
|
// Store platform API
|
|
246
271
|
this._platformAPI = platformAPI;
|
|
247
272
|
this._loadWasmModule = platformAPI.loadWasmModule;
|
|
@@ -326,23 +351,22 @@
|
|
|
326
351
|
* @returns {Promise<void>} Promise that resolves when initialization is complete
|
|
327
352
|
*/
|
|
328
353
|
async init(options = {}) {
|
|
329
|
-
//
|
|
354
|
+
// Concurrency control: if already initialized, return immediately
|
|
330
355
|
if (this.initialized) {
|
|
331
356
|
return;
|
|
332
357
|
}
|
|
333
358
|
|
|
334
|
-
//
|
|
359
|
+
// Concurrency control: if initialization is in progress, return the same Promise
|
|
335
360
|
if (this._initPromise) {
|
|
336
361
|
return this._initPromise;
|
|
337
362
|
}
|
|
338
363
|
|
|
339
|
-
//
|
|
364
|
+
// Create initialization Promise
|
|
340
365
|
this._initPromise = (async () => {
|
|
341
366
|
try {
|
|
342
367
|
const wasmTimeout = options.timeout || 30000;
|
|
343
|
-
const authTimeout = options.authTimeout || 10000;
|
|
344
368
|
|
|
345
|
-
//
|
|
369
|
+
// Wait for WASM module loading (with timeout)
|
|
346
370
|
try {
|
|
347
371
|
await Promise.race([
|
|
348
372
|
this._wasmLoadPromise,
|
|
@@ -354,48 +378,60 @@
|
|
|
354
378
|
|
|
355
379
|
const Module = this._getWasmModule();
|
|
356
380
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
);
|
|
381
|
+
// Setup usage report proxy for WASM
|
|
382
|
+
if (!Module.onReportUsage) {
|
|
383
|
+
Module.onReportUsage = async (payloadJson) => {
|
|
384
|
+
try {
|
|
385
|
+
const url = 'https://facebetter.pixpark.net/rest/v1/rpc/report_sdk_usage';
|
|
386
|
+
const response = await fetch(url, {
|
|
387
|
+
method: 'POST',
|
|
388
|
+
headers: {
|
|
389
|
+
'Content-Type': 'application/json'
|
|
390
|
+
},
|
|
391
|
+
body: payloadJson
|
|
392
|
+
});
|
|
393
|
+
return response.ok;
|
|
394
|
+
} catch (error) {
|
|
395
|
+
return false;
|
|
373
396
|
}
|
|
397
|
+
};
|
|
398
|
+
}
|
|
374
399
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
400
|
+
// Setup online auth proxy for WASM
|
|
401
|
+
if (!Module.onOnlineAuth) {
|
|
402
|
+
Module.onOnlineAuth = async (payloadJson) => {
|
|
403
|
+
try {
|
|
404
|
+
const url = 'https://facebetter.pixpark.net/rest/v1/rpc/online_auth_v2';
|
|
405
|
+
const response = await fetch(url, {
|
|
406
|
+
method: 'POST',
|
|
407
|
+
headers: {
|
|
408
|
+
'Content-Type': 'application/json'
|
|
409
|
+
},
|
|
410
|
+
body: payloadJson
|
|
411
|
+
});
|
|
412
|
+
if (!response.ok) {
|
|
413
|
+
console.warn('[JS Engine] Online auth fetch failed with status:', response.status);
|
|
414
|
+
return "";
|
|
415
|
+
}
|
|
416
|
+
return await response.text();
|
|
417
|
+
} catch (error) {
|
|
418
|
+
console.error('[JS Engine] Online auth fetch exception:', error);
|
|
419
|
+
return "";
|
|
384
420
|
}
|
|
385
|
-
|
|
386
|
-
}
|
|
421
|
+
};
|
|
387
422
|
}
|
|
388
423
|
|
|
389
|
-
//
|
|
390
|
-
// 如果通过 appId/appKey 获取到了响应,则使用响应作为 licenseJson
|
|
391
|
-
// WASM 层只需要验证 licenseJson,不需要发送 HTTP 请求
|
|
424
|
+
// Create engine instance, auth (online/offline) is handled by WASM layer via onOnlineAuth
|
|
392
425
|
const enginePtr = Module.ccall(
|
|
393
|
-
'
|
|
426
|
+
'CreateBeautyEffectEngineEx',
|
|
394
427
|
'number',
|
|
395
|
-
['string', 'string'],
|
|
428
|
+
['string', 'string', 'string', 'string', 'number'],
|
|
396
429
|
[
|
|
397
430
|
this.resourcePath,
|
|
398
|
-
|
|
431
|
+
this.licenseJson || '',
|
|
432
|
+
this.appId || '',
|
|
433
|
+
this.appKey || '',
|
|
434
|
+
this.config.externalContext ? 1 : 0
|
|
399
435
|
]
|
|
400
436
|
);
|
|
401
437
|
|
|
@@ -408,9 +444,9 @@
|
|
|
408
444
|
|
|
409
445
|
this.enginePtr = enginePtr;
|
|
410
446
|
this.initialized = true;
|
|
411
|
-
this._initPromise = null; //
|
|
447
|
+
this._initPromise = null; // Clear Promise cache, allow re-initialization (if needed)
|
|
412
448
|
} catch (error) {
|
|
413
|
-
this._initPromise = null; //
|
|
449
|
+
this._initPromise = null; // Clear Promise cache, allow retry
|
|
414
450
|
throw error;
|
|
415
451
|
}
|
|
416
452
|
})();
|
|
@@ -438,11 +474,18 @@
|
|
|
438
474
|
this.dstBufferPtr = null;
|
|
439
475
|
}
|
|
440
476
|
|
|
477
|
+
// Clean up shared memory
|
|
478
|
+
if (this._callbackSharedBufferPtr) {
|
|
479
|
+
Module._free(this._callbackSharedBufferPtr);
|
|
480
|
+
this._callbackSharedBufferPtr = null;
|
|
481
|
+
this._callbackSharedBufferSize = 0;
|
|
482
|
+
}
|
|
483
|
+
|
|
441
484
|
Module.ccall('DestroyBeautyEffectEngine', null, ['number'], [this.enginePtr]);
|
|
442
485
|
this.enginePtr = null;
|
|
443
486
|
this.initialized = false;
|
|
444
487
|
this.bufferSize = 0;
|
|
445
|
-
this._initPromise = null; //
|
|
488
|
+
this._initPromise = null; // Clear initialization Promise
|
|
446
489
|
|
|
447
490
|
// Clean up offscreen canvas
|
|
448
491
|
if (this._offscreenCanvas) {
|
|
@@ -547,7 +590,7 @@
|
|
|
547
590
|
|
|
548
591
|
/**
|
|
549
592
|
* Sets a basic beauty parameter
|
|
550
|
-
* @param {number} param - Parameter (use BasicParam enum)
|
|
593
|
+
* @param {number} param - Parameter (use BasicParam enum, e.g., BasicParam.Smoothing)
|
|
551
594
|
* @param {number} value - Parameter value (0.0 - 1.0)
|
|
552
595
|
*/
|
|
553
596
|
setBasicParam(param, value) {
|
|
@@ -557,7 +600,7 @@
|
|
|
557
600
|
|
|
558
601
|
/**
|
|
559
602
|
* Sets a reshape parameter
|
|
560
|
-
* @param {number} param - Parameter (use ReshapeParam enum)
|
|
603
|
+
* @param {number} param - Parameter (use ReshapeParam enum, e.g., ReshapeParam.FaceThinning)
|
|
561
604
|
* @param {number} value - Parameter value (0.0 - 1.0)
|
|
562
605
|
*/
|
|
563
606
|
setReshapeParam(param, value) {
|
|
@@ -567,7 +610,7 @@
|
|
|
567
610
|
|
|
568
611
|
/**
|
|
569
612
|
* Sets a makeup parameter
|
|
570
|
-
* @param {number} param - Parameter (use MakeupParam enum)
|
|
613
|
+
* @param {number} param - Parameter (use MakeupParam enum, e.g., MakeupParam.Lipstick)
|
|
571
614
|
* @param {number} value - Parameter value (0.0 - 1.0)
|
|
572
615
|
*/
|
|
573
616
|
setMakeupParam(param, value) {
|
|
@@ -575,6 +618,28 @@
|
|
|
575
618
|
this._setBeautyParam('SetBeautyParamMakeup', param, value);
|
|
576
619
|
}
|
|
577
620
|
|
|
621
|
+
/**
|
|
622
|
+
* Sets chroma key parameter
|
|
623
|
+
* @param {number} param - Parameter (use ChromaKeyParam enum, e.g., ChromaKeyParam.Similarity)
|
|
624
|
+
* @param {number} value - Parameter value (KeyColor: 0.0=Green, 1.0=Blue, 2.0=Red; others 0.0-1.0)
|
|
625
|
+
*/
|
|
626
|
+
setChromaKeyParam(param, value) {
|
|
627
|
+
this._ensureInitialized();
|
|
628
|
+
// For KeyColor, values are 0, 1, 2, not 0.0-1.0
|
|
629
|
+
if (param === 0) { // ChromaKeyParam.KeyColor
|
|
630
|
+
const Module = this._getWasmModule();
|
|
631
|
+
const result = Module.ccall(
|
|
632
|
+
'SetBeautyParamChromaKey',
|
|
633
|
+
'number',
|
|
634
|
+
['number', 'number', 'number'],
|
|
635
|
+
[this.enginePtr, param, value]
|
|
636
|
+
);
|
|
637
|
+
checkResult(result, `Failed to set chroma key parameter`);
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
this._setBeautyParam('SetBeautyParamChromaKey', param, value);
|
|
641
|
+
}
|
|
642
|
+
|
|
578
643
|
/**
|
|
579
644
|
* Internal method to set beauty parameters
|
|
580
645
|
* @private
|
|
@@ -595,6 +660,139 @@
|
|
|
595
660
|
checkResult(result, `Failed to set beauty parameter`);
|
|
596
661
|
}
|
|
597
662
|
|
|
663
|
+
/**
|
|
664
|
+
* Sets engine callbacks (face landmarks detection)
|
|
665
|
+
* @param {Object} callbacks - Callback functions
|
|
666
|
+
* @param {Function} callbacks.onFaceLandmarks - Callback for face landmarks detection
|
|
667
|
+
* @param {number} [callbacks.maxFaces=10] - Maximum number of faces to support (affects shared buffer size)
|
|
668
|
+
*/
|
|
669
|
+
setCallbacks(callbacks) {
|
|
670
|
+
this._ensureInitialized();
|
|
671
|
+
const Module = this._getWasmModule();
|
|
672
|
+
|
|
673
|
+
// Initialize callback storage (if it doesn't exist)
|
|
674
|
+
if (!Module._face_landmarks_callbacks) {
|
|
675
|
+
Module._face_landmarks_callbacks = [];
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
let callbackId = 0;
|
|
679
|
+
let sharedBufferPtr = null;
|
|
680
|
+
let sharedBufferSize = 0;
|
|
681
|
+
|
|
682
|
+
if (callbacks && callbacks.onFaceLandmarks) {
|
|
683
|
+
// Get max faces (default 10)
|
|
684
|
+
const maxFaces = callbacks.maxFaces || 10;
|
|
685
|
+
|
|
686
|
+
// Pre-allocate shared memory
|
|
687
|
+
// Metadata: 2 ints (frame_number, face_count) = 8 bytes
|
|
688
|
+
// Face data: maxFaces * 343 floats * 4 bytes = maxFaces * 1372 bytes
|
|
689
|
+
const metadataSize = 2 * 4; // 2 ints
|
|
690
|
+
const faceDataSize = maxFaces * 343 * 4; // 343 floats per face
|
|
691
|
+
sharedBufferSize = metadataSize + faceDataSize;
|
|
692
|
+
sharedBufferPtr = Module._malloc(sharedBufferSize);
|
|
693
|
+
|
|
694
|
+
if (!sharedBufferPtr) {
|
|
695
|
+
throw new FacebetterError('Failed to allocate shared buffer for callbacks');
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// Register callback function (internal wrapper, reads data from shared memory)
|
|
699
|
+
// Push first, then get index as callbackId (allows callbackId to be 0)
|
|
700
|
+
Module._face_landmarks_callbacks.push(
|
|
701
|
+
(frameNumber, faceCount, dataPtr) => {
|
|
702
|
+
// Read data from shared memory (zero-copy)
|
|
703
|
+
const heap = Module.HEAPF32;
|
|
704
|
+
const dataOffset = dataPtr / 4; // float is 4 bytes
|
|
705
|
+
|
|
706
|
+
const results = [];
|
|
707
|
+
const FLOATS_PER_FACE = 343;
|
|
708
|
+
|
|
709
|
+
for (let i = 0; i < faceCount; i++) {
|
|
710
|
+
const faceOffset = dataOffset + i * FLOATS_PER_FACE;
|
|
711
|
+
let offset = faceOffset;
|
|
712
|
+
|
|
713
|
+
// Read rect
|
|
714
|
+
const rect = {
|
|
715
|
+
x: heap[offset++],
|
|
716
|
+
y: heap[offset++],
|
|
717
|
+
width: heap[offset++],
|
|
718
|
+
height: heap[offset++]
|
|
719
|
+
};
|
|
720
|
+
|
|
721
|
+
// Read basic fields
|
|
722
|
+
const faceId = Math.round(heap[offset++]);
|
|
723
|
+
const faceAction = Math.round(heap[offset++]);
|
|
724
|
+
const score = heap[offset++];
|
|
725
|
+
const pitch = heap[offset++];
|
|
726
|
+
const roll = heap[offset++];
|
|
727
|
+
const yaw = heap[offset++];
|
|
728
|
+
|
|
729
|
+
// Read key_points (111 points)
|
|
730
|
+
const keyPoints = [];
|
|
731
|
+
for (let j = 0; j < 111; j++) {
|
|
732
|
+
keyPoints.push({
|
|
733
|
+
x: heap[offset++],
|
|
734
|
+
y: heap[offset++]
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// Read visibility (111 points)
|
|
739
|
+
const visibility = [];
|
|
740
|
+
for (let j = 0; j < 111; j++) {
|
|
741
|
+
visibility.push(heap[offset++]);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
results.push({
|
|
745
|
+
rect,
|
|
746
|
+
key_points: keyPoints,
|
|
747
|
+
visibility,
|
|
748
|
+
face_id: faceId,
|
|
749
|
+
face_action: faceAction,
|
|
750
|
+
score,
|
|
751
|
+
pitch,
|
|
752
|
+
roll,
|
|
753
|
+
yaw
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// Call user callback
|
|
758
|
+
callbacks.onFaceLandmarks(results);
|
|
759
|
+
}
|
|
760
|
+
);
|
|
761
|
+
|
|
762
|
+
// Get callback ID (index after push, can be 0)
|
|
763
|
+
callbackId = Module._face_landmarks_callbacks.length - 1;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// Call C API
|
|
767
|
+
const result = Module.ccall(
|
|
768
|
+
'SetCallbacks',
|
|
769
|
+
'number',
|
|
770
|
+
['number', 'number', 'number', 'number'],
|
|
771
|
+
[
|
|
772
|
+
this.enginePtr,
|
|
773
|
+
callbackId,
|
|
774
|
+
sharedBufferPtr || 0,
|
|
775
|
+
sharedBufferSize
|
|
776
|
+
]
|
|
777
|
+
);
|
|
778
|
+
|
|
779
|
+
checkResult(result, 'Failed to set callbacks');
|
|
780
|
+
|
|
781
|
+
// Clean up old shared memory (if it exists)
|
|
782
|
+
if (this._callbackSharedBufferPtr) {
|
|
783
|
+
Module._free(this._callbackSharedBufferPtr);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// Store new shared memory pointer for cleanup
|
|
787
|
+
if (sharedBufferPtr) {
|
|
788
|
+
this._callbackSharedBufferPtr = sharedBufferPtr;
|
|
789
|
+
this._callbackSharedBufferSize = sharedBufferSize;
|
|
790
|
+
} else {
|
|
791
|
+
this._callbackSharedBufferPtr = null;
|
|
792
|
+
this._callbackSharedBufferSize = 0;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
598
796
|
/**
|
|
599
797
|
* Sets virtual background options (unified API, matches C++/Java/OC)
|
|
600
798
|
* @param {VirtualBackgroundOptions|Object} options - Virtual background options
|
|
@@ -612,7 +810,7 @@
|
|
|
612
810
|
let imageBufferPtr = null;
|
|
613
811
|
|
|
614
812
|
try {
|
|
615
|
-
//
|
|
813
|
+
// If a background image is provided, convert to ImageData and prepare buffer
|
|
616
814
|
if (opts.backgroundImage) {
|
|
617
815
|
imageData = this._toImageData(opts.backgroundImage);
|
|
618
816
|
const bufferSize = imageData.width * imageData.height * 4;
|
|
@@ -623,7 +821,7 @@
|
|
|
623
821
|
view.set(imageData.data);
|
|
624
822
|
}
|
|
625
823
|
|
|
626
|
-
//
|
|
824
|
+
// Use unified SetVirtualBackground C interface (consistent with other platforms)
|
|
627
825
|
const result = Module.ccall(
|
|
628
826
|
'SetVirtualBackground',
|
|
629
827
|
'number',
|
|
@@ -631,7 +829,7 @@
|
|
|
631
829
|
[
|
|
632
830
|
this.enginePtr,
|
|
633
831
|
opts.mode,
|
|
634
|
-
imageBufferPtr || 0, //
|
|
832
|
+
imageBufferPtr || 0, // If null, pass 0
|
|
635
833
|
imageData ? imageData.width : 0,
|
|
636
834
|
imageData ? imageData.height : 0,
|
|
637
835
|
imageData ? imageData.width * 4 : 0
|
|
@@ -640,7 +838,7 @@
|
|
|
640
838
|
|
|
641
839
|
checkResult(result, 'Failed to set virtual background');
|
|
642
840
|
} finally {
|
|
643
|
-
//
|
|
841
|
+
// Free memory
|
|
644
842
|
if (imageBufferPtr) {
|
|
645
843
|
Module._free(imageBufferPtr);
|
|
646
844
|
}
|
|
@@ -680,10 +878,10 @@
|
|
|
680
878
|
* @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement|Uint8ClampedArray} input - Input image
|
|
681
879
|
* @param {number} width - Image width (required if input is Uint8ClampedArray)
|
|
682
880
|
* @param {number} height - Image height (required if input is Uint8ClampedArray)
|
|
683
|
-
* @param {number}
|
|
881
|
+
* @param {number} frameType - Frame type (use FrameType enum, default: FrameType.Video)
|
|
684
882
|
* @returns {ImageData} Processed image data
|
|
685
883
|
*/
|
|
686
|
-
processImage(input, width, height,
|
|
884
|
+
processImage(input, width, height, frameType = FrameType$1.Video) {
|
|
687
885
|
this._ensureInitialized();
|
|
688
886
|
const imageData = this._platformAPI.toImageData(input, width, height);
|
|
689
887
|
const Module = this._getWasmModule();
|
|
@@ -709,7 +907,7 @@
|
|
|
709
907
|
imageData.height,
|
|
710
908
|
imageData.width * 4,
|
|
711
909
|
this.dstBufferPtr,
|
|
712
|
-
|
|
910
|
+
frameType
|
|
713
911
|
]
|
|
714
912
|
);
|
|
715
913
|
|
|
@@ -728,6 +926,62 @@
|
|
|
728
926
|
}
|
|
729
927
|
}
|
|
730
928
|
|
|
929
|
+
/**
|
|
930
|
+
* Processes an external GPU texture.
|
|
931
|
+
*
|
|
932
|
+
* Note:
|
|
933
|
+
* - This interface is mainly used for integration with internal WASM or advanced scenarios.
|
|
934
|
+
* - textureHandle must be a texture handle (uint32) compatible with the OpenGL/WebGL context used by WASM.
|
|
935
|
+
* - For common web scenarios, it is recommended to use processImage with ImageData/Canvas etc.
|
|
936
|
+
*
|
|
937
|
+
* @param {number} textureHandle - External texture handle (GL_TEXTURE_2D)
|
|
938
|
+
* @param {number} width - Texture width
|
|
939
|
+
* @param {number} height - Texture height
|
|
940
|
+
* @param {number} stride - Row stride (usually width * 4)
|
|
941
|
+
* @param {number} [frameType] - Frame type (FrameType.Image / FrameType.Video)
|
|
942
|
+
* @returns {number} Processed texture handle (GL_TEXTURE_2D, uint32)
|
|
943
|
+
*/
|
|
944
|
+
processTexture(textureHandle,
|
|
945
|
+
width,
|
|
946
|
+
height,
|
|
947
|
+
stride,
|
|
948
|
+
frameType = FrameType$1.Video) {
|
|
949
|
+
this._ensureInitialized();
|
|
950
|
+
if (!textureHandle || width <= 0 || height <= 0 || stride <= 0) {
|
|
951
|
+
throw new FacebetterError('Invalid textureHandle or dimensions for processTexture');
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
const Module = this._getWasmModule();
|
|
955
|
+
|
|
956
|
+
try {
|
|
957
|
+
const result = Module.ccall(
|
|
958
|
+
'ProcessImageTexture',
|
|
959
|
+
'number',
|
|
960
|
+
['number', 'number', 'number', 'number', 'number', 'number'],
|
|
961
|
+
[
|
|
962
|
+
this.enginePtr,
|
|
963
|
+
textureHandle,
|
|
964
|
+
width,
|
|
965
|
+
height,
|
|
966
|
+
stride,
|
|
967
|
+
frameType
|
|
968
|
+
]
|
|
969
|
+
);
|
|
970
|
+
|
|
971
|
+
if (!result || result === 0) {
|
|
972
|
+
throw new FacebetterError('Failed to process external texture or got invalid output texture');
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
// Return processed texture handle for caller to continue using in same GL context
|
|
976
|
+
return result;
|
|
977
|
+
} catch (error) {
|
|
978
|
+
if (error instanceof FacebetterError) {
|
|
979
|
+
throw error;
|
|
980
|
+
}
|
|
981
|
+
throw new FacebetterError(`Texture processing error: ${error.message}`);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
|
|
731
985
|
/**
|
|
732
986
|
* Ensures the engine is initialized
|
|
733
987
|
* @private
|
|
@@ -797,6 +1051,15 @@
|
|
|
797
1051
|
return this._offscreenCanvas;
|
|
798
1052
|
}
|
|
799
1053
|
|
|
1054
|
+
/**
|
|
1055
|
+
* Internal method for online authentication (Deprecated: logic moved to WASM layer)
|
|
1056
|
+
* @private
|
|
1057
|
+
*/
|
|
1058
|
+
async _verifyAppKeyOnline(appId, appKey) {
|
|
1059
|
+
// This method is now deprecated as logic is moved to WASM layer via Module.onOnlineAuth
|
|
1060
|
+
return null;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
800
1063
|
}
|
|
801
1064
|
|
|
802
1065
|
/**
|
|
@@ -1109,7 +1372,7 @@
|
|
|
1109
1372
|
p_user_agent: getUserAgentString()
|
|
1110
1373
|
};
|
|
1111
1374
|
|
|
1112
|
-
const response = await fetch('https://facebetter.pixpark.net/rest/v1/rpc/
|
|
1375
|
+
const response = await fetch('https://facebetter.pixpark.net/rest/v1/rpc/online_auth_v2', {
|
|
1113
1376
|
method: 'POST',
|
|
1114
1377
|
headers: {
|
|
1115
1378
|
'Content-Type': 'application/json',
|
|
@@ -1187,7 +1450,7 @@
|
|
|
1187
1450
|
const ReshapeParam = ReshapeParam$1;
|
|
1188
1451
|
const MakeupParam = MakeupParam$1;
|
|
1189
1452
|
const BackgroundMode = BackgroundMode$1;
|
|
1190
|
-
const
|
|
1453
|
+
const FrameType = FrameType$1;
|
|
1191
1454
|
const VirtualBackgroundOptions = VirtualBackgroundOptions$1;
|
|
1192
1455
|
|
|
1193
1456
|
// Default export
|
|
@@ -1205,8 +1468,8 @@
|
|
|
1205
1468
|
exports.BeautyType = BeautyType;
|
|
1206
1469
|
exports.EngineConfig = EngineConfig;
|
|
1207
1470
|
exports.FacebetterError = FacebetterError;
|
|
1471
|
+
exports.FrameType = FrameType;
|
|
1208
1472
|
exports.MakeupParam = MakeupParam;
|
|
1209
|
-
exports.ProcessMode = ProcessMode;
|
|
1210
1473
|
exports.ReshapeParam = ReshapeParam;
|
|
1211
1474
|
exports.VirtualBackgroundOptions = VirtualBackgroundOptions;
|
|
1212
1475
|
exports.createBeautyEffectEngine = createBeautyEffectEngine;
|