react-native-rectangle-doc-scanner 3.207.0 → 3.209.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.
@@ -48,6 +48,7 @@ class CameraController(
48
48
 
49
49
  private var cameraId: String? = null
50
50
  private var sensorOrientation: Int = 0
51
+ private var sensorAspectRatio: Float? = null
51
52
  private var previewSize: Size? = null
52
53
  private var analysisSize: Size? = null
53
54
  private var previewChoices: Array<Size> = emptyArray()
@@ -254,6 +255,10 @@ class CameraController(
254
255
  cameraId = selected
255
256
  val characteristics = cameraManager.getCameraCharacteristics(selected)
256
257
  sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION) ?: 0
258
+ val activeArray = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE)
259
+ sensorAspectRatio = activeArray?.let { rect ->
260
+ if (rect.height() != 0) rect.width().toFloat() / rect.height().toFloat() else null
261
+ }
257
262
 
258
263
  val streamConfig = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
259
264
  previewChoices = streamConfig?.getOutputSizes(SurfaceTexture::class.java) ?: emptyArray()
@@ -267,15 +272,16 @@ class CameraController(
267
272
  null
268
273
  }
269
274
 
270
- logSizeCandidates("preview", previewChoices, targetRatio)
271
- logSizeCandidates("analysis", analysisChoices, targetRatio)
275
+ logSizeCandidates("preview", previewChoices, targetRatio, sensorAspectRatio)
276
+ logSizeCandidates("analysis", analysisChoices, targetRatio, sensorAspectRatio)
272
277
 
273
- previewSize = choosePreviewSize(previewChoices, targetRatio)
274
- analysisSize = chooseAnalysisSize(analysisChoices, targetRatio)
278
+ previewSize = choosePreviewSize(previewChoices, targetRatio, sensorAspectRatio)
279
+ analysisSize = chooseAnalysisSize(analysisChoices, targetRatio, sensorAspectRatio)
275
280
  Log.d(
276
281
  TAG,
277
282
  "[CAMERA2] chooseCamera view=${viewWidth}x${viewHeight} ratio=$targetRatio " +
278
- "sensorOrientation=$sensorOrientation preview=$previewSize analysis=$analysisSize"
283
+ "sensorOrientation=$sensorOrientation sensorRatio=$sensorAspectRatio " +
284
+ "preview=$previewSize analysis=$analysisSize"
279
285
  )
280
286
  }
281
287
 
@@ -399,8 +405,8 @@ class CameraController(
399
405
  null
400
406
  }
401
407
 
402
- val newPreview = choosePreviewSize(previewChoices, targetRatio)
403
- val newAnalysis = chooseAnalysisSize(analysisChoices, targetRatio)
408
+ val newPreview = choosePreviewSize(previewChoices, targetRatio, sensorAspectRatio)
409
+ val newAnalysis = chooseAnalysisSize(analysisChoices, targetRatio, sensorAspectRatio)
404
410
 
405
411
  if (newPreview != null && newPreview != previewSize) {
406
412
  previewSize = newPreview
@@ -428,39 +434,29 @@ class CameraController(
428
434
  }
429
435
 
430
436
  val rotationDegrees = getRotationDegrees()
431
- val bufferWidth = previewSize.width.toFloat()
432
- val bufferHeight = previewSize.height.toFloat()
433
- val rotatedBufferWidth = if (rotationDegrees == 90 || rotationDegrees == 270) {
434
- bufferHeight
435
- } else {
436
- bufferWidth
437
- }
438
- val rotatedBufferHeight = if (rotationDegrees == 90 || rotationDegrees == 270) {
439
- bufferWidth
437
+ val bufferRect = if (rotationDegrees == 90 || rotationDegrees == 270) {
438
+ RectF(0f, 0f, previewSize.height.toFloat(), previewSize.width.toFloat())
440
439
  } else {
441
- bufferHeight
440
+ RectF(0f, 0f, previewSize.width.toFloat(), previewSize.height.toFloat())
442
441
  }
442
+ val viewRect = RectF(0f, 0f, viewWidth.toFloat(), viewHeight.toFloat())
443
+ val centerX = viewRect.centerX()
444
+ val centerY = viewRect.centerY()
443
445
 
444
- // Fill the view like the default camera preview (center-crop).
445
- val scale = kotlin.math.max(
446
- viewWidth.toFloat() / rotatedBufferWidth,
447
- viewHeight.toFloat() / rotatedBufferHeight
448
- )
446
+ bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY())
449
447
 
450
448
  val matrix = Matrix()
451
- // Center buffer at origin, rotate, scale to fit, then move to view center.
452
- matrix.postTranslate(-bufferWidth / 2f, -bufferHeight / 2f)
449
+ // Scale to fill (center-crop) and then apply rotation around center.
450
+ matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL)
453
451
  if (rotationDegrees != 0) {
454
- matrix.postRotate(rotationDegrees.toFloat())
452
+ matrix.postRotate(rotationDegrees.toFloat(), centerX, centerY)
455
453
  }
456
- matrix.postScale(scale, scale)
457
- matrix.postTranslate(viewWidth / 2f, viewHeight / 2f)
458
454
 
459
455
  previewView.setTransform(matrix)
460
456
  Log.d(
461
457
  TAG,
462
- "[CAMERA2] transform view=${viewWidth}x${viewHeight} buffer=${bufferWidth}x${bufferHeight} " +
463
- "rotated=${rotatedBufferWidth}x${rotatedBufferHeight} rotation=$rotationDegrees scale=$scale"
458
+ "[CAMERA2] transform view=${viewWidth}x${viewHeight} buffer=${previewSize.width}x${previewSize.height} " +
459
+ "rotation=$rotationDegrees"
464
460
  )
465
461
  }
466
462
 
