Initial commit

This commit is contained in:
wangcongtao
2019-12-23 15:08:04 +08:00
commit 80cc1248b2
210 changed files with 17746 additions and 0 deletions

View File

@@ -0,0 +1,673 @@
package com.mogo.utils;
import android.annotation.SuppressLint;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.media.ExifInterface;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.opengl.GLES10;
import android.os.Build;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
/**
* @author wangzhiyuan
* @since 2017/8/30
*/
public class BitmapHelper {
private static final String TAG = "BitmapHelper";
static int ONE_KB = 1024;
static int ONE_MB = ONE_KB * 1024;
static int SIZE_DEFAULT = 2048;
static int SIZE_LIMIT = 2048;
/**
* 根据原图添加圆角
*
* @param source
* @return
*/
public static Bitmap createRoundCornerImage( Bitmap source, float corner ) {
final Paint paint = new Paint();
paint.setAntiAlias( true );
Bitmap target = Bitmap.createBitmap( source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888 );
Canvas canvas = new Canvas( target );
RectF rect = new RectF( 0, 0, source.getWidth(), source.getHeight() );
canvas.drawRoundRect( rect, corner, corner, paint );
paint.setXfermode( new PorterDuffXfermode( PorterDuff.Mode.SRC_IN ) );
canvas.drawBitmap( source, 0, 0, paint );
return target;
}
public static byte[] bitmapToBytes( Bitmap bitmap ) {
if ( bitmap == null ) {
return null;
}
ByteArrayOutputStream bos = null;
byte[] result = null;
try {
bos = new ByteArrayOutputStream();
bitmap.compress( Bitmap.CompressFormat.JPEG, 100, bos );
result = bos.toByteArray();
} catch ( Exception e ) {
e.printStackTrace();
result = null;
} finally {
IOUtils.closeSilently( bos );
}
return result;
}
/**
* Use quality compression to compress bitmap's size to be smaller than a max size, and convert it to bytes thereafter.
* Note that this method will not report compressing ratio related data.
*
* @param bitmap data source
* @param maxSize unit in kb
* @return bytes after compressing bitmap to a size smaller than a specific max size.
*/
public static byte[] bitmapToBytes( Bitmap bitmap, int maxSize ) {
final long start = System.currentTimeMillis();
if ( bitmap == null ) {
return null;
}
final int maxSizeOfBytes = maxSize * ONE_KB;
ByteArrayOutputStream bos = null;
byte[] result = null;
try {
bos = new ByteArrayOutputStream();
int quality = 100;
int fullSize = 0;
do {
bos.reset();
bitmap.compress( Bitmap.CompressFormat.JPEG, quality, bos );
if ( quality == 100 ) {
fullSize = bos.size();
}
Log.i( TAG, "quality<---->size, " + quality + "<---->" + bos.size() / 1024 );
}
while ( bos.size() > maxSizeOfBytes && ( quality -= ( fullSize > ONE_MB ) ? 10 : 5 ) >= 0 );
result = bos.toByteArray();
final long end = System.currentTimeMillis();
Log.i( TAG,
"bitmap to bytes costs " + ( end - start ) + "ms, \n" +
"bitmap full size is " + ( fullSize / 1024 ) + "kb, \n" +
"bitmap final size is " + ( bos.size() / 1024 ) + "kb, \n" +
"bitmap quality is " + quality );
} catch ( Exception e ) {
e.printStackTrace();
result = null;
} finally {
IOUtils.closeSilently( bos );
}
return result;
}
/**
* Use quality compression to compress bitmap to be smaller than a specific max size.
*
* @param bitmap data source
* @param maxSize a specific max size which's unit is kb.
* @return a compressed bitmap smaller than the max size.
*/
public static Bitmap compressBitmap( Bitmap bitmap, int maxSize, final OnCompressListener listener ) {
if ( bitmap == null || bitmap.isRecycled() ) {
return null;
}
listener.onBeforeCompress();
ByteArrayOutputStream bos = null;
Bitmap target = null;
try {
bos = new ByteArrayOutputStream();
int quality = 100;
do {
bos.reset();
bitmap.compress( Bitmap.CompressFormat.JPEG, quality, bos );
}
while ( bos.size() / 1024 > maxSize && ( quality -= 5 ) >= 0 );
byte[] result = bos.toByteArray();
target = bytesToBitmap( result );
if ( listener != null ) {
listener.onCompressSuccess( result );
}
} catch ( Exception e ) {
e.printStackTrace();
target = null;
if ( listener != null ) {
listener.onCompressFailed( "压缩失败" );
}
} finally {
IOUtils.closeSilently( bos );
}
return target;
}
public static Bitmap compressBitmap( Bitmap bitmap, int maxSize ) {
if ( bitmap == null ) {
return null;
}
ByteArrayOutputStream bos = null;
Bitmap target = null;
try {
bos = new ByteArrayOutputStream();
int quality = 100;
do {
bos.reset();
bitmap.compress( Bitmap.CompressFormat.JPEG, quality, bos );
}
while ( bos.size() / 1024 > maxSize && ( quality -= 5 ) >= 0 );
byte[] result = bos.toByteArray();
target = bytesToBitmap( result );
} catch ( Exception e ) {
e.printStackTrace();
target = null;
} finally {
IOUtils.closeSilently( bos );
}
return target;
}
/**
* Decode an immutable bitmap from the specified byte array.
*
* @param b byte array of compressed image data
* @return an immutable bitmap or null in case of exception.
*/
public static Bitmap bytesToBitmap( byte[] b ) {
if ( b != null && b.length != 0 ) {
return BitmapFactory.decodeByteArray( b, 0, b.length );
} else {
return null;
}
}
/**
* Decode an immutable bitmap from the specified byte array.
*
* @param b byte array of compressed image data
* @param options Options that control downsampling and whether the
* image should be completely decoded, or just is size returned.
* @return an immutable bitmap or null in case of exception.
*/
public static Bitmap bytesToBitmap( byte[] b, BitmapFactory.Options options ) {
if ( b.length != 0 ) {
return BitmapFactory.decodeByteArray( b, 0, b.length, options );
} else {
return null;
}
}
/**
* Get max supported image size which will differ from different devices.
*
* @return max size related to the device.
*/
public static int getMaxSupportedImageSize() {
int textureLimit = getMaxTextureSize();
if ( textureLimit == 0 ) {
return SIZE_DEFAULT;
} else {
return Math.min( textureLimit, SIZE_LIMIT );
}
}
public static int getMaxTextureSize2() {
// The OpenGL texture size is the maximum size that can be drawn in an ImageView
int[] maxSize = new int[1];
GLES10.glGetIntegerv( GLES10.GL_MAX_TEXTURE_SIZE, maxSize, 0 );
return maxSize[0];
}
/**
* Decode a bitmap's input stream to find a proper inSampleSize according to device's max supported size.
*
* @param is bitmap's data source
* @param close whether to close input stream after work is done.
* @return a proper inSampleSize
*/
public static int findProperInSampleSize( InputStream is, boolean close ) {
// Just decode image size into options
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
try {
BitmapFactory.decodeStream( is, null, options );
} catch ( Exception e ) {
e.printStackTrace();
} finally {
if ( close ) IOUtils.closeSilently( is );
}
int maxSize = getMaxSupportedImageSize();
int sampleSize = 1;
while ( options.outHeight / sampleSize > maxSize || options.outWidth / sampleSize > maxSize ) {
sampleSize = sampleSize << 1;
}
Log.i( TAG, "sample size is " + sampleSize );
return sampleSize;
}
/**
* Read a picture's degree from a file.
*
* @param file data source of a picture
* @return degrees range from 0 to 360
*/
public static int readPictureDegree( File file ) {
return readPictureDegree( file.getAbsolutePath() );
}
/**
* Read a picture's degree from a file, we use {@link ExifInterface} instead of {@link android.media.ExifInterface}
* to avoid some unexpected bugs.
*
* @param filePath file's absolute path which we can read data source of a picture from.
* @return degrees range from 0 to 360
*/
public static int readPictureDegree( String filePath ) {
int degree = 0;
try {
ExifInterface exifInterface = new ExifInterface( filePath );
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 ( Exception e ) {
e.printStackTrace();
}
Log.i( TAG, "ExifInterface, degree is " + degree );
return degree;
}
/**
* Rotate an bitmap to a specific angle.
*
* @param angle target angle
* @param bitmap data source
* @return Returns an immutable bitmap from subset of the source bitmap,
* transformed by the optional matrix. The new bitmap may be the
* same object as source, or a copy may have been made. It is
* initialized with the same density as the original bitmap.
* <p>
* If the source bitmap is immutable and the requested subset is the
* same as the source bitmap itself, then the source bitmap is
* returned and no new bitmap is created.
*/
public static Bitmap rotateBitmap( int angle, Bitmap bitmap ) {
if ( bitmap == null ) {
return null;
}
try {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Matrix matrix = new Matrix();
matrix.preRotate( angle );
return Bitmap.createBitmap( bitmap, 0, 0, width, height, matrix, true );
} catch ( Exception e ) {
e.printStackTrace();
return bitmap;
}
}
/**
* Get picture's absolute path according to its uri.
*
* @param context context
* @param uri picture's uri
* @return absolute path of uri.
*/
public static String getRealPathFromUri( Context context, Uri uri ) {
int sdkVersion = Build.VERSION.SDK_INT;
if ( sdkVersion >= 19 ) {
return getRealPathFromUriAboveApi19( context, uri );
} else {
return getRealPathFromUriBelowAPI19( context, uri );
}
}
/**
* Create a default {@link BitmapFactory.Options} .
* Note this options use rgb_565 and a proper inSampleSize in order to save memory.
*
* @param is data source of picture
* @param close whether to close data source
* @return options containing rgb_565 config and a proper inSampleSize.
*/
public static BitmapFactory.Options newDefaultOptions( InputStream is, boolean close ) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
options.inSampleSize = BitmapHelper.findProperInSampleSize( is, close );
return options;
}
/**
* Save picture to local file.
*
* @param bitmap data source
* @param file local file to store picture.
*/
public static void savePicture( Bitmap bitmap, File file ) {
final long start = System.currentTimeMillis();
if ( bitmap == null || file == null ) {
Log.i( TAG, "保存失败, bitmap or file is null." );
return;
}
if ( file.getParentFile() != null && !file.getParentFile().exists() ) {
file.getParentFile().mkdirs();
}
try {
final FileOutputStream fos = new FileOutputStream( file );
bitmap.compress( Bitmap.CompressFormat.JPEG, 100, fos );
fos.flush();
fos.close();
if ( file.exists() ) {
Log.i( TAG, "保存成功" );
}
} catch ( Exception e ) {
e.printStackTrace();
}
Log.i( TAG, "saving picture costs " + ( System.currentTimeMillis() - start ) + "ms" );
}
/**
* 适配api19以下(不包括api19),根据uri获取图片的绝对路径
*
* @param context 上下文对象
* @param uri 图片的Uri
* @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null
*/
private static String getRealPathFromUriBelowAPI19( Context context, Uri uri ) {
return getDataColumn( context, uri, null, null );
}
/**
* 适配api19及以上,根据uri获取图片的绝对路径
*
* @param context 上下文对象
* @param uri 图片的Uri
* @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null
*/
@SuppressLint( "NewApi" )
private static String getRealPathFromUriAboveApi19( Context context, Uri uri ) {
String filePath = null;
try {
// 如果是document类型的 uri, 则通过document id来进行处理
if ( DocumentsContract.isDocumentUri( context, uri ) ) {
String documentId = DocumentsContract.getDocumentId( uri );
if ( isMediaDocument( uri ) ) {
// 使用':'分割
String id = documentId.split( ":" )[1];
String selection = MediaStore.Images.Media._ID + "=?";
String[] selectionArgs = {id};
filePath = getDataColumn( context, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection, selectionArgs );
} else if ( isDownloadsDocument( uri ) ) {
Uri contentUri = ContentUris.withAppendedId( Uri.parse( "content://downloads/public_downloads" ), Long.valueOf( documentId ) );
filePath = getDataColumn( context, contentUri, null, null );
}
} else if ( "content".equalsIgnoreCase( uri.getScheme() ) ) {
filePath = getDataColumn( context, uri, null, null );
} else if ( "file".equals( uri.getScheme() ) ) {
filePath = uri.getPath();
}
} catch ( Exception e ) {
e.printStackTrace();
}
return filePath;
}
/**
* 获取数据库表中的 _data 列即返回Uri对应的文件路径
*/
private static String getDataColumn( Context context, Uri uri, String selection, String[] selectionArgs ) {
String path = null;
String[] projection = new String[]{MediaStore.Images.Media.DATA};
Cursor cursor = null;
try {
cursor = context.getContentResolver().query( uri, projection, selection, selectionArgs, null );
if ( cursor != null && cursor.moveToFirst() ) {
int columnIndex = cursor.getColumnIndexOrThrow( projection[0] );
path = cursor.getString( columnIndex );
}
} catch ( Exception e ) {
if ( cursor != null ) {
cursor.close();
cursor = null;
}
} finally {
if ( cursor != null ) {
cursor.close();
cursor = null;
}
}
return path;
}
/**
* @param uri the Uri to check
* @return Whether the Uri authority is MediaProvider
*/
private static boolean isMediaDocument( Uri uri ) {
return "com.android.providers.media.documents".equals( uri.getAuthority() );
}
/**
* @param uri the Uri to check
* @return Whether the Uri authority is DownloadsProvider
*/
private static boolean isDownloadsDocument( Uri uri ) {
return "com.android.providers.downloads.documents".equals( uri.getAuthority() );
}
public static int getMaxTextureSize() {
try {
// Safe minimum default size
final int IMAGE_MAX_BITMAP_DIMENSION = SIZE_DEFAULT;
// Get EGL Display
EGL10 egl = ( EGL10 ) EGLContext.getEGL();
EGLDisplay display = egl.eglGetDisplay( EGL10.EGL_DEFAULT_DISPLAY );
// Initialise
int[] version = new int[2];
egl.eglInitialize( display, version );
// Query total number of configurations
int[] totalConfigurations = new int[1];
egl.eglGetConfigs( display, null, 0, totalConfigurations );
// Query actual list configurations
EGLConfig[] configurationsList = new EGLConfig[totalConfigurations[0]];
egl.eglGetConfigs( display, configurationsList, totalConfigurations[0], totalConfigurations );
int[] textureSize = new int[1];
int maximumTextureSize = 0;
// Iterate through all the configurations to located the maximum texture size
for ( int i = 0; i < totalConfigurations[0]; i++ ) {
// Only need to check for width since opengl textures are always squared
egl.eglGetConfigAttrib( display, configurationsList[i], EGL10.EGL_MAX_PBUFFER_WIDTH, textureSize );
// Keep trackCustomEvent of the maximum texture size
if ( maximumTextureSize < textureSize[0] )
maximumTextureSize = textureSize[0];
}
// Release
egl.eglTerminate( display );
// Return largest texture size found, or default
return Math.max( maximumTextureSize, IMAGE_MAX_BITMAP_DIMENSION );
} catch ( Exception e ) {
e.printStackTrace();
}
return 0;
}
public static String bitmapToBase64( Bitmap bitmap ) {
String result = null;
try {
if ( bitmap != null ) {
result = Base64.encodeToString( bitmapToBytes( bitmap ), Base64.DEFAULT );
}
} catch ( Exception e ) {
e.printStackTrace();
}
return result;
}
public static Bitmap base64ToBitmap( String base64Data ) {
byte[] bytes = Base64.decode( base64Data, Base64.DEFAULT );
return BitmapFactory.decodeByteArray( bytes, 0, bytes.length );
}
/**
* 在系统返回的intent中获取图片信息并转化为uri
*
* @param data
* @return
*/
public static Uri convertUri( Context context, Intent data ) {
if ( data == null || data.getData() == null ) {
return null;
}
Uri localUri = data.getData();
String scheme = localUri.getScheme();
String imagePath = "";
if ( "content".equals( scheme ) ) {
String[] filePathColumns = {MediaStore.Images.Media.DATA};
Cursor c = context.getContentResolver().query( localUri, filePathColumns, null, null, null );
if ( c != null ) {
c.moveToFirst();
int columnIndex = c.getColumnIndex( filePathColumns[0] );
imagePath = c.getString( columnIndex );
c.close();
}
} else if ( "file".equals( scheme ) ) {//小米4选择云相册中的图片是根据此方法获得路径
imagePath = localUri.getPath();
}
if ( TextUtils.isEmpty( imagePath ) ) {
return localUri;
}
Uri uri = Uri.fromFile( new File( imagePath ) );
return uri != null ? uri : localUri;
}
public static Bitmap colorToBitmap( Context context, int colorResId ) {// drawable 转换成bitmap
Bitmap.Config config = Bitmap.Config.ARGB_8888;// 取drawable的颜色格式
Bitmap bitmap = Bitmap.createBitmap( 1, 1, config );// 建立对应bitmap
bitmap.eraseColor( context.getResources().getColor( colorResId ) );
return bitmap;
}
public static String getAlphaHexValue( float alpha ) {
String color = Integer.toHexString( ( int ) alpha * 255 );
return TextUtils.isEmpty( color ) ? color : color.toUpperCase();
}
/**
* 抓取本地视频缩略图(操作可能耗时,尽量异步进行)
*
* @param filePath
* @return
*/
public static Bitmap getVideoThumbnail( String filePath ) {
Bitmap b = null;
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try {
retriever.setDataSource( filePath );
b = retriever.getFrameAtTime();
} catch ( IllegalArgumentException e ) {
e.printStackTrace();
} catch ( RuntimeException e ) {
e.printStackTrace();
} finally {
try {
retriever.release();
} catch ( RuntimeException e ) {
e.printStackTrace();
}
}
return b;
}
public interface OnCompressListener {
void onCompressSuccess( byte[] data );
void onCompressFailed( String msg );
void onBeforeCompress();
}
}