I have a photo editing app. All it does is add images onto a base photo. The output photo is lower quality than the original photo which is taken by the camera. This is expected, but maybe I can improve on what I currently have. It's the same quality at 500px as 1000px which is very concerning... I can see I'm limiting quality somewhere other than pixels. The original photos in my camera gallery are JPG files. Here is everything I do to get the photo, create the bitmap from it, and then save it. Can you tell me where in the code the photo quality may get lower?
open gallery intent:
Intent photoPickerIntent = new Intent(Intent.ACTION_GET_CONTENT);
photoPickerIntent.setType("image/*");
startActivityForResult(photoPickerIntent, 1);
OnActivityResult() for the chosen photo:
if (intent != null && resultcode == RESULT_OK)
{
mProfilePicPath = ih.getSelectedImageFilePathFromGallery(this.getApplicationContext(), intent);
mPortraitPhoto = ih.decodeSampledBitmapFromImagePath(mProfilePicPath,
GlobalConstants.PROFILE_PICTURE_RESOLUTION,
GlobalConstants.PROFILE_PICTURE_RESOLUTION);
}
public String getSelectedImageFilePathFromGallery(Context ctx,
Intent intent) {
Uri selectedImage = intent.getData();
String[] filePathColumn = {MediaStore.Images.Media.DATA};
Cursor cursor = ctx.getContentResolver().query(selectedImage, filePathColumn, null, null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
String filePath = cursor.getString(columnIndex);
cursor.close();
return filePath;
}
public Bitmap decodeSampledBitmapFromImagePath(String imagePath, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imagePath, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
Log.d("Image dims", imageType + ", " + imageHeight + ", " + imageWidth + "size.");
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
Bitmap portraitPhoto = ImageHelper.convertToPortraitOrientation(options, imagePath);
return portraitPhoto;
}
public static 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) {
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;
}
}
return inSampleSize;
}
public static Bitmap convertToPortraitOrientation(BitmapFactory.Options options, String path) {
Uri actualUri = Uri.parse(path);
float degree = 0;
Bitmap bmp = BitmapFactory.decodeFile(path, options);
try {
ExifInterface exif = new ExifInterface(actualUri.getPath());
String exifOrientation = exif
.getAttribute(ExifInterface.TAG_ORIENTATION);
if (bmp != null) {
degree = getDegree(exifOrientation);
if (degree != 0)
bmp = createRotatedBitmap(bmp, degree);
}
}
catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return bmp;
}
public static Bitmap createRotatedBitmap(Bitmap bm, float degree) {
Bitmap bitmap = null;
if (degree != 0) {
Matrix matrix = new Matrix();
matrix.preRotate(degree);
bitmap = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(),
bm.getHeight(), matrix, true);
}
return bitmap;
}
public File createProfilePicSaveFileInternal(Context ctx) {
//mBackgroundImage.setImageBitmap(ih.decodeSampledBitmapFromImagePath(mProfilePicPath, 500, 500));
String path = ctx.getFilesDir() + File.separator + "My Folder";
File outputDir= new File(path);
outputDir.mkdirs();
File newFile = new File(path + "/" + mName + ".jpg");
return newFile;
}
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
public void saveImage( Bitmap bitmap, Context ctx, File newFile) {
FileOutputStream fos;
newFile.setReadable(true, false);
try {
fos = new FileOutputStream(newFile);
bitmap.compress(CompressFormat.JPEG, 100, fos);
fos.flush();
fos.close();
} catch (FileNotFoundException e) {
Log.e("GREC", e.getMessage(), e);
} catch (IOException e) {
Log.e("GREC", e.getMessage(), e);
}
ctx.sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"+ Environment.getExternalStorageDirectory())));
}
when a button is clicked:
File newInternalFile = ih.createProfilePicSaveFileInternal(this.getApplicationContext());
boolean s = newInternalFile.exists();
long length = newInternalFile.length();
ih.saveImage(mPortraitPhoto, this.getApplicationContext(), newInternalFile);
mPortraitPhoto = null;
public File createProfilePicSaveFileInternal(Context ctx) {
//mBackgroundImage.setImageBitmap(ih.decodeSampledBitmapFromImagePath(mProfilePicPath, 500, 500));
String path = ctx.getFilesDir() + File.separator + "My Folder";
File outputDir= new File(path);
outputDir.mkdirs();
File newFile = new File(path + "/" + mName + ".jpg");
return newFile;
}
After my image is first saved, I then get that saved image in it's true form by doing this (inSampleSize is now 1):
public Bitmap getPortraitBitmapNotSampled(String imagePath){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imagePath, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
Log.d("Dressing room photo dims", imageType + ", " + imageHeight + ", " + imageWidth + "size.");
// Calculate inSampleSize
options.inSampleSize = 1;
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
Bitmap portraitPhoto = ImageHelper.convertToPortraitOrientation(options, imagePath);
return portraitPhoto;
}
I end up taking a screen shot to get the output image like this:
public Bitmap takeScreenshot() {
View v = findViewById(R.id.DressBig);
v.setDrawingCacheEnabled(true);
return v.getDrawingCache();
}
Then the final save:
private void saveClicked(){
finishedOutfit = takeScreenshot();
File newFile = ih.createOutfitSaveFileExternal();
File newInternalFile = ih.createOutfitSaveFileInternal(this.getApplicationContext());
ih.saveImage(finishedOutfit, this.getApplicationContext(), newFile);
ih.saveImage(finishedOutfit, this.getApplicationContext(), newInternalFile);
String newFilePath = newInternalFile.toString();
String newExternalFilePath = newFile.toString();
Log.d("db file path: ", newFilePath);
Log.d("external file path: ", newExternalFilePath);
insertOutfitInDB(newFilePath, newExternalFilePath);
showImageSavedDialog();
}