软件世界网 购物 网址 三丰软件 | 小说 美女秀 图库大全 游戏 笑话 | 下载 开发知识库 新闻 开发 图片素材
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
移动开发 架构设计 编程语言 Web前端 互联网
开发杂谈 系统运维 研发管理 数据库 云计算 Android开发资料
  软件世界网 -> 移动开发 -> 图片压缩CompressUtil解析 -> 正文阅读
移动开发 最新文章
深入了解android中的消息机制Handler
Android
Libgdx之BitmapFont字体
AndroidApp发布到应用市场的流程
Android开发找工作之前先看看这些知识点吧
View的事件分发机制解析
简单介绍了解白鹭引擎Egret
Cocos2d
android获取本地图片(二)
动画特效七:碰撞动画

[移动开发]图片压缩CompressUtil解析

  2016-04-02 20:53:19

  • CompressUtil 流程图:


CompressUtil 类 详解
public class CompressUtil {
/**
 * 最终封装的压缩方法
 * @param imgPath
 * @return
 */
public static Bitmap process(String imgPath){
    int degree = readPictureDegree(imgPath);    //获取旋转角度
    Bitmap bmp =getBmpByMaxSize(imgPath, 480);  //获取当前路径图片的位图,最大尺寸180*1024
    if (degree != 0) {
        bmp = rotaingImageView(degree, bmp);    //有旋转角度的旋转角度
    }
    return bmp;
}
/**
 * 读取图片属性:旋转的角度
 * @param path 图片绝对路径
 * @return degree旋转的角度
 */
@SuppressLint("NewApi")
public static int readPictureDegree(String path){
    int degree  = 0;    //设置默认图片角度为0度
    try {
        /**
         * Exif: 图像信息
         * Exif是一种图像文件格式,它的数据存储与JPEG格式是完全相同的。
         * 实际上Exif格式就是在JPEG格式头部插入了数码照片的信息,包括
         * 拍摄时的光圈、快门、白平衡、ISO、焦距、日期时间等各种和拍摄
         * 条件以及相机品牌、型号、色彩编码、拍摄时录制的声音以及GPS全
         * 球定位系统数据、缩略图等。你可以利用任何可以查看JPEG文件的
         * 看图软件浏览Exif格式的照片,但并不是所有的图形程序都能处理
         * Exif信息。
         *
         * ExifInterface:  图像信息接口类
         */

        /**
         * ExifInterface构造函数   ExifInterface(String filename)
         * 从指定的JPEG文件中读取EXIF标签。
         */

        //获取指定图片的图片处理对象
        ExifInterface exifInterface = new ExifInterface(path);

        /**
         * int <- getAttributeInt(String tag, int defaultValue)
         * 返回指定标记的整数值
         *
         * Attribute : 属性
         *
         * tag : 指标 (一共21个)
         * ExifInterface.TAG_APERTURE => 光圈指标
         * ExifInterface.TAG_DATETIME => 日期时间指标
         * ExifInterface.TAG_EXPOSURE_TIME => 发布时间指标
         * ExifInterface.TAG_FLASH => 闪光灯
         * ExifInterface.TAG_FOCAL_LENGTH => 焦距指标
         * ExifInterface.TAG_GPS_ALTITUDE => GPS海拔高度指标
         * ExifInterface.TAG_GPS_ALTITUDE_REF => GPS海拔参考点指标
         * ExifInterface.TAG_GPS_DATESTAMP => GPS日期戳指标
         * ExifInterface.TAG_GPS_LATITUDE => GPS纬度指标
         * ExifInterface.TAG_GPS_LATITUDE_REF => GPS纬度参考点指标
         * ExifInterface.TAG_GPS_LONGITUDE => GPS经度指标
         * ExifInterface.TAG_GPS_LONGITUDE_REF => GPS经度参考点指标
         * ExifInterface.TAG_GPS_PROCESSING_METHOD => GPS加工指标
         * ExifInterface.TAG_GPS_TIMESTAMP => GPS时间戳指标
         * ExifInterface.TAG_IMAGE_LENGTH => 图像长度指标
         * ExifInterface.TAG_IMAGE_WIDTH => 图像宽度指标
         * ExifInterface.TAG_ISO => 标签
         * ExifInterface.TAG_MAKE => 制作
         * ExifInterface.TAG_MODEL => 模型
         * ExifInterface.TAG_ORIENTATION => 定位指标
         * ExifInterface.TAG_WHITE_BALANCE => 白平衡指标
         *
         * defaultValue : 默认值 (一共11个)
         *
         * ExifInterface.ORIENTATION_FLIP_HORIZONTAL => 水平翻转
         * ExifInterface.ORIENTATION_FLIP_VERTICAL => 垂直翻转
         * ExifInterface.ORIENTATION_NORMAL => 正常
         * ExifInterface.ORIENTATION_ROTATE_180 => 旋转180度
         * ExifInterface.ORIENTATION_ROTATE_270 => 旋转270度
         * ExifInterface.ORIENTATION_ROTATE_90 => 旋转90度
         * ExifInterface.ORIENTATION_TRANSPOSE => 取向的转置
         * ExifInterface.ORIENTATION_TRANSVERSE => 方位横向
         * ExifInterface.ORIENTATION_UNDEFINED => 方向未定义
         * ExifInterface.WHITEBALANCE_AUTO => 白平衡自动
         * ExifInterface.WHITEBALANCE_MANUAL => 手动白平衡
         */

        // 获取该图片的方向参数
        int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_ROTATE_90);
        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;
}

/**
 * 旋转图片
 * @param angle
 * @param bitmap
 * @return Bitmap
 *
 * Bitmap : 位图
 * Bitmap是Android系统中的图像处理的最重要类之一。
 * 用它可以获取图像文件信息,进行图像剪切、旋转、缩
 * 放等操作,并可以指定格式保存图像文件。
 *
 * Bitmap实现在android.graphics包中。但是Bitmap
 * 类的构造函数是私有的,外面并不能实例化,只能是通
 * 过JNI实例化。这必然是 某个辅助类提供了创建Bitmap
 * 的接口,而这个类的实现通过JNI接口来实例化Bitmap的,
 * 这个类就是BitmapFactory。
 *
 * decode : 解码
 *
 * BitmapFactory.decodeFile(String pathName)
 * BitmapFactory.decodeFile(String pathName,Options opts)
 * BitmapFactory.decodeResource(Resource res,int id)
 * BitmapFactory.decodeResource(Resource res,int id,Options opts)
 *
 * 利用BitmapFactory可以从一个指定文件中,利用decodeFile()解出Bitmap;
 * 也可以定义的图片资源中,利用decodeResource()解出Bitmap
 *
 * 其中Options是decode时的选项
 * 在使用方法decodeFile()/decodeResource()时,都可以指定一个BitmapFacotry.Options。
 *
 * 利用Options的下列属性,可以指定decode的选项
 * inPreferredConfig => decode到内存中,手机中所采用的编码,可选值定义在Bitmap.Config中。缺省值是ARGB_8888
 * inJustDecodeBounds => 如果设置为true,并不会把图像的数据完全解码,亦即decodeXyz()返回值为null,但是Options的outAbc中解出了图像的基本信息
 * inSampleSize => 设置decode时的缩放比例
 *
 */
public static Bitmap rotaingImageView(int angle , Bitmap bitmap) {

    // Bitmap可以和Matrix结合实现图像的剪切、旋转、缩放等操作

    //获取Matrix对象
    Matrix matrix = new Matrix();
    //设置旋转角度
    matrix.postRotate(angle);
    // 创建新的图片
    Bitmap resizedBitmap=bitmap;

    /**
     * 用原Bitmap通过变换生成新的Bitmap的方法:
     *
     * public static Bitmap createBitmap(Bitmap source,int x,int y,int width,int height,Matrix m,boolean filter)
     * 这种方法是最终的实现,后两种只是对这种方法的封装
     *
     * public static Bitmap createBitmap(Bitmap source,int x,int y,int width,int height)
     * 这种方法可以从源Bitmap中指定区域(x,y, width, height)中挖出一块来实现剪切
     *
     * public static Bitmap createScaledBitmap(Bitmap src,int dstWidth,int dstHeight,boolean filter)
     * 这种方法可以把源Bitmap缩放为dstWidth x dstHeight的Bitmap
     *
     * filter => 设为true => 对Bitmap进行滤波处理,会有抗锯齿的效果
     */

    try {
        resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);

        /**
         * Bitmap的recycle问题
         * recycle : 回收
         * 虽然Android有自己的垃圾回收机制,对于是不是要我们自己调用recycle,还的看情况而定。
         * 如果只是使用少量的几张图片,回收与否关系不大。可是若有大量bitmap需要垃圾回收处理,
         * 那必然垃圾回收需要做的次数就更多也发生地更频繁,会对系统资源造成负荷。所以,这个时
         * 候还是自己试用recycle来释放的比较好。
         *
         * 注意 => 只有当你确认你不会在使用这个bitmap的时候,就可以选择调用recycle()方法释放它。
         *
         */
        bitmap.recycle();

        //进行垃圾回收
        System.gc();
    }catch (OutOfMemoryError e){
        e.printStackTrace();
    }
    return resizedBitmap;
}

