August 13, 2021

Compress image in android programmatically

By lj007

In this time phone camera comes with high resolution, so it takes more memory. Normally an image size exceed 2MB. This is very high for slow network. Downloading and uploading this image takes too much time, so it requires to be compressed. So that we have following class to compress image.

Just initialise class and call copyFileStream method, you will get path for compressed file. 

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Matrix
import android.graphics.Paint
import android.net.Uri
import android.os.Environment
import java.io.*

/**
 * Created by lalit on 21/8/17.
 * compress image
 */

class ImageFile(val uri: Uri, name: String) {

    val filename: String

    init {
        val file = File(Environment.getExternalStorageDirectory().toString() + "/Documents")
        if (!file.exists()) {
            file.mkdirs()
        }
        val fileNoMedia = File(file.absolutePath + "/.nomedia")
        if (!fileNoMedia.exists())
            fileNoMedia.createNewFile()
        if (name.toLowerCase().endsWith(".pdf")) {
            filename = file.absolutePath + "/" + System.currentTimeMillis() + ".pdf"
        } else {
            filename = file.absolutePath + "/" + System.currentTimeMillis() + ".jpg"
        }
    }

    @Throws(IOException::class)
    fun copyFileStream(context: Context, uri: Uri): String {
        if (filename.endsWith(".pdf") || filename.endsWith(".PDF")) {
            var ins: InputStream? = null
            var os: OutputStream? = null
            try {
                ins = context.getContentResolver().openInputStream(uri)
                os = FileOutputStream(filename)
                val buffer = ByteArray(1024)
                var length: Int = ins.read(buffer)
                while (length > 0) {
                    os.write(buffer, 0, length);
                    length = ins.read(buffer)
                }
            } catch (e: Exception) {
                e.printStackTrace();
            } finally {
                ins?.close()
                os?.close()
            }
        } else {
            var ins: InputStream? = null
            var os: OutputStream? = null
            try {
                ins = context.getContentResolver().openInputStream(uri)
                var scaledBitmap: Bitmap? = null
                val options = BitmapFactory.Options()
                options.inJustDecodeBounds = true
                var bmp = BitmapFactory.decodeStream(ins, null, options)
                var actualHeight = options.outHeight
                var actualWidth = options.outWidth

                //      max Height and width values of the compressed image is taken as 816x612
                val maxHeight = 816.0f
                val maxWidth = 612.0f
                var imgRatio = (actualWidth / actualHeight).toFloat()
                val maxRatio = maxWidth / maxHeight

                //      width and height values are set maintaining the aspect ratio of the image
                if (actualHeight > maxHeight || actualWidth > maxWidth) {
                    if (imgRatio < maxRatio) {
                        imgRatio = maxHeight / actualHeight
                        actualWidth = (imgRatio * actualWidth).toInt()
                        actualHeight = maxHeight.toInt()
                    } else if (imgRatio > maxRatio) {
                        imgRatio = maxWidth / actualWidth
                        actualHeight = (imgRatio * actualHeight).toInt()
                        actualWidth = maxWidth.toInt()
                    } else {
                        actualHeight = maxHeight.toInt()
                        actualWidth = maxWidth.toInt()

                    }
                }

                //      setting inSampleSize value allows to load a scaled down version of the original image
                options.inSampleSize = calculateInSampleSize(options, actualWidth, actualHeight)

                //      inJustDecodeBounds set to false to load the actual bitmap
                options.inJustDecodeBounds = false

                //      this options allow android to claim the bitmap memory if it runs low on memory
                options.inPurgeable = true
                options.inInputShareable = true
                options.inTempStorage = ByteArray(16 * 1024)


                try {
                    //          load the bitmap from its path
                    ins.close()
                    ins = context.getContentResolver().openInputStream(uri)
                    bmp = BitmapFactory.decodeStream(ins, null, options)
                } catch (exception: OutOfMemoryError) {
                    exception.printStackTrace()

                }

                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.matrix = scaleMatrix
                canvas.drawBitmap(bmp, middleX - bmp.width / 2, middleY - bmp.height / 2, Paint(Paint.FILTER_BITMAP_FLAG))

                os = FileOutputStream(filename)
                scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 80, os)
                val buffer = ByteArray(1024)
                var length: Int = ins.read(buffer)
                while (length > 0) {
                    os.write(buffer, 0, length);
                    length = ins.read(buffer)
                }
            } catch (e: Exception) {
                e.printStackTrace();
            } finally {
                ins?.close()
                os?.close()
            }
        }
        return filename
    }

    fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
        val height = options.outHeight
        val width = options.outWidth
        var inSampleSize = 1
        if (height > reqHeight || width > reqWidth) {
            val heightRatio = Math.round(height.toFloat() / reqHeight.toFloat())
            val widthRatio = Math.round(width.toFloat() / reqWidth.toFloat())
            inSampleSize = if (heightRatio < widthRatio) heightRatio else widthRatio
        }
        val totalPixels = (width * height).toFloat()
        val totalReqPixelsCap = (reqWidth * reqHeight * 2).toFloat()
        while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
            inSampleSize++
        }

        return inSampleSize
    }
}