問題
Android Q以降はImageDecoder.decodeBitmap()、それ以前はMediaStore.Images.Media.getBitmap()でBitmapを取得し、EXIFから取得した回転情報を適用する実装がある。
val (bitmap, rotation) =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val bmp = ImageDecoder.decodeBitmap(ImageDecoder.createSource(context.contentResolver, uri))
bmp to getRotation(uri, context)
} else {
@Suppress("DEPRECATION")
val bmp = MediaStore.Images.Media.getBitmap(context.contentResolver, uri)
bmp to getRotation(uri, context)
}
val matrix = Matrix()
matrix.setRotate(rotation.toFloat())
val rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
getRotationはEXIFのOrientation情報から回転角度を返す。
private fun getRotation(
uri: Uri?,
context: Context,
): Int {
uri ?: return 0
val orientation =
context.contentResolver.openInputStream(uri)?.use { inputStream ->
ExifInterface(inputStream).getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL,
)
} ?: ExifInterface.ORIENTATION_NORMAL
return when (orientation) {
ExifInterface.ORIENTATION_ROTATE_90 -> 90
ExifInterface.ORIENTATION_ROTATE_180 -> 180
ExifInterface.ORIENTATION_ROTATE_270 -> 270
else -> 0
}
}
この実装では、ImageDecoder.decodeBitmap()経由で作成したBitmapに対して2重に回転が適用される。
原因
ImageDecoder.decodeBitmap()はEXIFのOrientation情報を自動で読み取り、Bitmapを回転して返す。
そのため、getRotation()で取得した角度をさらに適用すると、回転が2重になる。
一方、MediaStore.Images.Media.getBitmap()はEXIFを無視してBitmapを返すため、getRotation()による手動回転が必要になる。
修正
Android Q以降はImageDecoder.decodeBitmap()が自動で回転するため、手動回転の角度を0にする。
val (bitmap, rotation) =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val bmp = ImageDecoder.decodeBitmap(ImageDecoder.createSource(context.contentResolver, uri))
bmp to 0
} else {
@Suppress("DEPRECATION")
val bmp = MediaStore.Images.Media.getBitmap(context.contentResolver, uri)
bmp to getRotation(uri, context)
}
val matrix = Matrix()
matrix.setRotate(rotation.toFloat())
val rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
\確かな知識を身に着けたい、Androidアプリ開発を学びたい人にオススメ!/
