Skip to content

Instantly share code, notes, and snippets.

@Bloody-Badboy
Last active March 8, 2021 16:45
Show Gist options
  • Save Bloody-Badboy/93766c04a2c7301f2ef592a082a9d93a to your computer and use it in GitHub Desktop.
Save Bloody-Badboy/93766c04a2c7301f2ef592a082a9d93a to your computer and use it in GitHub Desktop.
A utility class load and compress image from Uri
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