@capgo/camera-preview 7.4.0-alpha.21 → 7.4.0-alpha.23
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.
|
@@ -1281,11 +1281,116 @@ public class CameraPreview
|
|
|
1281
1281
|
Log.d("CameraPreview", "12. PIXEL RATIO - " + pixelRatio);
|
|
1282
1282
|
Log.d("CameraPreview", "========================");
|
|
1283
1283
|
|
|
1284
|
+
// Calculate logical values with proper rounding to avoid sub-pixel issues
|
|
1285
|
+
double logicalWidth = width / pixelRatio;
|
|
1286
|
+
double logicalHeight = height / pixelRatio;
|
|
1287
|
+
double logicalX = x / pixelRatio;
|
|
1288
|
+
double logicalY = relativeY / pixelRatio;
|
|
1289
|
+
|
|
1290
|
+
// Log exact calculations to debug one-pixel difference
|
|
1291
|
+
Log.d("CameraPreview", "========================");
|
|
1292
|
+
Log.d("CameraPreview", "FINAL POSITION CALCULATIONS:");
|
|
1293
|
+
Log.d(
|
|
1294
|
+
"CameraPreview",
|
|
1295
|
+
"Pixel values: x=" +
|
|
1296
|
+
x +
|
|
1297
|
+
", y=" +
|
|
1298
|
+
relativeY +
|
|
1299
|
+
", width=" +
|
|
1300
|
+
width +
|
|
1301
|
+
", height=" +
|
|
1302
|
+
height
|
|
1303
|
+
);
|
|
1304
|
+
Log.d("CameraPreview", "Pixel ratio: " + pixelRatio);
|
|
1305
|
+
Log.d(
|
|
1306
|
+
"CameraPreview",
|
|
1307
|
+
"Logical values (exact): x=" +
|
|
1308
|
+
logicalX +
|
|
1309
|
+
", y=" +
|
|
1310
|
+
logicalY +
|
|
1311
|
+
", width=" +
|
|
1312
|
+
logicalWidth +
|
|
1313
|
+
", height=" +
|
|
1314
|
+
logicalHeight
|
|
1315
|
+
);
|
|
1316
|
+
Log.d(
|
|
1317
|
+
"CameraPreview",
|
|
1318
|
+
"Logical values (rounded): x=" +
|
|
1319
|
+
Math.round(logicalX) +
|
|
1320
|
+
", y=" +
|
|
1321
|
+
Math.round(logicalY) +
|
|
1322
|
+
", width=" +
|
|
1323
|
+
Math.round(logicalWidth) +
|
|
1324
|
+
", height=" +
|
|
1325
|
+
Math.round(logicalHeight)
|
|
1326
|
+
);
|
|
1327
|
+
|
|
1328
|
+
// Check if previewContainer has any padding or margin that might cause offset
|
|
1329
|
+
if (cameraXView != null) {
|
|
1330
|
+
View previewContainer = cameraXView.getPreviewContainer();
|
|
1331
|
+
if (previewContainer != null) {
|
|
1332
|
+
Log.d(
|
|
1333
|
+
"CameraPreview",
|
|
1334
|
+
"PreviewContainer padding: left=" +
|
|
1335
|
+
previewContainer.getPaddingLeft() +
|
|
1336
|
+
", top=" +
|
|
1337
|
+
previewContainer.getPaddingTop() +
|
|
1338
|
+
", right=" +
|
|
1339
|
+
previewContainer.getPaddingRight() +
|
|
1340
|
+
", bottom=" +
|
|
1341
|
+
previewContainer.getPaddingBottom()
|
|
1342
|
+
);
|
|
1343
|
+
ViewGroup.LayoutParams params = previewContainer.getLayoutParams();
|
|
1344
|
+
if (params instanceof ViewGroup.MarginLayoutParams) {
|
|
1345
|
+
ViewGroup.MarginLayoutParams marginParams =
|
|
1346
|
+
(ViewGroup.MarginLayoutParams) params;
|
|
1347
|
+
Log.d(
|
|
1348
|
+
"CameraPreview",
|
|
1349
|
+
"PreviewContainer margins: left=" +
|
|
1350
|
+
marginParams.leftMargin +
|
|
1351
|
+
", top=" +
|
|
1352
|
+
marginParams.topMargin +
|
|
1353
|
+
", right=" +
|
|
1354
|
+
marginParams.rightMargin +
|
|
1355
|
+
", bottom=" +
|
|
1356
|
+
marginParams.bottomMargin
|
|
1357
|
+
);
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
Log.d("CameraPreview", "========================");
|
|
1362
|
+
|
|
1284
1363
|
JSObject result = new JSObject();
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
result.put("
|
|
1364
|
+
// Return values with proper rounding to avoid gaps
|
|
1365
|
+
// For positions (x, y): floor to avoid gaps at top/left
|
|
1366
|
+
// For dimensions (width, height): ceil to avoid gaps at bottom/right
|
|
1367
|
+
result.put("width", Math.floor(logicalWidth));
|
|
1368
|
+
result.put("height", Math.floor(logicalHeight));
|
|
1369
|
+
result.put("x", Math.ceil(logicalX));
|
|
1370
|
+
result.put("y", Math.ceil(logicalY));
|
|
1371
|
+
|
|
1372
|
+
// Log what we're returning
|
|
1373
|
+
Log.d(
|
|
1374
|
+
"CameraPreview",
|
|
1375
|
+
"Returning to JS - x: " +
|
|
1376
|
+
Math.ceil(logicalX) +
|
|
1377
|
+
" (from " +
|
|
1378
|
+
logicalX +
|
|
1379
|
+
"), y: " +
|
|
1380
|
+
Math.ceil(logicalY) +
|
|
1381
|
+
" (from " +
|
|
1382
|
+
logicalY +
|
|
1383
|
+
"), width: " +
|
|
1384
|
+
Math.floor(logicalWidth) +
|
|
1385
|
+
" (from " +
|
|
1386
|
+
logicalWidth +
|
|
1387
|
+
"), height: " +
|
|
1388
|
+
Math.floor(logicalHeight) +
|
|
1389
|
+
" (from " +
|
|
1390
|
+
logicalHeight +
|
|
1391
|
+
")"
|
|
1392
|
+
);
|
|
1393
|
+
|
|
1289
1394
|
call.resolve(result);
|
|
1290
1395
|
bridge.releaseCall(call);
|
|
1291
1396
|
cameraStartCallbackId = null; // Prevent re-use
|
|
@@ -1391,10 +1496,16 @@ public class CameraPreview
|
|
|
1391
1496
|
float pixelRatio = metrics.density;
|
|
1392
1497
|
|
|
1393
1498
|
JSObject ret = new JSObject();
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1499
|
+
// Use same rounding strategy as start method
|
|
1500
|
+
double x = cameraXView.getPreviewX() / pixelRatio;
|
|
1501
|
+
double y = cameraXView.getPreviewY() / pixelRatio;
|
|
1502
|
+
double width = cameraXView.getPreviewWidth() / pixelRatio;
|
|
1503
|
+
double height = cameraXView.getPreviewHeight() / pixelRatio;
|
|
1504
|
+
|
|
1505
|
+
ret.put("x", Math.ceil(x));
|
|
1506
|
+
ret.put("y", Math.ceil(y));
|
|
1507
|
+
ret.put("width", Math.floor(width));
|
|
1508
|
+
ret.put("height", Math.floor(height));
|
|
1398
1509
|
call.resolve(ret);
|
|
1399
1510
|
}
|
|
1400
1511
|
|
|
@@ -135,6 +135,10 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
135
135
|
return isRunning;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
public View getPreviewContainer() {
|
|
139
|
+
return previewContainer;
|
|
140
|
+
}
|
|
141
|
+
|
|
138
142
|
private void saveImageToGallery(byte[] data) {
|
|
139
143
|
try {
|
|
140
144
|
// Detect image format from byte array header
|
|
@@ -268,6 +272,13 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
268
272
|
previewContainer.setClickable(true);
|
|
269
273
|
previewContainer.setFocusable(true);
|
|
270
274
|
|
|
275
|
+
// Disable any potential drawing artifacts that might cause 1px offset
|
|
276
|
+
previewContainer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
|
277
|
+
|
|
278
|
+
// Ensure no clip bounds that might cause visual offset
|
|
279
|
+
previewContainer.setClipChildren(false);
|
|
280
|
+
previewContainer.setClipToPadding(false);
|
|
281
|
+
|
|
271
282
|
// Create and setup the preview view
|
|
272
283
|
previewView = new PreviewView(context);
|
|
273
284
|
// Match iOS behavior: FIT when no aspect ratio, FILL when aspect ratio is set
|
|
@@ -3353,10 +3364,19 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
3353
3364
|
int webViewTopInset = getWebViewTopInset();
|
|
3354
3365
|
int webViewLeftInset = getWebViewLeftInset();
|
|
3355
3366
|
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
int
|
|
3367
|
+
// Use proper rounding strategy to avoid gaps:
|
|
3368
|
+
// - For positions (x, y): floor to avoid gaps at top/left
|
|
3369
|
+
// - For dimensions (width, height): ceil to avoid gaps at bottom/right
|
|
3370
|
+
int x = Math.max(
|
|
3371
|
+
0,
|
|
3372
|
+
(int) Math.ceil((actualX - webViewLeftInset) / pixelRatio)
|
|
3373
|
+
);
|
|
3374
|
+
int y = Math.max(
|
|
3375
|
+
0,
|
|
3376
|
+
(int) Math.ceil((actualY - webViewTopInset) / pixelRatio)
|
|
3377
|
+
);
|
|
3378
|
+
int width = (int) Math.floor(actualWidth / pixelRatio);
|
|
3379
|
+
int height = (int) Math.floor(actualHeight / pixelRatio);
|
|
3360
3380
|
|
|
3361
3381
|
return new int[] { x, y, width, height };
|
|
3362
3382
|
}
|
|
@@ -504,6 +504,33 @@ extension CameraController {
|
|
|
504
504
|
dataOutput?.connections.forEach { $0.videoOrientation = videoOrientation }
|
|
505
505
|
photoOutput?.connections.forEach { $0.videoOrientation = videoOrientation }
|
|
506
506
|
}
|
|
507
|
+
|
|
508
|
+
private func setDefaultZoomAfterFlip() {
|
|
509
|
+
let device = (currentCameraPosition == .rear) ? rearCamera : frontCamera
|
|
510
|
+
guard let device = device else {
|
|
511
|
+
print("[CameraPreview] No device available for default zoom after flip")
|
|
512
|
+
return
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// Set zoom to 1.0x in UI terms, accounting for display multiplier
|
|
516
|
+
let multiplier = self.getDisplayZoomMultiplier()
|
|
517
|
+
let targetUIZoom: Float = 1.0 // We want 1.0x in the UI
|
|
518
|
+
let nativeZoom = multiplier != 1.0 ? (targetUIZoom / multiplier) : targetUIZoom
|
|
519
|
+
|
|
520
|
+
let minZoom = device.minAvailableVideoZoomFactor
|
|
521
|
+
let maxZoom = min(device.maxAvailableVideoZoomFactor, saneMaxZoomFactor)
|
|
522
|
+
let clampedZoom = max(minZoom, min(CGFloat(nativeZoom), maxZoom))
|
|
523
|
+
|
|
524
|
+
do {
|
|
525
|
+
try device.lockForConfiguration()
|
|
526
|
+
device.videoZoomFactor = clampedZoom
|
|
527
|
+
device.unlockForConfiguration()
|
|
528
|
+
self.zoomFactor = clampedZoom
|
|
529
|
+
print("[CameraPreview] Set default zoom after flip: UI=\(targetUIZoom)x, native=\(clampedZoom), multiplier=\(multiplier)")
|
|
530
|
+
} catch {
|
|
531
|
+
print("[CameraPreview] Failed to set default zoom after flip: \(error)")
|
|
532
|
+
}
|
|
533
|
+
}
|
|
507
534
|
|
|
508
535
|
func switchCameras() throws {
|
|
509
536
|
guard let currentCameraPosition = currentCameraPosition,
|
|
@@ -594,6 +621,11 @@ extension CameraController {
|
|
|
594
621
|
|
|
595
622
|
// Update video orientation
|
|
596
623
|
self.updateVideoOrientation()
|
|
624
|
+
|
|
625
|
+
// Set default 1.0 zoom level after camera switch to prevent iOS 18+ zoom jumps
|
|
626
|
+
DispatchQueue.main.async { [weak self] in
|
|
627
|
+
self?.setDefaultZoomAfterFlip()
|
|
628
|
+
}
|
|
597
629
|
}
|
|
598
630
|
|
|
599
631
|
func captureImage(width: Int?, height: Int?, aspectRatio: String?, quality: Float, gpsLocation: CLLocation?, completion: @escaping (UIImage?, Data?, [AnyHashable: Any]?, Error?) -> Void) {
|