react-native-capture-studio 0.1.0 → 0.2.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.
Files changed (30) hide show
  1. package/android/generated/java/com/capturestudio/NativeCaptureStudioSpec.java +4 -0
  2. package/android/generated/jni/CMakeLists.txt +1 -4
  3. package/android/generated/jni/RNCaptureStudioSpec-generated.cpp +6 -0
  4. package/android/generated/jni/react/renderer/components/RNCaptureStudioSpec/RNCaptureStudioSpecJSI.h +9 -0
  5. package/android/src/main/java/com/capturestudio/CaptureStudioModule.kt +34 -0
  6. package/android/src/main/java/com/capturestudio/data/processing/ImageProcessor.kt +109 -9
  7. package/ios/CaptureStudio.mm +35 -0
  8. package/ios/ImageProcessor.h +3 -0
  9. package/ios/ImageProcessor.mm +60 -0
  10. package/ios/generated/Package.swift +1 -1
  11. package/ios/generated/ReactCodegen/RNCaptureStudioSpec/RNCaptureStudioSpec-generated.mm +7 -0
  12. package/ios/generated/ReactCodegen/RNCaptureStudioSpec/RNCaptureStudioSpec.h +3 -0
  13. package/ios/generated/ReactCodegen/RNCaptureStudioSpecJSI.h +9 -0
  14. package/lib/commonjs/NativeCaptureStudio.js.map +1 -1
  15. package/lib/commonjs/index.js +4 -0
  16. package/lib/commonjs/index.js.map +1 -1
  17. package/lib/module/NativeCaptureStudio.js.map +1 -1
  18. package/lib/module/index.js +3 -0
  19. package/lib/module/index.js.map +1 -1
  20. package/lib/typescript/commonjs/src/NativeCaptureStudio.d.ts +1 -0
  21. package/lib/typescript/commonjs/src/NativeCaptureStudio.d.ts.map +1 -1
  22. package/lib/typescript/commonjs/src/index.d.ts +5 -0
  23. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  24. package/lib/typescript/module/src/NativeCaptureStudio.d.ts +1 -0
  25. package/lib/typescript/module/src/NativeCaptureStudio.d.ts.map +1 -1
  26. package/lib/typescript/module/src/index.d.ts +5 -0
  27. package/lib/typescript/module/src/index.d.ts.map +1 -1
  28. package/package.json +1 -1
  29. package/src/NativeCaptureStudio.ts +1 -0
  30. package/src/index.tsx +9 -0
@@ -45,4 +45,8 @@ public abstract class NativeCaptureStudioSpec extends ReactContextBaseJavaModule
45
45
  @ReactMethod
46
46
  @DoNotStrip
47
47
  public abstract void fetchProcessingResult(String operationId, Promise promise);
48
+
49
+ @ReactMethod
50
+ @DoNotStrip
51
+ public abstract void generateThumbnail(ReadableMap item, Promise promise);
48
52
  }
@@ -25,7 +25,4 @@ target_link_libraries(
25
25
  reactnative
26
26
  )
27
27
 
28
- # Only call if the function exists (added in RN 0.79+)
29
- if(COMMAND target_compile_reactnative_options)
30
- target_compile_reactnative_options(react_codegen_RNCaptureStudioSpec PRIVATE)
31
- endif()
28
+ target_compile_reactnative_options(react_codegen_RNCaptureStudioSpec PRIVATE)
@@ -27,11 +27,17 @@ static facebook::jsi::Value __hostFunction_NativeCaptureStudioSpecJSI_fetchProce
27
27
  return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, PromiseKind, "fetchProcessingResult", "(Ljava/lang/String;Lcom/facebook/react/bridge/Promise;)V", args, count, cachedMethodId);
28
28
  }
29
29
 
30
+ static facebook::jsi::Value __hostFunction_NativeCaptureStudioSpecJSI_generateThumbnail(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
31
+ static jmethodID cachedMethodId = nullptr;
32
+ return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, PromiseKind, "generateThumbnail", "(Lcom/facebook/react/bridge/ReadableMap;Lcom/facebook/react/bridge/Promise;)V", args, count, cachedMethodId);
33
+ }
34
+
30
35
  NativeCaptureStudioSpecJSI::NativeCaptureStudioSpecJSI(const JavaTurboModule::InitParams &params)
