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;
}
}