package com.mcxiaoke.next.utils; import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.BlurMaskFilter; import android.graphics.Canvas; import android.graphics.LinearGradient; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.media.ExifInterface; import android.net.Uri; import android.provider.MediaStore; import android.util.Log; import com.mcxiaoke.next.core.BuildConfig; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; public final class BitmapUtils { public static final String TAG = BitmapUtils.class.getSimpleName(); public static final boolean DEBUG = BuildConfig.DEBUG; private static final boolean DEBUG_FLAG = false; private static final int DEFAULT_BLUR_RADIUS = 12; private BitmapUtils() { } /** * 旋转图片 * * @param bmp 原始图片 * @param angle 旋转的角度 * @return */ public static Bitmap rotate(Bitmap bmp, float angle) { Matrix matrixRotateLeft = new Matrix(); matrixRotateLeft.setRotate(angle); return Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrixRotateLeft, true); } /** * 按原比例缩放图片 * * @param contentResolver * @param uri 图片的URI地址 * @param maxWidth 缩放后的宽度 * @param maxHeight 缩放后的高度 * @return */ public static Bitmap scale(ContentResolver contentResolver, Uri uri, int maxWidth, int maxHeight) { String tag = "SCALE"; LogUtils.v(tag, "uri=" + uri.toString()); try { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; InputStream input = contentResolver.openInputStream(uri); BitmapFactory.decodeStream(input, null, options); int sourceWidth = options.outWidth; int sourceHeight = options.outHeight; LogUtils.v(tag, "sourceWidth=" + sourceWidth + ", sourceHeight=" + sourceHeight); LogUtils.v(tag, "maxWidth=" + maxWidth + ", maxHeight=" + maxHeight); input.close(); float rate = Math.max(sourceWidth / (float) maxWidth, sourceHeight / (float) maxHeight); options.inJustDecodeBounds = false; options.inSampleSize = (int) rate; LogUtils.v(tag, "rate=" + rate + ", inSampleSize=" + options.inSampleSize); input = contentResolver.openInputStream(uri); Bitmap bitmap = BitmapFactory.decodeStream(input, null, options); int w0 = bitmap.getWidth(); int h0 = bitmap.getHeight(); LogUtils.v(tag, "w0=" + w0 + ", h0=" + h0); float scaleWidth = maxWidth / (float) w0; float scaleHeight = maxHeight / (float) h0; float maxScale = Math.min(scaleWidth, scaleHeight); LogUtils.v(tag, "scaleWidth=" + scaleWidth + ", scaleHeight=" + scaleHeight); Matrix matrix = new Matrix(); matrix.reset(); if (maxScale < 1) matrix.postScale(maxScale, maxScale); Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, w0, h0, matrix, true); input.close(); // bitmap.recycle(); return resizedBitmap; } catch (FileNotFoundException e) { e.printStackTrace(); return null; } catch (IOException e) { e.printStackTrace(); return null; } } /** * Drawable转换为Bitmap * * @param drawable * @return */ public static Bitmap getBitmap(Drawable drawable) { if (drawable == null) return null; if (drawable instanceof BitmapDrawable) { return ((BitmapDrawable) drawable).getBitmap(); } else { Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), drawable.getOpacity() != PixelFormat.OPAQUE ? Config.ARGB_8888 : Config.RGB_565); Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); drawable.draw(canvas); return bitmap; } } /** *  为指定图片增加阴影 * * @param map  图片 * @param radius  阴影的半径 * @return */ public static Bitmap drawShadow(Bitmap map, int radius) { if (map == null) return null; BlurMaskFilter blurFilter = new BlurMaskFilter(radius, BlurMaskFilter.Blur.OUTER); Paint shadowPaint = new Paint(); shadowPaint.setMaskFilter(blurFilter); int[] offsetXY = new int[2]; Bitmap shadowImage = map.extractAlpha(shadowPaint, offsetXY); shadowImage = shadowImage.copy(Config.ARGB_8888, true); Canvas c = new Canvas(shadowImage); c.drawBitmap(map, -offsetXY[0], -offsetXY[1], null); return shadowImage; } /** * 获得圆角的bitmap * * @param bitmap * @param roundPx * @return */ public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, float roundPx) { Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888); Canvas canvas = new Canvas(output); final int color = 0xff424242; final Paint paint = new Paint(); final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); final RectF rectF = new RectF(rect); paint.setAntiAlias(true); canvas.drawARGB(0, 0, 0, 0); paint.setColor(color); canvas.drawRoundRect(rectF, roundPx, roundPx, paint); paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); canvas.drawBitmap(bitmap, rect, rect, paint); return output; } public static int getExifOrientation(ContentResolver cr, Uri contentUri) { int returnValue = 0; String uriString = contentUri.toString(); if (ContentResolver.SCHEME_CONTENT.equals(contentUri.getScheme())) { // can post image String[] proj = {MediaStore.Images.Media.ORIENTATION}; Cursor cursor = cr.query(contentUri, proj, null, null, null); if (null != cursor) { if (cursor.moveToFirst()) { returnValue = cursor.getInt(cursor .getColumnIndexOrThrow(MediaStore.Images.Media.ORIENTATION)); } cursor.close(); } } else if (ContentResolver.SCHEME_FILE.equals(contentUri.getScheme())) { returnValue = getExifOrientation(contentUri.getPath()); } else if (uriString.startsWith("/")) { returnValue = getExifOrientation(contentUri.getPath()); } return returnValue; } public static int getExifOrientation(String fileName) { if (StringUtils.isEmpty(fileName)) { return 0; } int degree = 0; ExifInterface exif = null; try { exif = new ExifInterface(fileName); } catch (IOException ex) { Log.e(TAG, "cannot read exif", ex); } if (exif != null) { int orientation = exif.getAttributeInt( ExifInterface.TAG_ORIENTATION, -1); if (orientation != -1) { // We only recognize a subset of orientation tag values. switch (orientation) { case ExifInterface.ORIENTATION_ROTATE_90: degree = 90; break; case ExifInterface.ORIENTATION_ROTATE_180: degree = 180; break; case ExifInterface.ORIENTATION_ROTATE_270: degree = 270; break; } } } return degree; } public static byte[] generateBitstream(Bitmap src, Bitmap.CompressFormat format, int quality) { ByteArrayOutputStream os = new ByteArrayOutputStream(); src.compress(format, quality, os); return os.toByteArray(); } public static Bitmap getReflectBitmap(Bitmap originalImage, float rate) { if (originalImage == null || originalImage.isRecycled()) return null; //The gap we want between the reflection and the original image final int reflectionGap = 4; int width = originalImage.getWidth(); int height = originalImage.getHeight(); int reflectHeight = Math.round(rate * height); //This will not scale but will flip on the Y axis Matrix matrix = new Matrix(); matrix.preScale(1, -1); //Create a Bitmap with the flip matrix applied to it. //We only want the bottom half of the image Bitmap reflectionImage = Bitmap.createBitmap(originalImage, 0, height - reflectHeight, width, reflectHeight, matrix, false); //Create a new bitmap with same width but taller to fit reflection Bitmap bitmapWithReflection = Bitmap.createBitmap(width, height + reflectHeight, Config.ARGB_8888); //Create a new Canvas with the bitmap that's big enough for //the image plus gap plus reflection Canvas canvas = new Canvas(bitmapWithReflection); //Draw in the original image canvas.drawBitmap(originalImage, 0, 0, null); //Draw in the gap Paint defaultPaint = new Paint(); canvas.drawRect(0, height, width, height + reflectionGap, defaultPaint); //Draw in the reflection canvas.drawBitmap(reflectionImage, 0, height + reflectionGap, null); //Create a shader that is a linear gradient that covers the reflection Paint paint = new Paint(); LinearGradient shader = new LinearGradient(0, originalImage.getHeight(), 0, bitmapWithReflection.getHeight() + reflectionGap, 0x70ffffff, 0x00ffffff, Shader.TileMode.CLAMP); //Set the paint to use this shader (linear gradient) paint.setShader(shader); //Set the Transfer mode to be porter duff and destination in paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN)); //Draw a rectangle using the paint with our linear gradient canvas.drawRect(0, height, width, bitmapWithReflection.getHeight() + reflectionGap, paint); if (!originalImage.isRecycled()) { originalImage.recycle(); } return bitmapWithReflection; } public static Bitmap getSquareBitmap(Bitmap src) { return getSquareBitmap(src, 0.1f); } public static Bitmap getSquareBitmap(Bitmap src, float rate) { if (src == null || src.isRecycled()) return null; Bitmap ret = src; int w = src.getWidth(); int h = src.getHeight(); int min = Math.min(w, h); int max = Math.max(w, h); float r = (float) (max - min) / (float) min; if (w != h && r > rate) { max = Math.round((1.0f + rate) * min); if (w > h) { ret = Bitmap.createBitmap(src, (w - max) / 2, 0, max, min); } else { ret = Bitmap.createBitmap(src, 0, (h - max) / 2, min, max); } } return ret; } public static Bitmap clipCircleBitmap(Bitmap bitmap) { if (bitmap == null || bitmap.isRecycled()) return null; int width = bitmap.getWidth(); int height = bitmap.getHeight(); float roundPx; float left, top = 0, right, bottom, dst_left, dst_top, dst_right, dst_bottom; width = width - 2; height = height - 2; if (width <= height) { roundPx = width / 2; bottom = width; left = 0; right = width; height = width; dst_left = 0; dst_top = 0; dst_right = width; dst_bottom = width; } else { roundPx = height / 2; float clip = (width - height) / 2; left = clip; right = width - clip; bottom = height; width = height; dst_left = 0; dst_top = 0; dst_right = height; dst_bottom = height; } Bitmap output = Bitmap.createBitmap(width, height, Config.ARGB_8888); Canvas canvas = new Canvas(output); final Paint paint = new Paint(); final Rect src = new Rect((int) left, (int) top, (int) right, (int) bottom); final Rect dst = new Rect((int) dst_left, (int) dst_top, (int) dst_right, (int) dst_bottom); final RectF rectF = new RectF(dst); paint.setAntiAlias(true); canvas.drawRoundRect(rectF, roundPx, roundPx, paint); paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); canvas.drawBitmap(bitmap, src, dst, paint); if (!bitmap.isRecycled()) { bitmap.recycle(); } return output; } public static Bitmap getBluredBitmap(Bitmap sentBitmap) { return getBluredBitmap(sentBitmap, DEFAULT_BLUR_RADIUS); } public static Bitmap getBluredBitmap(Bitmap sentBitmap, int radius) { // Stack Blur v1.0 from // http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html // // Java Author: Mario Klingemann <mario at quasimondo.com> // http://incubator.quasimondo.com // created Feburary 29, 2004 // Android port : Yahel Bouaziz <yahel at kayenko.com> // http://www.kayenko.com // ported april 5th, 2012 // This is a compromise between Gaussian Blur and Box blur // It creates much better looking blurs than Box Blur, but is // 7x faster than my Gaussian Blur implementation. // // I called it Stack Blur because this describes best how this // filter works internally: it creates a kind of moving stack // of colors whilst scanning through the image. Thereby it // just has to addPart one new block of color to the right side // of the stack and remove the leftmost color. The remaining // colors on the topmost layer of the stack are either added on // or reduced by one, depending on if they are on the right or // on the left side of the stack. // // If you are using this algorithm in your code please addPart // the following line: // // Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com> Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig(), true); if (radius < 1) { return (null); } int w = bitmap.getWidth(); int h = bitmap.getHeight(); int[] pix = new int[w * h]; bitmap.getPixels(pix, 0, w, 0, 0, w, h); int wm = w - 1; int hm = h - 1; int wh = w * h; int div = radius + radius + 1; int r[] = new int[wh]; int g[] = new int[wh]; int b[] = new int[wh]; int rsum, gsum, bsum, x, y, i, p, yp, yi, yw; int vmin[] = new int[Math.max(w, h)]; int divsum = (div + 1) >> 1; divsum *= divsum; int dv[] = new int[256 * divsum]; for (i = 0; i < 256 * divsum; i++) { dv[i] = (i / divsum); } yw = yi = 0; int[][] stack = new int[div][3]; int stackpointer; int stackstart; int[] sir; int rbs; int r1 = radius + 1; int routsum, goutsum, boutsum; int rinsum, ginsum, binsum; for (y = 0; y < h; y++) { rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; for (i = -radius; i <= radius; i++) { p = pix[yi + Math.min(wm, Math.max(i, 0))]; sir = stack[i + radius]; sir[0] = (p & 0xff0000) >> 16; sir[1] = (p & 0x00ff00) >> 8; sir[2] = (p & 0x0000ff); rbs = r1 - Math.abs(i); rsum += sir[0] * rbs; gsum += sir[1] * rbs; bsum += sir[2] * rbs; if (i > 0) { rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; } else { routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; } } stackpointer = radius; for (x = 0; x < w; x++) { r[yi] = dv[rsum]; g[yi] = dv[gsum]; b[yi] = dv[bsum]; rsum -= routsum; gsum -= goutsum; bsum -= boutsum; stackstart = stackpointer - radius + div; sir = stack[stackstart % div]; routsum -= sir[0]; goutsum -= sir[1]; boutsum -= sir[2]; if (y == 0) { vmin[x] = Math.min(x + radius + 1, wm); } p = pix[yw + vmin[x]]; sir[0] = (p & 0xff0000) >> 16; sir[1] = (p & 0x00ff00) >> 8; sir[2] = (p & 0x0000ff); rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; rsum += rinsum; gsum += ginsum; bsum += binsum; stackpointer = (stackpointer + 1) % div; sir = stack[(stackpointer) % div]; routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; rinsum -= sir[0]; ginsum -= sir[1]; binsum -= sir[2]; yi++; } yw += w; } for (x = 0; x < w; x++) { rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; yp = -radius * w; for (i = -radius; i <= radius; i++) { yi = Math.max(0, yp) + x; sir = stack[i + radius]; sir[0] = r[yi]; sir[1] = g[yi]; sir[2] = b[yi]; rbs = r1 - Math.abs(i); rsum += r[yi] * rbs; gsum += g[yi] * rbs; bsum += b[yi] * rbs; if (i > 0) { rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; } else { routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; } if (i < hm) { yp += w; } } yi = x; stackpointer = radius; for (y = 0; y < h; y++) { // Preserve alpha channel: ( 0xff000000 & pix[yi] ) pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum]; rsum -= routsum; gsum -= goutsum; bsum -= boutsum; stackstart = stackpointer - radius + div; sir = stack[stackstart % div]; routsum -= sir[0]; goutsum -= sir[1]; boutsum -= sir[2]; if (x == 0) { vmin[y] = Math.min(y + r1, hm) * w; } p = x + vmin[y]; sir[0] = r[p]; sir[1] = g[p]; sir[2] = b[p]; rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; rsum += rinsum; gsum += ginsum; bsum += binsum; stackpointer = (stackpointer + 1) % div; sir = stack[stackpointer]; routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; rinsum -= sir[0]; ginsum -= sir[1]; binsum -= sir[2]; yi += w; } } bitmap.setPixels(pix, 0, w, 0, 0, w, h); return (bitmap); } public static File compress(String src, String dest, int maxWidth, int quality) throws IOException { Bitmap bitmap = getCompressedBitmap(src, maxWidth); if (src == null || dest == null || bitmap == null) { return null; } if (DEBUG) { LogUtils.v(TAG, "compress() maxWidth=" + maxWidth + " quality=" + quality); LogUtils.v(TAG, "compress() src=" + src); LogUtils.v(TAG, "compress() dest=" + dest); LogUtils.v(TAG, "compress() bitmap=(" + bitmap.getWidth() + "," + bitmap.getHeight() + ")"); } FileOutputStream fos = null; try { Bitmap.CompressFormat format = Bitmap.CompressFormat.JPEG; if (src.toLowerCase().lastIndexOf("png") != -1) { format = Bitmap.CompressFormat.PNG; } fos = new FileOutputStream(dest); bitmap.compress(format, quality, fos); bitmap.recycle(); return new File(dest); } finally { IOUtils.closeQuietly(fos); } } private static Bitmap getCompressedBitmap(String path, int maxWidth) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(path, options); int inSampleSize = 1; for (int w = options.outWidth; w > maxWidth * 2 - 10; w /= 2) { inSampleSize++; } if (DEBUG) { LogUtils.v(TAG, "getCompressedBitmap() original bitmap=(" + options.outWidth + "," + options.outHeight + ")"); LogUtils.v(TAG, "getCompressedBitmap() inSampleSize=" + inSampleSize); } options.inSampleSize = inSampleSize; options.inJustDecodeBounds = false; Bitmap bitmap = BitmapFactory.decodeFile(path, options); if (bitmap != null) { int bw = bitmap.getWidth(); int bh = bitmap.getHeight(); if (DEBUG) { int length = bitmap.getRowBytes() * bitmap.getHeight(); LogUtils.v(TAG, "getCompressedBitmap() decode bitmap size=(" + bw + "," + bh + ")"); LogUtils.v(TAG, "getCompressedBitmap() decode bitmap length=(" + length / 1000 + "k)"); } Matrix m = new Matrix(); if (bw > maxWidth) { float scale = (float) maxWidth / (float) bw; m.postScale(scale, scale); if (DEBUG) { LogUtils.v(TAG, "getCompressedBitmap() matrix scale=" + scale); } } int rotation = getExifOrientation(path); if (getExifOrientation(path) != 0) { m.postRotate(rotation); } if (DEBUG) { LogUtils.v(TAG, "getCompressedBitmap() matrix rotation=" + rotation); } Bitmap resultBitmap = Bitmap.createBitmap(bitmap, 0, 0, bw, bh, m, true); if (resultBitmap != bitmap) { bitmap.recycle(); } if (DEBUG) { int sw = resultBitmap.getWidth(); int sh = resultBitmap.getHeight(); int length = resultBitmap.getRowBytes() * resultBitmap.getHeight(); LogUtils.v(TAG, "getCompressedBitmap() final bitmap size=(" + sw + "," + sh + ")"); LogUtils.v(TAG, "getCompressedBitmap() final bitmap length=(" + length / 1000 + "k)"); } return resultBitmap; } return null; } public static Bitmap decodeImage(final ContentResolver resolver, final Uri uri, final int maxDim) { // Get original dimensions BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; InputStream is = null; try { is = resolver.openInputStream(uri); BitmapFactory.decodeStream(is, null, options); } catch (Exception e) { if (DEBUG) { LogUtils.v(TAG, "decodeImage() ex=" + e); e.printStackTrace(); } return null; } finally { IOUtils.closeQuietly(is); } final int origWidth = options.outWidth; final int origHeight = options.outHeight; options.inJustDecodeBounds = false; options.inScaled = false; options.inPurgeable = true; options.inInputShareable = true; options.inDither = true; options.inPreferredConfig = Config.RGB_565; if (origWidth > maxDim || origHeight > maxDim) { int k = 1; int tmpHeight = origHeight, tmpWidth = origWidth; while ((tmpWidth / 2) >= maxDim || (tmpHeight / 2) >= maxDim) { tmpWidth /= 2; tmpHeight /= 2; k *= 2; } options.inSampleSize = k; } Bitmap bitmap = null; try { is = resolver.openInputStream(uri); bitmap = BitmapFactory.decodeStream(is, null, options); } catch (Exception e) { if (DEBUG) { LogUtils.v(TAG, "decodeImage() ex=" + e); e.printStackTrace(); } } finally { IOUtils.closeQuietly(is); } if (null != bitmap) { if (DEBUG) { LogUtils.v(TAG, "decodeImage() " + bitmap.getWidth() + "x" + bitmap.getHeight()); } } return bitmap; } public static Bitmap rotate(Bitmap original, final int angle) { if ((angle % 360) == 0) { return original; } final boolean dimensionsChanged = angle == 90 || angle == 270; final int oldWidth = original.getWidth(); final int oldHeight = original.getHeight(); final int newWidth = dimensionsChanged ? oldHeight : oldWidth; final int newHeight = dimensionsChanged ? oldWidth : oldHeight; Bitmap bitmap = Bitmap.createBitmap(newWidth, newHeight, original.getConfig()); Canvas canvas = new Canvas(bitmap); Matrix matrix = new Matrix(); matrix.preTranslate((newWidth - oldWidth) / 2f, (newHeight - oldHeight) / 2f); matrix.postRotate(angle, bitmap.getWidth() / 2f, bitmap.getHeight() / 2); canvas.drawBitmap(original, matrix, null); original.recycle(); return bitmap; } public static Bitmap getImage(Context context, final Uri uri, int maxDimen) { final int size = maxDimen; Bitmap bitmap = decodeImage(context.getContentResolver(), uri, size); Bitmap rotatedBitmap = rotate(bitmap, getExifOrientation(context.getContentResolver(), uri)); if (bitmap != rotatedBitmap) { bitmap.recycle(); } return rotatedBitmap; } }