yup, you can compress your image file by the following function
@Throws(IOException::class)
fun File.compressImage(
reqWidth: Float,
reqHeight: Float,
compressFormat: Bitmap.CompressFormat,
quality: Int,
destinationPath: String
): File {
var fileOutputStream: FileOutputStream? = null
val file = File(destinationPath).parentFile ?: return File(destinationPath)
if (!file.exists()) {
file.mkdirs()
}
try {
fileOutputStream = FileOutputStream(destinationPath)
// write the compressed bitmap at the destination specified by destinationPath.
decodeSampledBitmapFromFile(this, reqWidth, reqHeight)?.compress(
compressFormat,
quality,
fileOutputStream
)
} finally {
if (fileOutputStream != null) {
fileOutputStream.flush()
fileOutputStream.close()
}
}
return File(destinationPath)
}
@Throws(IOException::class)
private fun decodeSampledBitmapFromFile(
imageFile: File,
reqWidth: Float,
reqHeight: Float
): Bitmap? {
// First decode with inJustDecodeBounds=true to check dimensions
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
var bmp: Bitmap? = BitmapFactory.decodeFile(imageFile.absolutePath, options)
var actualHeight = options.outHeight
var actualWidth = options.outWidth
var imgRatio = actualWidth.toFloat() / actualHeight.toFloat()
val maxRatio = reqWidth / reqHeight
if (actualHeight > reqHeight || actualWidth > reqWidth) {
// If Height is greater
when {
imgRatio < maxRatio -> {
imgRatio = reqHeight / actualHeight
actualWidth = (imgRatio * actualWidth).toInt()
actualHeight = reqHeight.toInt()
} // If Width is greater
imgRatio > maxRatio -> {
imgRatio = reqWidth / actualWidth
actualHeight = (imgRatio * actualHeight).toInt()
actualWidth = reqWidth.toInt()
}
else -> {
actualHeight = reqHeight.toInt()
actualWidth = reqWidth.toInt()
}
}
}
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, actualWidth, actualHeight)
options.inJustDecodeBounds = false
if (bmp != null && canUseForInBitmap(bmp, options)) {
// inBitmap only works with mutable bitmaps, so force the decoder to
// return mutable bitmaps.
options.inMutable = true
options.inBitmap = bmp
}
options.inTempStorage = ByteArray(16 * 1024)
try {
bmp = BitmapFactory.decodeFile(imageFile.absolutePath, options)
} catch (exception: OutOfMemoryError) {
exception.printStackTrace()
}
var scaledBitmap: Bitmap? = null
try {
scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.ARGB_8888)
} catch (exception: OutOfMemoryError) {
exception.printStackTrace()
}
val ratioX = actualWidth / options.outWidth.toFloat()
val ratioY = actualHeight / options.outHeight.toFloat()
val middleX = actualWidth / 2.0f
val middleY = actualHeight / 2.0f
val scaleMatrix = Matrix()
scaleMatrix.setScale(ratioX, ratioY, middleX, middleY)
val canvas = Canvas(scaledBitmap!!)
canvas.setMatrix(scaleMatrix)
canvas.drawBitmap(
bmp!!, middleX - bmp.width / 2,
middleY - bmp.height / 2, Paint(Paint.FILTER_BITMAP_FLAG)
)
bmp.recycle()
val exif: ExifInterface
try {
exif = ExifInterface(imageFile.absolutePath)
val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0)
val matrix = Matrix()
if (orientation == 6) {
matrix.postRotate(90f)
} else if (orientation == 3) {
matrix.postRotate(180f)
} else if (orientation == 8) {
matrix.postRotate(270f)
}
scaledBitmap = Bitmap.createBitmap(
scaledBitmap, 0, 0, scaledBitmap.width,
scaledBitmap.height, matrix, true
)
} catch (e: IOException) {
e.printStackTrace()
}
return scaledBitmap
}
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) {
inSampleSize *= 2
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
}
/**
* Ref: https://developer.android.com/topic/performance/graphics/manage-memory#kotlin
*/
private fun canUseForInBitmap(candidate: Bitmap, targetOptions: BitmapFactory.Options): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// From Android 4.4 (KitKat) onward we can re-use if the byte size of
// the new bitmap is smaller than the reusable bitmap candidate
// allocation byte count.
val width: Int = targetOptions.outWidth / targetOptions.inSampleSize
val height: Int = targetOptions.outHeight / targetOptions.inSampleSize
val byteCount: Int = width * height * getBytesPerPixel(candidate.config)
byteCount <= candidate.allocationByteCount
} else {
// On earlier versions, the dimensions must match exactly and the inSampleSize must be 1
candidate.width == targetOptions.outWidth &&
candidate.height == targetOptions.outHeight &&
targetOptions.inSampleSize == 1
}
}
/**
* A helper function to return the byte usage per pixel of a bitmap based on its configuration.
*/
private fun getBytesPerPixel(config: Bitmap.Config): Int {
return when (config) {
Bitmap.Config.ARGB_8888 -> 4
Bitmap.Config.RGB_565, Bitmap.Config.ARGB_4444 -> 2
Bitmap.Config.ALPHA_8 -> 1
else -> 1
}
}
@Throws(IOException::class)
fun Context.createImageFile(): File {
// Create an image file name
val storageDir: File? = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
return File.createTempFile(
"JPEG_${System.currentTimeMillis()}_", /* prefix */
".jpg", /* suffix */
storageDir /* directory */
)
}
and simply call the following method to create compressed image file. here file is you image file
file.compressImage(
1024f,
1024f,
Bitmap.CompressFormat.JPEG,
70,
context.createImageFile().path
)