Fine tune scaled down bitmap to exact size

Last post genarate a scaled down bitmaps, approximate but not exact size. In order to fine tune bitmap to exact size, we can re-create another bitmap from this scaled bitmap using Bitmap.createScaledBitmap() method with expected width and height.

Scale down bitmap to exact size

Modify MainActivity.java from Last post to fine tune bitmap after scaled.
package com.test.androidimageprocessing;

import java.io.FileNotFoundException;

import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.example.androidimageprocessing.R;

public class MainActivity extends Activity {

Button btnLoadImage1, btnLoadImage2;
TextView textSource1, textSource2;
Button btnProcessing;
ImageView imageResult;
Spinner spinnerMode;

final int RQS_IMAGE1 = 1;
final int RQS_IMAGE2 = 2;

Uri source1, source2;

String[] arrayModeName = { "ADD", "CLEAR", "DARKEN", "DST", "DST_ATOP",
"DST_IN", "DST_OUT", "DST_OVER", "LIGHTEN", "MULTIPLY", "OVERLAY",
"SCREEN", "SRC", "SRC_ATOP", "SRC_IN", "SRC_OUT", "SRC_OVER", "XOR" };

/*
* To use Mode.ADD and Mode.OVERLAY, android:minSdkVersion have to be set
* "11" or higher.
*/
PorterDuff.Mode[] arrayMode = { Mode.ADD, Mode.CLEAR, Mode.DARKEN,
Mode.DST, Mode.DST_ATOP, Mode.DST_IN, Mode.DST_OUT, Mode.DST_OVER,
Mode.LIGHTEN, Mode.MULTIPLY, Mode.OVERLAY, Mode.SCREEN, Mode.SRC,
Mode.SRC_ATOP, Mode.SRC_IN, Mode.SRC_OUT, Mode.SRC_OVER, Mode.XOR };

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);
btnLoadImage1 = (Button) findViewById(R.id.loadimage1);
btnLoadImage2 = (Button) findViewById(R.id.loadimage2);
textSource1 = (TextView) findViewById(R.id.sourceuri1);
textSource2 = (TextView) findViewById(R.id.sourceuri2);
btnProcessing = (Button) findViewById(R.id.processing);
imageResult = (ImageView) findViewById(R.id.result);

spinnerMode = (Spinner) findViewById(R.id.mode);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
MainActivity.this, android.R.layout.simple_spinner_item,
arrayModeName);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinnerMode.setAdapter(adapter);

btnLoadImage1.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View arg0) {
Intent intent = new Intent(
Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, RQS_IMAGE1);
}
});

btnLoadImage2.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View arg0) {
Intent intent = new Intent(
Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, RQS_IMAGE2);
}
});

btnProcessing.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {

if (source1 != null && source2 != null) {
Bitmap processedBitmap = ProcessingBitmap();
if (processedBitmap != null) {
imageResult.setImageBitmap(processedBitmap);
Toast.makeText(getApplicationContext(), "Done",
Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getApplicationContext(),
"Something wrong in processing!",
Toast.LENGTH_LONG).show();
}
} else {
Toast.makeText(getApplicationContext(),
"Select both image!", Toast.LENGTH_LONG).show();
}

}
});
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
switch (requestCode) {
case RQS_IMAGE1:
source1 = data.getData();
textSource1.setText(source1.toString());
break;
case RQS_IMAGE2:
source2 = data.getData();
textSource2.setText(source2.toString());
break;
}
}
}

private Bitmap ProcessingBitmap() {
Bitmap bm1 = null;
Bitmap bm2 = null;
Bitmap newBitmap = null;

try {
/*
* bm1 = BitmapFactory.decodeStream(getContentResolver()
* .openInputStream(source1));
* bm2 = BitmapFactory.decodeStream(getContentResolver()
* .openInputStream(source2));
*/

bm1 = loadScaledBitmap(source1);
Toast.makeText(getApplicationContext(),
"bm1: " + bm1.getWidth() + " x " + bm1.getHeight(),
Toast.LENGTH_LONG).show();

bm2 = loadScaledBitmap(source2);
Toast.makeText(getApplicationContext(),
"bm2: " + bm2.getWidth() + " x " + bm2.getHeight(),
Toast.LENGTH_LONG).show();

int w;
if (bm1.getWidth() >= bm2.getWidth()) {
w = bm1.getWidth();
} else {
w = bm2.getWidth();
}

int h;
if (bm1.getHeight() >= bm2.getHeight()) {
h = bm1.getHeight();
} else {
h = bm2.getHeight();
}

Config config = bm1.getConfig();
if (config == null) {
config = Bitmap.Config.ARGB_8888;
}

newBitmap = Bitmap.createBitmap(w, h, config);
Canvas newCanvas = new Canvas(newBitmap);

newCanvas.drawBitmap(bm1, 0, 0, null);

Paint paint = new Paint();

int selectedPos = spinnerMode.getSelectedItemPosition();
PorterDuff.Mode selectedMode = arrayMode[selectedPos];

paint.setXfermode(new PorterDuffXfermode(selectedMode));
newCanvas.drawBitmap(bm2, 0, 0, paint);

} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

return newBitmap;
}

private Bitmap loadScaledBitmap(Uri src) throws FileNotFoundException {

// required max width/height
final int REQ_WIDTH = 1024;
final int REQ_HEIGHT = 1024;

Bitmap bm = null;

// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(getContentResolver().openInputStream(src),
null, options);

// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, REQ_WIDTH,
REQ_HEIGHT);

// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
bm = BitmapFactory.decodeStream(
getContentResolver().openInputStream(src), null, options);

/*
* Up to here, bm will be scaled down, but not exactly expected size!
*
* To fine tune the size, re-create another bitmap from this scaled bitmap
* using Bitmap.createScaledBitmap() method with expected width and height.
*/
final int LONGEST_LENGTH = 1024;
int dstWidth, dstHeight;

if(options.outWidth >= options.outHeight){
//Landscape orientation
dstWidth = LONGEST_LENGTH;
dstHeight = LONGEST_LENGTH * options.outHeight/options.outWidth;
}else{
//portrait orientation
dstHeight = LONGEST_LENGTH;
dstWidth = LONGEST_LENGTH * options.outWidth/options.outHeight;
}

//To save memory, re-use bm
bm = Bitmap.createScaledBitmap(bm, dstWidth, dstHeight, false);

return bm;
}

private int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;

if (height > reqHeight || width > reqWidth) {

// Calculate ratios of height and width to requested height and
// width
final int heightRatio = Math.round((float) height
/ (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);

// Choose the smallest ratio as inSampleSize value, this will
// guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}

Toast.makeText(getApplicationContext(),
"inSampleSize: " + inSampleSize, Toast.LENGTH_LONG).show();

return inSampleSize;
}

}


download filesDownload the files.



more: Something about processing images in Android