Skip to content

Instantly share code, notes, and snippets.

@vipulasri
Created December 19, 2015 06:01
Show Gist options
  • Save vipulasri/0cd97d012934531f1266 to your computer and use it in GitHub Desktop.
Save vipulasri/0cd97d012934531f1266 to your computer and use it in GitHub Desktop.
Whatsapp Like Image Compression
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.media.ExifInterface;
import android.os.AsyncTask;
import android.os.Environment;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* Created by HP-HP on 03-07-2015.
*/
public class ImageCompression extends AsyncTask<String, Void, String> {
private Context context;
private static final float maxHeight = 1280.0f;
private static final float maxWidth = 1280.0f;
public ImageCompression(Context context){
this.context=context;
}
@Override
protected String doInBackground(String... strings) {
if(strings.length == 0 || strings[0] == null)
return null;
return compressImage(strings[0]);
}
protected void onPostExecute(String imagePath){
// imagePath is path of new compressed image.
}
public String compressImage(String imagePath) {
Bitmap scaledBitmap = null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap bmp = BitmapFactory.decodeFile(imagePath, options);
int actualHeight = options.outHeight;
int actualWidth = options.outWidth;
float imgRatio = (float) actualWidth / (float) actualHeight;
float maxRatio = maxWidth / maxHeight;
if (actualHeight > maxHeight || actualWidth > maxWidth) {
if (imgRatio < maxRatio) {
imgRatio = maxHeight / actualHeight;
actualWidth = (int) (imgRatio * actualWidth);
actualHeight = (int) maxHeight;
} else if (imgRatio > maxRatio) {
imgRatio = maxWidth / actualWidth;
actualHeight = (int) (imgRatio * actualHeight);
actualWidth = (int) maxWidth;
} else {
actualHeight = (int) maxHeight;
actualWidth = (int) maxWidth;
}
}
options.inSampleSize = calculateInSampleSize(options, actualWidth, actualHeight);
options.inJustDecodeBounds = false;
options.inDither = false;
options.inPurgeable = true;
options.inInputShareable = true;
options.inTempStorage = new byte[16 * 1024];
try {
bmp = BitmapFactory.decodeFile(imagePath, options);
} catch (OutOfMemoryError exception) {
exception.printStackTrace();
}
try {
scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.RGB_565);
} catch (OutOfMemoryError exception) {
exception.printStackTrace();
}
float ratioX = actualWidth / (float) options.outWidth;
float ratioY = actualHeight / (float) options.outHeight;
float middleX = actualWidth / 2.0f;
float middleY = actualHeight / 2.0f;
Matrix scaleMatrix = new Matrix();
scaleMatrix.setScale(ratioX, ratioY, middleX, middleY);
Canvas canvas = new Canvas(scaledBitmap);
canvas.setMatrix(scaleMatrix);
canvas.drawBitmap(bmp, middleX - bmp.getWidth() / 2, middleY - bmp.getHeight() / 2, new Paint(Paint.FILTER_BITMAP_FLAG));
if(bmp!=null)
{
bmp.recycle();
}
ExifInterface exif;
try {
exif = new ExifInterface(imagePath);
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0);
Matrix matrix = new Matrix();
if (orientation == 6) {
matrix.postRotate(90);
} else if (orientation == 3) {
matrix.postRotate(180);
} else if (orientation == 8) {
matrix.postRotate(270);
}
scaledBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true);
} catch (IOException e) {
e.printStackTrace();
}
FileOutputStream out = null;
String filepath = getFilename();
try {
out = new FileOutputStream(filepath);
//write the compressed bitmap at the destination specified by filename.
scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 80, out);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return filepath;
}
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
final float totalPixels = width * height;
final float totalReqPixelsCap = reqWidth * reqHeight * 2;
while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
inSampleSize++;
}
return inSampleSize;
}
public String getFilename() {
File mediaStorageDir = new File(Environment.getExternalStorageDirectory()
+ "/Android/data/"
+ context.getApplicationContext().getPackageName()
+ "/Files/Compressed");
// Create the storage directory if it does not exist
if (! mediaStorageDir.exists()){
mediaStorageDir.mkdirs();
}
String mImageName="IMG_"+ String.valueOf(System.currentTimeMillis()) +".jpg";
String uriString = (mediaStorageDir.getAbsolutePath() + "/"+ mImageName);;
return uriString;
}
}
@LFRCORP
Copy link

LFRCORP commented Mar 6, 2017

Cool!

@guffyWave
Copy link

Thanks a lot bro 👍

@Deeptimay
Copy link

Hi,
It works great. Thanks a lot.
But if I want to compress the image size according to me. Then which field should I change.

@Ambarsetya
Copy link

Hi,
what method is used in whastaup like ?? is lossy or lossless

@mwshubham
Copy link

mwshubham commented Apr 6, 2018

Kotlin implementation if anyone interested...

