package com.mobimvp.cliques.util; import android.annotation.SuppressLint; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.BitmapRegionDecoder; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Build.VERSION; import com.mobimvp.cliques.AppData; import java.io.IOException; public class ImageUtils { private static final int MAX_TEXTURE_SIZE = getOpengl2MaxTextureSize(); private static final PorterDuffXfermode SRC_IN_MODE = new PorterDuffXfermode( PorterDuff.Mode.SRC_IN); private final static Paint SRC_IN_PAINT = new Paint(); static { SRC_IN_PAINT.setXfermode(SRC_IN_MODE); } public static int getOpengl2MaxTextureSize() { int[] maxTextureSize = new int[1]; maxTextureSize[0] = 2048; android.opengl.GLES20.glGetIntegerv(android.opengl.GLES20.GL_MAX_TEXTURE_SIZE, maxTextureSize, 0); return maxTextureSize[0]; } /** * Get the size in bytes of a bitmap. * * @param bitmap * @return size in bytes */ @SuppressLint("NewApi") public static int getBitmapSize(Bitmap bitmap) { if (VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) { return bitmap.getByteCount(); } // Pre HC-MR1 return bitmap.getRowBytes() * bitmap.getHeight(); } /** * Decode and sample down a bitmap from resources to the requested width and * height. * * @param res The resources object containing the image data * @param resId The resource id of the image data * @param reqWidth The requested width of the resulting bitmap * @param reqHeight The requested height of the resulting bitmap * @return A bitmap sampled down from the original with the same aspect * ratio and dimensions that are equal to or greater than the * requested width and height(inMutable) */ public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { return decodeSampledBitmapFromResource(res, resId, reqWidth, reqHeight, false); } /** * Decode and sample down a bitmap from resources to the requested width and * height. * * @param res The resources object containing the image data * @param resId The resource id of the image data * @param reqWidth The requested width of the resulting bitmap * @param reqHeight The requested height of the resulting bitmap * @param isMutable 可编辑 * @return A bitmap sampled down from the original with the same aspect * ratio and dimensions that are equal to or greater than the * requested width and height */ public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight, boolean isMutable) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; if (isMutable && VERSION.SDK_INT >= 11) { options.inMutable = true; } Bitmap result = BitmapFactory.decodeResource(res, resId, options); if (isMutable) { result = createMutableBitmap(result); } return result; } public static Bitmap decodeSampledBitmapFromFile(String filePath, int sampledSize) { final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(filePath, options); // Calculate inSampleSize options.inSampleSize = sampledSize; // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeFile(filePath, options); } /** * Decode and sample down a bitmap from a file to the requested width and * height. * * @param filePath The full path of the file to decode * @param reqWidth The requested width of the resulting bitmap * @param reqHeight The requested height of the resulting bitmap * @return A bitmap sampled down from the original with the same aspect * ratio and dimensions that are equal to or greater than the * requested width and height(inmutable) */ public static Bitmap decodeSampledBitmapFromFile(String filePath, int reqWidth, int reqHeight) { return decodeSampledBitmapFromFile(filePath, reqWidth, reqHeight, false); } /** * Decode and sample down a bitmap from a file to the requested width and * height. * * @param filePath The full path of the file to decode * @param reqWidth The requested width of the resulting bitmap * @param reqHeight The requested height of the resulting bitmap * @param isMutable 可编辑 * @return A bitmap sampled down from the original with the same aspect * ratio and dimensions that are equal to or greater than the * requested width and height */ public static Bitmap decodeSampledBitmapFromFile(String filePath, int reqWidth, int reqHeight, boolean isMutable) { if (filePath == null) { return null; } if (reqHeight == 0) { reqHeight = MAX_TEXTURE_SIZE; } if (reqWidth == 0) { reqWidth = MAX_TEXTURE_SIZE; } // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(filePath, options); if (options.outWidth == -1 || options.outHeight == -1) { return null; } // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); if (isMutable && VERSION.SDK_INT >= 11) { options.inMutable = true; } Bitmap result = null; if (options.outWidth > MAX_TEXTURE_SIZE || options.outHeight > MAX_TEXTURE_SIZE || (options.outHeight >= options.outWidth * 3)) { // 长图 try { result = regionDecode(filePath, reqWidth, reqHeight, options.outWidth, options.outHeight); } catch (IOException e) { e.printStackTrace(); } } else { result = BitmapFactory.decodeFile(filePath, options); } if (isMutable) { result = createMutableBitmap(result); } return result; } private static Bitmap regionDecode(String path, int reqWidth, int reqHeight, int outWidth, int outHeight) throws IOException { BitmapRegionDecoder regionDecoder = BitmapRegionDecoder.newInstance(path, true); if (reqWidth > outWidth) { reqWidth = outWidth; } if (reqHeight > outHeight) { reqHeight = outHeight; } return regionDecoder.decodeRegion(new Rect(0, 0, reqWidth, reqHeight), null); } /** * Calculate an inSampleSize for use in a * {@link android.graphics.BitmapFactory.Options} object when decoding * bitmaps using the decode* methods from * {@link android.graphics.BitmapFactory}. This implementation calculates * the closest inSampleSize that will result in the final decoded bitmap * having a width and height equal to or larger than the requested width and * height. This implementation does not ensure a power of 2 is returned for * inSampleSize which can be faster when decoding but results in a larger * bitmap which isn't as useful for caching purposes. * * @param options An options object with out* params already populated (run * through a decode* method with inJustDecodeBounds==true * @param reqWidth The requested width of the resulting bitmap * @param reqHeight The requested height of the resulting bitmap * @return The value to be used for inSampleSize */ 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) { int widthSampleSize = 0; int heightSampleSize = 0; if (reqWidth < width) { widthSampleSize = Math.round((float) width / (float) reqWidth); } if (reqHeight < height) { heightSampleSize = Math.round((float) height / (float) reqHeight); } inSampleSize = Math.max(widthSampleSize, heightSampleSize); // if (width > height) { // inSampleSize = Math.round((float) height / (float) reqHeight); // } else { // inSampleSize = Math.round((float) width / (float) reqWidth); // } // This offers some additional logic in case the image has a strange // aspect ratio. For example, a panorama may have a much larger // width than height. In these cases the total pixels might still // end up being too large to fit comfortably in memory, so we should // be more aggressive with sample down the image (=larger // inSampleSize). // final float totalPixels = width * height; // // // Anything more than 2x the requested pixels we'll sample down // // further. // float totalReqPixelsCap = reqWidth * reqHeight * 2; // while (totalPixels / (inSampleSize * inSampleSize) > // totalReqPixelsCap) { // inSampleSize++; // } } return inSampleSize; } /** * 通过srcbitmap 创建一个可编辑的bitmap * * @param src * @return */ public static Bitmap createMutableBitmap(Bitmap src) { Bitmap result = null; if (src == null) { return null; } result = src.copy(Config.ARGB_8888, true); return result; } /** * 将subBmp图像合并到oriBmp中 * * @param oriBmp * @param subBmp * @param oriRect subBmp中取出的bitmap需要填充到oriRect中的区域 * @param subRect 从subBmp中取出的区域 * @param paint * @return */ public static Bitmap mergeBitmap(Bitmap oriBmp, Bitmap subBmp, final Rect oriRect, final Rect subRect) { if (subBmp == null) { return oriBmp; } if (oriBmp == null) { return null; } if (!oriBmp.isMutable()) { oriBmp = createMutableBitmap(oriBmp); } Canvas canvas = new Canvas(oriBmp); canvas.drawBitmap(subBmp, subRect, oriRect, null); return oriBmp; } /** * 将subBmp图像合并到oriBmp中 * * @param oriBmp * @param subBmp * @param paint * @return oriBmp */ public static Bitmap mergeBitmap(Bitmap oriBmp, Bitmap subBmp) { if (subBmp == null) { return oriBmp; } if (oriBmp == null) { return null; } return mergeBitmap(oriBmp, subBmp, new Rect(0, 0, oriBmp.getWidth(), oriBmp.getHeight()), new Rect(0, 0, subBmp.getWidth(), subBmp.getHeight())); } /** * 遮罩图片 * * @param dstBmp * @param mask * @param paint * @return 遮罩后的图片 */ public static Bitmap maskBitmap(final Bitmap dstBmp, final Bitmap mask) { if (dstBmp == null || mask == null) { return dstBmp; } Bitmap result = Bitmap .createBitmap(dstBmp.getWidth(), dstBmp.getHeight(), Config.ARGB_8888); Canvas canvas = new Canvas(result); int sc = canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(), null, Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.FULL_COLOR_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG); canvas.drawBitmap(mask, new Rect(0, 0, mask.getWidth(), mask.getHeight()), new Rect(0, 0, dstBmp.getWidth(), dstBmp.getHeight()), null); canvas.drawBitmap(dstBmp, 0, 0, SRC_IN_PAINT); canvas.restoreToCount(sc); return result; } public static Bitmap convertToAlphaMask(Bitmap b) { Bitmap a = Bitmap.createBitmap(b.getWidth(), b.getHeight(), Config.ALPHA_8); Canvas c = new Canvas(a); c.drawBitmap(b, 0.0f, 0.0f, null); return a; } public static Bitmap decodeBitmapFromDrawableRes(int resId, final int width, final int height) { Drawable drawable = AppData.getContext().getResources().getDrawable(resId); drawable.setBounds(0, 0, width, height); Bitmap bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); drawable.draw(canvas); return bitmap; } }