31
36
  : JavaTurboModule(params) {
32
37
  methodMap_["openCaptureStudio"] = MethodMetadata {1, __hostFunction_NativeCaptureStudioSpecJSI_openCaptureStudio};
33
38
  methodMap_["processImages"] = MethodMetadata {1, __hostFunction_NativeCaptureStudioSpecJSI_processImages};
34
39
  methodMap_["fetchProcessingResult"] = MethodMetadata {1, __hostFunction_NativeCaptureStudioSpecJSI_fetchProcessingResult};
40
+ methodMap_["generateThumbnail"] = MethodMetadata {1, __hostFunction_NativeCaptureStudioSpecJSI_generateThumbnail};
35
41
  }
36
42
 
37
43
  std::shared_ptr<TurboModule> RNCaptureStudioSpec_ModuleProvider(const std::string &moduleName, const JavaTurboModule::InitParams &params) {
@@ -25,6 +25,7 @@ protected:
25
25
  methodMap_["openCaptureStudio"] = MethodMetadata {.argCount = 1, .invoker = __openCaptureStudio};
26
26
  methodMap_["processImages"] = MethodMetadata {.argCount = 1, .invoker = __processImages};
27
27
  methodMap_["fetchProcessingResult"] = MethodMetadata {.argCount = 1, .invoker = __fetchProcessingResult};
28
+ methodMap_["generateThumbnail"] = MethodMetadata {.argCount = 1, .invoker = __generateThumbnail};
28
29
  }
29
30
 
30
31
  private:
@@ -51,6 +52,14 @@ private:
51
52
  return bridging::callFromJs<jsi::Value>(rt, &T::fetchProcessingResult, static_cast<NativeCaptureStudioCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule),
52
53
  count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt));
53
54
  }
55
+
56
+ static jsi::Value __generateThumbnail(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
57
+ static_assert(
58
+ bridging::getParameterCount(&T::generateThumbnail) == 2,
59
+ "Expected generateThumbnail(...) to have 2 parameters");
60
+ return bridging::callFromJs<jsi::Value>(rt, &T::generateThumbnail, static_cast<NativeCaptureStudioCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule),
61
+ count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asObject(rt));
62
+ }
54
63
  };
55
64
 
56
65
  } // namespace facebook::react
@@ -6,6 +6,7 @@ import androidx.work.Data
6
6
  import androidx.work.OneTimeWorkRequestBuilder
7
7
  import androidx.work.WorkInfo
8
8
  import androidx.work.WorkManager
9
+ import com.capturestudio.data.processing.ImageProcessor
9
10
  import com.capturestudio.data.processing.ImageProcessingWorker
10
11
  import com.capturestudio.ui.camera.CameraActivity
11
12
  import com.facebook.react.bridge.Promise
@@ -151,6 +152,39 @@ class CaptureStudioModule(reactContext: ReactApplicationContext) :
151
152
  }
152
153
  }
153
154
 
155
+ /**
156
+ * Generate a thumbnail for a single image.
157
+ * Runs on a background thread.
158
+ *
159
+ * @param item Map with localPath (string) and maxSize (int)
160
+ * @param promise Resolves with thumbnail file path
161
+ */
162
+ override fun generateThumbnail(item: ReadableMap, promise: Promise) {
163
+ val localPath = item.getString("localPath") ?: ""
164
+ val maxSize = if (item.hasKey("maxSize")) item.getInt("maxSize") else 100
165
+
166
+ if (localPath.isEmpty()) {
167
+ promise.reject("INVALID_INPUT", "localPath is required")
168
+ return
169
+ }
170
+
171
+ val cleanPath = localPath.replace("file://", "")
172
+
173
+ Thread {
174
+ try {
175
+ val result = ImageProcessor.generateThumbnail(cleanPath, maxSize)
176
+ if (result.isSuccess) {
177
+ promise.resolve(result.getOrNull())
178
+ } else {
179
+ promise.reject("THUMBNAIL_FAILED", result.exceptionOrNull()?.message ?: "Unknown error")
180
+ }
181
+ } catch (e: Exception) {
182
+ Log.e(TAG, "Thumbnail generation failed", e)
183
+ promise.reject("THUMBNAIL_FAILED", e.message, e)
184
+ }
185
+ }.start()
186
+ }
187
+
154
188
  /**
155
189
  * Convert a ReadableArray to a JSON string.
156
190
  */
@@ -87,6 +87,17 @@ object ImageProcessor {
87
87
  useJpeg = compressJpeg
88
88
  )
89
89
 
