Android - 图片压缩(luban总结)

1,528 阅读2分钟

Android图片压缩有多种压缩方式,常用的有质量压缩、尺寸压缩(采样率压缩)、通过JNI调用libjpeg库来进行压缩(最后一种请移步blog.csdn.net/chenliguan/…

1.质量压缩

保持像素的前提下改变图片的位深及透明度,现在常见的压缩方式。
    /**
     * 质量压缩
     * 设置options 属性0-100,来实现压缩(因为png是无损压缩,所以该属性对png效果较弱)
     *
     * @param bitmap  需要压缩的图片
     * @param file  压缩后的输入文件
     * @param quality  图片质量 0-100 100为不压缩 
     */
    fun qualityCompress(bitmap: Bitmap?, file: File?, quality: Int) {
        val outputStream = ByteArrayOutputStream()
        // 把压缩后的数据存放到baos中
        bitmap?.compress(Bitmap.CompressFormat.JPEG, quality, outputStream)
        try {
            val fos = FileOutputStream(file)
            fos.write(outputStream.toByteArray())
            fos.flush()
            fos.close()
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

2.尺寸压缩(采样率压缩)

设置图片的采样率,降低图片像素
好处:是不会先将大图片读入内存,大大减少了内存的使用,也不必考虑将大图片读入内存后的释	放事宜。
问题:因为采样率是整数,所以不能很好的保证图片的质量。如我们需要的是在2和3采样率之间,用2的话图片就大了一点,但是用3的话图片质量就会有很明显的下降,这样也无法完全满足我的需要。
    /**
     * 质量压缩
     * 设置options 属性0-100,来实现压缩(因为png是无损压缩,所以该属性对png效果较弱)
     *
     * @param filePath  需要压缩的图片路径
     * @param file  压缩后的输入文件
     * @param inSampleSize  图片质量 1为不压缩
     */
    fun inSampleSizeCompress(filePath: String?, file: File?, inSampleSize: Int) {
        val options = BitmapFactory.Options()
        options.inSampleSize = inSampleSize
        val outputStream = ByteArrayOutputStream()
        // 把压缩后的数据存放到baos中
        val bitmap = BitmapFactory.decodeFile(filePath, options)
        bitmap?.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
        try {
            val fos = FileOutputStream(file)
            fos.write(outputStream.toByteArray())
            fos.flush()
            fos.close()
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

3.luban压缩 尺寸压缩+质量压缩

动态计算压缩比例,建议修改源码 以支持自定义quality
  private fun computeSize(): Int {
        srcWidth = if (srcWidth % 2 == 1) srcWidth + 1 else srcWidth
        srcHeight = if (srcHeight % 2 == 1) srcHeight + 1 else srcHeight
        val longSide: Int = Math.max(srcWidth, srcHeight)
        val shortSide: Int = Math.min(srcWidth, srcHeight)
        val scale = shortSide.toFloat() / longSide
        val result: Int
        result = if (scale <= 1 && scale > 0.5625) {
            // longSide % 1920 > (1920 * 0.8) 则 result++
            (longSide / 1920.0 + if (longSide % 1920 > 1920 * 0.8) 1 else 0).toInt()
        } else if (scale <= 0.5625 && scale > 0.5) {
            longSide / 1920
        } else {
            Math.ceil(shortSide / 1920.0).toInt()
        }
        // 压缩极限 1/(4*4)
        return Math.min(Math.max(result, 1), 4)
    }
    
    
    @Throws(IOException::class)
    fun compress(): File? {
        val options = BitmapFactory.Options()
        options.inSampleSize = computeSize()
        var tagBitmap = BitmapFactory.decodeStream(srcImg.open(), null, options)
        val stream = ByteArrayOutputStream()
        if (Checker.SINGLE.isJPG(srcImg.open())) {
            tagBitmap = rotatingImage(tagBitmap, Checker.SINGLE.getOrientation(srcImg.open()))
        }
        tagBitmap!!.compress(
            if (focusAlpha) Bitmap.CompressFormat.PNG else Bitmap.CompressFormat.JPEG, 
            mQuality, 
            stream
        )
        tagBitmap.recycle()
        val fos = FileOutputStream(tagImg)
        fos.write(stream.toByteArray())
        fos.flush()
        fos.close()
        stream.close()
        return tagImg
    }