Reputation: 125
My sever limit image size to upload image (2MB). I want to upload image from android device to server. I want to resize image. What's the best way I can do resize image?
Upvotes: 6
Views: 15170
Reputation: 660
Fix image resolution, file size.
private const val TAG = "Compressor"
private const val START_QUALITY = 100
private const val QUALITY_DECREASE_STEP = 10
private const val DEFAULT_SIZE_LIMIT = 512_000
private const val MARGIN = 30
//image resolution 1024*768
private const val LONG_SIZE = 1024
private const val SHORT_SIZE = 768
object Compressor {
private val FORMAT = Bitmap.CompressFormat.JPEG
fun compressImage(
imageFile: File,
limitSize: Int = DEFAULT_SIZE_LIMIT,
watermark: String? = null
): String {
val originalBitmap = BitmapFactory.decodeFile(imageFile.absolutePath)
val bitmapWithWaterMark = mark(originalBitmap, watermark)
val (newWidth, newHeight) = calculateNewWidthAndHeight(bitmapWithWaterMark)
val resizedBitmap = getResizedBitmap(bitmapWithWaterMark, newWidth, newHeight)
val stream = ByteArrayOutputStream()
var quality = START_QUALITY
//compressed Bitmap write to ByteArrayOutputStream
resizedBitmap.compress(FORMAT, quality, stream)
while (stream.size() > limitSize && quality > QUALITY_DECREASE_STEP) {
stream.reset()
quality -= QUALITY_DECREASE_STEP
resizedBitmap.compress(FORMAT, quality, stream)
}
val base64String = Base64.encodeToString(stream.toByteArray(), Base64.DEFAULT)
return base64String
}
private fun calculateNewWidthAndHeight(waterMarkBitmap: Bitmap): Pair<Int, Int> {
val newWidth = if (isPortrait(waterMarkBitmap)) SHORT_SIZE else LONG_SIZE
val newHeight = if (isPortrait(waterMarkBitmap)) LONG_SIZE else SHORT_SIZE
return Pair(newWidth, newHeight)
}
private fun isPortrait(bitmap: Bitmap) = bitmap.height > bitmap.width
private fun mark(source: Bitmap, watermark: String? = null): Bitmap {
if (watermark == null) return source
val width = source.width
val height = source.height
val result = Bitmap.createBitmap(width, height, source.config)
val canvas = Canvas(result)
canvas.drawBitmap(source, 0f, 0f, null)
val paint = getPaint(18)
val (widthPaint, heightPaint) = paint.getTextWidthAndHeight(watermark)
//watermark background
val backgroundPaint = getBackgroundPaint()
canvas.drawRect(
width - widthPaint - MARGIN,
height - heightPaint * 2,
width.toFloat(),
height.toFloat() - heightPaint + MARGIN,
backgroundPaint
)
//watermark text
canvas.drawText(
watermark,
width - widthPaint,
height - heightPaint,
paint
)
return result
}
private fun getBackgroundPaint(): Paint {
return Paint().apply {
style = Paint.Style.FILL
color = Color.BLACK
}
}
private fun dpToPx(dp: Int): Float {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
dp.toFloat(),
App.resources.displayMetrics
)
}
private fun getPaint(textSize: Int, isShadowEnable: Boolean = false): Paint {
return Paint(Paint.ANTI_ALIAS_FLAG).apply {
setTextSize(dpToPx(textSize))
typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
if (isShadowEnable) {
setShadowLayer(2f, 2f, 2f, Color.BLACK)
}
color = Color.WHITE
textAlign = Paint.Align.LEFT
}
}
private fun Paint.getTextWidthAndHeight(text: String): Pair<Float, Float> {
val baseline = -this.ascent() // ascent() is negative
val width: Float = this.measureText(text) + dpToPx(8)
val height: Float = baseline + this.descent() + dpToPx(4)
return Pair(width, height)
}
private fun getResizedBitmap(bitmap: Bitmap, newWidth: Int, newHeight: Int): Bitmap {
val width = bitmap.width
val height = bitmap.height
val scaleWidth = newWidth.toFloat() / width
val scaleHeight = newHeight.toFloat() / height
// CREATE A MATRIX FOR THE MANIPULATION
val matrix = Matrix()
// RESIZE THE BIT MAP
matrix.postScale(scaleWidth, scaleHeight)
// "RECREATE" THE NEW BITMAP
val resizedBitmap = Bitmap.createBitmap(
bitmap, 0, 0, width, height, matrix, false
)
bitmap.recycle()
return resizedBitmap
}
}
Upvotes: 0
Reputation: 1
You can also do this after encoding your image to string then squeeze the bytes our of it then calculate the length you want before saving the image. in this case, if it's big the user will be forced to change the image. I assumed you have already encoded your image.
long myBitmapLength = myEncodedImage.getBytes().length/1024;
if(myBitmapLength < 200){
//allow saving
}
Upvotes: 0
Reputation: 2663
Use the code below. With "MAX_IMAGE_SIZE" specify your max file size in kilobayts. In this code; first i resize the image, then i compress it. See comments in the code to understand the logic better.
public static String resizeAndCompressImageBeforeSend(Context context,String filePath,String fileName){
final int MAX_IMAGE_SIZE = 700 * 1024; // max final file size in kilobytes
// First decode with inJustDecodeBounds=true to check dimensions of image
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath,options);
// Calculate inSampleSize(First we are going to resize the image to 800x800 image, in order to not have a big but very low quality image.
//resizing the image will already reduce the file size, but after resizing we will check the file size and start to compress image
options.inSampleSize = calculateInSampleSize(options, 800, 800);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
options.inPreferredConfig= Bitmap.Config.ARGB_8888;
Bitmap bmpPic = BitmapFactory.decodeFile(filePath,options);
int compressQuality = 100; // quality decreasing by 5 every loop.
int streamLength;
do{
ByteArrayOutputStream bmpStream = new ByteArrayOutputStream();
Log.d("compressBitmap", "Quality: " + compressQuality);
bmpPic.compress(Bitmap.CompressFormat.JPEG, compressQuality, bmpStream);
byte[] bmpPicByteArray = bmpStream.toByteArray();
streamLength = bmpPicByteArray.length;
compressQuality -= 5;
Log.d("compressBitmap", "Size: " + streamLength/1024+" kb");
}while (streamLength >= MAX_IMAGE_SIZE);
try {
//save the resized and compressed file to disk cache
Log.d("compressBitmap","cacheDir: "+context.getCacheDir());
FileOutputStream bmpFile = new FileOutputStream(context.getCacheDir()+fileName);
bmpPic.compress(Bitmap.CompressFormat.JPEG, compressQuality, bmpFile);
bmpFile.flush();
bmpFile.close();
} catch (Exception e) {
Log.e("compressBitmap", "Error on saving file");
}
//return the path of resized and compressed file
return context.getCacheDir()+fileName;
}
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
String debugTag = "MemoryInformation";
// Image nin islenmeden onceki genislik ve yuksekligi
final int height = options.outHeight;
final int width = options.outWidth;
Log.d(debugTag,"image height: "+height+ "---image width: "+ width);
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int 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;
}
}
Log.d(debugTag,"inSampleSize: "+inSampleSize);
return inSampleSize;
}
Upvotes: 14