react-native-expo-cropper 1.2.41 → 1.2.43
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/dist/ImageCropper.js +37 -10
- package/package.json +1 -1
- package/src/ImageCropper.js +33 -9
package/dist/ImageCropper.js
CHANGED
|
@@ -445,13 +445,14 @@ var ImageCropper = function ImageCropper(_ref) {
|
|
|
445
445
|
var imageRectY = wrapper.y + imageRect.y;
|
|
446
446
|
|
|
447
447
|
// ✅ PRIORITY RULE #3: IF image comes from camera → Use EXACT green frame coordinates
|
|
448
|
-
// Image is displayed in "cover" mode (full wrapper), so green frame coords = white frame coords
|
|
448
|
+
// Image is displayed in "cover" mode (full wrapper), so green frame coords = white frame coords.
|
|
449
|
+
// Store points in WRAPPER-RELATIVE coordinates (greenFrame.x, greenFrame.y) so they match
|
|
450
|
+
// touch events (locationX/locationY are relative to the wrapper). This keeps display→image
|
|
451
|
+
// conversion correct in Confirm.
|
|
449
452
|
if (cameraFrameData.current && cameraFrameData.current.greenFrame && originalImageDimensions.current.width > 0) {
|
|
450
453
|
var greenFrame = cameraFrameData.current.greenFrame;
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
var _boxX = wrapper.x + greenFrame.x;
|
|
454
|
-
var _boxY = wrapper.y + greenFrame.y;
|
|
454
|
+
var _boxX = greenFrame.x;
|
|
455
|
+
var _boxY = greenFrame.y;
|
|
455
456
|
var _boxWidth = greenFrame.width;
|
|
456
457
|
var _boxHeight = greenFrame.height;
|
|
457
458
|
|
|
@@ -464,7 +465,7 @@ var ImageCropper = function ImageCropper(_ref) {
|
|
|
464
465
|
return;
|
|
465
466
|
}
|
|
466
467
|
|
|
467
|
-
// ✅
|
|
468
|
+
// ✅ Points in wrapper-relative coords (same as touch events)
|
|
468
469
|
var _newPoints = [{
|
|
469
470
|
x: _boxX,
|
|
470
471
|
y: _boxY
|
|
@@ -507,7 +508,7 @@ var ImageCropper = function ImageCropper(_ref) {
|
|
|
507
508
|
width: _boxWidth.toFixed(2),
|
|
508
509
|
height: _boxHeight.toFixed(2)
|
|
509
510
|
},
|
|
510
|
-
note: "
|
|
511
|
+
note: "Points in wrapper-relative coords - same as touch events and crop_image_size"
|
|
511
512
|
});
|
|
512
513
|
setPoints(_newPoints);
|
|
513
514
|
hasInitializedCropBox.current = true; // ✅ CRITICAL: Mark as initialized
|
|
@@ -1243,6 +1244,15 @@ var ImageCropper = function ImageCropper(_ref) {
|
|
|
1243
1244
|
});
|
|
1244
1245
|
case 4:
|
|
1245
1246
|
rotated = _context.v;
|
|
1247
|
+
// ✅ Send rotated image to backend: use rotated URI and dimensions so crop bbox matches
|
|
1248
|
+
sourceImageUri.current = rotated.uri;
|
|
1249
|
+
originalImageDimensions.current = {
|
|
1250
|
+
width: rotated.width,
|
|
1251
|
+
height: rotated.height
|
|
1252
|
+
};
|
|
1253
|
+
cameraFrameData.current = null; // rotated image is no longer "camera preview" frame
|
|
1254
|
+
imageSource.current = 'gallery'; // so layout callbacks run initializeCropBox() and show the white box
|
|
1255
|
+
|
|
1246
1256
|
setPoints([]);
|
|
1247
1257
|
hasInitializedCropBox.current = false;
|
|
1248
1258
|
setImage(rotated.uri);
|
|
@@ -1470,7 +1480,7 @@ var ImageCropper = function ImageCropper(_ref) {
|
|
|
1470
1480
|
}, "Reset")), /*#__PURE__*/_react["default"].createElement(_reactNative.TouchableOpacity, {
|
|
1471
1481
|
style: _ImageCropperStyles["default"].button,
|
|
1472
1482
|
onPress: /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
|
|
1473
|
-
var actualImageWidth, actualImageHeight, layout, contentRect, displayedWidth, displayedHeight,
|
|
1483
|
+
var _cameraFrameData$curr4, actualImageWidth, actualImageHeight, isCoverMode, captured, layout, contentRect, displayedWidth, displayedHeight, scale, coverOffsetX, coverOffsetY, scaledWidth, scaledHeight, originalUri, cropMeta, imagePoints, minX, minY, maxX, maxY, cropX, cropY, cropEndX, cropEndY, cropWidth, cropHeight, bbox, polygon, name, _t2;
|
|
1474
1484
|
return _regenerator().w(function (_context2) {
|
|
1475
1485
|
while (1) switch (_context2.p = _context2.n) {
|
|
1476
1486
|
case 0:
|
|
@@ -1478,7 +1488,25 @@ var ImageCropper = function ImageCropper(_ref) {
|
|
|
1478
1488
|
_context2.p = 1;
|
|
1479
1489
|
console.log("=== Starting pixel-perfect metadata export (no bitmap crop on mobile) ===");
|
|
1480
1490
|
actualImageWidth = originalImageDimensions.current.width;
|
|
1481
|
-
actualImageHeight = originalImageDimensions.current.height;
|
|
1491
|
+
actualImageHeight = originalImageDimensions.current.height; // ✅ CRITICAL: Camera JPEGs often have EXIF 6 (90° CW). The Image component displays
|
|
1492
|
+
// the EXIF-corrected view (3120×4160 portrait) but takePictureAsync returns raw (4160×3120).
|
|
1493
|
+
// Use swapped dimensions for coordinate conversion so bbox matches what the user sees.
|
|
1494
|
+
isCoverMode = !!(cameraFrameData.current && cameraFrameData.current.greenFrame);
|
|
1495
|
+
captured = (_cameraFrameData$curr4 = cameraFrameData.current) === null || _cameraFrameData$curr4 === void 0 ? void 0 : _cameraFrameData$curr4.capturedImageSize;
|
|
1496
|
+
if (isCoverMode && captured && captured.width > captured.height) {
|
|
1497
|
+
actualImageWidth = captured.height;
|
|
1498
|
+
actualImageHeight = captured.width;
|
|
1499
|
+
console.log("✅ Using EXIF-swapped dimensions for bbox (raw was landscape, display is portrait):", {
|
|
1500
|
+
raw: {
|
|
1501
|
+
w: captured.width,
|
|
1502
|
+
h: captured.height
|
|
1503
|
+
},
|
|
1504
|
+
display: {
|
|
1505
|
+
w: actualImageWidth,
|
|
1506
|
+
h: actualImageHeight
|
|
1507
|
+
}
|
|
1508
|
+
});
|
|
1509
|
+
}
|
|
1482
1510
|
if (!(actualImageWidth === 0 || actualImageHeight === 0)) {
|
|
1483
1511
|
_context2.n = 2;
|
|
1484
1512
|
break;
|
|
@@ -1514,7 +1542,6 @@ var ImageCropper = function ImageCropper(_ref) {
|
|
|
1514
1542
|
case 3:
|
|
1515
1543
|
throw new Error("Displayed image dimensions not available.");
|
|
1516
1544
|
case 4:
|
|
1517
|
-
isCoverMode = !!(cameraFrameData.current && cameraFrameData.current.greenFrame);
|
|
1518
1545
|
coverOffsetX = 0, coverOffsetY = 0;
|
|
1519
1546
|
if (isCoverMode) {
|
|
1520
1547
|
scale = Math.max(displayedWidth / actualImageWidth, displayedHeight / actualImageHeight);
|
package/package.json
CHANGED
package/src/ImageCropper.js
CHANGED
|
@@ -334,13 +334,15 @@ const ImageCropper = ({ onConfirm, openCameraFirst, initialImage, addheight, rot
|
|
|
334
334
|
const imageRectY = wrapper.y + imageRect.y;
|
|
335
335
|
|
|
336
336
|
// ✅ PRIORITY RULE #3: IF image comes from camera → Use EXACT green frame coordinates
|
|
337
|
-
// Image is displayed in "cover" mode (full wrapper), so green frame coords = white frame coords
|
|
337
|
+
// Image is displayed in "cover" mode (full wrapper), so green frame coords = white frame coords.
|
|
338
|
+
// Store points in WRAPPER-RELATIVE coordinates (greenFrame.x, greenFrame.y) so they match
|
|
339
|
+
// touch events (locationX/locationY are relative to the wrapper). This keeps display→image
|
|
340
|
+
// conversion correct in Confirm.
|
|
338
341
|
if (cameraFrameData.current && cameraFrameData.current.greenFrame && originalImageDimensions.current.width > 0) {
|
|
339
342
|
const greenFrame = cameraFrameData.current.greenFrame;
|
|
340
343
|
|
|
341
|
-
|
|
342
|
-
const
|
|
343
|
-
const boxY = wrapper.y + greenFrame.y;
|
|
344
|
+
const boxX = greenFrame.x;
|
|
345
|
+
const boxY = greenFrame.y;
|
|
344
346
|
const boxWidth = greenFrame.width;
|
|
345
347
|
const boxHeight = greenFrame.height;
|
|
346
348
|
|
|
@@ -353,7 +355,7 @@ const ImageCropper = ({ onConfirm, openCameraFirst, initialImage, addheight, rot
|
|
|
353
355
|
return;
|
|
354
356
|
}
|
|
355
357
|
|
|
356
|
-
// ✅
|
|
358
|
+
// ✅ Points in wrapper-relative coords (same as touch events)
|
|
357
359
|
const newPoints = [
|
|
358
360
|
{ x: boxX, y: boxY }, // Top-left
|
|
359
361
|
{ x: boxX + boxWidth, y: boxY }, // Top-right
|
|
@@ -371,7 +373,7 @@ const ImageCropper = ({ onConfirm, openCameraFirst, initialImage, addheight, rot
|
|
|
371
373
|
console.log("✅ Initializing crop box for camera image (COVER MODE - exact green frame):", {
|
|
372
374
|
greenFrame: { x: greenFrame.x, y: greenFrame.y, width: greenFrame.width, height: greenFrame.height },
|
|
373
375
|
whiteFrame: { x: boxX.toFixed(2), y: boxY.toFixed(2), width: boxWidth.toFixed(2), height: boxHeight.toFixed(2) },
|
|
374
|
-
note: "
|
|
376
|
+
note: "Points in wrapper-relative coords - same as touch events and crop_image_size",
|
|
375
377
|
});
|
|
376
378
|
|
|
377
379
|
setPoints(newPoints);
|
|
@@ -1048,6 +1050,15 @@ const rotatePreviewImage = async (degrees) => {
|
|
|
1048
1050
|
}
|
|
1049
1051
|
);
|
|
1050
1052
|
|
|
1053
|
+
// ✅ Send rotated image to backend: use rotated URI and dimensions so crop bbox matches
|
|
1054
|
+
sourceImageUri.current = rotated.uri;
|
|
1055
|
+
originalImageDimensions.current = {
|
|
1056
|
+
width: rotated.width,
|
|
1057
|
+
height: rotated.height,
|
|
1058
|
+
};
|
|
1059
|
+
cameraFrameData.current = null; // rotated image is no longer "camera preview" frame
|
|
1060
|
+
imageSource.current = 'gallery'; // so layout callbacks run initializeCropBox() and show the white box
|
|
1061
|
+
|
|
1051
1062
|
setPoints([]);
|
|
1052
1063
|
hasInitializedCropBox.current = false;
|
|
1053
1064
|
setImage(rotated.uri);
|
|
@@ -1255,8 +1266,22 @@ const rotatePreviewImage = async (degrees) => {
|
|
|
1255
1266
|
try {
|
|
1256
1267
|
console.log("=== Starting pixel-perfect metadata export (no bitmap crop on mobile) ===");
|
|
1257
1268
|
|
|
1258
|
-
|
|
1259
|
-
|
|
1269
|
+
let actualImageWidth = originalImageDimensions.current.width;
|
|
1270
|
+
let actualImageHeight = originalImageDimensions.current.height;
|
|
1271
|
+
|
|
1272
|
+
// ✅ CRITICAL: Camera JPEGs often have EXIF 6 (90° CW). The Image component displays
|
|
1273
|
+
// the EXIF-corrected view (3120×4160 portrait) but takePictureAsync returns raw (4160×3120).
|
|
1274
|
+
// Use swapped dimensions for coordinate conversion so bbox matches what the user sees.
|
|
1275
|
+
const isCoverMode = !!(cameraFrameData.current && cameraFrameData.current.greenFrame);
|
|
1276
|
+
const captured = cameraFrameData.current?.capturedImageSize;
|
|
1277
|
+
if (isCoverMode && captured && captured.width > captured.height) {
|
|
1278
|
+
actualImageWidth = captured.height;
|
|
1279
|
+
actualImageHeight = captured.width;
|
|
1280
|
+
console.log("✅ Using EXIF-swapped dimensions for bbox (raw was landscape, display is portrait):", {
|
|
1281
|
+
raw: { w: captured.width, h: captured.height },
|
|
1282
|
+
display: { w: actualImageWidth, h: actualImageHeight },
|
|
1283
|
+
});
|
|
1284
|
+
}
|
|
1260
1285
|
|
|
1261
1286
|
if (actualImageWidth === 0 || actualImageHeight === 0) {
|
|
1262
1287
|
throw new Error("Original image dimensions not available. Please wait for image to load.");
|
|
@@ -1287,7 +1312,6 @@ const rotatePreviewImage = async (degrees) => {
|
|
|
1287
1312
|
}
|
|
1288
1313
|
}
|
|
1289
1314
|
|
|
1290
|
-
const isCoverMode = !!(cameraFrameData.current && cameraFrameData.current.greenFrame);
|
|
1291
1315
|
let scale, coverOffsetX = 0, coverOffsetY = 0;
|
|
1292
1316
|
if (isCoverMode) {
|
|
1293
1317
|
scale = Math.max(displayedWidth / actualImageWidth, displayedHeight / actualImageHeight);
|