react-native-expo-cropper 1.0.31 → 1.1.31

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.
@@ -9,8 +9,7 @@ var _react = _interopRequireWildcard(require("react"));
9
9
  var _reactNative = require("react-native");
10
10
  var _reactNativeSafeAreaContext = require("react-native-safe-area-context");
11
11
  var _expoCamera = require("expo-camera");
12
- var ImageManipulator = _interopRequireWildcard(require("expo-image-manipulator"));
13
- function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t3 in e) "default" !== _t3 && {}.hasOwnProperty.call(e, _t3) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t3)) && (i.get || i.set) ? o(f, _t3, i) : f[_t3] = e[_t3]); return f; })(e, t); }
12
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t2 in e) "default" !== _t2 && {}.hasOwnProperty.call(e, _t2) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t2)) && (i.get || i.set) ? o(f, _t2, i) : f[_t2] = e[_t2]); return f; })(e, t); }
14
13
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
15
14
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
16
15
  function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
@@ -78,84 +77,53 @@ function CustomCamera(_ref) {
78
77
  setImmediate(_tick);
79
78
  });
80
79
  };
81
-
82
- // Helper function to fix image orientation (removes EXIF orientation)
83
- var fixImageOrientation = /*#__PURE__*/function () {
84
- var _ref3 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(uri) {
85
- var fixedImage, _t;
80
+ var takePicture = /*#__PURE__*/function () {
81
+ var _ref3 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
82
+ var photo, fixedUri, _t;
86
83
  return _regenerator().w(function (_context2) {
87
84
  while (1) switch (_context2.p = _context2.n) {
88
- case 0:
89
- _context2.p = 0;
90
- _context2.n = 1;
91
- return ImageManipulator.manipulateAsync(uri, [],
92
- // Empty array = apply EXIF orientation automatically
93
- {
94
- compress: 1,
95
- format: ImageManipulator.SaveFormat.PNG
96
- });
97
- case 1:
98
- fixedImage = _context2.v;
99
- return _context2.a(2, fixedImage.uri);
100
- case 2:
101
- _context2.p = 2;
102
- _t = _context2.v;
103
- console.error("Error fixing image orientation:", _t);
104
- return _context2.a(2, uri);
105
- }
106
- }, _callee2, null, [[0, 2]]);
107
- }));
108
- return function fixImageOrientation(_x) {
109
- return _ref3.apply(this, arguments);
110
- };
111
- }();
112
- var takePicture = /*#__PURE__*/function () {
113
- var _ref4 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3() {
114
- var photo, fixedUri, _t2;
115
- return _regenerator().w(function (_context3) {
116
- while (1) switch (_context3.p = _context3.n) {
117
85
  case 0:
118
86
  if (!cameraRef.current) {
119
- _context3.n = 6;
87
+ _context2.n = 5;
120
88
  break;
121
89
  }
122
- _context3.p = 1;
90
+ _context2.p = 1;
123
91
  // Show loading after a delay (using setImmediate for iOS compatibility)
124
- waitForRender(20).then(function () {
92
+ waitForRender(5).then(function () {
125
93
  setLoadingBeforeCapture(true);
126
94
  });
127
95
 
128
96
  // Wait a bit before taking picture (works on iOS)
129
- _context3.n = 2;
130
- return waitForRender(3);
97
+ _context2.n = 2;
98
+ return waitForRender(2);
131
99
  case 2:
132
- _context3.n = 3;
100
+ _context2.n = 3;
133
101
  return cameraRef.current.takePictureAsync({
134
102
  quality: 1,
135
- shutterSound: false
103
+ shutterSound: false,
104
+ skipProcessing: false,
105
+ exif: true // Include EXIF data for orientation
136
106
  });
137
107
  case 3:
138
- photo = _context3.v;
139
- _context3.n = 4;
140
- return fixImageOrientation(photo.uri);
141
- case 4:
142
- fixedUri = _context3.v;
108
+ photo = _context2.v;
109
+ // Fix orientation on Android (iOS handles it automatically)
110
+ fixedUri = photo.uri; // Go directly to ImageCropper (preview will be shown there on Android)
143
111
  onPhotoCaptured(fixedUri);
144
112
  setLoadingBeforeCapture(false);
145
- _context3.n = 6;
113
+ _context2.n = 5;
146
114
  break;
147
- case 5:
148
- _context3.p = 5;
149
- _t2 = _context3.v;
115
+ case 4:
116
+ _context2.p = 4;
117
+ _t = _context2.v;
150
118
  setLoadingBeforeCapture(false);
151
119
  _reactNative.Alert.alert("Erreur");
152
- case 6:
153
- return _context3.a(2);
120
+ case 5:
121
+ return _context2.a(2);
154
122
  }
155
- }, _callee3, null, [[1, 5]]);
123
+ }, _callee2, null, [[1, 4]]);
156
124
  }));
157
125
  return function takePicture() {
158
- return _ref4.apply(this, arguments);
126
+ return _ref3.apply(this, arguments);
159
127
  };
160
128
  }();
161
129
  return /*#__PURE__*/_react["default"].createElement(_reactNative.SafeAreaView, {
@@ -12,7 +12,10 @@ var _reactNativeSvg = _interopRequireWildcard(require("react-native-svg"));
12
12
  var _reactNativeViewShot = require("react-native-view-shot");
13
13
  var _CustomCamera = _interopRequireDefault(require("./CustomCamera"));
14
14
  var _ImageProcessor = require("./ImageProcessor");
15
- function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t2 in e) "default" !== _t2 && {}.hasOwnProperty.call(e, _t2) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t2)) && (i.get || i.set) ? o(f, _t2, i) : f[_t2] = e[_t2]); return f; })(e, t); }
15
+ var ImageManipulator = _interopRequireWildcard(require("expo-image-manipulator"));
16
+ var _vectorIcons = require("@expo/vector-icons");
17
+ var _reactNativeSafeAreaContext = require("react-native-safe-area-context");
18
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t3 in e) "default" !== _t3 && {}.hasOwnProperty.call(e, _t3) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t3)) && (i.get || i.set) ? o(f, _t3, i) : f[_t3] = e[_t3]); return f; })(e, t); }
16
19
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
17
20
  function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
