package com.todayinfo.utils; import java.io.Closeable; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import android.content.Context; import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.net.Uri; import android.provider.MediaStore.Images.ImageColumns; import android.util.Log; /** * bitmap操作的工具类 * * @author longtao.li 2012-10-18 * */ public class BitmapUtils { private static final String TAG = "BitmapUtils"; private static final int DEFAULT_COMPRESS_QUALITY = 90; private static final int INDEX_ORIENTATION = 0; private static final String[] IMAGE_PROJECTION = new String[] { ImageColumns.ORIENTATION }; private final Context context; public BitmapUtils(Context context) { this.context = context; } /** * Creates a mutable bitmap from subset of source bitmap, transformed by the optional matrix. */ private static Bitmap createBitmap( Bitmap source, int x, int y, int width, int height, Matrix m) { // Re-implement Bitmap createBitmap() to always return a mutable bitmap. Canvas canvas = new Canvas(); Bitmap bitmap; Paint paint; if ((m == null) || m.isIdentity()) { bitmap = Bitmap.createBitmap(width, height, source.getConfig()); paint = null; } else { RectF rect = new RectF(0, 0, width, height); m.mapRect(rect); bitmap = Bitmap.createBitmap( Math.round(rect.width()), Math.round(rect.height()), source.getConfig()); canvas.translate(-rect.left, -rect.top); canvas.concat(m); paint = new Paint(Paint.FILTER_BITMAP_FLAG); if (!m.rectStaysRect()) { paint.setAntiAlias(true); } } bitmap.setDensity(source.getDensity()); canvas.setBitmap(bitmap); Rect srcBounds = new Rect(x, y, x + width, y + height); RectF dstBounds = new RectF(0, 0, width, height); canvas.drawBitmap(source, srcBounds, dstBounds, paint); return bitmap; } private void closeStream(Closeable stream) { if (stream != null) { try { stream.close(); } catch (Exception e) { e.printStackTrace(); } } } public Rect getBitmapBounds(byte[] data){ Rect bounds = new Rect(); try { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeByteArray(data, 0, data.length, options); bounds.right = options.outWidth; bounds.bottom = options.outHeight; Log.i(TAG, "options.outWidth="+options.outWidth+" , "+"options.outHeight="+options.outHeight); } catch (Exception e) { }finally { } return bounds; } public Rect getBitmapBounds(Uri uri) { Rect bounds = new Rect(); InputStream is = null; try { is = context.getContentResolver().openInputStream(uri); BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(is, null, options); bounds.right = options.outWidth; bounds.bottom = options.outHeight; Log.i(TAG, "options.outWidth="+options.outWidth+" , "+"options.outHeight="+options.outHeight); } catch (Exception e) { e.printStackTrace(); } finally { closeStream(is); } return bounds; } public Rect getBitmapBounds(InputStream is, boolean isClose) { Rect bounds = new Rect(); try { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(is, null, options); bounds.right = options.outWidth; bounds.bottom = options.outHeight; Log.i(TAG, "options.outWidth="+options.outWidth+" , "+"options.outHeight="+options.outHeight); } catch (Exception e) { e.printStackTrace(); } finally { if( isClose ) closeStream(is); } return bounds; } private int getOrientation(Uri uri) { int orientation = 0; Cursor cursor = null; try { cursor = context.getContentResolver().query(uri, IMAGE_PROJECTION, null, null, null); if ((cursor != null) && cursor.moveToNext()) { orientation = cursor.getInt(INDEX_ORIENTATION); } } catch (Exception e) { // Ignore error for no orientation column; just use the default orientation value 0. } finally { if (cursor != null) { cursor.close(); } } return orientation; } /** * Decodes bitmap (maybe immutable) that keeps aspect-ratio and spans most within the bounds. */ public Bitmap decodeBitmapByStream(InputStream is, Rect bounds, int width, int height) { Log.i(TAG, "width = " + width + " , " + "height = " + height); Bitmap bitmap = null; try { // TODO: Take max pixels allowed into account for calculation to avoid possible OOM. // Rect bounds = getBitmapBounds(is, false); int sampleSize = Math.max(bounds.width() / width, bounds.height() / height); sampleSize = Math.min(sampleSize, Math.max(bounds.width() / height, bounds.height() / width)); BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = Math.max(sampleSize, 1); options.inPreferredConfig = Bitmap.Config.ARGB_8888; Log.i(TAG, "sampleSize = " + sampleSize + " , " + "options.inSampleSize = " + options.inSampleSize); bitmap = BitmapFactory.decodeStream(is, null, options);//!!!!溢出 } catch (Exception e) { Log.e(TAG, e.getMessage()); } finally { closeStream(is); } // Ensure bitmap in 8888 format, good for editing as well as GL compatible. if ((bitmap != null) && (bitmap.getConfig() != Bitmap.Config.ARGB_8888)) { Bitmap copy = bitmap.copy(Bitmap.Config.ARGB_8888, true); bitmap.recycle(); bitmap = copy; } if (bitmap != null) { // Scale down the sampled bitmap if it's still larger than the desired dimension. float scale = Math.min((float) width / bitmap.getWidth(), (float) height / bitmap.getHeight()); scale = Math.max(scale, Math.min((float) height / bitmap.getWidth(), (float) width / bitmap.getHeight())); if (scale < 1) { Matrix m = new Matrix(); m.setScale(scale, scale); Bitmap transformed = createBitmap( bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m); bitmap.recycle(); return transformed; } } return bitmap; } /** * Decodes bitmap (maybe immutable) that keeps aspect-ratio and spans most within the bounds. */ public Bitmap decodeBitmap(byte[] data, int width, int height){ Log.i(TAG, "width = " + width + " , " + "height = " + height); Bitmap bitmap = null; try { // TODO: Take max pixels allowed into account for calculation to avoid possible OOM. Rect bounds = getBitmapBounds(data); int sampleSize = Math.max(bounds.width() / width, bounds.height() / height); sampleSize = Math.min(sampleSize, Math.max(bounds.width() / height, bounds.height() / width)); BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = Math.max(sampleSize, 1); options.inPreferredConfig = Bitmap.Config.ARGB_8888; Log.i(TAG, "sampleSize = " + sampleSize + " , " + "options.inSampleSize = " + options.inSampleSize); bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options);//!!!!溢出 } catch (Exception e) { Log.e(TAG, e.getMessage()); } finally { data = null; } // Ensure bitmap in 8888 format, good for editing as well as GL compatible. if ((bitmap != null) && (bitmap.getConfig() != Bitmap.Config.ARGB_8888)) { Bitmap copy = bitmap.copy(Bitmap.Config.ARGB_8888, true); bitmap.recycle(); bitmap = copy; } if (bitmap != null) { // Scale down the sampled bitmap if it's still larger than the desired dimension. float scale = Math.min((float) width / bitmap.getWidth(), (float) height / bitmap.getHeight()); scale = Math.max(scale, Math.min((float) height / bitmap.getWidth(), (float) width / bitmap.getHeight())); if (scale < 1) { Matrix m = new Matrix(); m.setScale(scale, scale); Bitmap transformed = createBitmap( bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m); bitmap.recycle(); return transformed; } } return bitmap; } /** * Decodes bitmap (maybe immutable) that keeps aspect-ratio and spans most within the bounds. */ private Bitmap decodeBitmap(Uri uri, int width, int height) { Log.i(TAG, "width = " + width + " , " + "height = " + height); InputStream is = null; Bitmap bitmap = null; try { // TODO: Take max pixels allowed into account for calculation to avoid possible OOM. Rect bounds = getBitmapBounds(uri); int sampleSize = Math.max(bounds.width() / width, bounds.height() / height); sampleSize = Math.min(sampleSize, Math.max(bounds.width() / height, bounds.height() / width)); BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = Math.max(sampleSize, 1); options.inPreferredConfig = Bitmap.Config.ARGB_8888; is = context.getContentResolver().openInputStream(uri); Log.i(TAG, "sampleSize = " + sampleSize + " , " + "options.inSampleSize = " + options.inSampleSize); bitmap = BitmapFactory.decodeStream(is, null, options);//!!!!溢出 } catch (Exception e) { } finally { closeStream(is); } // Ensure bitmap in 8888 format, good for editing as well as GL compatible. if ((bitmap != null) && (bitmap.getConfig() != Bitmap.Config.ARGB_8888)) { Bitmap copy = bitmap.copy(Bitmap.Config.ARGB_8888, true); bitmap.recycle(); bitmap = copy; } if (bitmap != null) { // Scale down the sampled bitmap if it's still larger than the desired dimension. float scale = Math.min((float) width / bitmap.getWidth(), (float) height / bitmap.getHeight()); scale = Math.max(scale, Math.min((float) height / bitmap.getWidth(), (float) width / bitmap.getHeight())); if (scale < 1) { Matrix m = new Matrix(); m.setScale(scale, scale); Bitmap transformed = createBitmap( bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m); bitmap.recycle(); return transformed; } } return bitmap; } /** * Gets decoded bitmap that keeps orientation as well. */ public Bitmap getBitmap(Uri uri, int width, int height) { Bitmap bitmap = decodeBitmap(uri, width, height); // Rotate the decoded bitmap according to its orientation if it's necessary. if (bitmap != null) { int orientation = getOrientation(uri); if (orientation != 0) { Matrix m = new Matrix(); m.setRotate(orientation); Bitmap transformed = createBitmap( bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m); bitmap.recycle(); return transformed; } } return bitmap; } /** * Saves the bitmap by given directory, filename, and format; if the directory is given null, * then saves it under the cache directory. */ public File saveBitmap( Bitmap bitmap, String directory, String filename, CompressFormat format) { if (directory == null) { directory = context.getCacheDir().getAbsolutePath(); } else { // Check if the given directory exists or try to create it. File file = new File(directory); if (!file.isDirectory() && !file.mkdirs()) { return null; } } File file = null; OutputStream os = null; try { filename = (format == CompressFormat.PNG) ? filename + ".png" : filename + ".jpg"; file = new File(directory, filename); os = new FileOutputStream(file); bitmap.compress(format, DEFAULT_COMPRESS_QUALITY, os); } catch (FileNotFoundException e) { e.printStackTrace(); } finally { closeStream(os); } return file; } /** * 缩放bitmap * @param bitmap * @param w * @param h * @return */ public static Bitmap zoomBitmap(Bitmap bitmap, float w, float h){ int width = bitmap.getWidth(); int height = bitmap.getHeight(); Matrix matrix = new Matrix(); float scaleW = ((float)w / width); float scaleH = ((float)h / height); matrix.postScale(scaleW, scaleH); Bitmap newBmp = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true); return newBmp; } public static Bitmap rotateBitmap(Bitmap bitmap, float degrees){ if (degrees != 0 && bitmap != null) { Matrix m = new Matrix(); m.postRotate(degrees); // m.setRotate(degrees, // (float) bitmap.getWidth() / 2, (float) bitmap.getHeight() / 2); try { bitmap = Bitmap.createBitmap( bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m, true); // if (bitmap != b2) { // bitmap.recycle(); //Android开发网再次提示Bitmap操作完应该显示的释放 // bitmap = b2; // } } catch (OutOfMemoryError ex) { // Android建议大家如果出现了内存不足异常,最好return 原始的bitmap对象。. } } return bitmap; } public static Bitmap drawTextToBitmap(Context gContext, int gResId, String gText) { Log.i(TAG, "drawTextToBitmap = " + gText); Resources resources = gContext.getResources(); float scale = resources.getDisplayMetrics().density; Bitmap bitmap = BitmapFactory.decodeResource(resources, gResId); android.graphics.Bitmap.Config bitmapConfig = bitmap.getConfig(); // set default bitmap config if none if(bitmapConfig == null) { bitmapConfig = android.graphics.Bitmap.Config.ARGB_8888; } // resource bitmaps are imutable, // so we need to convert it to mutable one bitmap = bitmap.copy(bitmapConfig, true); Canvas canvas = new Canvas(bitmap); // new antialised Paint Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); // text color - #3D3D3D paint.setColor(Color.WHITE); // text size in pixels paint.setTextSize((int) (12 * scale)); // text shadow // paint.setShadowLayer(1f, 0f, 1f, Color.WHITE); // draw text to the Canvas center Rect bounds = new Rect(); paint.getTextBounds(gText, 0, gText.length(), bounds); int x = (bitmap.getWidth() - bounds.width())/2; int y = (bitmap.getHeight())/2 + (int)scale*2; canvas.drawText(gText, x, y, paint); canvas.save(Canvas.ALL_SAVE_FLAG); canvas.restore(); return bitmap; } }