90
+ // 5. Reset EXIF orientation since pixels are already rotated
91
+ if (orientation != ExifInterface.ORIENTATION_NORMAL &&
92
+ orientation != ExifInterface.ORIENTATION_UNDEFINED) {
93
+ val newExif = ExifInterface(imagePath)
94
+ newExif.setAttribute(
95
+ ExifInterface.TAG_ORIENTATION,
96
+ ExifInterface.ORIENTATION_NORMAL.toString()
97
+ )
98
+ newExif.saveAttributes()
99
+ }
100
+
90
101
  watermarkedBitmap.recycle()
91
102
 
92
103
  Log.d(TAG, "Successfully processed: $imagePath")
@@ -94,18 +105,107 @@ object ImageProcessor {
94
105
  }
95
106
 
96
107
  /**
97
- * Rotate bitmap based on EXIF orientation.
108
+ * Generate a small thumbnail from an image file.
109
+ * Uses BitmapFactory.Options.inSampleSize for memory-efficient downsampling.
110
+ * Does NOT load the full bitmap into memory.
111
+ *
112
+ * @param imagePath Path to the source image
113
+ * @param maxSize Max pixel dimension for the thumbnail (e.g. 200)
114
+ * @return Result with thumbnail path on success
98
115
  */
99
- private fun rotateBitmapIfNeeded(bitmap: Bitmap, orientation: Int): Bitmap {
100
- val rotationAngle = when (orientation) {
101
- ExifInterface.ORIENTATION_ROTATE_90 -> 90f
102
- ExifInterface.ORIENTATION_ROTATE_180 -> 180f
103
- ExifInterface.ORIENTATION_ROTATE_270 -> 270f
104
- else -> return bitmap // No rotation needed
116
+ fun generateThumbnail(imagePath: String, maxSize: Int): Result<String> {
117
+ return runCatching {
118
+ val file = File(imagePath)
119
+ if (!file.exists()) {
120
+ throw IllegalArgumentException("File not found: $imagePath")
121
+ }
122
+
123
+ // Step 1: Decode only bounds (no memory allocation for pixels)
124
+ val options = BitmapFactory.Options().apply {
125
+ inJustDecodeBounds = true
126
+ }
127
+ BitmapFactory.decodeFile(imagePath, options)
128
+
129
+ val imageWidth = options.outWidth
130
+ val imageHeight = options.outHeight
131
+
132
+ // Step 2: Calculate inSampleSize for memory-efficient downsampling
133
+ var inSampleSize = 1
134
+ if (imageWidth > maxSize || imageHeight > maxSize) {
135
+ val halfWidth = imageWidth / 2
136
+ val halfHeight = imageHeight / 2
137
+ while ((halfWidth / inSampleSize) >= maxSize && (halfHeight / inSampleSize) >= maxSize) {
138
+ inSampleSize *= 2
139
+ }
140
+ }
141
+
142
+ // Step 3: Decode with inSampleSize (loads only ~thumbnail-sized bitmap)
143
+ val decodeOptions = BitmapFactory.Options().apply {
144
+ this.inSampleSize = inSampleSize
145
+ }
146
+ val sampledBitmap = BitmapFactory.decodeFile(imagePath, decodeOptions)
147
+ ?: throw IllegalStateException("Failed to decode image: $imagePath")
148
+
149
+ // Step 4: Scale to exact max size
150
+ val scale = maxSize.toFloat() / maxOf(sampledBitmap.width, sampledBitmap.height)
151
+ val targetWidth = (sampledBitmap.width * scale).toInt()
152
+ val targetHeight = (sampledBitmap.height * scale).toInt()
153
+ val thumbnail = Bitmap.createScaledBitmap(sampledBitmap, targetWidth, targetHeight, true)
154
+
155
+ if (thumbnail !== sampledBitmap) {
156
+ sampledBitmap.recycle()
157
+ }
158
+
159
+ // Step 5: Handle EXIF rotation
160
+ val exif = ExifInterface(imagePath)
161
+ val orientation = exif.getAttributeInt(
162
+ ExifInterface.TAG_ORIENTATION,
163
+ ExifInterface.ORIENTATION_NORMAL
164
+ )
165
+ val rotatedThumbnail = rotateBitmapIfNeeded(thumbnail, orientation)
166
+ if (rotatedThumbnail !== thumbnail) {
167
+ thumbnail.recycle()
168
+ }
169
+
170
+ // Step 6: Save as JPEG
171
+ val nameWithoutExt = imagePath.substringBeforeLast(".")
172
+ val thumbPath = "${nameWithoutExt}_thumb.jpg"
173
+
174
+ FileOutputStream(thumbPath).use { fos ->
175
+ rotatedThumbnail.compress(Bitmap.CompressFormat.JPEG, 60, fos)
176
+ }
177
+
178
+ rotatedThumbnail.recycle()
179
+
180
+ Log.d(TAG, "Generated thumbnail: $thumbPath")
181
+ thumbPath
105
182
  }
183
+ }
106
184
 
107
- val matrix = Matrix().apply {
108
- postRotate(rotationAngle)
185
+ /**
186
+ * Rotate and/or flip bitmap based on EXIF orientation.
187
+ * Handles all 8 EXIF orientation cases including mirrored variants
188
+ * produced by front-facing (selfie) cameras.
189
+ */
190
+ private fun rotateBitmapIfNeeded(bitmap: Bitmap, orientation: Int): Bitmap {
191
+ val matrix = Matrix()
192
+
193
+ when (orientation) {
194
+ ExifInterface.ORIENTATION_NORMAL -> return bitmap
195
+ ExifInterface.ORIENTATION_FLIP_HORIZONTAL -> matrix.postScale(-1f, 1f)
196
+ ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180f)
197
+ ExifInterface.ORIENTATION_FLIP_VERTICAL -> matrix.postScale(1f, -1f)
198
+ ExifInterface.ORIENTATION_TRANSPOSE -> {
199
+ matrix.postRotate(90f)
200
+ matrix.postScale(-1f, 1f)
201
+ }
202
+ ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90f)
203
+ ExifInterface.ORIENTATION_TRANSVERSE -> {
204
+ matrix.postRotate(-90f)
205
+ matrix.postScale(-1f, 1f)
206
+ }
207
+ ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270f)
208
+ else -> return bitmap
109
209
  }
110
210
 
111
211
  return Bitmap.createBitmap(
@@ -175,6 +175,41 @@ RCT_EXPORT_MODULE()
175
175
  }
176
176
  }