@@ -604,25 +600,23 @@ class CameraController(
604
600
 
605
601
  private fun choosePreviewSize(
606
602
  choices: Array<Size>,
607
- targetRatio: Float?
603
+ targetRatio: Float?,
604
+ sensorRatio: Float?
608
605
  ): Size? {
609
606
  if (choices.isEmpty()) {
610
607
  return null
611
608
  }
612
609
  val candidates = choices.toList()
613
610
 
614
- if (targetRatio == null) {
611
+ val ratioBase = sensorRatio ?: targetRatio
612
+ if (ratioBase == null) {
615
613
  return candidates.maxByOrNull { it.width * it.height }
616
614
  }
617
615
 
618
- val normalizedTarget = targetRatio
616
+ val normalizedTarget = ratioBase
619
617
  val sorted = candidates.sortedWith(
620
618
  compareBy<Size> { size ->
621
- val ratio = if (normalizedTarget < 1f) {
622
- size.height.toFloat() / size.width.toFloat()
623
- } else {
624
- size.width.toFloat() / size.height.toFloat()
625
- }
619
+ val ratio = size.width.toFloat() / size.height.toFloat()
626
620
  kotlin.math.abs(ratio - normalizedTarget)
627
621
  }.thenByDescending { size ->
628
622
  size.width * size.height
@@ -633,7 +627,8 @@ class CameraController(
633
627
 
634
628
  private fun chooseAnalysisSize(
635
629
  choices: Array<Size>,
636
- targetRatio: Float?
630
+ targetRatio: Float?,
631
+ sensorRatio: Float?
637
632
  ): Size? {
638
633
  if (choices.isEmpty()) {
639
634
  return null
@@ -642,18 +637,15 @@ class CameraController(
642
637
  val capped = choices.filter { it.width <= MAX_ANALYSIS_WIDTH && it.height <= MAX_ANALYSIS_HEIGHT }
643
638
  val candidates = if (capped.isNotEmpty()) capped else choices.toList()
644
639
 
645
- if (targetRatio == null) {
640
+ val ratioBase = sensorRatio ?: targetRatio
641
+ if (ratioBase == null) {
646
642
  return candidates.maxByOrNull { it.width * it.height }
647
643
  }
648
644
 
649
- val normalizedTarget = targetRatio
645
+ val normalizedTarget = ratioBase
650
646
  val sorted = candidates.sortedWith(
651
647
  compareBy<Size> { size ->
652
- val ratio = if (normalizedTarget < 1f) {
653
- size.height.toFloat() / size.width.toFloat()
654
- } else {
655
- size.width.toFloat() / size.height.toFloat()
656
- }
648
+ val ratio = size.width.toFloat() / size.height.toFloat()
657
649
  kotlin.math.abs(ratio - normalizedTarget)
658
650
  }.thenByDescending { size ->
659
651
  size.width * size.height
@@ -665,26 +657,24 @@ class CameraController(
665
657
  private fun logSizeCandidates(
666
658
  label: String,
667
659
  choices: Array<Size>,
668
- targetRatio: Float?
660
+ targetRatio: Float?,
661
+ sensorRatio: Float?
669
662
  ) {
670
663
  if (choices.isEmpty()) {
671
664
  Log.d(TAG, "[CAMERA2] $label sizes: none")
672
665
  return
673
666
  }
674
667
 
675
- if (targetRatio == null) {
676
- Log.d(TAG, "[CAMERA2] $label sizes: ${choices.size}, targetRatio=null")
668
+ val ratioBase = sensorRatio ?: targetRatio
669
+ if (ratioBase == null) {
670
+ Log.d(TAG, "[CAMERA2] $label sizes: ${choices.size}, ratioBase=null")
677
671
  return
678
672
  }
679
673
 
680
- val normalizedTarget = targetRatio
674
+ val normalizedTarget = ratioBase
681
675
  val sorted = choices.sortedWith(
682
676
  compareBy<Size> { size ->
683
- val ratio = if (normalizedTarget < 1f) {
684
- size.height.toFloat() / size.width.toFloat()
685
- } else {
686
- size.width.toFloat() / size.height.toFloat()
687
- }
677
+ val ratio = size.width.toFloat() / size.height.toFloat()
688
678
  kotlin.math.abs(ratio - normalizedTarget)
689
679
  }.thenByDescending { size ->
690
680
  size.width * size.height
@@ -692,16 +682,16 @@ class CameraController(
692
682
  )
693
683
 
694
684
  val top = sorted.take(5).joinToString { size ->
695
- val ratio = if (normalizedTarget < 1f) {
696
- size.height.toFloat() / size.width.toFloat()
697
- } else {
698
- size.width.toFloat() / size.height.toFloat()
699
- }
685
+ val ratio = size.width.toFloat() / size.height.toFloat()
700
686
  val diff = kotlin.math.abs(ratio - normalizedTarget)
701
687
  "${size.width}x${size.height}(r=${"%.3f".format(ratio)},d=${"%.3f".format(diff)})"
702
688
  }
703
689
 
704
- Log.d(TAG, "[CAMERA2] $label sizes: ${choices.size}, target=${"%.3f".format(normalizedTarget)} top=$top")
690
+ Log.d(
691
+ TAG,
692
+ "[CAMERA2] $label sizes: ${choices.size}, ratioBase=${"%.3f".format(normalizedTarget)} " +
693
+ "sensor=${sensorRatio?.let { "%.3f".format(it) }} target=${targetRatio?.let { "%.3f".format(it) }} top=$top"
694
+ )
705
695
  }
706
696
 
707
697
  private fun startBackgroundThread() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-rectangle-doc-scanner",
3
- "version": "3.207.0",
3
+ "version": "3.209.0",
4
4
  "description": "Native-backed document scanner for React Native with customizable overlays.",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",