/**
 * 减少图片质量压缩
 * @param bmp
 * @param maxSize
 * @param fileSize
 * @return
 */
public static Bitmap compressBmp(Bitmap bmp, int maxSize, long fileSize) {
    Bitmap newBmp=bmp;
    //ByteArrayOutputStream => 捕获内存缓冲区的数据,转换成字节数组。
    ByteArrayOutputStream baos=null;
    //ByteArrayInputStream => 将字节数组转化为输入流
    ByteArrayInputStream bais=null;

    int quality = 100;
    if(fileSize>4*1024*1024){
        quality=40;
    }else if(fileSize>2*1024*1024){
        quality=50;
    }else if(fileSize>800*1024){
        quality=60;
    }
    try {
        /**
         * ByteArrayOutputStream类是在创建它的实例时,程序内部创建一个byte型别数组的缓冲区,
         * 然后利用ByteArrayOutputStream和ByteArrayInputStream的实例向数组中写入或读出
         * byte型数据。在网络传输中我们往往要传输很多变量,我们可以利用ByteArrayOutputStream
         * 把所有的变量收集到一起,然后一次性把数据发送出去。
         */
        baos = new ByteArrayOutputStream();
        System.out.print("开始压缩: " + quality);
        /**
         * 图片压缩
         * Bitmap.compress(CompressFormat format, int quality, OutputStream stream)
         * 方法的参数format可设置JPEG或PNG格式;quality可选择压缩质量;fOut是输出流(OutputStream)
         */
        bmp.compress(Bitmap.CompressFormat.JPEG, quality, baos);
        float maxByte = maxSize * 1024;
        baos.flush();
        float scale = 1;
        while (baos.size() > maxByte) {
            System.out.print("压缩大小:" + baos.size() / 1024);
            System.out.print("压缩大小2:" + baos.toByteArray().length / 1024);
            scale = Math.round((float) baos.size() / maxByte);
            if (scale < 1) scale = 1;
            quality -= scale * 2;
            baos.reset();   //重置流,使流计数=0。重置该流丢弃所有当前累积输出。
            bmp.compress(Bitmap.CompressFormat.JPEG, quality, baos);
            baos.flush();
        }

// File file=new File(FileUtil.getAudioPath()+File.separator+System.currentTimeMillis()+”.jpg”);
// BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(file));
// bos.write(baos.toByteArray());
// bos.flush();
// bos.close();
        System.out.print("压缩后大小:" + baos.size() / 1024);
        bais = new ByteArrayInputStream(baos.toByteArray());
        baos.flush();
        newBmp = BitmapFactory.decodeStream(bais);
        bmp.recycle();
        System.gc();
    }catch (OutOfMemoryError e){
        e.printStackTrace();
        //内存溢出则压缩更多
        if(!bmp.isRecycled()) {
            try {
                quality=quality/2;
                baos = new ByteArrayOutputStream();
                bmp.compress(Bitmap.CompressFormat.JPEG, quality, baos);
                bais = new ByteArrayInputStream(baos.toByteArray());
                baos.flush();
                newBmp = BitmapFactory.decodeStream(bais);
            }catch (Exception ex){}
        }
    }catch (Exception e){
        e.printStackTrace();
    }finally{
        try {
            if (bais != null) {
                bais.close();
            }
            if (baos != null) {
                baos.close();
            }
        }catch (Exception e){}
    }
    return newBmp;
}