177
177
 
178
+ #pragma mark - generateThumbnail
179
+
180
+ - (void)generateThumbnail:(NSDictionary *)item
181
+ resolve:(RCTPromiseResolveBlock)resolve
182
+ reject:(RCTPromiseRejectBlock)reject
183
+ {
184
+ NSString *localPath = item[@"localPath"];
185
+ NSNumber *maxSizeNum = item[@"maxSize"];
186
+
187
+ if (!localPath || localPath.length == 0) {
188
+ reject(@"INVALID_INPUT", @"localPath is required", nil);
189
+ return;
190
+ }
191
+
192
+ NSInteger maxSize = maxSizeNum ? [maxSizeNum integerValue] : 100;
193
+
194
+ // Remove file:// prefix if present
195
+ NSString *cleanPath = [localPath stringByReplacingOccurrencesOfString:@"file://"
196
+ withString:@""];
197
+
198
+ // Run on background queue to avoid blocking JS thread
199
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
200
+ BOOL success = [ImageProcessor generateThumbnailAtPath:cleanPath maxSize:maxSize];
201
+
202
+ if (success) {
203
+ // Return the thumbnail path
204
+ NSString *nameWithoutExt = [cleanPath stringByDeletingPathExtension];
205
+ NSString *thumbPath = [NSString stringWithFormat:@"%@_thumb.jpg", nameWithoutExt];
206
+ resolve(thumbPath);
207
+ } else {
208
+ reject(@"THUMBNAIL_FAILED", @"Failed to generate thumbnail", nil);
209
+ }
210
+ });
211
+ }
212
+
178
213
  #pragma mark - TurboModule
179
214
 
180
215
  - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
@@ -17,6 +17,9 @@ NS_ASSUME_NONNULL_BEGIN
17
17
  compressJpeg:(BOOL)compressJpeg
18
18
  replaceOriginal:(BOOL)replaceOriginal;
19
19
 
20
+ + (BOOL)generateThumbnailAtPath:(NSString *)path
21
+ maxSize:(NSInteger)maxSize;
22
+
20
23
  @end
21
24
 
22
25
  NS_ASSUME_NONNULL_END
@@ -380,4 +380,64 @@
380
380
  return success;
381
381
  }
382
382
 
