在很多App中都有上传背景图功能,需用BitmapFactory读取用户本地相册图片,缩放平移裁剪以后上传到服务端。一般App为了防止OOM,都会限制最大长度或最大像素,如Lofter之前在Android3.0以上限制最大4MB,超过则sample,相信90%以上的App都是这么做的。这种方式尴尬的地方在于,Android的sample只能是2的幂次,若图片为4.1MB,就会被sample到2.05MB。这样有可能用户传了一张高清的图片反而变模糊。对于普通图片社交应用而言,这种方式也完全够用,也可以通过RunTime.maxMemory来获取当前可分配的最大内存,在高端手机上可以加大单张图片的分配上限。设置largeHeap=true可以增加app内存上限,但上限值依赖于system/build.prop文件的设置。
Bitmap outBmp = Bitmap.createBitmap(editWidth, editHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(outBmp);
PaintFlagsDrawFilter pfd = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
canvas.setDrawFilter(pfd);
Bitmap oriBmp = ((BitmapDrawable) mBitmapDisplayed.getBitmap()).getBitmap();
Matrix oriMatrix = new Matrix();
float[] matrixValues = new float[9];
Matrix displayMatrix = getImageViewMatrix();
displayMatrix.getValues(matrixValues);
float transX = matrixValues[Matrix.MTRANS_X];
float transY = matrixValues[Matrix.MTRANS_Y];
float osx = editWidth * 1f / getWidth();
float osy = editHeight * 1f / getHeight();
oriMatrix.postScale(osx, osy);
oriMatrix.postConcat(displayMatrix);
oriMatrix.postTranslate(-transX, -transY);
oriMatrix.postTranslate(transX * osx, transY * osy);
canvas.drawBitmap(oriBmp, oriMatrix, null);
return outBmp;
public static Bitmap decodeRegionInternal(String operFilepath, int orientation,
Rect originRegion, Matrix originMatrix,
Rect cropRegion, float cropRegionScale,
boolean deleteFile)
BitmapRegionDecoder regionDecoder = BitmapRegionDecoder.newInstance(operFilepath, false);
RectF rotatedOriginRectF = new RectF(0, 0, rotatedOriginWidth, rotatedOriginHeight);
RectF operRectF = new RectF();
originMatrix.mapRect(operRectF, rotatedOriginRectF);
Rect cropRect = new Rect(0, 0, cropWidth, cropHeight);
int[] offset = new int[2];
offset[0] = 0 - (int) operRectF.left;
offset[1] = 0 - (int) operRectF.top;
cropRect.offset(offset[0], offset[1]);
if (cropRegionScale != 1f) {
cropRect.offset((int) (cropRect.left * cropRegionScale - cropRect.left), (int) (cropRect.top * cropRegionScale - cropRect.top));
cropRect.right = (int)(cropRect.left + cropWidth * cropRegionScale);
cropRect.bottom = (int)(cropRect.top + cropHeight * cropRegionScale);
}
if (orientation != 0) {
tmpRectF = new RectF(0, 0, operWidth, operHeight);
Matrix backRotateMatrix = new Matrix();
backRotateMatrix.postRotate(-orientation);
backRotateMatrix.mapRect(tmpRectF);
float transX = 0 - tmpRectF.left;
float transY = 0 - tmpRectF.top;
tmpRectF.offset(transX, transY);
RectF cropRectF = new RectF(cropRect.left, cropRect.top, cropRect.right, cropRect.bottom);
backRotateMatrix.mapRect(tmpRectF, cropRectF);
tmpRectF.offset(transX, transY);
cropRect = new Rect((int)tmpRectF.left, (int)tmpRectF.top, (int)tmpRectF.right, (int)tmpRectF.bottom);
}
cropBitmap = regionDecoder.decodeRegion(cropRect, new BitmapFactory.Options());
public static Bitmap smartCrop(final String filepath, int orientation,
Rect CROP_RECT, Rect DISPLAY_RECT,
Matrix displayMatrix, float suppScale, float maxScale)
Matrix originMatrix = new Matrix();
originMatrix.postScale(CROP_TO_DISPLAY_SCALE, CROP_TO_DISPLAY_SCALE);
originMatrix.postConcat(displayMatrix);
originMatrix.postTranslate(-displayTransX, -displayTransY);
originMatrix.postTranslate(displayTransX * CROP_TO_DISPLAY_SCALE, displayTransY * CROP_TO_DISPLAY_SCALE);
if (Math.abs(suppScale - maxScale) < 0.01) {
return decodeRegionInternal(filepath, orientation, ROTATED_ORIGIN_RECT, originMatrix,
CROP_RECT, 1f, false);
}
else if (1 < maxScale / suppScale && maxScale / suppScale < 2) { //x
float scale = maxScale / suppScale;
return decodeRegionInternal(filepath, orientation, ROTATED_ORIGIN_RECT, originMatrix,
CROP_RECT, scale, false);
}
if ( maxScale / suppScale >= 2) { //x
int sample = (int) ( maxScale / suppScale);
option = new BitmapFactory.Options();
option.inJustDecodeBounds = false;
option.inSampleSize = sample;
Bitmap sampleBitmap = BitmapFactory.decodeFile(filepath, option);
if (sampleBitmap == null || sampleBitmap.isRecycled()) {
System.gc();
Log.e(tag, "sample bitmap error");
return null;
}
Bitmap rotatedSampleBitmap = sampleBitmap;
if (orientation != 0) {
rotatedSampleBitmap = Bitmap.createBitmap(sampleBitmap, 0, 0, sampleBitmap.getWidth(), sampleBitmap.getHeight(),
rotateMatrix, true);
}
sampleBitmap = null;
if (rotatedSampleBitmap == null || rotatedSampleBitmap.isRecycled()) {
System.gc();
Log.e(tag, "rotate sample bitmap error");
return null;
}
int operWidth = (int)(rotatedOriginWidth / maxScale);
int operHeight = (int)(rotatedOriginHeight / maxScale);
Bitmap operBitmap = Bitmap.createScaledBitmap(rotatedSampleBitmap, operWidth, operHeight, true); //相当于用matrix再缩放一层小数点
rotatedSampleBitmap.recycle();
rotatedSampleBitmap = null;
if (operBitmap == null || operBitmap.isRecycled()) {
System.gc();
Log.e(tag, "scale sample bitmap error");
}
String tmpFilePath = getCustomDirectory(APP_PIC_DIR) + "smartcrop_sample.jpg";
savePhoto(operBitmap, tmpFilePath);
operBitmap.recycle();
operBitmap = null;
return decodeRegionInternal(tmpFilePath, orientation, ROTATED_ORIGIN_RECT, originMatrix, CROP_RECT, 1f, true); //tmpFilePath已经是rotated图片了,可能这里应该orientation=0,待测
}
本文来自网易实践者社区,经作者范晨灿授权发布。