react-native-rectangle-doc-scanner 7.9.0 → 7.11.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
|
|
@@ -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,
|
|
@@ -475,10 +481,11 @@ class CameraController(
|
|
|
475
481
|
val buffer = image.planes[0].buffer
|
|
476
482
|
val bytes = ByteArray(buffer.remaining())
|
|
477
483
|
buffer.get(bytes)
|
|
484
|
+
val exifRotation = readExifRotation(bytes)
|
|
478
485
|
val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
|
|
479
486
|
?: throw IllegalStateException("Failed to decode JPEG")
|
|
480
487
|
|
|
481
|
-
val rotated = rotateAndMirror(bitmap,
|
|
488
|
+
val rotated = rotateAndMirror(bitmap, exifRotation, useFrontCamera)
|
|
482
489
|
val photoFile = File(pending.outputDirectory, "doc_scan_${System.currentTimeMillis()}.jpg")
|
|
483
490
|
FileOutputStream(photoFile).use { out ->
|
|
484
491
|
rotated.compress(Bitmap.CompressFormat.JPEG, 95, out)
|
|
@@ -516,15 +523,12 @@ class CameraController(
|
|
|
516
523
|
|
|
517
524
|
private fun computeRotationDegrees(): Int {
|
|
518
525
|
val displayRotation = displayRotationDegrees()
|
|
519
|
-
|
|
520
|
-
// For back camera with sensor orientation 0 and display rotation 90 (portrait),
|
|
521
|
-
// we need 270 degree rotation to display correctly
|
|
526
|
+
val baseRotation = (sensorOrientation + displayRotation) % 360
|
|
522
527
|
val rotation = if (useFrontCamera) {
|
|
523
|
-
(
|
|
528
|
+
(360 - baseRotation) % 360
|
|
524
529
|
} else {
|
|
525
|
-
|
|
530
|
+
baseRotation
|
|
526
531
|
}
|
|
527
|
-
|
|
528
532
|
Log.d(TAG, "[ROTATION] sensor=$sensorOrientation display=$displayRotation front=$useFrontCamera -> rotation=$rotation")
|
|
529
533
|
return rotation
|
|
530
534
|
}
|
|
@@ -545,47 +549,28 @@ class CameraController(
|
|
|
545
549
|
val viewHeight = previewView.height.toFloat()
|
|
546
550
|
val preview = previewSize ?: return
|
|
547
551
|
if (viewWidth == 0f || viewHeight == 0f) return
|
|
548
|
-
|
|
549
|
-
val
|
|
550
|
-
Log.d(TAG, "[TRANSFORM] rotation=$
|
|
552
|
+
val rotation = previewView.display?.rotation ?: Surface.ROTATION_0
|
|
553
|
+
val rotationDegrees = displayRotationDegrees()
|
|
554
|
+
Log.d(TAG, "[TRANSFORM] rotation=$rotationDegrees view=${viewWidth}x${viewHeight} preview=${preview.width}x${preview.height}")
|
|
551
555
|
|
|
552
556
|
val matrix = Matrix()
|
|
553
|
-
val
|
|
554
|
-
val
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
// This will crop the image but fill the entire screen
|
|
566
|
-
val scaleX = viewWidth / rotatedWidth
|
|
567
|
-
val scaleY = viewHeight / rotatedHeight
|
|
568
|
-
val scale = maxOf(scaleX, scaleY)
|
|
569
|
-
|
|
570
|
-
Log.d(TAG, "[TRANSFORM] scaleX=$scaleX scaleY=$scaleY finalScale=$scale")
|
|
571
|
-
|
|
572
|
-
// Apply rotation around center first
|
|
573
|
-
matrix.postRotate(rotation.toFloat(), centerX, centerY)
|
|
574
|
-
|
|
575
|
-
// Then scale to fill (will crop excess)
|
|
576
|
-
matrix.postScale(scale, scale, centerX, centerY)
|
|
577
|
-
} else {
|
|
578
|
-
// For 0 or 180 degree rotation
|
|
579
|
-
val scaleX = viewWidth / preview.width.toFloat()
|
|
580
|
-
val scaleY = viewHeight / preview.height.toFloat()
|
|
581
|
-
val scale = maxOf(scaleX, scaleY)
|
|
582
|
-
|
|
583
|
-
Log.d(TAG, "[TRANSFORM] scaleX=$scaleX scaleY=$scaleY finalScale=$scale")
|
|
584
|
-
|
|
585
|
-
if (rotation != 0) {
|
|
586
|
-
matrix.postRotate(rotation.toFloat(), centerX, centerY)
|
|
587
|
-
}
|
|
557
|
+
val viewRect = RectF(0f, 0f, viewWidth, viewHeight)
|
|
558
|
+
val bufferRect = RectF(0f, 0f, preview.height.toFloat(), preview.width.toFloat())
|
|
559
|
+
val centerX = viewRect.centerX()
|
|
560
|
+
val centerY = viewRect.centerY()
|
|
561
|
+
|
|
562
|
+
if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
|
|
563
|
+
bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY())
|
|
564
|
+
matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL)
|
|
565
|
+
val scale = max(
|
|
566
|
+
viewHeight / preview.height.toFloat(),
|
|
567
|
+
viewWidth / preview.width.toFloat()
|
|
568
|
+
)
|
|
588
569
|
matrix.postScale(scale, scale, centerX, centerY)
|
|
570
|
+
val rotateDegrees = if (rotation == Surface.ROTATION_90) -90f else 90f
|
|
571
|
+
matrix.postRotate(rotateDegrees, centerX, centerY)
|
|
572
|
+
} else if (rotation == Surface.ROTATION_180) {
|
|
573
|
+
matrix.postRotate(180f, centerX, centerY)
|
|
589
574
|
}
|
|
590
575
|
|
|
591
576
|
previewView.setTransform(matrix)
|
|
@@ -649,22 +634,37 @@ class CameraController(
|
|
|
649
634
|
private fun rotateAndMirror(bitmap: Bitmap, rotationDegrees: Int, mirror: Boolean): Bitmap {
|
|
650
635
|
Log.d(TAG, "[ROTATE_MIRROR] rotationDegrees=$rotationDegrees mirror=$mirror bitmap=${bitmap.width}x${bitmap.height}")
|
|
651
636
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
if (!mirror) {
|
|
655
|
-
// Back camera: no additional processing needed since JPEG_ORIENTATION handles rotation.
|
|
656
|
-
Log.d(TAG, "[ROTATE_MIRROR] Back camera: returning bitmap as-is (JPEG_ORIENTATION already applied)")
|
|
637
|
+
if (rotationDegrees == 0 && !mirror) {
|
|
638
|
+
Log.d(TAG, "[ROTATE_MIRROR] No rotation/mirror needed, returning bitmap as-is")
|
|
657
639
|
return bitmap
|
|
658
640
|
}
|
|
659
641
|
|
|
660
|
-
// Front camera: apply horizontal mirror
|
|
661
642
|
val matrix = Matrix()
|
|
662
|
-
|
|
663
|
-
|
|
643
|
+
if (rotationDegrees != 0) {
|
|
644
|
+
matrix.postRotate(rotationDegrees.toFloat(), bitmap.width / 2f, bitmap.height / 2f)
|
|
645
|
+
}
|
|
646
|
+
if (mirror) {
|
|
647
|
+
matrix.postScale(-1f, 1f, bitmap.width / 2f, bitmap.height / 2f)
|
|
648
|
+
}
|
|
664
649
|
|
|
665
650
|
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
|
|
666
651
|
}
|
|
667
652
|
|
|
653
|
+
private fun readExifRotation(bytes: ByteArray): Int {
|
|
654
|
+
return try {
|
|
655
|
+
val exif = ExifInterface(ByteArrayInputStream(bytes))
|
|
656
|
+
when (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)) {
|
|
657
|
+
ExifInterface.ORIENTATION_ROTATE_90 -> 90
|
|
658
|
+
ExifInterface.ORIENTATION_ROTATE_180 -> 180
|
|
659
|
+
ExifInterface.ORIENTATION_ROTATE_270 -> 270
|
|
660
|
+
else -> 0
|
|
661
|
+
}
|
|
662
|
+
} catch (e: Exception) {
|
|
663
|
+
Log.w(TAG, "[CAMERA2] Failed to read EXIF rotation", e)
|
|
664
|
+
0
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
668
|
private fun refineWithOpenCv(
|
|
669
669
|
nv21: ByteArray,
|
|
670
670
|
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
|
|