383
+ #pragma mark - Thumbnail Generation
384
+
385
+ + (BOOL)generateThumbnailAtPath:(NSString *)path
386
+ maxSize:(NSInteger)maxSize
387
+ {
388
+ @autoreleasepool {
389
+ NSURL *fileURL = [NSURL fileURLWithPath:path];
390
+ CGImageSourceRef source = CGImageSourceCreateWithURL((__bridge CFURLRef)fileURL, NULL);
391
+
392
+ if (!source) {
393
+ return NO;
394
+ }
395
+
396
+ // Request a thumbnail at max dimension, downsampling during decode
397
+ // This is MEMORY EFFICIENT — does NOT load the full image into memory
398
+ NSDictionary *options = @{
399
+ (NSString *)kCGImageSourceThumbnailMaxPixelSize: @(maxSize),
400
+ (NSString *)kCGImageSourceCreateThumbnailFromImageAlways: @YES,
401
+ (NSString *)kCGImageSourceCreateThumbnailWithTransform: @YES,
402
+ };
403
+
404
+ CGImageRef thumbnail = CGImageSourceCreateThumbnailAtIndex(source, 0, (__bridge CFDictionaryRef)options);
405
+ CFRelease(source);
406
+
407
+ if (!thumbnail) {
408
+ return NO;
409
+ }
410
+
411
+ // Build thumbnail output path: /path/to/IMG_123.jpg -> /path/to/IMG_123_thumb.jpg
412
+ NSString *nameWithoutExt = [path stringByDeletingPathExtension];
413
+ NSString *thumbPath = [NSString stringWithFormat:@"%@_thumb.jpg", nameWithoutExt];
414
+
415
+ // Write as JPEG with quality 60%
416
+ NSURL *thumbURL = [NSURL fileURLWithPath:thumbPath];
417
+ CGImageDestinationRef destination = CGImageDestinationCreateWithURL(
418
+ (__bridge CFURLRef)thumbURL,
419
+ (__bridge CFStringRef)UTTypeJPEG.identifier,
420
+ 1,
421
+ NULL
422
+ );
423
+
424
+ if (!destination) {
425
+ CGImageRelease(thumbnail);
426
+ return NO;
427
+ }
428
+
429
+ NSDictionary *destOptions = @{
430
+ (__bridge NSString *)kCGImageDestinationLossyCompressionQuality: @(0.6)
431
+ };
432
+
433
+ CGImageDestinationAddImage(destination, thumbnail, (__bridge CFDictionaryRef)destOptions);
434
+ BOOL success = CGImageDestinationFinalize(destination);
435
+
436
+ CFRelease(destination);
437
+ CGImageRelease(thumbnail);
438
+
439
+ return success;
440
+ }
441
+ }
442
+
383
443
  @end
@@ -16,7 +16,7 @@ let package = Package(
16
16
  targets: ["ReactAppDependencyProvider"]),
17
17
  ],
18
18
  dependencies: [
19
- .package(name: "React", path: "../../../../../../../node_modules/react-native")
19
+ .package(name: "React", path: "../../../../../../node_modules/react-native")
20
20
  ],
21
21
  targets: [
22
22
  // Targets are the basic building blocks of a package, defining a module or a test suite.
@@ -38,6 +38,10 @@ namespace facebook::react {
38
38
  return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, PromiseKind, "fetchProcessingResult", @selector(fetchProcessingResult:resolve:reject:), args, count);
39
39
  }
40
40
 
41
+ static facebook::jsi::Value __hostFunction_NativeCaptureStudioSpecJSI_generateThumbnail(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
42
+ return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, PromiseKind, "generateThumbnail", @selector(generateThumbnail:resolve:reject:), args, count);
43
+ }
44
+
41
45
  NativeCaptureStudioSpecJSI::NativeCaptureStudioSpecJSI(const ObjCTurboModule::InitParams &params)
42
46
  : ObjCTurboModule(params) {
43
47
 
@@ -49,5 +53,8 @@ namespace facebook::react {
49
53
 
50
54
  methodMap_["fetchProcessingResult"] = MethodMetadata {1, __hostFunction_NativeCaptureStudioSpecJSI_fetchProcessingResult};
51
55
 
56
+
57
+ methodMap_["generateThumbnail"] = MethodMetadata {1, __hostFunction_NativeCaptureStudioSpecJSI_generateThumbnail};
58
+
52
59
  }
53
60
  } // namespace facebook::react
@@ -44,6 +44,9 @@ NS_ASSUME_NONNULL_BEGIN
44
44
  - (void)fetchProcessingResult:(NSString *)operationId
45
45
  resolve:(RCTPromiseResolveBlock)resolve
46
46
  reject:(RCTPromiseRejectBlock)reject;
47
+ - (void)generateThumbnail:(NSDictionary *)item
48
+ resolve:(RCTPromiseResolveBlock)resolve
49
+ reject:(RCTPromiseRejectBlock)reject;
47
50
 
48
51
  @end
49
52
 
