facebetter 1.1.0 → 1.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  # Facebetter Web SDK
2
2
 
3
3
  <h2 align="center">
4
- <a href="https://github.com/pixpark/facebetter-demo"><img src="./logo.svg" alt="Facebetter Logo" width="300"></a>
4
+ <a href="https://github.com/pixpark/facebetter-sdk"><img src="./logo.svg" alt="Facebetter Logo" width="300"></a>
5
5
  </h2>
6
6
 
7
7
  Facebetter is a high-performance, cross-platform SDK for real-time face beauty and AR effects. The Web version leverages WebAssembly (WASM) and WebGL acceleration to provide professional-grade skin retouching, face reshaping, makeup, and virtual background capabilities directly in the browser.
@@ -47,7 +47,7 @@ class EngineConfig {
47
47
  this.appId = config.appId || null;
48
48
  this.appKey = config.appKey || null;
49
49
  this.licenseJson = config.licenseJson || null;
50
- this.resourcePath = '/facebetter/resource.bundle';
50
+ this.resourcePath = '/resource.fbd';
51
51
  /**
52
52
  * Whether to use an external GL context (native platforms only).
53
53
  * In Web/WASM environments, this field is kept for configuration structure alignment,
@@ -92,7 +92,9 @@ const BeautyType$1 = {
92
92
  Reshape: 1, // Face reshaping (face thinning, big eyes, etc.)
93
93
  Makeup: 2, // Makeup effects (lipstick, blush, etc.)
94
94
  VirtualBackground: 3, // Virtual background (blur, image replacement)
95
- ChromaKey: 4 // Chroma key (green screen removal)
95
+ ChromaKey: 4, // Chroma key (green screen removal)
96
+ Filter: 5, // LUT based filter
97
+ Sticker: 6 // 2D/3D sticker
96
98
  };
97
99
 
98
100
  /**
@@ -159,6 +161,16 @@ const FrameType$1 = {
159
161
  Video: 1 // Video mode
160
162
  };
161
163
 
164
+ /**
165
+ * Mirror mode enumeration (apply to input before processing)
166
+ */
167
+ const MirrorMode$1 = {
168
+ None: 0, // No mirror
169
+ Horizontal: 1, // Mirror horizontally (e.g. front camera selfie)
170
+ Vertical: 2, // Mirror vertically
171
+ Both: 3 // Mirror both axes
172
+ };
173
+
162
174
  /**
163
175
  * Virtual background options class
164
176
  * Matches the C++/Java/OC API design
@@ -195,6 +207,7 @@ var constants = /*#__PURE__*/Object.freeze({
195
207
  ChromaKeyParam: ChromaKeyParam$1,
196
208
  FrameType: FrameType$1,
197
209
  MakeupParam: MakeupParam$1,
210
+ MirrorMode: MirrorMode$1,
198
211
  ReshapeParam: ReshapeParam$1,
199
212
  VirtualBackgroundOptions: VirtualBackgroundOptions$1
200
213
  });
@@ -250,7 +263,7 @@ class BeautyEffectEngine {
250
263
  this.appId = engineConfig.appId;
251
264
  this.appKey = engineConfig.appKey;
252
265
  this.licenseJson = engineConfig.licenseJson;
253
- this.resourcePath = '/facebetter/resource.bundle';
266
+ this.resourcePath = '/resource.fbd';
254
267
  this.enginePtr = null;
255
268
  this.initialized = false;
256
269
  this.srcBufferPtr = null;
@@ -267,7 +280,6 @@ class BeautyEffectEngine {
267
280
  this._getWasmModule = platformAPI.getWasmModule;
268
281
  this._getWasmBuffer = platformAPI.getWasmBuffer;
269
282
  this._toImageData = platformAPI.toImageData;
270
- this._verifyAppKeyOnline = platformAPI.verifyAppKeyOnline;
271
283
  this._ensureGPUPixelCanvas = platformAPI.ensureGPUPixelCanvas;
272
284
  this._cleanupGPUPixelCanvas = platformAPI.cleanupGPUPixelCanvas;
273
285
 
@@ -376,7 +388,7 @@ class BeautyEffectEngine {
376
388
  if (!Module.onReportUsage) {
377
389
  Module.onReportUsage = async (payloadJson) => {
378
390
  try {
379
- const url = 'https://facebetter.pixpark.net/rest/v1/rpc/report_sdk_usage';
391
+ const url = 'https://facebetter.pixpark.net/facebetter/v1/report';
380
392
  const response = await fetch(url, {
381
393
  method: 'POST',
382
394
  headers: {
@@ -395,7 +407,7 @@ class BeautyEffectEngine {
395
407
  if (!Module.onOnlineAuth) {
396
408
  Module.onOnlineAuth = async (payloadJson) => {
397
409
  try {
398
- const url = 'https://facebetter.pixpark.net/rest/v1/rpc/online_auth_v2';
410
+ const url = 'https://facebetter.pixpark.net/facebetter/v1/auth';
399
411
  const response = await fetch(url, {
400
412
  method: 'POST',
401
413
  headers: {
@@ -635,7 +647,231 @@ class BeautyEffectEngine {
635
647
  }
636
648
 
637
649
  /**
638
- * Internal method to set beauty parameters
650
+ * Sets a LUT-based filter
651
+ * @param {string} filterId - Unique identifier of the filter (e.g., "chuxin"). Pass "" to clear.
652
+ */
653
+ setFilter(filterId) {
654
+ this._ensureInitialized();
655
+ const Module = this._getWasmModule();
656
+ const result = Module.ccall(
657
+ 'SetFilter',
658
+ 'number',
659
+ ['number', 'string'],
660
+ [this.enginePtr, filterId || '']
661
+ );
662
+ checkResult(result, 'Failed to set filter');
663
+ }
664
+
665
+ /**
666
+ * Sets the intensity of the current filter
667
+ * @param {number} intensity - Filter intensity (0.0 - 1.0)
668
+ */
669
+ setFilterIntensity(intensity) {
670
+ this._ensureInitialized();
671
+ const Module = this._getWasmModule();
672
+ const result = Module.ccall(
673
+ 'SetFilterIntensity',
674
+ 'number',
675
+ ['number', 'number'],
676
+ [this.enginePtr, intensity]
677
+ );
678
+ checkResult(result, 'Failed to set filter intensity');
679
+ }
680
+
681
+ /**
682
+ * Sets a 2D sticker
683
+ * @param {string} stickerId - Unique identifier of the sticker (e.g., "樱花"). Pass "" to clear.
684
+ */
685
+ setSticker(stickerId) {
686
+ this._ensureInitialized();
687
+ const Module = this._getWasmModule();
688
+ const result = Module.ccall(
689
+ 'SetSticker',
690
+ 'number',
691
+ ['number', 'string'],
692
+ [this.enginePtr, stickerId || '']
693
+ );
694
+ checkResult(result, 'Failed to set sticker');
695
+ }
696
+
697
+ /**
698
+ * Registers a filter from a file path or data
699
+ * @param {string} filterId - Unique identifier for the filter
700
+ * @param {string|Uint8Array} resource - Path to .fbd file or Uint8Array data
701
+ */
702
+ registerFilter(filterId, resource) {
703
+ this._ensureInitialized();
704
+ const Module = this._getWasmModule();
705
+
706
+ if (typeof resource === 'string') {
707
+ const result = Module.ccall(
708
+ 'RegisterFilterPath',
709
+ 'number',
710
+ ['number', 'string', 'string'],
711
+ [this.enginePtr, filterId, resource]
712
+ );
713
+ checkResult(result, `Failed to register filter path: ${filterId}`);
714
+ } else if (resource instanceof Uint8Array) {
715
+ const dataPtr = Module._malloc(resource.length);
716
+ Module.HEAPU8.set(resource, dataPtr);
717
+ try {
718
+ const result = Module.ccall(
719
+ 'RegisterFilterData',
720
+ 'number',
721
+ ['number', 'string', 'number', 'number'],
722
+ [this.enginePtr, filterId, dataPtr, resource.length]
723
+ );
724
+ checkResult(result, `Failed to register filter data: ${filterId}`);
725
+ } finally {
726
+ Module._free(dataPtr);
727
+ }
728
+ } else {
729
+ throw new FacebetterError('Resource must be a string path or Uint8Array data');
730
+ }
731
+ }
732
+
733
+ /**
734
+ * Registers a sticker from a file path or data
735
+ * @param {string} stickerId - Unique identifier for the sticker
736
+ * @param {string|Uint8Array} resource - Path to .fbd file or Uint8Array data
737
+ */
738
+ registerSticker(stickerId, resource) {
739
+ this._ensureInitialized();
740
+ const Module = this._getWasmModule();
741
+
742
+ if (typeof resource === 'string') {
743
+ const result = Module.ccall(
744
+ 'RegisterStickerPath',
745
+ 'number',
746
+ ['number', 'string', 'string'],
747
+ [this.enginePtr, stickerId, resource]
748
+ );
749
+ checkResult(result, `Failed to register sticker path: ${stickerId}`);
750
+ } else if (resource instanceof Uint8Array) {
751
+ const dataPtr = Module._malloc(resource.length);
752
+ Module.HEAPU8.set(resource, dataPtr);
753
+ try {
754
+ const result = Module.ccall(
755
+ 'RegisterStickerData',
756
+ 'number',
757
+ ['number', 'string', 'number', 'number'],
758
+ [this.enginePtr, stickerId, dataPtr, resource.length]
759
+ );
760
+ checkResult(result, `Failed to register sticker data: ${stickerId}`);
761
+ } finally {
762
+ Module._free(dataPtr);
763
+ }
764
+ } else {
765
+ throw new FacebetterError('Resource must be a string path or Uint8Array data');
766
+ }
767
+ }
768
+
769
+ /**
770
+ * Unregisters a specific filter
771
+ * @param {string} filterId - Filter ID to unregister
772
+ */
773
+ unregisterFilter(filterId) {
774
+ this._ensureInitialized();
775
+ const Module = this._getWasmModule();
776
+ const result = Module.ccall(
777
+ 'UnregisterFilter',
778
+ 'number',
779
+ ['number', 'string'],
780
+ [this.enginePtr, filterId]
781
+ );
782
+ checkResult(result, `Failed to unregister filter: ${filterId}`);
783
+ }
784
+
785
+ /**
786
+ * Unregisters all filters
787
+ */
788
+ unregisterAllFilters() {
789
+ this._ensureInitialized();
790
+ const Module = this._getWasmModule();
791
+ const result = Module.ccall(
792
+ 'UnregisterAllFilters',
793
+ 'number',
794
+ ['number'],
795
+ [this.enginePtr]
796
+ );
797
+ checkResult(result, 'Failed to unregister all filters');
798
+ }
799
+
800
+ /**
801
+ * Unregisters a specific sticker
802
+ * @param {string} stickerId - Sticker ID to unregister
803
+ */
804
+ unregisterSticker(stickerId) {
805
+ this._ensureInitialized();
806
+ const Module = this._getWasmModule();
807
+ const result = Module.ccall(
808
+ 'UnregisterSticker',
809
+ 'number',
810
+ ['number', 'string'],
811
+ [this.enginePtr, stickerId]
812
+ );
813
+ checkResult(result, `Failed to unregister sticker: ${stickerId}`);
814
+ }
815
+
816
+ /**
817
+ * Unregisters all stickers
818
+ */
819
+ unregisterAllStickers() {
820
+ this._ensureInitialized();
821
+ const Module = this._getWasmModule();
822
+ const result = Module.ccall(
823
+ 'UnregisterAllStickers',
824
+ 'number',
825
+ ['number'],
826
+ [this.enginePtr]
827
+ );
828
+ checkResult(result, 'Failed to unregister all stickers');
829
+ }
830
+
831
+ /**
832
+ * Gets the list of registered filter IDs
833
+ * @returns {string[]} Array of registered filter IDs
834
+ */
835
+ getRegisteredFilters() {
836
+ this._ensureInitialized();
837
+ const Module = this._getWasmModule();
838
+ const jsonStr = Module.ccall(
839
+ 'GetRegisteredFilters',
840
+ 'string',
841
+ ['number'],
842
+ [this.enginePtr]
843
+ );
844
+ try {
845
+ return JSON.parse(jsonStr || '[]');
846
+ } catch (e) {
847
+ console.error('Failed to parse registered filters JSON:', e);
848
+ return [];
849
+ }
850
+ }
851
+
852
+ /**
853
+ * Gets the list of registered sticker IDs
854
+ * @returns {string[]} Array of registered sticker IDs
855
+ */
856
+ getRegisteredStickers() {
857
+ this._ensureInitialized();
858
+ const Module = this._getWasmModule();
859
+ const jsonStr = Module.ccall(
860
+ 'GetRegisteredStickers',
861
+ 'string',
862
+ ['number'],
863
+ [this.enginePtr]
864
+ );
865
+ try {
866
+ return JSON.parse(jsonStr || '[]');
867
+ } catch (e) {
868
+ console.error('Failed to parse registered stickers JSON:', e);
869
+ return [];
870
+ }
871
+ }
872
+
873
+ /**
874
+ * Internal method to set beauty parameters
639
875
  * @private
640
876
  */
641
877
  _setBeautyParam(functionName, param, value) {
@@ -873,9 +1109,10 @@ class BeautyEffectEngine {
873
1109
  * @param {number} width - Image width (required if input is Uint8ClampedArray)
874
1110
  * @param {number} height - Image height (required if input is Uint8ClampedArray)
875
1111
  * @param {number} frameType - Frame type (use FrameType enum, default: FrameType.Video)
1112
+ * @param {number} mirrorMode - Mirror mode applied to input before processing (MirrorMode enum, default: MirrorMode.None)
876
1113
  * @returns {ImageData} Processed image data
877
1114
  */
878
- processImage(input, width, height, frameType = FrameType$1.Video) {
1115
+ processImage(input, width, height, frameType = FrameType$1.Video, mirrorMode = MirrorMode$1.None) {
879
1116
  this._ensureInitialized();
880
1117
  const imageData = this._platformAPI.toImageData(input, width, height);
881
1118
  const Module = this._getWasmModule();
@@ -893,7 +1130,7 @@ class BeautyEffectEngine {
893
1130
  const result = Module.ccall(
894
1131
  'ProcessImageRGBA',
895
1132
  'number',
896
- ['number', 'number', 'number', 'number', 'number', 'number', 'number'],
1133
+ ['number', 'number', 'number', 'number', 'number', 'number', 'number', 'number'],
897
1134
  [
898
1135
  this.enginePtr,
899
1136
  this.srcBufferPtr,
@@ -901,7 +1138,8 @@ class BeautyEffectEngine {
901
1138
  imageData.height,
902
1139
  imageData.width * 4,
903
1140
  this.dstBufferPtr,
904
- frameType
1141
+ frameType,
1142
+ mirrorMode
905
1143
  ]
906
1144
  );
907
1145
 
@@ -933,13 +1171,15 @@ class BeautyEffectEngine {
933
1171
  * @param {number} height - Texture height
934
1172
  * @param {number} stride - Row stride (usually width * 4)
935
1173
  * @param {number} [frameType] - Frame type (FrameType.Image / FrameType.Video)
1174
+ * @param {number} [mirrorMode] - Mirror mode (MirrorMode enum; texture path does not support mirror, param reserved)
936
1175
  * @returns {number} Processed texture handle (GL_TEXTURE_2D, uint32)
937
1176
  */
938
1177
  processTexture(textureHandle,
939
1178
  width,
940
1179
  height,
941
1180
  stride,
942
- frameType = FrameType$1.Video) {
1181
+ frameType = FrameType$1.Video,
1182
+ mirrorMode = MirrorMode$1.None) {
943
1183
  this._ensureInitialized();
944
1184
  if (!textureHandle || width <= 0 || height <= 0 || stride <= 0) {
945
1185
  throw new FacebetterError('Invalid textureHandle or dimensions for processTexture');
@@ -951,14 +1191,15 @@ class BeautyEffectEngine {
951
1191
  const result = Module.ccall(
952
1192
  'ProcessImageTexture',
953
1193
  'number',
954
- ['number', 'number', 'number', 'number', 'number', 'number'],
1194
+ ['number', 'number', 'number', 'number', 'number', 'number', 'number'],
955
1195
  [
956
1196
  this.enginePtr,
957
1197
  textureHandle,
958
1198
  width,
959
1199
  height,
960
1200
  stride,
961
- frameType
1201
+ frameType,
1202
+ mirrorMode
962
1203
  ]
963
1204
  );
964
1205
 
@@ -1045,15 +1286,6 @@ class BeautyEffectEngine {
1045
1286
  return this._offscreenCanvas;
1046
1287
  }
1047
1288
 
1048
- /**
1049
- * Internal method for online authentication (Deprecated: logic moved to WASM layer)
1050
- * @private
1051
- */
1052
- async _verifyAppKeyOnline(appId, appKey) {
1053
- // This method is now deprecated as logic is moved to WASM layer via Module.onOnlineAuth
1054
- return null;
1055
- }
1056
-
1057
1289
  }
1058
1290
 
1059
1291
  /**
@@ -1105,77 +1337,6 @@ function toImageData(source, width, height) {
1105
1337
  throw new FacebetterError('Unsupported image source type');
1106
1338
  }
1107
1339
 
1108
- /**
1109
- * Gets User-Agent string for web platform
1110
- * @returns {string} User-Agent string
1111
- */
1112
- function getUserAgentString() {
1113
- return 'FB/1.0 (web; wasm32)';
1114
- }
1115
-
1116
- /**
1117
- * Generates HMAC-SHA256 signature (browser version)
1118
- * @param {string} key - HMAC key (app_key)
1119
- * @param {string} data - Data to sign (timestamp string)
1120
- * @returns {Promise<string>} Promise that resolves with hex-encoded signature
1121
- */
1122
- async function generateHMACSignature(key, data) {
1123
- const encoder = new TextEncoder();
1124
- const keyData = encoder.encode(key);
1125
- const messageData = encoder.encode(data);
1126
-
1127
- const cryptoKey = await crypto.subtle.importKey(
1128
- 'raw',
1129
- keyData,
1130
- { name: 'HMAC', hash: 'SHA-256' },
1131
- false,
1132
- ['sign']
1133
- );
1134
-
1135
- const signature = await crypto.subtle.sign('HMAC', cryptoKey, messageData);
1136
-
1137
- // Convert to hex string
1138
- const hashArray = Array.from(new Uint8Array(signature));
1139
- const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
1140
- return hashHex;
1141
- }
1142
-
1143
- /**
1144
- * Calls online_auth API to verify app_key (browser version)
1145
- * @param {string} appId - Application ID
1146
- * @param {string} appKey - Application key
1147
- * @returns {Promise<Object>} Promise that resolves with response data
1148
- */
1149
- async function verifyAppKeyOnline(appId, appKey) {
1150
- try {
1151
- const timestamp = Math.floor(Date.now() / 1000);
1152
- const timestampStr = timestamp.toString();
1153
- const hmacSignature = await generateHMACSignature(appKey, timestampStr);
1154
-
1155
- const requestBody = {
1156
- p_app_id: appId,
1157
- p_hmac_signature: hmacSignature,
1158
- p_timestamp: timestamp,
1159
- p_user_agent: getUserAgentString()
1160
- };
1161
-
1162
- const response = await fetch('https://facebetter.pixpark.net/rest/v1/rpc/online_auth_v2', {
1163
- method: 'POST',
1164
- headers: {
1165
- 'Content-Type': 'application/json',
1166
- 'User-Agent': getUserAgentString()
1167
- },
1168
- body: JSON.stringify(requestBody)
1169
- });
1170
-
1171
- const responseData = await response.json();
1172
- return responseData;
1173
- } catch (error) {
1174
- console.error('Online auth request failed:', error);
1175
- return null;
1176
- }
1177
- }
1178
-
1179
1340
  /**
1180
1341
  * ESM Entry Point
1181
1342
  * For Vue/React/Vite/Webpack (ES Module)
@@ -1317,7 +1478,6 @@ const platformAPI = {
1317
1478
  getWasmModule,
1318
1479
  getWasmBuffer,
1319
1480
  toImageData: toImageData,
1320
- verifyAppKeyOnline: verifyAppKeyOnline,
1321
1481
  ensureGPUPixelCanvas,
1322
1482
  cleanupGPUPixelCanvas
1323
1483
  };
@@ -1344,6 +1504,7 @@ const MakeupParam = MakeupParam$1;
1344
1504
  const ChromaKeyParam = ChromaKeyParam$1;
1345
1505
  const BackgroundMode = BackgroundMode$1;
1346
1506
  const FrameType = FrameType$1;
1507
+ const MirrorMode = MirrorMode$1;
1347
1508
  const VirtualBackgroundOptions = VirtualBackgroundOptions$1;
1348
1509
 
1349
1510
  // Default export
@@ -1355,5 +1516,5 @@ var index = {
1355
1516
  loadWasmModule
1356
1517
  };
1357
1518
 
1358
- export { BackgroundMode, BasicParam, ESMBeautyEffectEngine as BeautyEffectEngine, BeautyType, ChromaKeyParam, EngineConfig, FacebetterError, FrameType, MakeupParam, ReshapeParam, VirtualBackgroundOptions, createBeautyEffectEngine, index as default, getWasmBuffer, getWasmModule, loadWasmModule };
1519
+ export { BackgroundMode, BasicParam, ESMBeautyEffectEngine as BeautyEffectEngine, BeautyType, ChromaKeyParam, EngineConfig, FacebetterError, FrameType, MakeupParam, MirrorMode, ReshapeParam, VirtualBackgroundOptions, createBeautyEffectEngine, index as default, getWasmBuffer, getWasmModule, loadWasmModule };
1359
1520
  //# sourceMappingURL=facebetter.esm.js.map