图片压缩

图片压缩简介:
图片的压缩:(分辨率压缩/像素压缩,质量压缩)

分辨率压缩个人理解:
利用 bitmap = BitmapFactory.decodeFile(path, newOpts.inSampleSize像素比率); 得到显示的bitmap,而且还可以进行输出的质量压缩,即利用
bitmap.compress(有损JPEG或无损压缩png,quality,);

质量压缩有两种:
  有损压缩:将色相和色纯度相近的色素合并,使图片信息减少,达到降质压缩效果,仅仅通过减少重复达到缩小体积目的(如:JPEG 格式);
    无损压缩:将相同的色素只保留一次,是对文件的数据存储方式进行优化,解压后仍能还原以前的像素数的压缩(如:PNG 格式);
也就是说,bitmap.compress()参数采用PNG格式的,就是默认使用的是无损压缩方式,同时透明度效果有效,而若采用JPEG格式的,默认使用的是有损压缩,无透明效果;

额外部分:
打印机使用 DPI 来确定图片的每一个像素和实际的一个长度单位(英寸)之间的大小比例(比如300-72 DPI)。
图片像素密度:单位英寸内像素点的个数;
图片分辨率:屏幕中横纵向像素值的乘机;
图片尺寸越大则像素密度越小,而分辨率其实还是没变的;

代码示例:

public class HeadPhotoUpload implements View.OnClickListener{
    public Dialog picDialog;
    public Activity activity;
    public static final int RESULT_OK = -1;
    protected static final int CHOOSE_PICTURE = 99;//选择相册中的图片作为头像
    protected static final int TAKE_PICTURE = 100;//选择相册中的图片作为头像
    private Bitmap bitmap;
    File file;
    private File file1;
    private  File file2;
    private static HeadPhotoUpload instance = new HeadPhotoUpload();
    public int targetH,targetW ;
    private ImageView imageView3;


    private HeadPhotoUpload() {}


    public void setHW(int targetH ,int targetW){
        this.targetH = targetH;
        this.targetW = targetW;
    }


    public static HeadPhotoUpload getInstance() {
        return instance;
    }
    //打开相册或相机
    public void showChoosePicDialog(Activity activity) {
        this.activity = activity;
        //AlertDialog用的是v24的,若用v7的则无分割线显示,需要其他设置
        View dialogContentView = LayoutInflater.from(activity).inflate(R.layout.userinfo_headphoto, null, false);
        TextView takePhoto = (TextView) dialogContentView.findViewById(R.id.take_head_photo_tv);
        TextView selectPhoto = (TextView) dialogContentView.findViewById(R.id.select_head_photo_tv);
        TextView cancelTv = (TextView) dialogContentView.findViewById(R.id.cancel_dialog_tv);
        takePhoto.setOnClickListener(this);
        selectPhoto.setOnClickListener(this);
        cancelTv.setOnClickListener(this);
        picDialog = new Dialog(activity, R.style.AlertDialogStyle);
        Window dialogWindow = picDialog.getWindow();
        picDialog.setContentView(dialogContentView);
        picDialog.setCancelable(true);
        dialogWindow.setWindowAnimations(R.style.DialogBottom);
        dialogWindow.setGravity(Gravity.BOTTOM);
        picDialog.show();
    }


    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.select_head_photo_tv:     // 选择本地照片
                localAlbumChoose();
                break;
            case R.id.take_head_photo_tv:       // 拍照
                cameraChoose();
                break;
            case R.id.cancel_dialog_tv:         //取消头像dialog
                break;
        }
        picDialog.dismiss();
    }


    private void cameraChoose() {
        // 执行拍照前,判断SD卡是否存在
        String SDState = Environment.getExternalStorageState();
        if (!SDState.equals(Environment.MEDIA_MOUNTED)) {
            ToastUtil.showContinuousToast("内存卡不存在");
            return;
        }
        //动态权限检测及申请,未设置权限请求结果监听
        if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.CAMERA}, 20);
            return;
        }
        Intent openCameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//        Uri tempUri = Uri.fromFile(file); //指明路径能获得高清图片