@@ -25,6 +25,7 @@ protected:
25
25
  methodMap_["openCaptureStudio"] = MethodMetadata {.argCount = 1, .invoker = __openCaptureStudio};
26
26
  methodMap_["processImages"] = MethodMetadata {.argCount = 1, .invoker = __processImages};
27
27
  methodMap_["fetchProcessingResult"] = MethodMetadata {.argCount = 1, .invoker = __fetchProcessingResult};
28
+ methodMap_["generateThumbnail"] = MethodMetadata {.argCount = 1, .invoker = __generateThumbnail};
28
29
  }
29
30
 
30
31
  private:
@@ -51,6 +52,14 @@ private:
51
52
  return bridging::callFromJs<jsi::Value>(rt, &T::fetchProcessingResult, static_cast<NativeCaptureStudioCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule),
52
53
  count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt));
53
54
  }
55
+
56
+ static jsi::Value __generateThumbnail(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
57
+ static_assert(
58
+ bridging::getParameterCount(&T::generateThumbnail) == 2,
59
+ "Expected generateThumbnail(...) to have 2 parameters");
60
+ return bridging::callFromJs<jsi::Value>(rt, &T::generateThumbnail, static_cast<NativeCaptureStudioCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule),
61
+ count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asObject(rt));
62
+ }
54
63
  };
55
64
 
56
65
  } // namespace facebook::react
