Skip to content

Commit 406db87

Browse files
committed
fix: parse image orientation from exif data (closes #497)
1 parent f7b1b01 commit 406db87

File tree

3 files changed

+66
-7
lines changed

3 files changed

+66
-7
lines changed

app/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ dependencies {
6767
implementation("androidx.core:core-ktx:1.16.0")
6868
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.9.1")
6969
implementation("androidx.activity:activity-compose:1.10.1")
70+
implementation("androidx.exifinterface:exifinterface:1.4.1")
7071
testImplementation("junit:junit:4.13.2")
7172
androidTestImplementation("androidx.test.ext:junit:1.2.1")
7273
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")

app/src/main/java/com/bnyro/translate/ui/components/ImageCropDialog.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@
1717

1818
package com.bnyro.translate.ui.components
1919

20+
import android.annotation.SuppressLint
2021
import android.graphics.Bitmap
2122
import androidx.compose.foundation.Canvas
2223
import androidx.compose.foundation.gestures.detectDragGestures
2324
import androidx.compose.foundation.layout.BoxWithConstraints
2425
import androidx.compose.foundation.layout.fillMaxSize
2526
import androidx.compose.material.icons.Icons
27+
import androidx.compose.material.icons.automirrored.filled.ArrowBack
2628
import androidx.compose.material.icons.filled.ArrowBack
2729
import androidx.compose.material.icons.filled.Done
2830
import androidx.compose.material3.CenterAlignedTopAppBar
@@ -88,7 +90,7 @@ fun ImageCropDialog(
8890
title = { Text(stringResource(R.string.image_translation)) },
8991
navigationIcon = {
9092
StyledIconButton(
91-
imageVector = Icons.Default.ArrowBack,
93+
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
9294
onClick = { onDismissRequest() }
9395
)
9496
},
@@ -123,6 +125,7 @@ fun ImageCropDialog(
123125
}
124126
}
125127

128+
@SuppressLint("UnusedBoxWithConstraintsScope")
126129
@Composable
127130
fun CropImageView(
128131
modifier: Modifier = Modifier,

app/src/main/java/com/bnyro/translate/util/ImageHelper.kt

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,12 @@ import android.content.Context
2121
import android.graphics.Bitmap
2222
import android.graphics.BitmapFactory
2323
import android.graphics.Canvas
24+
import android.graphics.Matrix
2425
import android.graphics.Paint
26+
import androidx.exifinterface.media.ExifInterface
2527
import android.net.Uri
28+
import android.os.Build
29+
import androidx.core.graphics.createBitmap
2630

2731
data class ImageTransform(
2832
val width: Int,
@@ -33,17 +37,68 @@ data class ImageTransform(
3337

3438
object ImageHelper {
3539
fun getImage(context: Context, uri: Uri): Bitmap? {
36-
return context.contentResolver.openInputStream(uri)?.use {
40+
val bitmap = context.contentResolver.openInputStream(uri)?.use {
3741
BitmapFactory.decodeStream(it)
42+
} ?: return null
43+
44+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
45+
val `is` = context.contentResolver.openInputStream(uri) ?: return bitmap
46+
val exifMetadata =
47+
ExifInterface(`is`).getAttributeInt(
48+
ExifInterface.TAG_ORIENTATION,
49+
ExifInterface.ORIENTATION_NORMAL
50+
)
51+
return rotateBitmap(bitmap, exifMetadata) ?: bitmap
52+
}
53+
54+
return bitmap
55+
}
56+
57+
fun rotateBitmap(bitmap: Bitmap, orientation: Int): Bitmap? {
58+
val matrix: Matrix = Matrix()
59+
when (orientation) {
60+
ExifInterface.ORIENTATION_NORMAL -> return bitmap
61+
ExifInterface.ORIENTATION_FLIP_HORIZONTAL -> matrix.setScale(-1F, 1F)
62+
ExifInterface.ORIENTATION_ROTATE_180 -> matrix.setRotate(180F)
63+
ExifInterface.ORIENTATION_FLIP_VERTICAL -> {
64+
matrix.setRotate(180F)
65+
matrix.postScale(-1F, 1F)
66+
}
67+
68+
ExifInterface.ORIENTATION_TRANSPOSE -> {
69+
matrix.setRotate(90F)
70+
matrix.postScale(-1F, 1F)
71+
}
72+
73+
ExifInterface.ORIENTATION_ROTATE_90 -> matrix.setRotate(90F)
74+
ExifInterface.ORIENTATION_TRANSVERSE -> {
75+
matrix.setRotate(-90F)
76+
matrix.postScale(-1F, 1F)
77+
}
78+
79+
ExifInterface.ORIENTATION_ROTATE_270 -> matrix.setRotate(-90F)
80+
else -> return bitmap
81+
}
82+
try {
83+
val bmRotated = Bitmap.createBitmap(
84+
bitmap,
85+
0,
86+
0,
87+
bitmap.width,
88+
bitmap.height,
89+
matrix,
90+
true
91+
)
92+
bitmap.recycle()
93+
return bmRotated
94+
} catch (e: OutOfMemoryError) {
95+
e.printStackTrace()
96+
return null
3897
}
3998
}
4099

41100
fun setAlpha(originalBitmap: Bitmap, alpha: Int): Bitmap {
42-
val newBitmap = Bitmap.createBitmap(
43-
originalBitmap.getWidth(),
44-
originalBitmap.getHeight(),
45-
Bitmap.Config.ARGB_8888
46-
)
101+
val newBitmap = createBitmap(originalBitmap.width, originalBitmap.height)
47102
val canvas = Canvas(newBitmap)
48103
val paint = Paint().apply { this.alpha = alpha }
49104
canvas.drawBitmap(originalBitmap, 0f, 0f, paint)

0 commit comments

Comments
 (0)