public static Bitmap getBmpByMaxSize(String path, int maxSize){
    /**
     * Android使用BitmapFactory.Options解决加载大图片内存溢出问题
     *
     * 由于Android对图片使用内存有限制,若是加载几兆的大图片便内存溢出。
     * Bitmap会将图片的所有像素(即长x宽)加载到内存中,如果图片分辨率
     * 过大,会直接导致内存溢出(java.lang.OutOfMemoryError),只有
     * 在BitmapFactory加载图片时使用BitmapFactory.Options对相关参
     * 数进行配置来减少加载的像素。
     *
     * BitmapFactory.Options这个类,有一个字段叫做 inJustDecodeBounds 。
     * 如果我们把它设为true,那么BitmapFactory.decodeFile(String path,
     * Options opt)并不会真的返回一个Bitmap给你,它仅仅会把它的宽,高取回
     * 来给你,这样就不会占用太多的内存,也就不会那么频繁的发生OOM(Out Of
     * Memory)了。
     *
     */
    BitmapFactory.Options options=new BitmapFactory.Options();
    //使图片最小边框缩小到800像素
    options.inJustDecodeBounds=true;
    //这里返回的bitmap=null,但可以通过options.outWidth 和 options.outHeight就是我们想要的宽和高了
    BitmapFactory.decodeFile(path, options);
    //最短的永远都是宽度
    double width=options.outWidth<options.outHeight? options.outWidth: options.outHeight;
    //实际宽度/理想宽度 => 上传图片缩放比例
    int sampleSize=(int)Math.round(width/480);
    System.out.print("上传图片缩放比例:" + sampleSize);
    if(sampleSize<1) sampleSize=1;
    /**
     * inSampleSize表示缩略图大小为原始图片大小的几分之一,
     * 即如果这个值为2,则取出的缩略图的宽和高都是原始图片的1/2,图片大小就为原始大小的1/4。
     */
    options.inSampleSize = sampleSize;
    options.inJustDecodeBounds=false;
    options.inDither=false;    /*不进行图片抖动处理*/
    options.inPreferredConfig=null;  /*设置让解码器以最佳方式解码*/
    /**
     * 下面两个字段需要组合使用  节约内存
     */
    options.inPurgeable = true; // 允许可清除
    options.inInputShareable = true;

    long length=new File(path).length();
    if(length>(1.5*1024*1024)){  //大于1.5M时
        options.inSampleSize+=(int)(length/1024/1024)*0.5; //当大于2M时为避免内存溢出缩小
    }

    Bitmap bitmap=null;
    try {
        bitmap = BitmapFactory.decodeFile(path, options);
    }catch (OutOfMemoryError e){
        e.printStackTrace();
        //内存溢出则将图片缩小一半
        if(options.inSampleSize<1) options.inSampleSize=1;
        options.inSampleSize=options.inSampleSize*2;
        bitmap=BitmapFactory.decodeFile(path, options);
    }
    if(length>(800*1024)) { //大于20K字节压缩
        bitmap = compressBmp(bitmap, maxSize, length);
    }

    return bitmap;
}

}
上一篇文章      下一篇文章      查看所有文章
2016-04-02 20:51:48  
360图书馆 论文大全 母婴/育儿 软件开发资料 网页快照 文字转语音 购物精选 软件 美食菜谱 新闻中心 电影下载 小游戏 Chinese Culture
生肖星座解梦 人民的名义 人民的名义在线看 三沣玩客 拍拍 视频 开发 Android开发 站长 古典小说 网文精选 搜图网 天下美图
中国文化英文 多播视频 装修知识库
2017-4-29 9:48:07
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  软件世界网 --