react-native-rectangle-doc-scanner 7.8.0 → 7.10.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.
|
@@ -24,12 +24,14 @@ import android.util.Size
|
|
|
24
24
|
import android.view.Surface
|
|
25
25
|
import android.view.TextureView
|
|
26
26
|
import androidx.core.content.ContextCompat
|
|
27
|
+
import androidx.exifinterface.media.ExifInterface
|
|
27
28
|
import com.google.mlkit.vision.common.InputImage
|
|
28
29
|
import com.google.mlkit.vision.objects.ObjectDetection
|
|
29
30
|
import com.google.mlkit.vision.objects.defaults.ObjectDetectorOptions
|
|
30
31
|
import org.opencv.core.Point
|
|
31
32
|
import java.io.File
|
|
32
33
|
import java.io.FileOutputStream
|
|
34
|
+
import java.io.ByteArrayInputStream
|
|
33
35
|
import java.util.concurrent.atomic.AtomicReference
|
|
34
36
|
import java.util.concurrent.atomic.AtomicBoolean
|
|
35
37
|
import kotlin.math.abs
|
|
@@ -118,10 +120,10 @@ class CameraController(
|
|
|
118
120
|
return
|
|
119
121
|
}
|
|
120
122
|
|
|
123
|
+
// Always set the listener so we get size-change callbacks for transform updates.
|
|
124
|
+
previewView.surfaceTextureListener = textureListener
|
|
121
125
|
if (previewView.isAvailable) {
|
|
122
126
|
openCamera()
|
|
123
|
-
} else {
|
|
124
|
-
previewView.surfaceTextureListener = textureListener
|
|
125
127
|
}
|
|
126
128
|
}
|
|
127
129
|
|
|
@@ -131,6 +133,10 @@ class CameraController(
|
|
|
131
133
|
closeSession()
|
|
132
134
|
}
|
|
133
135
|
|
|
136
|
+
fun refreshTransform() {
|
|
137
|
+
configureTransform()
|
|
138
|
+
}
|
|
139
|
+
|
|
134
140
|
fun capturePhoto(
|
|
135
141
|
outputDirectory: File,
|
|
136
142
|
onImageCaptured: (File) -> Unit,
|
|
@@ -150,8 +156,8 @@ class CameraController(
|
|
|
150
156
|
}
|
|
151
157
|
|
|
152
158
|
try {
|
|
153
|
-
//
|
|
154
|
-
val jpegOrientation =
|
|
159
|
+
// Match JPEG orientation to current device rotation and sensor orientation.
|
|
160
|
+
val jpegOrientation = computeRotationDegrees()
|
|
155
161
|
Log.d(TAG, "[CAPTURE] Setting JPEG_ORIENTATION to $jpegOrientation")
|
|
156
162
|
|
|
157
163
|
val requestBuilder = device.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE).apply {
|
|
@@ -240,9 +246,10 @@ class CameraController(
|
|
|
240
246
|
val previewSizes = streamConfigMap.getOutputSizes(SurfaceTexture::class.java)
|
|
241
247
|
Log.d(TAG, "[CAMERA2] Available preview sizes: ${previewSizes?.take(10)?.joinToString { "${it.width}x${it.height}" }}")
|
|
242
248
|
|
|
243
|
-
//
|
|
244
|
-
previewSize = previewSizes
|
|
245
|
-
|
|
249
|
+
// Prefer a preview size that matches the view aspect to avoid letterboxing.
|
|
250
|
+
previewSize = chooseBestSize(previewSizes, viewAspect, null, preferClosestAspect = true)
|
|
251
|
+
?: previewSizes?.maxByOrNull { it.width * it.height }
|
|
252
|
+
Log.d(TAG, "[CAMERA2] Selected preview size: ${previewSize?.width}x${previewSize?.height}")
|
|
246
253
|
|
|
247
254
|
val previewAspect = previewSize?.let { it.width.toDouble() / it.height.toDouble() } ?: viewAspect
|
|
248
255
|
val analysisSizes = streamConfigMap.getOutputSizes(ImageFormat.YUV_420_888)
|
|
@@ -474,10 +481,11 @@ class CameraController(
|
|
|
474
481
|
val buffer = image.planes[0].buffer
|
|
475
482
|
val bytes = ByteArray(buffer.remaining())
|
|
476
483
|
buffer.get(bytes)
|
|
484
|
+
val exifRotation = readExifRotation(bytes)
|
|
477
485
|
val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
|
|
478
486
|
?: throw IllegalStateException("Failed to decode JPEG")
|
|
479
487
|
|
|
480
|
-
val rotated = rotateAndMirror(bitmap,
|
|
488
|
+
val rotated = rotateAndMirror(bitmap, exifRotation, useFrontCamera)
|
|
481
489
|
val photoFile = File(pending.outputDirectory, "doc_scan_${System.currentTimeMillis()}.jpg")
|
|
482
490
|
FileOutputStream(photoFile).use { out ->
|
|
483
491
|
rotated.compress(Bitmap.CompressFormat.JPEG, 95, out)
|
|
@@ -648,22 +656,37 @@ class CameraController(
|
|
|
648
656
|
private fun rotateAndMirror(bitmap: Bitmap, rotationDegrees: Int, mirror: Boolean): Bitmap {
|
|
649
657
|
Log.d(TAG, "[ROTATE_MIRROR] rotationDegrees=$rotationDegrees mirror=$mirror bitmap=${bitmap.width}x${bitmap.height}")
|
|
650
658
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
if (!mirror) {
|
|
654
|
-
// Back camera: no additional processing needed since JPEG_ORIENTATION handles rotation
|
|
655
|
-
Log.d(TAG, "[ROTATE_MIRROR] Back camera: returning bitmap as-is (JPEG_ORIENTATION=90 already applied)")
|
|
659
|
+
if (rotationDegrees == 0 && !mirror) {
|
|
660
|
+
Log.d(TAG, "[ROTATE_MIRROR] No rotation/mirror needed, returning bitmap as-is")
|
|
656
661
|
return bitmap
|
|
657
662
|
}
|
|
658
663
|
|
|
659
|
-
// Front camera: apply horizontal mirror
|
|
660
664
|
val matrix = Matrix()
|
|
661
|
-
|
|
662
|
-
|
|
665
|
+
if (rotationDegrees != 0) {
|
|
666
|
+
matrix.postRotate(rotationDegrees.toFloat(), bitmap.width / 2f, bitmap.height / 2f)
|
|
667
|
+
}
|
|
668
|
+
if (mirror) {
|
|
669
|
+
matrix.postScale(-1f, 1f, bitmap.width / 2f, bitmap.height / 2f)
|
|
670
|
+
}
|
|
663
671
|
|
|
664
672
|
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
|
|
665
673
|
}
|
|
666
674
|
|
|
675
|
+
private fun readExifRotation(bytes: ByteArray): Int {
|
|
676
|
+
return try {
|
|
677
|
+
val exif = ExifInterface(ByteArrayInputStream(bytes))
|
|
678
|
+
when (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)) {
|
|
679
|
+
ExifInterface.ORIENTATION_ROTATE_90 -> 90
|
|
680
|
+
ExifInterface.ORIENTATION_ROTATE_180 -> 180
|
|
681
|
+
ExifInterface.ORIENTATION_ROTATE_270 -> 270
|
|
682
|
+
else -> 0
|
|
683
|
+
}
|
|
684
|
+
} catch (e: Exception) {
|
|
685
|
+
Log.w(TAG, "[CAMERA2] Failed to read EXIF rotation", e)
|
|
686
|
+
0
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
667
690
|
private fun refineWithOpenCv(
|
|
668
691
|
nv21: ByteArray,
|
|
669
692
|
imageWidth: Int,
|
package/android/src/camera2/kotlin/com/reactnativerectangledocscanner/DocumentScannerView.kt
CHANGED
|
@@ -135,6 +135,7 @@ class DocumentScannerView(context: ThemedReactContext) : FrameLayout(context), L
|
|
|
135
135
|
super.onLayout(changed, left, top, right, bottom)
|
|
136
136
|
if (changed) {
|
|
137
137
|
Log.d(TAG, "[LAYOUT] View size: ${right - left}x${bottom - top}, PreviewView: ${previewView.width}x${previewView.height}")
|
|
138
|
+
cameraController?.refreshTransform()
|
|
138
139
|
}
|
|
139
140
|
}
|
|
140
141
|
|