package com.sonaive.v2ex.util; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Matrix; import android.media.ExifInterface; import android.util.Log; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; /** * Created by liutao on 1/16/15. */ public class ImageUtils { public static final String TAG = ImageUtils.class.getSimpleName(); /** * 获取缩放的bitmap * @param filePath * @param maxWidth * @param maxHeight * @return */ public static Bitmap getScaledBitmap(String filePath, int maxWidth, int maxHeight) { BitmapFactory.Options decodeOptions = new BitmapFactory.Options(); Bitmap bitmap; // If we have to resize this image, first get the natural bounds. decodeOptions.inJustDecodeBounds = true; BitmapFactory.decodeFile(filePath, decodeOptions); int actualWidth = decodeOptions.outWidth; int actualHeight = decodeOptions.outHeight; Log.d(TAG, "Actual width: " + actualWidth + ", actual height: " + actualHeight); // Then compute the dimensions we would ideally like to decode to. int desiredWidth = getResizedDimension(maxWidth, maxHeight, actualWidth, actualHeight); int desiredHeight = getResizedDimension(maxHeight, maxWidth, actualHeight, actualWidth); Log.d(TAG, "Desired width: " + desiredWidth + ", desired height: " + desiredHeight); // Decode to the nearest power of two scaling factor. decodeOptions.inJustDecodeBounds = false; // TODO(ficus): Do we need this or is it okay since API 8 doesn't support it? // decodeOptions.inPreferQualityOverSpeed = PREFER_QUALITY_OVER_SPEED; decodeOptions.inSampleSize = findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight); Bitmap tempBitmap = BitmapFactory.decodeFile(filePath, decodeOptions); // If necessary, scale down to the maximal acceptable size. if (tempBitmap != null && (tempBitmap.getWidth() > desiredWidth || tempBitmap.getHeight() > desiredHeight)) { bitmap = Bitmap.createScaledBitmap(tempBitmap, desiredWidth, desiredHeight, true); tempBitmap.recycle(); } else { bitmap = tempBitmap; } return bitmap; } /** * 获取缩放的bitmap * @param context * @param imageResId * @param maxWidth * @param maxHeight * @return */ public static Bitmap getScaledBitmap(Context context, int imageResId, int maxWidth, int maxHeight) { BitmapFactory.Options decodeOptions = new BitmapFactory.Options(); Bitmap bitmap = null; // If we have to resize this image, first get the natural bounds. decodeOptions.inJustDecodeBounds = true; BitmapFactory.decodeResource(context.getResources(), imageResId, decodeOptions); int actualWidth = decodeOptions.outWidth; int actualHeight = decodeOptions.outHeight; Log.d(TAG, "Actual width: " + actualWidth + ", actual height: " + actualHeight); // Then compute the dimensions we would ideally like to decode to. int desiredWidth = getResizedDimension(maxWidth, maxHeight, actualWidth, actualHeight); int desiredHeight = getResizedDimension(maxHeight, maxWidth, actualHeight, actualWidth); Log.d(TAG, "Desired width: " + desiredWidth + ", desired height: " + desiredHeight); // Decode to the nearest power of two scaling factor. decodeOptions.inJustDecodeBounds = false; // TODO(ficus): Do we need this or is it okay since API 8 doesn't support it? // decodeOptions.inPreferQualityOverSpeed = PREFER_QUALITY_OVER_SPEED; decodeOptions.inSampleSize = findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight); Bitmap tempBitmap = BitmapFactory.decodeResource(context.getResources(), imageResId, decodeOptions); // If necessary, scale down to the maximal acceptable size. if (tempBitmap != null && (tempBitmap.getWidth() > desiredWidth || tempBitmap.getHeight() > desiredHeight)) { bitmap = Bitmap.createScaledBitmap(tempBitmap, desiredWidth, desiredHeight, true); tempBitmap.recycle(); } else { bitmap = tempBitmap; } return bitmap; } /** * Returns the largest power-of-two divisor for use in downscaling a bitmap * that will not result in the scaling past the desired dimensions. * * @param actualWidth Actual width of the bitmap * @param actualHeight Actual height of the bitmap * @param desiredWidth Desired width of the bitmap * @param desiredHeight Desired height of the bitmap */ // Visible for testing. public static int findBestSampleSize( int actualWidth, int actualHeight, int desiredWidth, int desiredHeight) { double wr = (double) actualWidth / desiredWidth; double hr = (double) actualHeight / desiredHeight; double ratio = Math.min(wr, hr); float n = 1.0f; while ((n * 2) <= ratio) { n *= 2; } return (int) n; } /** * Scales one side of a rectangle to fit aspect ratio. * * @param maxPrimary Maximum size of the primary dimension (i.e. width for * max width), or zero to maintain aspect ratio with secondary * dimension * @param maxSecondary Maximum size of the secondary dimension, or zero to * maintain aspect ratio with primary dimension * @param actualPrimary Actual size of the primary dimension * @param actualSecondary Actual size of the secondary dimension */ public static int getResizedDimension(int maxPrimary, int maxSecondary, int actualPrimary, int actualSecondary) { // If no dominant value at all, just return the actual. if (maxPrimary == 0 && maxSecondary == 0) { return actualPrimary; } // If primary is unspecified, scale primary to match secondary's scaling ratio. if (maxPrimary == 0) { double ratio = (double) maxSecondary / (double) actualSecondary; return (int) (actualPrimary * ratio); } if (maxSecondary == 0) { return maxPrimary; } double ratio = (double) actualSecondary / (double) actualPrimary; int resized = maxPrimary; if (resized * ratio > maxSecondary) { resized = (int) (maxSecondary / ratio); } return resized; } /** * 获取图片实际尺寸 * @param imagePath * @return */ public static int[] getActualImageDimension(String imagePath) { int[] imageSize = new int[2]; BitmapFactory.Options decodeOptions = new BitmapFactory.Options(); // If we have to resize this image, first get the natural bounds. decodeOptions.inJustDecodeBounds = true; BitmapFactory.decodeFile(imagePath, decodeOptions); int actualWidth = decodeOptions.outWidth; int actualHeight = decodeOptions.outHeight; imageSize[0] = actualWidth; imageSize[1] = actualHeight; return imageSize; } /** * 获取图片实际尺寸 * @param imageResId * @return */ public static int[] getActualImageDimension(Context context, int imageResId) { int[] imageSize = new int[2]; BitmapFactory.Options decodeOptions = new BitmapFactory.Options(); // If we have to resize this image, first get the natural bounds. decodeOptions.inJustDecodeBounds = true; BitmapFactory.decodeResource(context.getResources(), imageResId, decodeOptions); int actualWidth = decodeOptions.outWidth; int actualHeight = decodeOptions.outHeight; imageSize[0] = actualWidth; imageSize[1] = actualHeight; return imageSize; } /** * 根据显示的最大宽度或最大高度,保持原图的宽高比缩放 * @param imagePath 图片路径 * @param maxWidth 图片显示的最大宽度 * @param maxHeight 图片显示的最大高度 * @return */ public static int[] getDesiredImageDimension(String imagePath, int maxWidth, int maxHeight) { int[] desiredImageDimension = new int[2]; int[] actualImageDimension = getActualImageDimension(imagePath); Log.d(TAG, "Actual width: " + actualImageDimension[0] + ", actual height: " + actualImageDimension[1]); int maxPrimary; int maxSecondary; if (actualImageDimension[0] >= actualImageDimension[1]) { maxPrimary = maxWidth; maxSecondary = 0; desiredImageDimension[0] = getResizedDimension(maxPrimary, maxSecondary, actualImageDimension[0], actualImageDimension[1]); desiredImageDimension[1] = getResizedDimension(maxSecondary, maxPrimary, actualImageDimension[1], actualImageDimension[0]); } else { maxPrimary = maxHeight; maxSecondary = 0; desiredImageDimension[1] = getResizedDimension(maxPrimary, maxSecondary, actualImageDimension[1], actualImageDimension[0]); desiredImageDimension[0] = getResizedDimension(maxPrimary, maxSecondary, actualImageDimension[0], actualImageDimension[1]); } Log.d(TAG, "Desired width: " + desiredImageDimension[0] + ", desired height: " + desiredImageDimension[1]); return desiredImageDimension; } /** * 根据显示的最大宽度或最大高度,保持原图的宽高比缩放 * @param imageResId 图片资源id * @param maxWidth 图片显示的最大宽度 * @param maxHeight 图片显示的最大高度 * @return */ public static int[] getDesiredImageDimension(Context context, int imageResId, int maxWidth, int maxHeight) { int[] desiredImageDimension = new int[2]; int[] actualImageDimension = getActualImageDimension(context, imageResId); Log.d(TAG, "Actual width: " + actualImageDimension[0] + ", actual height: " + actualImageDimension[1]); int maxPrimary; int maxSecondary; if (actualImageDimension[0] >= actualImageDimension[1]) { maxPrimary = maxWidth; maxSecondary = 0; desiredImageDimension[0] = getResizedDimension(maxPrimary, maxSecondary, actualImageDimension[0], actualImageDimension[1]); desiredImageDimension[1] = getResizedDimension(maxSecondary, maxPrimary, actualImageDimension[1], actualImageDimension[0]); } else { maxPrimary = maxHeight; maxSecondary = 0; desiredImageDimension[1] = getResizedDimension(maxPrimary, maxSecondary, actualImageDimension[1], actualImageDimension[0]); desiredImageDimension[0] = getResizedDimension(maxPrimary, maxSecondary, actualImageDimension[0], actualImageDimension[1]); } Log.d(TAG, "Desired width: " + desiredImageDimension[0] + ", desired height: " + desiredImageDimension[1]); return desiredImageDimension; } public static int[] getDesiredImageDimension(int[] dimension, int maxWidth, int maxHeight) { int[] desiredImageDimension = new int[2]; int[] actualImageDimension = dimension; Log.d(TAG, "Actual width: " + actualImageDimension[0] + ", actual height: " + actualImageDimension[1]); int maxPrimary; int maxSecondary; if (actualImageDimension[0] >= actualImageDimension[1]) { maxPrimary = maxWidth; maxSecondary = 0; desiredImageDimension[0] = getResizedDimension(maxPrimary, maxSecondary, actualImageDimension[0], actualImageDimension[1]); desiredImageDimension[1] = getResizedDimension(maxSecondary, maxPrimary, actualImageDimension[1], actualImageDimension[0]); } else { maxPrimary = maxHeight; maxSecondary = 0; desiredImageDimension[1] = getResizedDimension(maxPrimary, maxSecondary, actualImageDimension[1], actualImageDimension[0]); desiredImageDimension[0] = getResizedDimension(maxPrimary, maxSecondary, actualImageDimension[0], actualImageDimension[1]); } Log.d(TAG, "Desired width: " + desiredImageDimension[0] + ", desired height: " + desiredImageDimension[1]); return desiredImageDimension; } public static void compress(String path, int maxWidth, int maxHeight) { FileOutputStream out; try { Bitmap scaledBitmap = getScaledBitmap(path, maxWidth, maxHeight); out = new FileOutputStream(path); // write the compressed bitmap at the destination specified by filename. scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 90, out); // File file = new File(path); // Log.d(TAG, "Image size is : " + file.length() / 1024 + "kb"); } catch (FileNotFoundException e) { e.printStackTrace(); } } // http://developer.android.com/training/displaying-bitmaps/load-bitmap.html public static void watermark(Context context, String srcPath, int watermarkRes, int maxWidth, int maxHeight, int quality) { FileOutputStream out; try { Bitmap scaledBitmap = getScaledBitmap(srcPath, maxWidth, maxHeight); Bitmap rotatedBitmap = rotateBitmap(getBitmapDegree(srcPath), scaledBitmap); Bitmap scaledWatermark = getScaledBitmap(context, watermarkRes, maxWidth, maxHeight); out = new FileOutputStream(srcPath); Canvas canvas = new Canvas(rotatedBitmap); canvas.drawBitmap(scaledWatermark, 0, 0, null); // write the compressed bitmap at the destination specified by filename. rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, quality, out); // File file = new File(srcPath); // Log.d(TAG, "Image size is : " + file.length() / 1024 + "kb"); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (OutOfMemoryError e) { Log.e(TAG, "Add watermark result in OOM"); e.printStackTrace(); } } public static int getBitmapDegree(String path) { int degree = 0; try { ExifInterface exifInterface = new ExifInterface(path); int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); 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; } } catch (IOException e) { e.printStackTrace(); } return degree; } public static Bitmap rotateBitmap(int angle, Bitmap bitmap) { Matrix matrix = new Matrix(); matrix.postRotate(angle); return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); } }