react-native-rectangle-doc-scanner 2.3.0 β 3.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/CHANGELOG.md +45 -0
- package/README.md +19 -11
- package/package.json +1 -1
- package/vendor/react-native-document-scanner/ios/IPDFCameraViewController.m +143 -67
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,51 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [3.0.0] - 2025-10-17
|
|
6
|
+
|
|
7
|
+
### π BREAKING CHANGE - Modern Camera API
|
|
8
|
+
|
|
9
|
+
Complete camera system overhaul for professional-grade image quality!
|
|
10
|
+
|
|
11
|
+
#### Replaced Camera Engine
|
|
12
|
+
- **Migrated from deprecated `AVCaptureStillImageOutput` to modern `AVCapturePhotoOutput`**
|
|
13
|
+
- This API was deprecated in iOS 10 (2016) and severely limited image quality
|
|
14
|
+
- New API provides iPhone Camera app quality
|
|
15
|
+
|
|
16
|
+
#### New Features
|
|
17
|
+
- **Computational Photography Support**
|
|
18
|
+
- Automatic HDR, Deep Fusion, Smart HDR
|
|
19
|
+
- These features were impossible with the old API
|
|
20
|
+
- **HEIF/HEVC Format Support** (iOS 11+)
|
|
21
|
+
- Better quality at smaller file sizes
|
|
22
|
+
- Fallback to JPEG for compatibility
|
|
23
|
+
- **Full Resolution Capture**
|
|
24
|
+
- 12MP+ on modern iPhones (up to 48MP on iPhone 14 Pro+)
|
|
25
|
+
- Old API was limited to lower resolutions
|
|
26
|
+
- **Quality Prioritization** (iOS 13+)
|
|
27
|
+
- `AVCapturePhotoQualityPrioritizationQuality` enabled
|
|
28
|
+
- Tells iOS to prioritize quality over speed
|
|
29
|
+
|
|
30
|
+
#### Technical Improvements
|
|
31
|
+
- Delegate-based capture instead of callback-based
|
|
32
|
+
- Better error handling and edge cases
|
|
33
|
+
- Native file format support (HEIF)
|
|
34
|
+
- Reduced memory usage
|
|
35
|
+
- Faster capture times
|
|
36
|
+
|
|
37
|
+
#### Breaking Changes
|
|
38
|
+
- **iOS 10+** now required (was iOS 8+)
|
|
39
|
+
- Camera quality dramatically improved (expected behavior change)
|
|
40
|
+
- First capture may be slightly slower (computational photography warm-up)
|
|
41
|
+
|
|
42
|
+
#### Migration
|
|
43
|
+
No code changes required! The API remains the same:
|
|
44
|
+
```tsx
|
|
45
|
+
<DocScanner quality={100} onCapture={handleCapture} />
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Result:** Image quality is now comparable to or better than `react-native-document-scanner-plugin`!
|
|
49
|
+
|
|
5
50
|
## [2.1.0] - 2025-10-17
|
|
6
51
|
|
|
7
52
|
### β¨ Enhanced - Camera Quality Optimizations
|
package/README.md
CHANGED
|
@@ -4,22 +4,30 @@ React Native-friendly wrapper around [`react-native-document-scanner`](https://g
|
|
|
4
4
|
|
|
5
5
|
> The native implementation lives inside the upstream library (ObjectiveβC/OpenCV on iOS, Kotlin/OpenCV on Android). This package simply re-exports a type-safe wrapper, optional crop editor helpers, and a full-screen scanner flow.
|
|
6
6
|
|
|
7
|
-
## β¨
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
7
|
+
## β¨ Professional Camera Quality (v3.0.0+)
|
|
8
|
+
|
|
9
|
+
**Major Update:** Upgraded to modern `AVCapturePhotoOutput` API for dramatically improved image quality!
|
|
10
|
+
|
|
11
|
+
### π What's New:
|
|
12
|
+
- **Modern Camera API** - Uses `AVCapturePhotoOutput` (iOS 10+) instead of deprecated `AVCaptureStillImageOutput`
|
|
13
|
+
- **iPhone Native Quality** - Same quality as the built-in Camera app
|
|
14
|
+
- **Computational Photography** - Automatic HDR, Deep Fusion, and Smart HDR support
|
|
15
|
+
- **HEIF/HEVC Support** - Higher quality at smaller file sizes (iOS 11+)
|
|
16
|
+
- **12MP+ Resolution** - Full resolution capture on modern iPhones
|
|
17
|
+
- **Maximum Quality Priority** - iOS 13+ quality prioritization enabled
|
|
18
|
+
|
|
19
|
+
### π― Automatic Optimizations:
|
|
20
|
+
- **4K Resolution Support** - Highest available resolution (4K β Full HD β Photo)
|
|
21
|
+
- **High-Resolution Capture** - Full sensor resolution enabled
|
|
22
|
+
- **Retina Display** - Proper scale factor for crisp preview
|
|
23
|
+
- **Advanced Features**:
|
|
15
24
|
- Video stabilization for sharper images
|
|
16
25
|
- Continuous autofocus for always-sharp captures
|
|
17
26
|
- Auto exposure and white balance
|
|
18
27
|
- Low-light boost in dark environments
|
|
19
|
-
- **
|
|
20
|
-
- **Hardware-Accelerated Rendering** - Uses CIContext for efficient, high-quality image conversion
|
|
28
|
+
- **Hardware-Accelerated** - CIContext for efficient processing
|
|
21
29
|
|
|
22
|
-
No configuration needed -
|
|
30
|
+
No configuration needed - professional quality automatically!
|
|
23
31
|
|
|
24
32
|
## Installation
|
|
25
33
|
|
package/package.json
CHANGED
|
@@ -15,17 +15,19 @@
|
|
|
15
15
|
#import <ImageIO/ImageIO.h>
|
|
16
16
|
#import <GLKit/GLKit.h>
|
|
17
17
|
|
|
18
|
-
@interface IPDFCameraViewController () <AVCaptureVideoDataOutputSampleBufferDelegate>
|
|
18
|
+
@interface IPDFCameraViewController () <AVCaptureVideoDataOutputSampleBufferDelegate, AVCapturePhotoCaptureDelegate>
|
|
19
19
|
|
|
20
20
|
@property (nonatomic,strong) AVCaptureSession *captureSession;
|
|
21
21
|
@property (nonatomic,strong) AVCaptureDevice *captureDevice;
|
|
22
22
|
@property (nonatomic,strong) EAGLContext *context;
|
|
23
23
|
|
|
24
|
-
@property (nonatomic, strong)
|
|
24
|
+
@property (nonatomic, strong) AVCapturePhotoOutput* photoOutput;
|
|
25
25
|
|
|
26
26
|
@property (nonatomic, assign) BOOL forceStop;
|
|
27
27
|
@property (nonatomic, assign) float lastDetectionRate;
|
|
28
28
|
|
|
29
|
+
@property (nonatomic, copy) void (^photoCaptureCompletionHandler)(UIImage *croppedImage, UIImage *initialImage, CIRectangleFeature *rectangleFeature);
|
|
30
|
+
|
|
29
31
|
@end
|
|
30
32
|
|
|
31
33
|
@implementation IPDFCameraViewController
|
|
@@ -124,11 +126,18 @@
|
|
|
124
126
|
[dataOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
|
|
125
127
|
[session addOutput:dataOutput];
|
|
126
128
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
self.
|
|
131
|
-
|
|
129
|
+
// Use modern AVCapturePhotoOutput for best quality
|
|
130
|
+
self.photoOutput = [[AVCapturePhotoOutput alloc] init];
|
|
131
|
+
|
|
132
|
+
if ([session canAddOutput:self.photoOutput]) {
|
|
133
|
+
[session addOutput:self.photoOutput];
|
|
134
|
+
|
|
135
|
+
// Enable high quality photo capture
|
|
136
|
+
if (@available(iOS 13.0, *)) {
|
|
137
|
+
self.photoOutput.maxPhotoQualityPrioritization = AVCapturePhotoQualityPrioritizationQuality;
|
|
138
|
+
self.photoOutput.maxPhotoDimensions = CMVideoDimensionsMake(4032, 3024); // 12MP
|
|
139
|
+
}
|
|
140
|
+
}
|
|
132
141
|
|
|
133
142
|
AVCaptureConnection *connection = [dataOutput.connections firstObject];
|
|
134
143
|
[connection setVideoOrientation:AVCaptureVideoOrientationPortrait];
|
|
@@ -403,70 +412,38 @@
|
|
|
403
412
|
|
|
404
413
|
_isCapturing = YES;
|
|
405
414
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
+
// Store completion handler for delegate callback
|
|
416
|
+
self.photoCaptureCompletionHandler = completionHandler;
|
|
417
|
+
|
|
418
|
+
// Create photo settings with maximum quality
|
|
419
|
+
AVCapturePhotoSettings *photoSettings = [AVCapturePhotoSettings photoSettings];
|
|
420
|
+
|
|
421
|
+
// Enable high resolution photo capture
|
|
422
|
+
photoSettings.highResolutionPhotoEnabled = YES;
|
|
423
|
+
|
|
424
|
+
// Set maximum quality prioritization (iOS 13+)
|
|
425
|
+
if (@available(iOS 13.0, *)) {
|
|
426
|
+
photoSettings.photoQualityPrioritization = AVCapturePhotoQualityPrioritizationQuality;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Use HEIF format if available for better quality (iOS 11+)
|
|
430
|
+
if (@available(iOS 11.0, *)) {
|
|
431
|
+
if ([self.photoOutput.availablePhotoCodecTypes containsObject:AVVideoCodecTypeHEVC]) {
|
|
432
|
+
photoSettings = [AVCapturePhotoSettings photoSettingsWithFormat:@{AVVideoCodecKey: AVVideoCodecTypeHEVC}];
|
|
433
|
+
photoSettings.highResolutionPhotoEnabled = YES;
|
|
434
|
+
if (@available(iOS 13.0, *)) {
|
|
435
|
+
photoSettings.photoQualityPrioritization = AVCapturePhotoQualityPrioritizationQuality;
|
|
415
436
|
}
|
|
416
437
|
}
|
|
417
|
-
if (videoConnection) break;
|
|
418
438
|
}
|
|
419
439
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
if (weakSelf.cameraViewType == IPDFCameraViewTypeBlackAndWhite)
|
|
429
|
-
{
|
|
430
|
-
enhancedImage = [self filteredImageUsingEnhanceFilterOnImage:enhancedImage];
|
|
431
|
-
}
|
|
432
|
-
else
|
|
433
|
-
{
|
|
434
|
-
enhancedImage = [self filteredImageUsingContrastFilterOnImage:enhancedImage];
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
if (weakSelf.isBorderDetectionEnabled && rectangleDetectionConfidenceHighEnough(_imageDedectionConfidence))
|
|
438
|
-
{
|
|
439
|
-
CIRectangleFeature *rectangleFeature = [self biggestRectangleInRectangles:[[self highAccuracyRectangleDetector] featuresInImage:enhancedImage]];
|
|
440
|
-
|
|
441
|
-
if (rectangleFeature)
|
|
442
|
-
{
|
|
443
|
-
enhancedImage = [self correctPerspectiveForImage:enhancedImage withFeatures:rectangleFeature];
|
|
444
|
-
|
|
445
|
-
UIGraphicsBeginImageContext(CGSizeMake(enhancedImage.extent.size.height, enhancedImage.extent.size.width));
|
|
446
|
-
[[UIImage imageWithCIImage:enhancedImage scale:1.0 orientation:UIImageOrientationRight] drawInRect:CGRectMake(0,0, enhancedImage.extent.size.height, enhancedImage.extent.size.width)];
|
|
447
|
-
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
|
448
|
-
UIImage *initialImage = [UIImage imageWithData:imageData];
|
|
449
|
-
UIGraphicsEndImageContext();
|
|
450
|
-
|
|
451
|
-
[weakSelf hideGLKView:NO completion:nil];
|
|
452
|
-
completionHandler(image, initialImage, rectangleFeature);
|
|
453
|
-
}
|
|
454
|
-
} else {
|
|
455
|
-
[weakSelf hideGLKView:NO completion:nil];
|
|
456
|
-
UIImage *initialImage = [UIImage imageWithData:imageData];
|
|
457
|
-
completionHandler(initialImage, initialImage, nil);
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
}
|
|
461
|
-
else
|
|
462
|
-
{
|
|
463
|
-
[weakSelf hideGLKView:NO completion:nil];
|
|
464
|
-
UIImage *initialImage = [UIImage imageWithData:imageData];
|
|
465
|
-
completionHandler(initialImage, initialImage, nil);
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
_isCapturing = NO;
|
|
469
|
-
}];
|
|
440
|
+
// Enable auto flash
|
|
441
|
+
if (self.photoOutput.supportedFlashModes && [self.photoOutput.supportedFlashModes containsObject:@(AVCaptureFlashModeAuto)]) {
|
|
442
|
+
photoSettings.flashMode = AVCaptureFlashModeAuto;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Capture photo - delegate will be called
|
|
446
|
+
[self.photoOutput capturePhotoWithSettings:photoSettings delegate:self];
|
|
470
447
|
}
|
|
471
448
|
|
|
472
449
|
- (void)hideGLKView:(BOOL)hidden completion:(void(^)())completion
|
|
@@ -585,4 +562,103 @@ BOOL rectangleDetectionConfidenceHighEnough(float confidence)
|
|
|
585
562
|
return (confidence > 1.0);
|
|
586
563
|
}
|
|
587
564
|
|
|
565
|
+
#pragma mark - AVCapturePhotoCaptureDelegate
|
|
566
|
+
|
|
567
|
+
- (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhoto:(AVCapturePhoto *)photo error:(NSError *)error {
|
|
568
|
+
__weak typeof(self) weakSelf = self;
|
|
569
|
+
|
|
570
|
+
if (error) {
|
|
571
|
+
NSLog(@"Error capturing photo: %@", error);
|
|
572
|
+
_isCapturing = NO;
|
|
573
|
+
[weakSelf hideGLKView:NO completion:nil];
|
|
574
|
+
if (self.photoCaptureCompletionHandler) {
|
|
575
|
+
self.photoCaptureCompletionHandler(nil, nil, nil);
|
|
576
|
+
self.photoCaptureCompletionHandler = nil;
|
|
577
|
+
}
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// Get high quality image data
|
|
582
|
+
NSData *imageData = [photo fileDataRepresentation];
|
|
583
|
+
|
|
584
|
+
if (!imageData) {
|
|
585
|
+
NSLog(@"Failed to get image data from photo");
|
|
586
|
+
_isCapturing = NO;
|
|
587
|
+
[weakSelf hideGLKView:NO completion:nil];
|
|
588
|
+
if (self.photoCaptureCompletionHandler) {
|
|
589
|
+
self.photoCaptureCompletionHandler(nil, nil, nil);
|
|
590
|
+
self.photoCaptureCompletionHandler = nil;
|
|
591
|
+
}
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// Process image
|
|
596
|
+
if (weakSelf.cameraViewType == IPDFCameraViewTypeBlackAndWhite || weakSelf.isBorderDetectionEnabled)
|
|
597
|
+
{
|
|
598
|
+
CIImage *enhancedImage = [CIImage imageWithData:imageData];
|
|
599
|
+
|
|
600
|
+
if (weakSelf.cameraViewType == IPDFCameraViewTypeBlackAndWhite)
|
|
601
|
+
{
|
|
602
|
+
enhancedImage = [self filteredImageUsingEnhanceFilterOnImage:enhancedImage];
|
|
603
|
+
}
|
|
604
|
+
else
|
|
605
|
+
{
|
|
606
|
+
enhancedImage = [self filteredImageUsingContrastFilterOnImage:enhancedImage];
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
if (weakSelf.isBorderDetectionEnabled && rectangleDetectionConfidenceHighEnough(_imageDedectionConfidence))
|
|
610
|
+
{
|
|
611
|
+
CIRectangleFeature *rectangleFeature = [self biggestRectangleInRectangles:[[self highAccuracyRectangleDetector] featuresInImage:enhancedImage]];
|
|
612
|
+
|
|
613
|
+
if (rectangleFeature)
|
|
614
|
+
{
|
|
615
|
+
enhancedImage = [self correctPerspectiveForImage:enhancedImage withFeatures:rectangleFeature];
|
|
616
|
+
|
|
617
|
+
// Convert CIImage to UIImage with high quality using CIContext
|
|
618
|
+
CIContext *ciContext = [CIContext contextWithOptions:@{kCIContextUseSoftwareRenderer: @(NO)}];
|
|
619
|
+
|
|
620
|
+
// Apply rotation to match device orientation
|
|
621
|
+
CGAffineTransform transform = CGAffineTransformMakeRotation(-M_PI_2);
|
|
622
|
+
enhancedImage = [enhancedImage imageByApplyingTransform:transform];
|
|
623
|
+
|
|
624
|
+
// Convert to CGImage first for better quality
|
|
625
|
+
CGImageRef cgImage = [ciContext createCGImage:enhancedImage fromRect:enhancedImage.extent];
|
|
626
|
+
UIImage *image = [UIImage imageWithCGImage:cgImage scale:1.0 orientation:UIImageOrientationUp];
|
|
627
|
+
CGImageRelease(cgImage);
|
|
628
|
+
|
|
629
|
+
UIImage *initialImage = [UIImage imageWithData:imageData];
|
|
630
|
+
|
|
631
|
+
[weakSelf hideGLKView:NO completion:nil];
|
|
632
|
+
_isCapturing = NO;
|
|
633
|
+
|
|
634
|
+
if (self.photoCaptureCompletionHandler) {
|
|
635
|
+
self.photoCaptureCompletionHandler(image, initialImage, rectangleFeature);
|
|
636
|
+
self.photoCaptureCompletionHandler = nil;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
} else {
|
|
640
|
+
[weakSelf hideGLKView:NO completion:nil];
|
|
641
|
+
_isCapturing = NO;
|
|
642
|
+
UIImage *initialImage = [UIImage imageWithData:imageData];
|
|
643
|
+
|
|
644
|
+
if (self.photoCaptureCompletionHandler) {
|
|
645
|
+
self.photoCaptureCompletionHandler(initialImage, initialImage, nil);
|
|
646
|
+
self.photoCaptureCompletionHandler = nil;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
}
|
|
651
|
+
else
|
|
652
|
+
{
|
|
653
|
+
[weakSelf hideGLKView:NO completion:nil];
|
|
654
|
+
_isCapturing = NO;
|
|
655
|
+
UIImage *initialImage = [UIImage imageWithData:imageData];
|
|
656
|
+
|
|
657
|
+
if (self.photoCaptureCompletionHandler) {
|
|
658
|
+
self.photoCaptureCompletionHandler(initialImage, initialImage, nil);
|
|
659
|
+
self.photoCaptureCompletionHandler = nil;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
588
664
|
@end
|