@@ -1 +1 @@
1
- {"version":3,"names":["_reactNative","require","_default","exports","default","TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeCaptureStudio.ts"],"mappings":";;;;;;AACA,IAAAA,YAAA,GAAAC,OAAA;AAAmD,IAAAC,QAAA,GAAAC,OAAA,CAAAC,OAAA,GASpCC,gCAAmB,CAACC,YAAY,CAAO,eAAe,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["_reactNative","require","_default","exports","default","TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeCaptureStudio.ts"],"mappings":";;;;;;AACA,IAAAA,YAAA,GAAAC,OAAA;AAAmD,IAAAC,QAAA,GAAAC,OAAA,CAAAC,OAAA,GAUpCC,gCAAmB,CAACC,YAAY,CAAO,eAAe,CAAC","ignoreList":[]}
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.fetchProcessingResult = fetchProcessingResult;
7
+ exports.generateThumbnail = generateThumbnail;
7
8
  exports.openCaptureStudio = openCaptureStudio;
8
9
  exports.processImages = processImages;
9
10
  var _NativeCaptureStudio = _interopRequireDefault(require("./NativeCaptureStudio.js"));
@@ -17,4 +18,7 @@ function processImages(images) {
17
18
  function fetchProcessingResult(operationId) {
18
19
  return _NativeCaptureStudio.default.fetchProcessingResult(operationId);
19
20
  }
21
+ function generateThumbnail(item) {
22
+ return _NativeCaptureStudio.default.generateThumbnail(item);
23
+ }
20
24
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["_NativeCaptureStudio","_interopRequireDefault","require","e","__esModule","default","openCaptureStudio","options","CaptureStudio","processImages","images","fetchProcessingResult","operationId"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;;;;;;;AAAA,IAAAA,oBAAA,GAAAC,sBAAA,CAAAC,OAAA;AAAkD,SAAAD,uBAAAE,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAmB3C,SAASG,iBAAiBA,CAACC,OAAuB,GAAG,CAAC,CAAC,EAAgB;EAC5E,OAAOC,4BAAa,CAACF,iBAAiB,CAACC,OAAO,CAAC;AACjD;AAEO,SAASE,aAAaA,CAACC,MAA6B,EAAmB;EAC5E,OAAOF,4BAAa,CAACC,aAAa,CAACC,MAAM,CAAC;AAC5C;AAEO,SAASC,qBAAqBA,CAACC,WAAmB,EAAmB;EAC1E,OAAOJ,4BAAa,CAACG,qBAAqB,CAACC,WAAW,CAAC;AACzD","ignoreList":[]}
1
+ {"version":3,"names":["_NativeCaptureStudio","_interopRequireDefault","require","e","__esModule","default","openCaptureStudio","options","CaptureStudio","processImages","images","fetchProcessingResult","operationId","generateThumbnail","item"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;;;;;;;;AAAA,IAAAA,oBAAA,GAAAC,sBAAA,CAAAC,OAAA;AAAkD,SAAAD,uBAAAE,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAwB3C,SAASG,iBAAiBA,CAACC,OAAuB,GAAG,CAAC,CAAC,EAAgB;EAC5E,OAAOC,4BAAa,CAACF,iBAAiB,CAACC,OAAO,CAAC;AACjD;AAEO,SAASE,aAAaA,CAACC,MAA6B,EAAmB;EAC5E,OAAOF,4BAAa,CAACC,aAAa,CAACC,MAAM,CAAC;AAC5C;AAEO,SAASC,qBAAqBA,CAACC,WAAmB,EAAmB;EAC1E,OAAOJ,4BAAa,CAACG,qBAAqB,CAACC,WAAW,CAAC;AACzD;AAEO,SAASC,iBAAiBA,CAACC,IAAmB,EAAmB;EACtE,OAAON,4BAAa,CAACK,iBAAiB,CAACC,IAAI,CAAC;AAC9C","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeCaptureStudio.ts"],"mappings":";;AACA,SAASA,mBAAmB,QAAQ,cAAc;AASlD,eAAeA,mBAAmB,CAACC,YAAY,CAAO,eAAe,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeCaptureStudio.ts"],"mappings":";;AACA,SAASA,mBAAmB,QAAQ,cAAc;AAUlD,eAAeA,mBAAmB,CAACC,YAAY,CAAO,eAAe,CAAC","ignoreList":[]}
@@ -10,4 +10,7 @@ export function processImages(images) {
10
10
  export function fetchProcessingResult(operationId) {
11
11
  return CaptureStudio.fetchProcessingResult(operationId);
12
12
  }
13
+ export function generateThumbnail(item) {
14
+ return CaptureStudio.generateThumbnail(item);
15
+ }
13
16
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["CaptureStudio","openCaptureStudio","options","processImages","images","fetchProcessingResult","operationId"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,OAAOA,aAAa,MAAM,0BAAuB;AAmBjD,OAAO,SAASC,iBAAiBA,CAACC,OAAuB,GAAG,CAAC,CAAC,EAAgB;EAC5E,OAAOF,aAAa,CAACC,iBAAiB,CAACC,OAAO,CAAC;AACjD;AAEA,OAAO,SAASC,aAAaA,CAACC,MAA6B,EAAmB;EAC5E,OAAOJ,aAAa,CAACG,aAAa,CAACC,MAAM,CAAC;AAC5C;AAEA,OAAO,SAASC,qBAAqBA,CAACC,WAAmB,EAAmB;EAC1E,OAAON,aAAa,CAACK,qBAAqB,CAACC,WAAW,CAAC;AACzD","ignoreList":[]}
1
+ {"version":3,"names":["CaptureStudio","openCaptureStudio","options","processImages","images","fetchProcessingResult","operationId","generateThumbnail","item"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,OAAOA,aAAa,MAAM,0BAAuB;AAwBjD,OAAO,SAASC,iBAAiBA,CAACC,OAAuB,GAAG,CAAC,CAAC,EAAgB;EAC5E,OAAOF,aAAa,CAACC,iBAAiB,CAACC,OAAO,CAAC;AACjD;AAEA,OAAO,SAASC,aAAaA,CAACC,MAA6B,EAAmB;EAC5E,OAAOJ,aAAa,CAACG,aAAa,CAACC,MAAM,CAAC;AAC5C;AAEA,OAAO,SAASC,qBAAqBA,CAACC,WAAmB,EAAmB;EAC1E,OAAON,aAAa,CAACK,qBAAqB,CAACC,WAAW,CAAC;AACzD;AAEA,OAAO,SAASC,iBAAiBA,CAACC,IAAmB,EAAmB;EACtE,OAAOR,aAAa,CAACO,iBAAiB,CAACC,IAAI,CAAC;AAC9C","ignoreList":[]}
@@ -3,6 +3,7 @@ export interface Spec extends TurboModule {
3
3
  openCaptureStudio(options: Object): Promise<Object>;
4
4
  processImages(images: Object[]): Promise<string>;
5
5
  fetchProcessingResult(operationId: string): Promise<string>;
6
+ generateThumbnail(item: Object): Promise<string>;
6
7
  }
7
8
  declare const _default: Spec;
8
9
  export default _default;
@@ -1 +1 @@
1
- {"version":3,"file":"NativeCaptureStudio.d.ts","sourceRoot":"","sources":["../../../../src/NativeCaptureStudio.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEpD,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACjD,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC7D;;AAED,wBAAuE"}
1
+ {"version":3,"file":"NativeCaptureStudio.d.ts","sourceRoot":"","sources":["../../../../src/NativeCaptureStudio.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEpD,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACjD,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5D,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAClD;;AAED,wBAAuE"}
@@ -13,7 +13,12 @@ export type ImageProcessingItem = {
13
13
  compressJpegImage?: boolean;
14
14
  replaceOriginal?: boolean;
15
15
  };
16
+ export type ThumbnailItem = {
17
+ localPath: string;
18
+ maxSize?: number;
19
+ };
16
20
  export declare function openCaptureStudio(options?: CaptureOptions): Promise<any>;
17
21
  export declare function processImages(images: ImageProcessingItem[]): Promise<string>;
18
22
  export declare function fetchProcessingResult(operationId: string): Promise<string>;
23
+ export declare function generateThumbnail(item: ThumbnailItem): Promise<string>;
19
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/index.tsx"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE;QACT,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,GAAG,CAAC,CAE5E;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,mBAAmB,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAE5E;AAED,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAE1E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/index.tsx"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE;QACT,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,GAAG,CAAC,CAE5E;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,mBAAmB,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAE5E;AAED,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAE1E;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAEtE"}
@@ -3,6 +3,7 @@ export interface Spec extends TurboModule {
3
3
  openCaptureStudio(options: Object): Promise<Object>;
4
4
  processImages(images: Object[]): Promise<string>;
5
5
  fetchProcessingResult(operationId: string): Promise<string>;
6
+ generateThumbnail(item: Object): Promise<string>;
6
7
  }
7
8
  declare const _default: Spec;
8
9
  export default _default;
@@ -1 +1 @@
1
- {"version":3,"file":"NativeCaptureStudio.d.ts","sourceRoot":"","sources":["../../../../src/NativeCaptureStudio.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEpD,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACjD,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC7D;;AAED,wBAAuE"}
1
+ {"version":3,"file":"NativeCaptureStudio.d.ts","sourceRoot":"","sources":["../../../../src/NativeCaptureStudio.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEpD,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACjD,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5D,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAClD;;AAED,wBAAuE"}
@@ -13,7 +13,12 @@ export type ImageProcessingItem = {
13
13
  compressJpegImage?: boolean;
14
14
  replaceOriginal?: boolean;
15
15
  };
16
+ export type ThumbnailItem = {
17
+ localPath: string;
18
+ maxSize?: number;
19
+ };
16
20
  export declare function openCaptureStudio(options?: CaptureOptions): Promise<any>;
17
21
  export declare function processImages(images: ImageProcessingItem[]): Promise<string>;
18
22
  export declare function fetchProcessingResult(operationId: string): Promise<string>;
23
+ export declare function generateThumbnail(item: ThumbnailItem): Promise<string>;
19
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/index.tsx"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE;QACT,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,GAAG,CAAC,CAE5E;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,mBAAmB,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAE5E;AAED,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAE1E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/index.tsx"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE;QACT,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,GAAG,CAAC,CAE5E;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,mBAAmB,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAE5E;AAED,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAE1E;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAEtE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-capture-studio",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Cross-platform native image capture, editing, and compression studio for React Native",
5
5
  "source": "./src/index.tsx",
6
6
  "main": "./lib/commonjs/index.js",
@@ -6,6 +6,7 @@ export interface Spec extends TurboModule {
6
6
  // Image processing methods
7
7
  processImages(images: Object[]): Promise<string>;
8
8
  fetchProcessingResult(operationId: string): Promise<string>;
9
+ generateThumbnail(item: Object): Promise<string>;
9
10
  }
10
11
 
11
12
  export default TurboModuleRegistry.getEnforcing<Spec>('CaptureStudio');
package/src/index.tsx CHANGED
@@ -17,6 +17,11 @@ export type ImageProcessingItem = {
17
17
  replaceOriginal?: boolean; // true = override original, false = create new file (default: true)
18
18
  };
19
19
 
20
+ export type ThumbnailItem = {
21
+ localPath: string;
22
+ maxSize?: number;
23
+ };
24
+
20
25
  export function openCaptureStudio(options: CaptureOptions = {}): Promise<any> {
21
26
  return CaptureStudio.openCaptureStudio(options);
22
27
  }
@@ -28,3 +33,7 @@ export function processImages(images: ImageProcessingItem[]): Promise<string> {
28
33
  export function fetchProcessingResult(operationId: string): Promise<string> {
29
34
  return CaptureStudio.fetchProcessingResult(operationId);
30
35
  }
36
+
37
+ export function generateThumbnail(item: ThumbnailItem): Promise<string> {
38
+ return CaptureStudio.generateThumbnail(item);
39
+ }