Skip to content

Commit b46aa27

Browse files
craleycraley
craley
authored and
craley
committed
Using createScaledBitmap, using a scaled matrix and calling createBitmap, and other methods involving scale always caused bad looking images. Using inSampleSize while decoding resulted in much better looking images. The only downside to using it is that you cannot get an exact dimension. This is because isSampleSize must be a power of 2. So if you aren't picky about the exact dimension of your final scaled image and want better quality, you can use withScaleMethod and specify BETTER_QUALITY_BEST_FIT. The default uses the traditional method.
1 parent 2db9b6e commit b46aa27

File tree

2 files changed

+51
-6
lines changed

2 files changed

+51
-6
lines changed

lib/src/main/java/com/soundcloud/android/crop/Crop.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,19 @@ interface Extra {
2626
String MAX_X = "max_x";
2727
String MAX_Y = "max_y";
2828
String ERROR = "error";
29+
String SCALE_METHOD = "scale_method";
30+
}
31+
32+
public enum ScaleMethod {
33+
/**
34+
* Using the exact dimensions provided and using a scaling function for creating a scale bitmap. When scaling a large amount, the quality can suffer.
35+
*/
36+
EXACT,
37+
/**
38+
* This approach uses sampling while decoding the image which provides better results; however, this requires the image be scaled by powers of 2 and cannot provide
39+
* dimensions exactly requested. This will result in better quality images when the scale amount is large.
40+
*/
41+
BETTER_QUALITY_BEST_FIT;
2942
}
3043

3144
private Intent cropIntent;
@@ -44,6 +57,7 @@ private Crop(Uri source, Uri destination) {
4457
cropIntent = new Intent();
4558
cropIntent.setData(source);
4659
cropIntent.putExtra(MediaStore.EXTRA_OUTPUT, destination);
60+
cropIntent.putExtra(Extra.SCALE_METHOD, ScaleMethod.EXACT.ordinal());
4761
}
4862

4963
/**
@@ -79,6 +93,16 @@ public Crop withMaxSize(int width, int height) {
7993
return this;
8094
}
8195

96+
/**
97+
* Set how the image will be scaled when providing {@link Extra#MAX_X} and {@link Extra#MAX_Y} with the {@link #withMaxSize(int, int)}
98+
* @param type
99+
* @return
100+
*/
101+
public Crop withScaleMethod(ScaleMethod type) {
102+
cropIntent.putExtra(Extra.SCALE_METHOD, type.ordinal());
103+
return this;
104+
}
105+
82106
/**
83107
* Send the crop Intent from an Activity
84108
*

lib/src/main/java/com/soundcloud/android/crop/CropImageActivity.java

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
import android.graphics.Bitmap;
2222
import android.graphics.BitmapFactory;
2323
import android.graphics.BitmapRegionDecoder;
24+
import android.graphics.Canvas;
2425
import android.graphics.Matrix;
26+
import android.graphics.Paint;
2527
import android.graphics.Rect;
2628
import android.graphics.RectF;
2729
import android.net.Uri;
@@ -56,6 +58,7 @@ public class CropImageActivity extends MonitoredActivity {
5658
private int maxX;
5759
private int maxY;
5860
private int exifRotation;
61+
private Crop.ScaleMethod scaleMethod;
5962

6063
private Uri sourceUri;
6164
private Uri saveUri;
@@ -126,6 +129,7 @@ private void loadInput() {
126129
maxX = extras.getInt(Crop.Extra.MAX_X);
127130
maxY = extras.getInt(Crop.Extra.MAX_Y);
128131
saveUri = extras.getParcelable(MediaStore.EXTRA_OUTPUT);
132+
scaleMethod = Crop.ScaleMethod.values()[extras.getInt(Crop.Extra.SCALE_METHOD, 0)];
129133
}
130134

131135
sourceUri = intent.getData();
@@ -343,11 +347,29 @@ private Bitmap decodeRegionCrop(Rect rect, int outWidth, int outHeight) {
343347
}
344348

345349
try {
346-
croppedImage = decoder.decodeRegion(rect, new BitmapFactory.Options());
347-
if (croppedImage != null && (rect.width() > outWidth || rect.height() > outHeight)) {
348-
Matrix matrix = new Matrix();
349-
matrix.postScale((float) outWidth / rect.width(), (float) outHeight / rect.height());
350-
croppedImage = Bitmap.createBitmap(croppedImage, 0, 0, croppedImage.getWidth(), croppedImage.getHeight(), matrix, true);
350+
BitmapFactory.Options options = new BitmapFactory.Options();
351+
if ((rect.width() > outWidth || rect.height() > outHeight)) {
352+
switch (scaleMethod) {
353+
case EXACT:
354+
croppedImage = decoder.decodeRegion(rect, options);
355+
Matrix matrix = new Matrix();
356+
matrix.postScale((float) outWidth / rect.width(), (float) outHeight / rect.height());
357+
croppedImage = Bitmap.createBitmap(croppedImage, 0, 0, croppedImage.getWidth(), croppedImage.getHeight(), matrix, true);
358+
break;
359+
case BETTER_QUALITY_BEST_FIT:
360+
int w, h;
361+
int inSampleSize = 1;
362+
do {
363+
inSampleSize *= 2;
364+
w = rect.width() / inSampleSize;
365+
h = rect.height() / inSampleSize;
366+
} while(w > outWidth && h > outHeight);
367+
options.inSampleSize = inSampleSize;
368+
croppedImage = decoder.decodeRegion(rect, options);
369+
break;
370+
}
371+
} else {
372+
croppedImage = decoder.decodeRegion(rect, options);
351373
}
352374
} catch (IllegalArgumentException e) {
353375
// Rethrow with some extra information
@@ -433,5 +455,4 @@ private void setResultUri(Uri uri) {
433455
private void setResultException(Throwable throwable) {
434456
setResult(Crop.RESULT_ERROR, new Intent().putExtra(Crop.Extra.ERROR, throwable));
435457
}
436-
437458
}

0 commit comments

Comments
 (0)