fun compressImage(context: Context, imagePath: String): String? {
        var scaledBitmap: Bitmap?

        val options = BitmapFactory.Options()
        options.inJustDecodeBounds = true
        @Suppress("VARIABLE_WITH_REDUNDANT_INITIALIZER")
        var bmp: Bitmap? = BitmapFactory.decodeFile(imagePath, options)

        var actualHeight = options.outHeight
        var actualWidth = options.outWidth

        var imgRatio = actualWidth.toFloat() / actualHeight.toFloat()
        val maxRatio = maxWidth / maxHeight

        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()

            }
        }

        options.inSampleSize = calculateInSampleSize(options, actualWidth, actualHeight)
        options.inJustDecodeBounds = false
        options.inDither = false
        options.inPurgeable = true
        options.inInputShareable = true
        options.inTempStorage = ByteArray(16 * 1024)

        try {
            bmp = BitmapFactory.decodeFile(imagePath, options)
        } catch (exception: OutOfMemoryError) {
            exception.printStackTrace()
            return null
        }

        try {
            scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.RGB_565)
        } catch (exception: OutOfMemoryError) {
            exception.printStackTrace()
            return null
        }

        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(FILTER_BITMAP_FLAG))

        bmp.recycle()

        val exif: ExifInterface
        try {
            exif = ExifInterface(imagePath)
            val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)
            val matrix = Matrix()
            when (orientation) {
                ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90f)
                ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180f)
                ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270f)
            }
            scaledBitmap = Bitmap.createBitmap(scaledBitmap!!, 0, 0, scaledBitmap.width, scaledBitmap.height, matrix, true)
        } catch (e: IOException) {
            e.printStackTrace()
        }

        val out: FileOutputStream?
        val filepath = getFilename(context)
        try {
            out = FileOutputStream(filepath)
            //write the compressed bitmap at the destination specified by filename.
            scaledBitmap!!.compress(Bitmap.CompressFormat.JPEG, 80, out)

        } catch (e: FileNotFoundException) {
            e.printStackTrace()
        }

        return filepath
    }

    private 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
    }

    private fun getFilename(context: Context): String {
        val mediaStorageDir = File("${Environment.getExternalStorageDirectory()}/Android/data/${context.applicationContext.packageName}/Files/Compressed")
        // Create the storage directory if it does not exist
        if (!mediaStorageDir.exists()) {
            mediaStorageDir.mkdirs()
        }

        val mImageName = "IMG_" + System.currentTimeMillis().toString() + ".jpg"
        return mediaStorageDir.getAbsolutePath() + "/" + mImageName
    }

@narsiram
Copy link

narsiram commented May 3, 2018

scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.RGB_565) at this line , i am getting exception that actual width and height is greater than 0

@Soorya696
Copy link

I too have the same issues!

@pratikpatel09
Copy link

scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.RGB_565) at this line , i am getting exception that actual width and height is greater than 0

Please give me a solution of this exception!!!

@AbdulrahmanKassem
Copy link

scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.RGB_565) at this line , i am getting exception that actual width and height is greater than 0

Please give me a solution of this exception @vipulasri

@j2mistry
Copy link

scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.RGB_565) at this line , i am getting exception that actual width and height is greater than 0

Please give me a solution of this exception!!!

Add Try Catch block around the bitmap creation

@RakeshPatil111
Copy link

Thank you for your solution. I used this in my app but I want to increase the quality of image.
Where should I make the change ?

@j2mistry
Copy link

scaledBitmap!!.compress(Bitmap.CompressFormat.JPEG, 80, out)
make above code 100% to achieve similar quality.
Refer RGB manpulation algo, to increase the quality of code

@RakeshPatil111
Copy link

RakeshPatil111 commented Jan 21, 2020 via email

@gropaul
Copy link

gropaul commented Apr 29, 2020

That´s awesome, great work!

@developervineetjoshi
Copy link

sir i passed the image path directly to the class by writing :

new ImageCompression(context).execute("content://com.android.providers.media.documents/document/image%3A20702");

but its showing error

can you please help me out with this

@prashantDahat1234
Copy link

prashantDahat1234 commented Jun 16, 2020

Caused by: java.lang.IllegalArgumentException: width and height must be > 0. in "scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.RGB_565) "
please help me...

@ajinkya5130
Copy link

Work like Pro...Thank you.

@Prabha10cs
Copy link

inPurgeable and inInputShareable deprecated

any solution

@ABHINAVABHISHEK
Copy link

I am getting error java.lang.IllegalArgumentException: width and height must be > 0

@ravikant2712
Copy link

I am getting error java.lang.IllegalArgumentException: width and height must be > 0

SAME HERE

@AbdullahJaved-dev
Copy link

I am getting error java.lang.IllegalArgumentException: width and height must be > 0

pass actual path of image instead of content Uri to avoid this exception.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment