Last active
March 8, 2021 16:45
-
-
Save Bloody-Badboy/93766c04a2c7301f2ef592a082a9d93a to your computer and use it in GitHub Desktop.
A utility class load and compress image from Uri
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.content.Context | |
import android.graphics.Bitmap | |
import android.graphics.BitmapFactory | |
import android.graphics.Matrix | |
import android.net.Uri | |
import android.os.ParcelFileDescriptor | |
import android.util.Log | |
import androidx.exifinterface.media.ExifInterface | |
import java.io.ByteArrayOutputStream | |
import java.io.FileNotFoundException | |
import java.io.IOException | |
/** | |
* Created by Arpan Sarkar [https://github.com/Bloody-Badboy/] on 10/9/19. | |
*/ | |
object ImageCompressor { | |
private const val TAG = "ImageCompressor" | |
private const val MAX_WIDTH = 612 | |
private const val MAX_HEIGHT = 816 | |
private const val QUALITY = 80 | |
fun compressImage(context: Context, imageUri: Uri): ByteArray? { | |
val parcelFileDescriptor: ParcelFileDescriptor? | |
try { | |
parcelFileDescriptor = context.contentResolver.openFileDescriptor(imageUri, "r") | |
} catch (e: FileNotFoundException) { | |
Log.e(TAG, "File not found for given Uri: $imageUri") | |
return null | |
} | |
if (parcelFileDescriptor == null) { | |
Log.e(TAG, "ParcelFileDescriptor was null for given Uri: $imageUri") | |
return null | |
} | |
val fileDescriptor = parcelFileDescriptor.fileDescriptor | |
val options = BitmapFactory.Options() | |
options.inJustDecodeBounds = true | |
BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options) | |
if (options.outWidth < 0 || options.outHeight < 0) { | |
Log.e(TAG, "Size of the bitmap could not be retrieved for given Uri: $imageUri") | |
return null | |
} | |
val (h, w) = calculateScaledHeightWidth( | |
options, | |
MAX_WIDTH, | |
MAX_HEIGHT | |
) | |
options.inJustDecodeBounds = false | |
options.inSampleSize = calculateInSampleSize(options, h, w) | |
Log.d(TAG, "Decoding bitmap using inSampleSize:${options.inSampleSize}") | |
var decodeSampledBitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options) | |
if (decodeSampledBitmap == null) { | |
Log.e(TAG, "Failed to decode bitmap for given Uri: $imageUri") | |
return null | |
} | |
val exif = ExifInterface(fileDescriptor) | |
val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0) | |
val degrees = exifToDegrees(orientation) | |
val translation = exifToTranslation(orientation) | |
val matrix = Matrix() | |
if (degrees != 0f) { | |
matrix.postRotate(degrees) | |
} | |
if (translation != 1f) { | |
matrix.postScale(translation, 1f) | |
} | |
decodeSampledBitmap = Bitmap.createBitmap( | |
decodeSampledBitmap, | |
0, | |
0, | |
decodeSampledBitmap.width, | |
decodeSampledBitmap.height, | |
matrix, | |
true | |
) | |
try { | |
parcelFileDescriptor.close() | |
} catch (e: IOException) { | |
Log.e(TAG, "Failed to close ParcelFileDescriptor", e) | |
} | |
return ByteArrayOutputStream().apply { | |
decodeSampledBitmap.compress(Bitmap.CompressFormat.JPEG, QUALITY, this) | |
decodeSampledBitmap.recycle() | |
}.toByteArray() | |
} | |
private fun calculateScaledHeightWidth( | |
options: BitmapFactory.Options, | |
reqWidth: Int, | |
reqHeight: Int | |
): IntArray { | |
var height = options.outHeight | |
var width = options.outWidth | |
val reqRatio = reqWidth.toFloat() / reqHeight.toFloat() | |
var imgRatio = width.toFloat() / height.toFloat() | |
Log.d(TAG, "Actual bitmap height:$height, width: $width") | |
if (height > reqHeight || width > reqWidth) { | |
when { | |
imgRatio < reqRatio -> { | |
imgRatio = reqHeight.toFloat() / height.toFloat() | |
width = (imgRatio * width).toInt() | |
height = reqHeight | |
} | |
imgRatio > reqRatio -> { | |
imgRatio = reqWidth.toFloat() / width.toFloat() | |
height = (imgRatio * height).toInt() | |
width = reqWidth | |
} | |
else -> { | |
height = reqHeight | |
width = reqWidth | |
} | |
} | |
} | |
Log.d(TAG, "Scaled bitmap height:$height, width: $width") | |
return intArrayOf(height, width) | |
} | |
private fun calculateInSampleSize( | |
options: BitmapFactory.Options, | |
reqWidth: Int, | |
reqHeight: Int | |
): Int { | |
// Raw height and width of image | |
val height = options.outHeight | |
val width = options.outWidth | |
var inSampleSize = 1 | |
if (height > reqHeight || width > reqWidth) { | |
val halfHeight = height / 2 | |
val halfWidth = width / 2 | |
// Calculate the largest inSampleSize value that is a power of 2 and keeps both | |
// height and width larger than the requested height and width. | |
while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) { | |
inSampleSize *= 2 | |
} | |
} | |
return inSampleSize | |
} | |
private fun exifToDegrees(exifOrientation: Int) = when (exifOrientation) { | |
ExifInterface.ORIENTATION_ROTATE_90, | |
ExifInterface.ORIENTATION_TRANSPOSE -> 90f | |
ExifInterface.ORIENTATION_ROTATE_180, | |
ExifInterface.ORIENTATION_FLIP_VERTICAL -> 180f | |
ExifInterface.ORIENTATION_ROTATE_270, | |
ExifInterface.ORIENTATION_TRANSVERSE -> 270f | |
else -> 0f | |
} | |
private fun exifToTranslation(exifOrientation: Int) = when (exifOrientation) { | |
ExifInterface.ORIENTATION_FLIP_HORIZONTAL, | |
ExifInterface.ORIENTATION_FLIP_VERTICAL, | |
ExifInterface.ORIENTATION_TRANSPOSE, | |
ExifInterface.ORIENTATION_TRANSVERSE -> -1f | |
else -> 1f | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment