Glide中解析图片(静态图片)

作者: 721d739b6619 | 来源:发表于2017-11-11 23:55 被阅读260次

对Glide的加载图片流程不了解的可以看看之前写的Glide范例源码分析

这篇主要介绍Glide在网络获取流之后解析图片,这里讲静态图片。

我们一般在Android中解析一张图片一般是这样的:

 public static Bitmap decodeBitmap(byte[] source,int reqesutWidth,int requestHeight){
        Bitmap resultBitmap = null;
        BitmapFactory.Options option = new BitmapFactory.Options();
        //设置option.inJustDecodeBounds 为true获取图片宽高
        option.inJustDecodeBounds = true;
        BitmapFactory.decodeByteArray(source,0,source.length,option);
        //通过BitmapFactory.Options.inSampleSize的值保存压缩比例值
        option.inSampleSize = caulateInSampleSize(option,reqesutWidth,requestHeight);
        option.inJustDecodeBounds = false;
        resultBitmap = BitmapFactory.decodeByteArray(source,0,source.length,option);
        return resultBitmap;
    }

/**
     * 计算压缩比例
     * 通过BitmapFactory.Options.inSampleSize的值保存该值
     * */
    private static int caulateInSampleSize(BitmapFactory.Options option, int reqesutWidth, int requestHeight) {
        //inSampleSize为1即图片原本大小 如:4 即宽高比为原来1/4
        int inSampleSize = 1;
        //原图片宽高
        int width = option.outWidth;
        int height = option.outHeight;
        //原图片宽高其中一个大于要求的宽高才压缩比例
        if(height > requestHeight || width > reqesutWidth){
            inSampleSize = Math.min(Math.round((float)height / requestHeight), Math.round((float)width / reqesutWidth));
        }
        return inSampleSize;
    }

BitmapFactory.decodeByteArray(source,0,source.length,option);
这句话可以换成BitmapFactory.decodeSteam()或BitmapFactory.decodeResource()

这里抛砖引玉:其实Glide的核心代码也是这样解析的,没有什么特别之处;只不过逼格高一点;

Glide的decode是Downsampler类

Downsampler类

这个截图是上篇文章的,该类是一个抽象类:


image.png

是这里抽象:

image.png

该方法的作用就是上面抛出代码计算BitmapFactory.Options.inSampleSize的;
这样说吧:其实就是通过该字段进行缩放;Glide也是在Downsampler类上实现了三种getSampleSize()该方法:

其中AT_LEAST的计算就是和上面抛出的代码一毛一样:

image.png

另外两个:


image.png image.png

BitmapFactory.inSampleSize是什么

这里要说一下inSampleSize干什么的,看官方解释:

/**
* If set to a value > 1, requests the decoder to subsample the original
* image, returning a smaller image to save memory. The sample size is
* the number of pixels in either dimension that correspond to a single
* pixel in the decoded bitmap. For example, inSampleSize == 4 returns
* an image that is 1/4 the width/height of the original, and 1/16 the
* number of pixels. Any value <= 1 is treated the same as 1. Note: the
* decoder uses a final value based on powers of 2, any other value will
* be rounded down to the nearest power of 2.
*/
public int inSampleSize;

简单理解就是该值越大,解析出来的Bitmap就越小。该值最小为1,取值为2的幂次。

下面是通过google翻译得出:

如果设置为大于1的值,则请求解码器对原始样本进行二次采样
图像,返回一个较小的图像,以节省内存。 样本大小是
任一维度中对应于单个像素的像素数量
像素在解码的位图中。 例如,inSampleSize == 4返回
原始宽度/高度的1/4,1/16的图像
像素数。 任何值<= 1的处理都与1相同。注意:
解码器使用基于2的幂的最终值,任何其他值将会
向下舍入到最接近的2的幂。

知道inSampleSize后,再回Downsampler类的decode()方法

Downsampler类decode()方法:

public Bitmap decode(InputStream is, BitmapPool pool, int outWidth, int outHeight, DecodeFormat decodeFormat) {}

该方法返回一个Bitmap即解析流后的位图。

  • InputStream is 网络返回的流
  • BitmapPool pool 用于解析Bitmap的缓存
  • int outWidth ImageView要求的宽
  • int outHeight ImageView要求的高
  • DecodeFormat decodeFormat 要求解析的格式(默认为RGB_565)


    DecodeFormat类

RGB_565为一个像素占2个字节,而ARGB_8888为一个像素占4个字节;所以用ARGB_8888解析出来的图片会占内存。

decode()方法前期准备

image.png
  • decode()方法开始会 创建两个64k大小的字节数组;

  • 获取BitmapFactory.Options对象,该对象通过一个队列管理,尽量减少创建新对象:


    BitmapFactory.Options对象管理
  • RecyclableBufferedInputStream 创建一个缓冲区,用于解析图片头部判别图片方向

  • ExceptionCatchingInputStream 用于在读取流中检索异常(感觉挺牛B的,可以学习学习)

  • MarkEnforcingInputStream最终通过此流解析图片

解析图片的方向

image.png

这里在流中设置一个标记位,目的就是通过最小读取流来判断图片的方向。标记位最大值为5M

设置临时保存bitmap的容量;获取图片宽高;获取图片旋转的角度

image.png
  • options.inTempStorage该值是用于临时储存Bitmap,官网建议是16K


    image.png
  • getDimensions(invalidatingStream, bufferedStream, options);
    该方法其实最开始已经有代码写了,就是获取宽高


    image.png

decodeStream

该方法后面真正解析图片还会调用一次,这里调用就是为了获取宽高:


image.png

这里因为不是解析bitmap,所以result为null,目的之前说了就是为了获取宽高,所以也设置一个标记位减少读取时间。

  • final int sampleSize = getRoundedSampleSize(degreesToRotate, inWidth, inHeight, outWidth, outHeight);

该方法就是之前说的通过计算宽高比获取sampleSize,最开头说了,Glide默认用AT_LEAST

核心方法downsampleWithSize 解析图片

这里直接上源码:

private Bitmap downsampleWithSize(MarkEnforcingInputStream is, RecyclableBufferedInputStream  bufferedStream,
            BitmapFactory.Options options, BitmapPool pool, int inWidth, int inHeight, int sampleSize,
            DecodeFormat decodeFormat) {
        // Prior to KitKat, the inBitmap size must exactly match the size of the bitmap we're decoding.
        //在KitKat之前,inBitmap大小必须与我们正在解码的位图大小完全匹配
        Bitmap.Config config = getConfig(is, decodeFormat);
        //sampleSize,该值为1 即图片原大小
        options.inSampleSize = sampleSize;
        options.inPreferredConfig = config;
        //当版本高于KITKAT 时候使用 BitmapPool
        if ((options.inSampleSize == 1 || Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT) && shouldUsePool(is)) {
            int targetWidth = (int) Math.ceil(inWidth / (double) sampleSize);
            int targetHeight = (int) Math.ceil(inHeight / (double) sampleSize);
            // BitmapFactory will clear out the Bitmap before writing to it, so getDirty is safe.
            //在写入BitmapFactory之前,BitmapFactory会清除Bitmap,所以getDirty是安全的
            setInBitmap(options, pool.getDirty(targetWidth, targetHeight, config));
        }
        return decodeStream(is, bufferedStream, options);
    }

Android4.1版本使用:ARGB_8888;


image.png

如果图片有透明度使用:ARGB_8888;

当inSampleSize为1或大于Android4.4版本,并且属于:
JPEG,PNG_A,PNG类型使用BitmapPool和BitmapFactory.Options.inBitmap
当然如果你的版本是4.4但是inSampleSize为1是那三种图片类型还是可以使用上面说的两个做缓存;

最后其实就是一句:
final Bitmap result = BitmapFactory.decodeStream(is, null, options);
所以万变不离其中;当然Glide团队使用了各种缓存,优化解析工作。

最后总结一下:

  • 其实解析图片还是 final Bitmap result = BitmapFactory.decodeStream(is, null, options);
  • 通过ExceptionCatchingInputStream此包装类可以捕获在流读取过程中的异常,值的学习。
  • 通过集合管理对象,尽量减少对象的创建,毕竟对象的创建对内存有一定开销。

最尾说一下:BitmapPool这个类


image.png

相关文章

网友评论

    本文标题:Glide中解析图片(静态图片)

    本文链接:https://www.haomeiwen.com/subject/hepgmxtx.html