//        openCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, tempUri);
        activity.startActivityForResult(openCameraIntent, TAKE_PICTURE);
    }


    private void localAlbumChoose() {
        Intent openAlbumIntent = new Intent(Intent.ACTION_GET_CONTENT);
        openAlbumIntent.setType("image/*");
        activity.startActivityForResult(openAlbumIntent, CHOOSE_PICTURE);
    }


    public void onActivityResult(int requestCode, int resultCode, Intent data, ImageView toIv1,ImageView toIv2,ImageView toIv3) {
        imageView3 = toIv3;
        if (resultCode == RESULT_OK) { // 如果返回码是可以用的
            switch (requestCode) {
                case TAKE_PICTURE:  //从相机中获取图片
                    Bundle bundle = data.getExtras();
                    if (bundle != null) {
                        bitmap = (Bitmap) bundle.get("data");
                        toIv1.setImageBitmap(bitmap);
                        toMakeSmallIV(bitmap,toIv2);
                    }
                    break;


                case CHOOSE_PICTURE:    //从相册中获取图片
                    ContentResolver resolver = activity.getContentResolver();
                    Uri originalUri = data.getData(); //相册中获取图片地址
                    if (originalUri != null) {
                        try {
                            bitmap = MediaStore.Images.Media.getBitmap(resolver, originalUri);
                            toIv1.setImageBitmap(bitmap);
                            toMakeSmallIV(bitmap,toIv2);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
            }
        }
    }


    private void toMakeSmallIV(Bitmap image, ImageView toIv2) {  //像素压缩(显示)
        Log.d("file1 图片未压缩大小",String.valueOf(getBitmapSize(image)));
        ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
        image.compress(Bitmap.CompressFormat.PNG, 100, baos);
        BitmapFactory.Options newOpts = new BitmapFactory.Options();
        newOpts.inJustDecodeBounds = true;
        //(无损像素压缩模式)
        BitmapFactory.decodeByteArray(baos.toByteArray(), 0, baos.toString().length(), newOpts);
        newOpts.inJustDecodeBounds = false;
        int w = newOpts.outWidth;
        int h = newOpts.outHeight;
        // 缩放比,由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
        int be = 1;// be=1表示不缩放
        if (h > targetH || w > targetW) {
            final int heightRatio = Math.round((float) h / targetH);
            final int widthRatio = Math.round((float) w / targetW);
            be = heightRatio < widthRatio ? heightRatio : widthRatio;
        }
        newOpts.inSampleSize = be;// 设置缩放比例
        // 重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
        image = BitmapFactory.decodeStream(isBm, null, newOpts);
        // 压缩好比例大小后还可以再进行质量压缩
        toIv2.setImageBitmap(image);
        if (file1 == null) {//本地临时存储图片地址
            file1 = new File(Environment.getExternalStorageDirectory(), "smalIV_1.jpg");
        }
        saveBitmap(image, file1);
        int bitmapSize = getBitmapSize(image);
        Log.d("file2:图片像素压缩后大小", String.valueOf(bitmapSize));
        Log.d("file2:图片像素压缩后文件大小", String.valueOf(file1.length()));
        compressImage(image);

    }


    public Bitmap compressImage(Bitmap image) { //图片质量压缩(输出)
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        image.compress(Bitmap.CompressFormat.JPEG, 100, baos);// 有损质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
        int options = 100;
        while (baos.toByteArray().length / 1024 > 50) { // 循环判断如果压缩后图片是否大于50kb,大于继续压缩
            baos.reset();// 重置baos即清空baos
            image.compress(Bitmap.CompressFormat.JPEG, options, baos);//有损质量压缩
            options -= 10;// 每次都减少10
        }
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把压缩后的数据baos存放到ByteArrayInputStream中
        image = BitmapFactory.decodeStream(isBm, null, null);// 把ByteArrayInputStream数据生成图片


        if (file2 == null) {//本地临时存储图片地址
            file2 = new File(Environment.getExternalStorageDirectory(), "smalIV_2.jpg");
        }
        saveBitmap(image, file2);
        int bitmapSize = getBitmapSize(image);
        Log.d("file3:图片质量压缩后大小",String.valueOf(bitmapSize));
        Log.d("file3:图片质量压缩后文件大小",String.valueOf(file2.length()));
        imageView3.setImageBitmap(image);
        return image;
    }


    public static void saveBitmap(Bitmap bitmap, File file) {
        FileOutputStream fos = null;
        try {
            File parentFile = file.getParentFile();
            if(!parentFile.exists()){
                parentFile.mkdirs();
            }
            fos = new FileOutputStream(file,false);
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
            fos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    public File getFileMe() {
        File ivFile;
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
            ivFile = new File(Environment.getExternalStorageDirectory(), "DCIM/camera");
        else ivFile = Environment.getDataDirectory();
        if (!ivFile.exists() || !ivFile.isDirectory()) ivFile.mkdirs();
        return new File(ivFile, "out.jpg");
    }


    /**
     * 得到bitmap的大小
     */
    public static int getBitmapSize(Bitmap bitmap) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {    //API 19
            return bitmap.getAllocationByteCount();
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {//API 12
            return bitmap.getByteCount();
        }
        // 在低版本中用一行的字节x高度
        return bitmap.getRowBytes() * bitmap.getHeight();              //earlier version
    }
}
public byte[] getBody() throws AuthFailureError {
        Map
    
    
     
      params = getParams();
        if (params != null && params.size() > 0) {
            return encodeParameters(params, getParamsEncoding());
        }
        return null;
    }

//即JsonRequest中的getBody()方法变成这样:
  @Override
    public byte[] getBody() {
        try {
            return mRequestBody == null ? null : mRequestBody.getBytes(PROTOCOL_CHARSET);
        } catch (UnsupportedEncodingException uee) {
            VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s",
                    mRequestBody, PROTOCOL_CHARSET);
            return null;
        }
    }
    
    

实例结果:
   D/file1 图片未压缩大小: 31961088       
   D/file2:图片像素压缩后图片大小: 124848  文件经过图片解析,使得内存占用加大     
   D/file2:图片像素压缩后文件大小:  40185  图片文件在硬盘中存储大小,该文件大小大于其输出流大小
   D/file3:图片质量压缩后图片大小: 124848   质量压缩后只是图片文件存储大小变小,而用于显示的分辨率未变
   D/file3:图片质量压缩后文件大小:  23242   

注意:保存图片的文件大小一定小于Bitmap大小,因为Bitmap是经过文件解析后显示的数字图片数据,而图片文件的输出流大小也一定会小于文件;

总结:
    决定图片显示时占内存大小的决定因素,就是图片的分辨率,所以在图片显示时,可采用分辨率压缩(bitmap = BitmapFactory.decodeFile(path, newOpts.inSampleSize像素比率)),使图片显示时少占用空间;
若减小图片存储占用内存,可采用分辨率压缩和质量压缩(bitmap.compress(有损JPEG或无损压缩png,quality,))相结合方式实现;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值