18
21
  function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
@@ -71,7 +74,12 @@ var ImageCropper = function ImageCropper(_ref) {
71
74
  _useState10 = _slicedToArray(_useState1, 2),
72
75
  showFullScreenCapture = _useState10[0],
73
76
  setShowFullScreenCapture = _useState10[1];
77
+ var _useState11 = (0, _react.useState)(false),
78
+ _useState12 = _slicedToArray(_useState11, 2),
79
+ isRotating = _useState12[0],
80
+ setIsRotating = _useState12[1];
74
81
  var lastValidPosition = (0, _react.useRef)(null);
82
+ var insets = (0, _reactNativeSafeAreaContext.useSafeAreaInsets)();
75
83
  (0, _react.useEffect)(function () {
76
84
  if (openCameraFirst) {
77
85
  setShowCustomCamera(true);
@@ -208,6 +216,51 @@ var ImageCropper = function ImageCropper(_ref) {
208
216
  // setPoints([]);
209
217
  initializeCropBox();
210
218
  };
219
+ var rotatePreviewImage = /*#__PURE__*/function () {
220
+ var _ref2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(degrees) {
221
+ var rotated, _t;
222
+ return _regenerator().w(function (_context) {
223
+ while (1) switch (_context.p = _context.n) {
224
+ case 0:
225
+ if (!(!image || isRotating)) {
226
+ _context.n = 1;
227
+ break;
228
+ }
229
+ return _context.a(2);
230
+ case 1:
231
+ setIsRotating(true);
232
+ _context.p = 2;
233
+ _context.n = 3;
234
+ return ImageManipulator.manipulateAsync(image, [{
235
+ rotate: degrees
236
+ }], {
237
+ compress: 0.85,
238
+ format: ImageManipulator.SaveFormat.JPEG
239
+ });
240
+ case 3:
241
+ rotated = _context.v;
242
+ // Update image - onImageLayout will call initializeCropBox automatically
243
+ setImage(rotated.uri);
244
+ _context.n = 5;
245
+ break;
246
+ case 4:
247
+ _context.p = 4;
248
+ _t = _context.v;
249
+ console.error("Error rotating image:", _t);
250
+ alert("Error rotating image");
251
+ case 5:
252
+ _context.p = 5;
253
+ setIsRotating(false);
254
+ return _context.f(5);
255
+ case 6:
256
+ return _context.a(2);
257
+ }
258
+ }, _callee, null, [[2, 4, 5, 6]]);
259
+ }));
260
+ return function rotatePreviewImage(_x) {
261
+ return _ref2.apply(this, arguments);
262
+ };
263
+ }();
211
264
 
212
265
  // Helper function to wait for multiple render cycles (works on iOS)
213
266
  var waitForRender = function waitForRender() {
@@ -237,68 +290,78 @@ var ImageCropper = function ImageCropper(_ref) {
237
290
  }
238
291
  }) : /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, !showResult && /*#__PURE__*/_react["default"].createElement(_reactNative.View, {
239
292
  style: image ? _ImageCropperStyles["default"].buttonContainer : _ImageCropperStyles["default"].centerButtonsContainer
240
- }, image && /*#__PURE__*/_react["default"].createElement(_reactNative.TouchableOpacity, {
293
+ }, image && _reactNative.Platform.OS === 'android' && /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, /*#__PURE__*/_react["default"].createElement(_reactNative.TouchableOpacity, {
294
+ style: _ImageCropperStyles["default"].iconButton,
295
+ onPress: function onPress() {
296
+ return rotatePreviewImage(90);
297
+ },
298
+ disabled: isRotating
299
+ }, /*#__PURE__*/_react["default"].createElement(_vectorIcons.Ionicons, {
300
+ name: "sync",
301
+ size: 24,
302
+ color: "white"
303
+ }))), image && /*#__PURE__*/_react["default"].createElement(_reactNative.TouchableOpacity, {
241
304
  style: _ImageCropperStyles["default"].button,
242
305
  onPress: handleReset
243
306
  }, /*#__PURE__*/_react["default"].createElement(_reactNative.Text, {
244
307
  style: _ImageCropperStyles["default"].buttonText
245
308
  }, "Reset")), image && /*#__PURE__*/_react["default"].createElement(_reactNative.TouchableOpacity, {
246
309
  style: _ImageCropperStyles["default"].button,
247
- onPress: /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
248
- var capturedUri, enhancedUri, name, _t;
249
- return _regenerator().w(function (_context) {
250
- while (1) switch (_context.p = _context.n) {
310
+ onPress: /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
311
+ var capturedUri, enhancedUri, name, _t2;
312
+ return _regenerator().w(function (_context2) {
313
+ while (1) switch (_context2.p = _context2.n) {
251
314
  case 0:
252
315
  // setShowFullScreenCapture(true);
253
316
  setIsLoading(true);
254
317
  setShowResult(true);
255
- _context.p = 1;
256
- _context.n = 2;
257
- return waitForRender(8);
318
+ _context2.p = 1;
319
+ _context2.n = 2;
320
+ return waitForRender(5);
258
321
  case 2:
259
322
  console.log("Starting capture...");
260
- _context.n = 3;
323
+ _context2.n = 3;
261
324
  return (0, _reactNativeViewShot.captureRef)(viewRef, {
262
325
  format: 'png',
263
326
  quality: 1
264
327
  });
265
328
  case 3:
266
- capturedUri = _context.v;
329
+ capturedUri = _context2.v;
267
330
  console.log("Capture successful:", capturedUri);
268
331
  if (capturedUri) {
269
- _context.n = 4;
332
+ _context2.n = 4;
270
333
  break;
271
334
  }
272
335
  throw new Error("Capture returned empty URI");
273
336
  case 4:
274
337
  console.log("Enhancing image...");
275
- _context.n = 5;
338
+ _context2.n = 5;
276
339
  return (0, _ImageProcessor.enhanceImage)(capturedUri, addheight);
277
340
  case 5:
278
- enhancedUri = _context.v;
341
+ enhancedUri = _context2.v;
279
342
  console.log("Image enhanced:", enhancedUri);
280
343
  name = "IMAGE XTK".concat(Date.now(), ".png");
281
344
  if (onConfirm) {
282
345
  console.log("Calling onConfirm with:", enhancedUri, name);
283
346
  onConfirm(enhancedUri, name);
284
347
  }
285
- _context.n = 7;
348
+ _context2.n = 7;
286
349
  break;
287
350
  case 6:
288
- _context.p = 6;
289
- _t = _context.v;
290
- console.error("Erreur lors de la capture :", _t);
291
- alert("Erreur lors de la capture ! " + _t.message);
351
+ _context2.p = 6;
352
+ _t2 = _context2.v;
353
+ console.error("Erreur lors de la capture :", _t2);
354
+ alert("Erreur lors de la capture ! " + _t2.message);
292
355
  case 7:
293
- _context.p = 7;
356
+ _context2.p = 7;
294
357
  setShowResult(false);
295
358
  setIsLoading(false);
296
359
  setShowFullScreenCapture(false);
297
- return _context.f(7);
360
+ return _context2.f(7);
298
361
  case 8:
299
- return _context.a(2);
362
+ return _context2.a(2);
300
363
  }
301
- }, _callee, null, [[1, 6, 7, 8]]);
364
+ }, _callee2, null, [[1, 6, 7, 8]]);
302
365
  }))
303
366
  }, /*#__PURE__*/_react["default"].createElement(_reactNative.Text, {
304
367
  style: _ImageCropperStyles["default"].buttonText
@@ -29,7 +29,17 @@ var styles = _reactNative.StyleSheet.create({
29
29
  justifyContent: 'center',
30
30
  alignItems: 'center',
31
31
  paddingHorizontal: 10,
32
- zIndex: 10
32
+ zIndex: 10,
33
+ gap: 10
34
+ },
35
+ iconButton: {
36
+ backgroundColor: PRIMARY_GREEN,
37
+ width: 60,
38
+ height: 60,
39
+ borderRadius: 30,
40
+ alignItems: 'center',
41
+ justifyContent: 'center',
42
+ marginRight: 5
33
43
  },
34
44
  button: {
35
45
  flex: 1,
@@ -172,6 +182,37 @@ var styles = _reactNative.StyleSheet.create({
172
182
  justifyContent: 'center',
173
183
  alignItems: 'center',
174
184
  zIndex: 9999
185
+ },
186
+ previewContainer: {
187
+ flex: 1,
188
+ backgroundColor: DEEP_BLACK,
189
+ justifyContent: 'center',
190
+ alignItems: 'center',
191
+ width: '100%'
192
+ },
193
+ previewImage: {
194
+ width: SCREEN_WIDTH,
195
+ height: '80%'
196
+ },
197
+ previewButtonContainer: {
198
+ position: 'absolute',
199
+ bottom: 50,
200
+ left: 0,
201
+ right: 0,
202
+ flexDirection: 'row',
203
+ justifyContent: 'space-around',
204
+ paddingHorizontal: 20
205
+ },
206
+ previewButton: {
207
+ backgroundColor: PRIMARY_GREEN,
208
+ width: 60,
209
+ height: 60,
210
+ borderRadius: 30,
211
+ alignItems: 'center',
212
+ justifyContent: 'center'
213
+ },
214
+ useButton: {
215
+ backgroundColor: GLOW_WHITE
175
216
  }
176
217
  });
177
218
  var _default = exports["default"] = styles;
@@ -13,26 +13,21 @@ function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.
13
13
  function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
14
14
  var enhanceImage = exports.enhanceImage = /*#__PURE__*/function () {
15
15
  var _ref = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(uri, addheight) {
16
- var fixedOrientationImage, ratio, maxHeight, newWidth, newHeight, result, _t;
16
+ var imageInfo, ratio, maxHeight, newWidth, newHeight, result, _t;
17
17
  return _regenerator().w(function (_context) {
18
18
  while (1) switch (_context.p = _context.n) {
19
19
  case 0:
20
20
  _context.p = 0;
21
21
  _context.n = 1;
22
- return ImageManipulator.manipulateAsync(uri, [], {
23
- compress: 1,
24
- format: ImageManipulator.SaveFormat.PNG
25
- });
22
+ return ImageManipulator.manipulateAsync(uri, []);
26
23
  case 1:
27
- fixedOrientationImage = _context.v;
28
- // Get the correct dimensions after orientation fix
29
- // The width and height properties reflect the actual image dimensions after orientation fix
30
- ratio = fixedOrientationImage.height / fixedOrientationImage.width;
24
+ imageInfo = _context.v;
25
+ ratio = imageInfo.height / imageInfo.width;
31
26
  maxHeight = addheight;
32
27
  newWidth = Math.round(maxHeight / ratio);
33
- newHeight = Math.round(newWidth * ratio); // Resize using the orientation-fixed image
28
+ newHeight = Math.round(newWidth * ratio);
34
29
  _context.n = 2;
35
- return ImageManipulator.manipulateAsync(fixedOrientationImage.uri, [{
30
+ return ImageManipulator.manipulateAsync(uri, [{
36
31
  resize: {
37
32
  width: newWidth,
38
33
  height: newHeight
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-expo-cropper",
3
- "version": "1.0.31",
3
+ "version": "1.1.31",
4
4
  "description": "Recadrage polygonal d'images.",
5
5
  "main": "index.js",
6
6
  "author": "PCS AGRI",
@@ -49,6 +49,7 @@
49
49
  "@babel/cli": "^7.28.3",
50
50
  "@babel/core": "^7.28.5",
51
51
  "@babel/preset-env": "^7.28.5",
52
- "@babel/preset-react": "^7.28.5"
52
+ "@babel/preset-react": "^7.28.5",
53
+ "baseline-browser-mapping": "^2.9.6"
53
54
  }
54
55
  }
@@ -11,7 +11,6 @@ import {
11
11
  } from 'react-native';
12
12
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
13
13
  import { Camera, CameraView } from 'expo-camera';
14
- import * as ImageManipulator from 'expo-image-manipulator';
15
14
  const { width } = Dimensions.get('window');
16
15
 
17
16
  export default function CustomCamera({ onPhotoCaptured}) {
@@ -48,51 +47,32 @@ useEffect(() => {
48
47
  });
49
48
  };
50
49
 
51
- // Helper function to fix image orientation (removes EXIF orientation)
52
- const fixImageOrientation = async (uri) => {
53
- try {
54
- // Use manipulateAsync with empty array to fix orientation based on EXIF data
55
- // Empty array [] tells manipulateAsync to automatically apply EXIF orientation
56
- // This ensures horizontal photos stay horizontal and vertical photos stay vertical
57
- // The EXIF orientation tag is removed and the image is physically rotated if needed
58
- const fixedImage = await ImageManipulator.manipulateAsync(
59
- uri,
60
- [], // Empty array = apply EXIF orientation automatically
61
- {
62
- compress: 1,
63
- format: ImageManipulator.SaveFormat.PNG
64
- }
65
- );
66
-
67
- return fixedImage.uri;
68
- } catch (error) {
69
- console.error("Error fixing image orientation:", error);
70
- return uri; // Return original if fixing fails
71
- }
72
- };
50
+
73
51
 
74
52
  const takePicture = async () => {
75
53
  if (cameraRef.current) {
76
54
  try {
77
- // Show loading after a delay (using setImmediate for iOS compatibility)
78
- waitForRender(20).then(() => {
79
- setLoadingBeforeCapture(true);
80
- });
55
+ // Show loading after a delay (using setImmediate for iOS compatibility)
56
+ waitForRender(5).then(() => {
57
+ setLoadingBeforeCapture(true);
58
+ });
81
59
 
82
60
  // Wait a bit before taking picture (works on iOS)
83
- await waitForRender(3);
61
+ await waitForRender(2);
84
62
 
85
- const photo = await cameraRef.current.takePictureAsync({
86
- quality: 1,
87
- shutterSound: false,
88
- });
63
+ const photo = await cameraRef.current.takePictureAsync({
64
+ quality: 1,
65
+ shutterSound: false,
66
+ skipProcessing: false,
67
+ exif: true, // Include EXIF data for orientation
68
+ });
89
69
 
90
- // Fix orientation to preserve vertical/horizontal position as taken
91
- const fixedUri = await fixImageOrientation(photo.uri);
92
-
93
- onPhotoCaptured(fixedUri);
70
+ // Fix orientation on Android (iOS handles it automatically)
71
+ const fixedUri = photo.uri;
94
72
 
95
- setLoadingBeforeCapture(false);
73
+ // Go directly to ImageCropper (preview will be shown there on Android)
74
+ onPhotoCaptured(fixedUri);
75
+ setLoadingBeforeCapture(false);
96
76
  } catch (error) {
97
77
  setLoadingBeforeCapture(false);
98
78
  Alert.alert("Erreur");
@@ -229,4 +209,4 @@ const styles = StyleSheet.create({
229
209
  color: GLOW_WHITE,
230
210
  fontWeight: '600',
231
211
  },
232
- });
212
+ });
@@ -1,290 +1,330 @@
1
- import styles from './ImageCropperStyles';
2
- import React, { useState, useRef, useEffect } from 'react';
3
- import { Modal,View, Image, Dimensions, TouchableOpacity, Animated, Text } from 'react-native';
4
- import Svg, { Path, Circle } from 'react-native-svg';
5
- import { captureRef } from 'react-native-view-shot';
6
- import CustomCamera from './CustomCamera';
7
- import { enhanceImage } from './ImageProcessor';
8
-
9
- const ImageCropper = ({ onConfirm, openCameraFirst, initialImage ,addheight}) => {
10
-
11
-
12
- const [image, setImage] = useState(null);
13
- const [points, setPoints] = useState([]);
14
- const [showResult, setShowResult] = useState(false);
15
- const [showCustomCamera, setShowCustomCamera] = useState(false);
16
- const viewRef = useRef(null);
17
- const imageMeasure = useRef({ x: 0, y: 0, width: 0, height: 0 });
18
- const selectedPointIndex = useRef(null);
19
- const lastTap = useRef(null);
20
-
21
- const [isLoading, setIsLoading] = useState(false);
22
- const [showFullScreenCapture, setShowFullScreenCapture] = useState(false);
23
- const lastValidPosition = useRef(null);
24
-
25
-
26
-
27
-
28
-
29
- useEffect(() => {
30
- if (openCameraFirst) {
31
- setShowCustomCamera(true);
32
- } else if (initialImage) {
33
- setImage(initialImage);
34
- }
35
- }, [openCameraFirst, initialImage]);
36
-
37
-
38
- useEffect(() => {
39
- if (!image) return;
40
-
41
- Image.getSize(image, (imgWidth, imgHeight) => {
42
- const screenRatio = SCREEN_WIDTH / SCREEN_HEIGHT;
43
- const imageRatio = imgWidth / imgHeight;
44
-
45
- if (imageRatio > screenRatio) {
46
- imageMeasure.current = {
47
- width: SCREEN_WIDTH,
48
- height: SCREEN_WIDTH / imageRatio,
49
- };
50
- } else {
51
- imageMeasure.current = {
52
- width: SCREEN_HEIGHT * imageRatio,
53
- height: SCREEN_HEIGHT,
54
- };
55
- }
56
- });
57
- }, [image]);
58
-
59
-
60
- const initializeCropBox = () => {
61
- const { width, height } = imageMeasure.current;
62
- // if (width === 0 || height === 0 || points.length > 0) return;
63
- const boxWidth = width * 0.8;
64
- const boxHeight = height * 0.8;
65
- const centerX = width / 2;
66
- const centerY = height / 2;
67
- setPoints([
68
- { x: centerX - boxWidth / 2, y: centerY - boxHeight / 2 },
69
- { x: centerX + boxWidth / 2, y: centerY - boxHeight / 2 },
70
- { x: centerX + boxWidth / 2, y: centerY + boxHeight / 2 },
71
- { x: centerX - boxWidth / 2, y: centerY + boxHeight / 2 },
72
- ]);
73
- };
74
-
75
-
76
- const onImageLayout = (e) => {
77
- const layout = e.nativeEvent.layout;
78
- imageMeasure.current = {
79
- x: layout.x,
80
- y: layout.y,
81
- width: layout.width,
82
- height: layout.height
83
- };
84
- initializeCropBox();
85
- };
86
-
87
- const createPath = () => {
88
- if (points.length < 1) return '';
89
- let path = `M ${points[0].x} ${points[0].y} `;
90
- points.forEach(point => path += `L ${point.x} ${point.y} `);
91
- return path + 'Z';
92
- };
93
-
94
- const handleTap = (e) => {
95
- if (!image || showResult) return;
96
- const now = Date.now();
97
- const { locationX: tapX, locationY: tapY } = e.nativeEvent;
98
-
99
- if (lastTap.current && now - lastTap.current < 300) {
100
- const exists = points.some(p => Math.abs(p.x - tapX) < 15 && Math.abs(p.y - tapY) < 15);
101
- if (!exists) setPoints([...points, { x: tapX, y: tapY }]);
102
- lastTap.current = null;
103
- } else {
104
- const index = points.findIndex(p => Math.abs(p.x - tapX) < 20 && Math.abs(p.y - tapY) < 20);
105
- if (index !== -1) {
106
- selectedPointIndex.current = index;
107
- lastValidPosition.current = { ...points[index] }; // store original position before move
108
- }
109
- lastTap.current = now;
110
- }
111
- };
112
-
113
- const handleMove = (e) => {
114
- if (showResult || selectedPointIndex.current === null) return;
115
-
116
- const { locationX: moveX, locationY: moveY } = e.nativeEvent;
117
- const width = imageMeasure.current.width;
118
- const height = imageMeasure.current.height;
119
-
120
- const boundedX = Math.max(0, Math.min(moveX, width));
121
- const boundedY = Math.max(0, Math.min(moveY, height));
122
-
123
- const edgeThreshold = 10;
124
- const isNearTopOrBottomEdge =
125
- boundedY <= edgeThreshold || boundedY >= height - edgeThreshold;
126
-
127
- const isNearLeftOrRightEdge =
128
- boundedX <= edgeThreshold || boundedX >= width - edgeThreshold;
129
-
130
- if (isNearTopOrBottomEdge || isNearLeftOrRightEdge) {
131
- // Reset point to last known position
132
- if (lastValidPosition.current && selectedPointIndex.current !== null) {
133
- setPoints(prev =>
134
- prev.map((p, i) =>
135
- i === selectedPointIndex.current ? lastValidPosition.current : p
136
- )
137
- );
138
- }
139
- selectedPointIndex.current = null;
140
- return;
141
- }
142
-
143
- // Valid move — update point and store as new last valid position
144
- const updatedPoint = { x: boundedX, y: boundedY };
145
- lastValidPosition.current = updatedPoint;
146
-
147
- setPoints(prev =>
148
- prev.map((p, i) =>
149
- i === selectedPointIndex.current ? updatedPoint : p
150
- )
151
- );
152
- };
153
-
154
- const handleRelease = () => {
155
- selectedPointIndex.current = null;
156
- };
157
-
158
- const handleReset = () => {
159
- // setPoints([]);
160
- initializeCropBox();
161
- };
162
-
163
- // Helper function to wait for multiple render cycles (works on iOS)
164
- const waitForRender = (cycles = 5) => {
165
- return new Promise((resolve) => {
166
- let count = 0;
167
- const tick = () => {
168
- count++;
169
- if (count >= cycles) {
170
- resolve();
171
- } else {
172
- setImmediate(tick);
173
- }
174
- };
175
- setImmediate(tick);
176
- });
177
- };
178
-
179
-
180
- return (
181
- <View style={styles.container}>
182
-
183
- {showCustomCamera ? (
184
- <CustomCamera
185
- onPhotoCaptured={(uri) => { setImage(uri); setShowCustomCamera(false); }}
186
- onCancel={() => setShowCustomCamera(false)}
187
- />
188
- ) : (
189
- <>
190
- {!showResult && (
191
- <View style={image ? styles.buttonContainer : styles.centerButtonsContainer}>
192
-
193
- {image && (
194
- <TouchableOpacity style={styles.button} onPress={handleReset}>
195
- <Text style={styles.buttonText}>Reset</Text>
196
- </TouchableOpacity>
197
- )}
198
- {image && (
199
- <TouchableOpacity
200
- style={styles.button}
201
- onPress={async () => {
202
- // setShowFullScreenCapture(true);
203
- setIsLoading(true);
204
- setShowResult(true);
205
- try {
206
- // Wait for UI to render - give React time to update the SVG with white fill
207
- await waitForRender(8);
208
-
209
- console.log("Starting capture...");
210
- const capturedUri = await captureRef(viewRef, {
211
- format: 'png',
212
- quality: 1,
213
- });
214
- console.log("Capture successful:", capturedUri);
215
-
216
- if (!capturedUri) {
217
- throw new Error("Capture returned empty URI");
218
- }
219
-
220
- console.log("Enhancing image...");
221
- const enhancedUri = await enhanceImage(capturedUri, addheight);
222
- console.log("Image enhanced:", enhancedUri);
223
-
224
- const name = `IMAGE XTK${Date.now()}.png`;
225
-
226
- if (onConfirm) {
227
- console.log("Calling onConfirm with:", enhancedUri, name);
228
- onConfirm(enhancedUri, name);
229
- }
230
- } catch (error) {
231
- console.error("Erreur lors de la capture :", error);
232
- alert("Erreur lors de la capture ! " + error.message);
233
- } finally {
234
- setShowResult(false);
235
- setIsLoading(false);
236
- setShowFullScreenCapture(false);
237
- }
238
- }}
239
- >
240
- <Text style={styles.buttonText}>Confirm</Text>
241
- </TouchableOpacity>
242
- )}
243
-
244
- </View>
245
- )}
246
-
247
-
248
- {image && (
249
- <View
250
- ref={viewRef}
251
- collapsable={false}
252
- style={showFullScreenCapture ? styles.fullscreenImageContainer : styles.imageContainer}
253
- onStartShouldSetResponder={() => true}
254
- onResponderStart={handleTap}
255
- onResponderMove={handleMove}
256
- onResponderRelease={handleRelease}
257
- >
258
- <Image source={{ uri: image }} style={styles.image} onLayout={onImageLayout} />
259
- <Svg style={styles.overlay}>
260
- <Path
261
- d={`M 0 0 H ${imageMeasure.current.width} V ${imageMeasure.current.height} H 0 Z ${createPath()}`}
262
- fill={showResult ? 'white' : 'rgba(0, 0, 0, 0.8)'}
263
- fillRule="evenodd"
264
- />
265
- {!showResult && points.length > 0 && (
266
- <Path d={createPath()} fill="transparent" stroke="white" strokeWidth={2} />
267
- )}
268
- {!showResult && points.map((point, index) => (
269
- <Circle key={index} cx={point.x} cy={point.y} r={10} fill="white" />
270
- ))}
271
- </Svg>
272
- </View>
273
- )}
274
- </>
275
- )}
276
-
277
- <Modal visible={isLoading} transparent animationType="fade">
278
- <View style={styles.loadingOverlay}>
279
- <Image
280
- source={require('../src/assets/loadingCamera.gif')}
281
- style={styles.loadingGif}
282
- resizeMode="contain"
283
- />
284
- </View>
285
- </Modal>
286
- </View>
287
- );
288
- };
289
-
1
+ import styles from './ImageCropperStyles';
2
+ import React, { useState, useRef, useEffect } from 'react';
3
+ import { Modal,View, Image, Dimensions, TouchableOpacity, Animated, Text, Platform, SafeAreaView } from 'react-native';
4
+ import Svg, { Path, Circle } from 'react-native-svg';
5
+ import { captureRef } from 'react-native-view-shot';
6
+ import CustomCamera from './CustomCamera';
7
+ import { enhanceImage } from './ImageProcessor';
8
+ import * as ImageManipulator from 'expo-image-manipulator';
9
+ import { Ionicons } from '@expo/vector-icons';
10
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
11
+
12
+ const ImageCropper = ({ onConfirm, openCameraFirst, initialImage ,addheight}) => {
13
+
14
+
15
+ const [image, setImage] = useState(null);
16
+ const [points, setPoints] = useState([]);
17
+ const [showResult, setShowResult] = useState(false);
18
+ const [showCustomCamera, setShowCustomCamera] = useState(false);
19
+ const viewRef = useRef(null);
20
+ const imageMeasure = useRef({ x: 0, y: 0, width: 0, height: 0 });
21
+ const selectedPointIndex = useRef(null);
22
+ const lastTap = useRef(null);
23
+
24
+ const [isLoading, setIsLoading] = useState(false);
25
+ const [showFullScreenCapture, setShowFullScreenCapture] = useState(false);
26
+ const [isRotating, setIsRotating] = useState(false);
27
+ const lastValidPosition = useRef(null);
28
+ const insets = useSafeAreaInsets();
29
+
30
+
31
+
32
+
33
+
34
+ useEffect(() => {
35
+ if (openCameraFirst) {
36
+ setShowCustomCamera(true);
37
+ } else if (initialImage) {
38
+ setImage(initialImage);
39
+ }
40
+ }, [openCameraFirst, initialImage]);
41
+
42
+
43
+ useEffect(() => {
44
+ if (!image) return;
45
+
46
+ Image.getSize(image, (imgWidth, imgHeight) => {
47
+ const screenRatio = SCREEN_WIDTH / SCREEN_HEIGHT;
48
+ const imageRatio = imgWidth / imgHeight;
49
+
50
+ if (imageRatio > screenRatio) {
51
+ imageMeasure.current = {
52
+ width: SCREEN_WIDTH,
53
+ height: SCREEN_WIDTH / imageRatio,
54
+ };
55
+ } else {
56
+ imageMeasure.current = {
57
+ width: SCREEN_HEIGHT * imageRatio,
58
+ height: SCREEN_HEIGHT,
59
+ };
60
+ }
61
+ });
62
+ }, [image]);
63
+
64
+
65
+ const initializeCropBox = () => {
66
+ const { width, height } = imageMeasure.current;
67
+ // if (width === 0 || height === 0 || points.length > 0) return;
68
+ const boxWidth = width * 0.8;
69
+ const boxHeight = height * 0.8;
70
+ const centerX = width / 2;
71
+ const centerY = height / 2;
72
+ setPoints([
73
+ { x: centerX - boxWidth / 2, y: centerY - boxHeight / 2 },
74
+ { x: centerX + boxWidth / 2, y: centerY - boxHeight / 2 },
75
+ { x: centerX + boxWidth / 2, y: centerY + boxHeight / 2 },
76
+ { x: centerX - boxWidth / 2, y: centerY + boxHeight / 2 },
77
+ ]);
78
+ };
79
+
80
+
81
+ const onImageLayout = (e) => {
82
+ const layout = e.nativeEvent.layout;
83
+ imageMeasure.current = {
84
+ x: layout.x,
85
+ y: layout.y,
86
+ width: layout.width,
87
+ height: layout.height
88
+ };
89
+ initializeCropBox();
90
+ };
91
+
92
+ const createPath = () => {
93
+ if (points.length < 1) return '';
94
+ let path = `M ${points[0].x} ${points[0].y} `;
95
+ points.forEach(point => path += `L ${point.x} ${point.y} `);
96
+ return path + 'Z';
97
+ };
98
+
99
+ const handleTap = (e) => {
100
+ if (!image || showResult) return;
101
+ const now = Date.now();
102
+ const { locationX: tapX, locationY: tapY } = e.nativeEvent;
103
+
104
+ if (lastTap.current && now - lastTap.current < 300) {
105
+ const exists = points.some(p => Math.abs(p.x - tapX) < 15 && Math.abs(p.y - tapY) < 15);
106
+ if (!exists) setPoints([...points, { x: tapX, y: tapY }]);
107
+ lastTap.current = null;
108
+ } else {
109
+ const index = points.findIndex(p => Math.abs(p.x - tapX) < 20 && Math.abs(p.y - tapY) < 20);
110
+ if (index !== -1) {
111
+ selectedPointIndex.current = index;
112
+ lastValidPosition.current = { ...points[index] }; // store original position before move
113
+ }
114
+ lastTap.current = now;
115
+ }
116
+ };
117
+
118
+ const handleMove = (e) => {
119
+ if (showResult || selectedPointIndex.current === null) return;
120
+
121
+ const { locationX: moveX, locationY: moveY } = e.nativeEvent;
122
+ const width = imageMeasure.current.width;
123
+ const height = imageMeasure.current.height;
124
+
125
+ const boundedX = Math.max(0, Math.min(moveX, width));
126
+ const boundedY = Math.max(0, Math.min(moveY, height));
127
+
128
+ const edgeThreshold = 10;
129
+ const isNearTopOrBottomEdge =
130
+ boundedY <= edgeThreshold || boundedY >= height - edgeThreshold;
131
+
132
+ const isNearLeftOrRightEdge =
133
+ boundedX <= edgeThreshold || boundedX >= width - edgeThreshold;
134
+
135
+ if (isNearTopOrBottomEdge || isNearLeftOrRightEdge) {
136
+ // Reset point to last known position
137
+ if (lastValidPosition.current && selectedPointIndex.current !== null) {
138
+ setPoints(prev =>
139
+ prev.map((p, i) =>
140
+ i === selectedPointIndex.current ? lastValidPosition.current : p
141
+ )
142
+ );
143
+ }
144
+ selectedPointIndex.current = null;
145
+ return;
146
+ }
147
+
148
+ // Valid move — update point and store as new last valid position
149
+ const updatedPoint = { x: boundedX, y: boundedY };
150
+ lastValidPosition.current = updatedPoint;
151
+
152
+ setPoints(prev =>
153
+ prev.map((p, i) =>
154
+ i === selectedPointIndex.current ? updatedPoint : p
155
+ )
156
+ );
157
+ };
158
+
159
+ const handleRelease = () => {
160
+ selectedPointIndex.current = null;
161
+ };
162
+
163
+ const handleReset = () => {
164
+ // setPoints([]);
165
+ initializeCropBox();
166
+ };
167
+
168
+ const rotatePreviewImage = async (degrees) => {
169
+ if (!image || isRotating) return;
170
+ setIsRotating(true);
171
+
172
+ try {
173
+ // Rotate the image with optimized settings for speed
174
+ // Using JPEG with 0.85 compression for faster processing while maintaining good quality
175
+ const rotated = await ImageManipulator.manipulateAsync(
176
+ image,
177
+ [{ rotate: degrees }],
178
+ { compress: 0.85, format: ImageManipulator.SaveFormat.JPEG }
179
+ );
180
+
181
+ // Update image - onImageLayout will call initializeCropBox automatically
182
+ setImage(rotated.uri);
183
+ } catch (error) {
184
+ console.error("Error rotating image:", error);
185
+ alert("Error rotating image");
186
+ } finally {
187
+ setIsRotating(false);
188
+ }
189
+ };
190
+
191
+ // Helper function to wait for multiple render cycles (works on iOS)
192
+ const waitForRender = (cycles = 5) => {
193
+ return new Promise((resolve) => {
194
+ let count = 0;
195
+ const tick = () => {
196
+ count++;
197
+ if (count >= cycles) {
198
+ resolve();
199
+ } else {
200
+ setImmediate(tick);
201
+ }
202
+ };
203
+ setImmediate(tick);
204
+ });
205
+ };
206
+
207
+
208
+ return (
209
+ <View style={styles.container}>
210
+
211
+ {showCustomCamera ? (
212
+ <CustomCamera
213
+ onPhotoCaptured={(uri) => { setImage(uri); setShowCustomCamera(false); }}
214
+ onCancel={() => setShowCustomCamera(false)}
215
+ />
216
+ ) : (
217
+ <>
218
+ {!showResult && (
219
+ <View style={image ? styles.buttonContainer : styles.centerButtonsContainer}>
220
+
221
+ {image && Platform.OS === 'android' && (
222
+ <>
223
+ <TouchableOpacity
224
+ style={styles.iconButton}
225
+ onPress={() => rotatePreviewImage(90)}
226
+ disabled={isRotating}
227
+ >
228
+ <Ionicons name="sync" size={24} color="white" />
229
+ </TouchableOpacity>
230
+ </>
231
+ )}
232
+
233
+ {image && (
234
+ <TouchableOpacity style={styles.button} onPress={handleReset}>
235
+ <Text style={styles.buttonText}>Reset</Text>
236
+ </TouchableOpacity>
237
+ )}
238
+ {image && (
239
+ <TouchableOpacity
240
+ style={styles.button}
241
+ onPress={async () => {
242
+ // setShowFullScreenCapture(true);
243
+ setIsLoading(true);
244
+ setShowResult(true);
245
+ try {
246
+ // Wait for UI to render - give React time to update the SVG with white fill
247
+ await waitForRender(5);
248
+
249
+ console.log("Starting capture...");
250
+ const capturedUri = await captureRef(viewRef, {
251
+ format: 'png',
252
+ quality: 1,
253
+ });
254
+ console.log("Capture successful:", capturedUri);
255
+
256
+ if (!capturedUri) {
257
+ throw new Error("Capture returned empty URI");
258
+ }
259
+
260
+ console.log("Enhancing image...");
261
+ const enhancedUri = await enhanceImage(capturedUri, addheight);
262
+ console.log("Image enhanced:", enhancedUri);
263
+
264
+ const name = `IMAGE XTK${Date.now()}.png`;
265
+
266
+ if (onConfirm) {
267
+ console.log("Calling onConfirm with:", enhancedUri, name);
268
+ onConfirm(enhancedUri, name);
269
+ }
270
+ } catch (error) {
271
+ console.error("Erreur lors de la capture :", error);
272
+ alert("Erreur lors de la capture ! " + error.message);
273
+ } finally {
274
+ setShowResult(false);
275
+ setIsLoading(false);
276
+ setShowFullScreenCapture(false);
277
+ }
278
+ }}
279
+ >
280
+ <Text style={styles.buttonText}>Confirm</Text>
281
+ </TouchableOpacity>
282
+ )}
283
+
284
+ </View>
285
+ )}
286
+
287
+
288
+ {image && (
289
+ <View
290
+ ref={viewRef}
291
+ collapsable={false}
292
+ style={showFullScreenCapture ? styles.fullscreenImageContainer : styles.imageContainer}
293
+ onStartShouldSetResponder={() => true}
294
+ onResponderStart={handleTap}
295
+ onResponderMove={handleMove}
296
+ onResponderRelease={handleRelease}
297
+ >
298
+ <Image source={{ uri: image }} style={styles.image} onLayout={onImageLayout} />
299
+ <Svg style={styles.overlay}>
300
+ <Path
301
+ d={`M 0 0 H ${imageMeasure.current.width} V ${imageMeasure.current.height} H 0 Z ${createPath()}`}
302
+ fill={showResult ? 'white' : 'rgba(0, 0, 0, 0.8)'}
303
+ fillRule="evenodd"
304
+ />
305
+ {!showResult && points.length > 0 && (
306
+ <Path d={createPath()} fill="transparent" stroke="white" strokeWidth={2} />
307
+ )}
308
+ {!showResult && points.map((point, index) => (
309
+ <Circle key={index} cx={point.x} cy={point.y} r={10} fill="white" />
310
+ ))}
311
+ </Svg>
312
+ </View>
313
+ )}
314
+ </>
315
+ )}
316
+
317
+ <Modal visible={isLoading} transparent animationType="fade">
318
+ <View style={styles.loadingOverlay}>
319
+ <Image
320
+ source={require('../src/assets/loadingCamera.gif')}
321
+ style={styles.loadingGif}
322
+ resizeMode="contain"
323
+ />
324
+ </View>
325
+ </Modal>
326
+ </View>
327
+ );
328
+ };
329
+
290
330
  export default ImageCropper;
@@ -25,6 +25,16 @@ const styles = StyleSheet.create({
25
25
  alignItems: 'center',
26
26
  paddingHorizontal: 10,
27
27
  zIndex: 10,
28
+ gap: 10,
29
+ },
30
+ iconButton: {
31
+ backgroundColor: PRIMARY_GREEN,
32
+ width: 60,
33
+ height: 60,
34
+ borderRadius: 30,
35
+ alignItems: 'center',
36
+ justifyContent: 'center',
37
+ marginRight: 5,
28
38
  },
29
39
  button: {
30
40
  flex: 1,
@@ -173,6 +183,37 @@ fullscreenImageContainer: {
173
183
  alignItems: 'center',
174
184
  zIndex: 9999,
175
185
  },
186
+ previewContainer: {
187
+ flex: 1,
188
+ backgroundColor: DEEP_BLACK,
189
+ justifyContent: 'center',
190
+ alignItems: 'center',
191
+ width: '100%',
192
+ },
193
+ previewImage: {
194
+ width: SCREEN_WIDTH,
195
+ height: '80%',
196
+ },
197
+ previewButtonContainer: {
198
+ position: 'absolute',
199
+ bottom: 50,
200
+ left: 0,
201
+ right: 0,
202
+ flexDirection: 'row',
203
+ justifyContent: 'space-around',
204
+ paddingHorizontal: 20,
205
+ },
206
+ previewButton: {
207
+ backgroundColor: PRIMARY_GREEN,
208
+ width: 60,
209
+ height: 60,
210
+ borderRadius: 30,
211
+ alignItems: 'center',
212
+ justifyContent: 'center',
213
+ },
214
+ useButton: {
215
+ backgroundColor: GLOW_WHITE,
216
+ },
176
217
 
177
218
 
178
219
  });
@@ -1,38 +1,28 @@
1
- import * as ImageManipulator from 'expo-image-manipulator';
2
-
3
- export const enhanceImage = async (uri , addheight) => {
4
- try {
5
- // First, fix orientation by applying EXIF orientation data
6
- // Empty array [] tells manipulateAsync to automatically apply EXIF orientation
7
- // This ensures the image is correctly oriented and EXIF tag is removed
8
- const fixedOrientationImage = await ImageManipulator.manipulateAsync(uri, [], {
9
- compress: 1,
10
- format: ImageManipulator.SaveFormat.PNG
11
- });
12
-
13
- // Get the correct dimensions after orientation fix
14
- // The width and height properties reflect the actual image dimensions after orientation fix
15
- const ratio = fixedOrientationImage.height / fixedOrientationImage.width;
16
-
17
- const maxHeight = addheight;
18
- const newWidth = Math.round(maxHeight / ratio);
19
- const newHeight = Math.round(newWidth * ratio);
20
-
21
- // Resize using the orientation-fixed image
22
- const result = await ImageManipulator.manipulateAsync(
23
- fixedOrientationImage.uri,
24
- [
25
- { resize: { width: newWidth, height: newHeight } },
26
- ],
27
- {
28
- compress: 1,
29
- format: ImageManipulator.SaveFormat.PNG
30
- }
31
- );
32
-
33
- return result.uri;
34
- } catch (error) {
35
- console.error("Erreur T404K:", error);
36
- return uri;
37
- }
1
+ import * as ImageManipulator from 'expo-image-manipulator';
2
+
3
+ export const enhanceImage = async (uri , addheight) => {
4
+ try {
5
+ const imageInfo = await ImageManipulator.manipulateAsync(uri, []);
6
+ const ratio = imageInfo.height / imageInfo.width;
7
+
8
+ const maxHeight = addheight;
9
+ const newWidth = Math.round(maxHeight / ratio);
10
+ const newHeight = Math.round(newWidth * ratio);
11
+
12
+ const result = await ImageManipulator.manipulateAsync(
13
+ uri,
14
+ [
15
+ { resize: { width: newWidth, height: newHeight } },
16
+ ],
17
+ {
18
+ compress: 1,
19
+ format: ImageManipulator.SaveFormat.PNG
20
+ }
21
+ );
22
+
23
+ return result.uri;
24
+ } catch (error) {
25
+ console.error("Erreur T404K:", error);
26
+ return uri;
27
+ }
38
28
  };