@@ -193,8 +193,28 @@ class ImageEditorModuleImpl(private val reactContext: ReactApplicationContext) {
193
193
} else {
194
194
@Suppress(" DEPRECATION" ) BitmapRegionDecoder .newInstance(it, false )
195
195
}
196
+
197
+ val imageHeight: Int = decoder!! .height
198
+ val imageWidth: Int = decoder!! .width
199
+ val orientation = getOrientation(reactContext, Uri .parse(uri))
200
+
201
+ val (left, top) =
202
+ when (orientation) {
203
+ 90 -> y to imageHeight - width - x
204
+ 180 -> imageWidth - width - x to imageHeight - height - y
205
+ 270 -> imageWidth - height - y to x
206
+ else -> x to y
207
+ }
208
+
209
+ val (right, bottom) =
210
+ when (orientation) {
211
+ 90 ,
212
+ 270 -> left + height to top + width
213
+ else -> left + width to top + height
214
+ }
215
+
196
216
return @use try {
197
- val rect = Rect (x, y, x + width, y + height )
217
+ val rect = Rect (left, top, right, bottom )
198
218
decoder!! .decodeRegion(rect, outOptions)
199
219
} finally {
200
220
decoder!! .recycle()
@@ -218,12 +238,12 @@ class ImageEditorModuleImpl(private val reactContext: ReactApplicationContext) {
218
238
private fun cropAndResizeTask (
219
239
outOptions : BitmapFactory .Options ,
220
240
uri : String ,
221
- x : Int ,
222
- y : Int ,
223
- width : Int ,
224
- height : Int ,
225
- targetWidth : Int ,
226
- targetHeight : Int ,
241
+ xPos : Int ,
242
+ yPos : Int ,
243
+ rectWidth : Int ,
244
+ rectHeight : Int ,
245
+ outputWidth : Int ,
246
+ outputHeight : Int ,
227
247
): Bitmap ? {
228
248
Assertions .assertNotNull(outOptions)
229
249
@@ -233,6 +253,35 @@ class ImageEditorModuleImpl(private val reactContext: ReactApplicationContext) {
233
253
// This uses scaling mode COVER
234
254
235
255
// Where would the crop rect end up within the scaled bitmap?
256
+
257
+ val bitmap =
258
+ openBitmapInputStream(uri)?.use {
259
+ // This can use significantly less memory than decoding the full-resolution bitmap
260
+ BitmapFactory .decodeStream(it, null , outOptions)
261
+ } ? : return null
262
+
263
+ val orientation = getOrientation(reactContext, Uri .parse(uri))
264
+ val (x, y) =
265
+ when (orientation) {
266
+ 90 -> yPos to bitmap.height - rectWidth - xPos
267
+ 270 -> bitmap.width - rectHeight - yPos to xPos
268
+ 180 -> bitmap.width - rectWidth - xPos to bitmap.height - rectHeight - yPos
269
+ else -> xPos to yPos
270
+ }
271
+
272
+ val (width, height) =
273
+ when (orientation) {
274
+ 90 ,
275
+ 270 -> rectHeight to rectWidth
276
+ else -> rectWidth to rectHeight
277
+ }
278
+ val (targetWidth, targetHeight) =
279
+ when (orientation) {
280
+ 90 ,
281
+ 270 -> outputHeight to outputWidth
282
+ else -> outputWidth to outputHeight
283
+ }
284
+
236
285
val cropRectRatio = width / height.toFloat()
237
286
val targetRatio = targetWidth / targetHeight.toFloat()
238
287
val isCropRatioLargerThanTargetRatio = cropRectRatio > targetRatio
@@ -250,11 +299,6 @@ class ImageEditorModuleImpl(private val reactContext: ReactApplicationContext) {
250
299
// Decode the bitmap. We have to open the stream again, like in the example linked above.
251
300
// Is there a way to just continue reading from the stream?
252
301
outOptions.inSampleSize = getDecodeSampleSize(width, height, targetWidth, targetHeight)
253
- val bitmap =
254
- openBitmapInputStream(uri)?.use {
255
- // This can use significantly less memory than decoding the full-resolution bitmap
256
- BitmapFactory .decodeStream(it, null , outOptions)
257
- } ? : return null
258
302
259
303
val cropX = (newX / outOptions.inSampleSize.toFloat()).roundToInt()
260
304
val cropY = (newY / outOptions.inSampleSize.toFloat()).roundToInt()
@@ -296,30 +340,119 @@ class ImageEditorModuleImpl(private val reactContext: ReactApplicationContext) {
296
340
@SuppressLint(" InlinedApi" )
297
341
private val EXIF_ATTRIBUTES =
298
342
arrayOf(
343
+ ExifInterface .TAG_APERTURE_VALUE ,
344
+ ExifInterface .TAG_MAX_APERTURE_VALUE ,
345
+ ExifInterface .TAG_METERING_MODE ,
346
+ ExifInterface .TAG_ARTIST ,
347
+ ExifInterface .TAG_BITS_PER_SAMPLE ,
348
+ ExifInterface .TAG_COMPRESSION ,
349
+ ExifInterface .TAG_BODY_SERIAL_NUMBER ,
350
+ ExifInterface .TAG_BRIGHTNESS_VALUE ,
351
+ ExifInterface .TAG_CONTRAST ,
352
+ ExifInterface .TAG_CAMERA_OWNER_NAME ,
353
+ ExifInterface .TAG_COLOR_SPACE ,
354
+ ExifInterface .TAG_COPYRIGHT ,
299
355
ExifInterface .TAG_DATETIME ,
300
356
ExifInterface .TAG_DATETIME_DIGITIZED ,
357
+ ExifInterface .TAG_DATETIME_ORIGINAL ,
358
+ ExifInterface .TAG_DEVICE_SETTING_DESCRIPTION ,
359
+ ExifInterface .TAG_DIGITAL_ZOOM_RATIO ,
360
+ ExifInterface .TAG_EXIF_VERSION ,
361
+ ExifInterface .TAG_EXPOSURE_BIAS_VALUE ,
362
+ ExifInterface .TAG_EXPOSURE_INDEX ,
363
+ ExifInterface .TAG_EXPOSURE_MODE ,
301
364
ExifInterface .TAG_EXPOSURE_TIME ,
365
+ ExifInterface .TAG_EXPOSURE_PROGRAM ,
302
366
ExifInterface .TAG_FLASH ,
367
+ ExifInterface .TAG_FLASH_ENERGY ,
303
368
ExifInterface .TAG_FOCAL_LENGTH ,
369
+ ExifInterface .TAG_FOCAL_LENGTH_IN_35MM_FILM ,
370
+ ExifInterface .TAG_FOCAL_PLANE_RESOLUTION_UNIT ,
371
+ ExifInterface .TAG_FOCAL_PLANE_X_RESOLUTION ,
372
+ ExifInterface .TAG_FOCAL_PLANE_Y_RESOLUTION ,
373
+ ExifInterface .TAG_PHOTOMETRIC_INTERPRETATION ,
374
+ ExifInterface .TAG_PLANAR_CONFIGURATION ,
375
+ ExifInterface .TAG_F_NUMBER ,
376
+ ExifInterface .TAG_GAIN_CONTROL ,
377
+ ExifInterface .TAG_GAMMA ,
304
378
ExifInterface .TAG_GPS_ALTITUDE ,
305
379
ExifInterface .TAG_GPS_ALTITUDE_REF ,
380
+ ExifInterface .TAG_GPS_AREA_INFORMATION ,
306
381
ExifInterface .TAG_GPS_DATESTAMP ,
382
+ ExifInterface .TAG_GPS_DOP ,
307
383
ExifInterface .TAG_GPS_LATITUDE ,
308
384
ExifInterface .TAG_GPS_LATITUDE_REF ,
309
385
ExifInterface .TAG_GPS_LONGITUDE ,
310
386
ExifInterface .TAG_GPS_LONGITUDE_REF ,
387
+ ExifInterface .TAG_GPS_STATUS ,
388
+ ExifInterface .TAG_GPS_DEST_BEARING ,
389
+ ExifInterface .TAG_GPS_DEST_BEARING_REF ,
390
+ ExifInterface .TAG_GPS_DEST_DISTANCE ,
391
+ ExifInterface .TAG_GPS_DEST_DISTANCE_REF ,
392
+ ExifInterface .TAG_GPS_DEST_LATITUDE ,
393
+ ExifInterface .TAG_GPS_DEST_LATITUDE_REF ,
394
+ ExifInterface .TAG_GPS_DEST_LONGITUDE ,
395
+ ExifInterface .TAG_GPS_DEST_LONGITUDE_REF ,
396
+ ExifInterface .TAG_GPS_DIFFERENTIAL ,
397
+ ExifInterface .TAG_GPS_IMG_DIRECTION ,
398
+ ExifInterface .TAG_GPS_IMG_DIRECTION_REF ,
399
+ ExifInterface .TAG_GPS_MAP_DATUM ,
400
+ ExifInterface .TAG_GPS_MEASURE_MODE ,
311
401
ExifInterface .TAG_GPS_PROCESSING_METHOD ,
402
+ ExifInterface .TAG_GPS_SATELLITES ,
403
+ ExifInterface .TAG_GPS_SPEED ,
404
+ ExifInterface .TAG_GPS_SPEED_REF ,
405
+ ExifInterface .TAG_GPS_STATUS ,
312
406
ExifInterface .TAG_GPS_TIMESTAMP ,
313
- ExifInterface .TAG_IMAGE_LENGTH ,
314
- ExifInterface .TAG_IMAGE_WIDTH ,
407
+ ExifInterface .TAG_GPS_TRACK ,
408
+ ExifInterface .TAG_GPS_TRACK_REF ,
409
+ ExifInterface .TAG_GPS_VERSION_ID ,
410
+ ExifInterface .TAG_IMAGE_DESCRIPTION ,
411
+ ExifInterface .TAG_IMAGE_UNIQUE_ID ,
412
+ ExifInterface .TAG_ISO_SPEED ,
413
+ ExifInterface .TAG_PHOTOGRAPHIC_SENSITIVITY ,
414
+ ExifInterface .TAG_JPEG_INTERCHANGE_FORMAT ,
415
+ ExifInterface .TAG_JPEG_INTERCHANGE_FORMAT_LENGTH ,
416
+ ExifInterface .TAG_LENS_MAKE ,
417
+ ExifInterface .TAG_LENS_MODEL ,
418
+ ExifInterface .TAG_LENS_SERIAL_NUMBER ,
419
+ ExifInterface .TAG_LENS_SPECIFICATION ,
420
+ ExifInterface .TAG_LIGHT_SOURCE ,
315
421
ExifInterface .TAG_MAKE ,
422
+ ExifInterface .TAG_MAKER_NOTE ,
316
423
ExifInterface .TAG_MODEL ,
317
424
ExifInterface .TAG_ORIENTATION ,
318
- ExifInterface .TAG_SUBSEC_TIME ,
425
+ ExifInterface .TAG_SATURATION ,
426
+ ExifInterface .TAG_SHARPNESS ,
427
+ ExifInterface .TAG_SHUTTER_SPEED_VALUE ,
428
+ ExifInterface .TAG_SOFTWARE ,
429
+ ExifInterface .TAG_SUBJECT_DISTANCE ,
430
+ ExifInterface .TAG_SUBJECT_DISTANCE_RANGE ,
431
+ ExifInterface .TAG_SUBJECT_LOCATION ,
432
+ ExifInterface .TAG_USER_COMMENT ,
319
433
ExifInterface .TAG_WHITE_BALANCE
320
434
)
321
435
322
436
// Utils
437
+ private fun getOrientation (context : Context , uri : Uri ): Int {
438
+ val file = getFileFromUri(context, uri)
439
+ if (file == null ) {
440
+ return 0
441
+ }
442
+ val exif = ExifInterface (file.absolutePath)
443
+ return when (
444
+ exif.getAttributeInt(
445
+ ExifInterface .TAG_ORIENTATION ,
446
+ ExifInterface .ORIENTATION_NORMAL
447
+ )
448
+ ) {
449
+ ExifInterface .ORIENTATION_ROTATE_90 -> 90
450
+ ExifInterface .ORIENTATION_ROTATE_180 -> 180
451
+ ExifInterface .ORIENTATION_ROTATE_270 -> 270
452
+ else -> 0
453
+ }
454
+ }
455
+
323
456
@Throws(IOException ::class )
324
457
private fun copyExif (context : Context , oldImage : Uri , newFile : File ) {
325
458
val oldFile = getFileFromUri(context, oldImage)
0 commit comments