Glide4.8源码拆解(四)Bitmap解析之"下采样"浅析

栏目: Android · 发布时间: 6年前

内容简介:Glide归根结底是一个图片加载框架,它一定会涉及到本文主要分析这两个类:假设我们有一张宽高100x200的网络图片,需要加载到300x300像素的ImageView上(scaleType属性为CenterCrop),用Glide加载,不做任何处理会得到多大的Bitmap?

Glide归根结底是一个图片加载框架,它一定会涉及到 BitmapFactory 相关API把 Bitmap 读取到内存;可能大家已经很熟悉如何高效的加载Bitmap(比如使用inSample等),这一章还是要看一看Glide是如何玩转的;

本文主要分析这两个类:

DownsampleStrategy
Downsampler

从"Glide会对原图进行放大"案例开始

假设我们有一张宽高100x200的网络图片,需要加载到300x300像素的ImageView上(scaleType属性为CenterCrop),用Glide加载,不做任何处理会得到多大的Bitmap?

Glide.with(MainActivity.this).load(URL).listener(new RequestListener<Drawable>() {
        @Override
        public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
          return false;
        }

        @Override
        public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
          if (resource instanceof BitmapDrawable){
            Bitmap bitmap = ((BitmapDrawable) resource).getBitmap();
            Log.d("onResourceReady",bitmap.toString());
          }
          return false;
        }
      }).into(vh.imageView);
复制代码

不出意外会得到300*300的Bitmap,Bitmap比例显然被放大了;

我怀疑是进行了 Transformation 操作,所以我们要禁用 Transformation ;

假设我们对RequestOptions加上noTransformation的属性,比如这个样子:

RequestOptions requestOptions = RequestOptions.noTransformation();
 Glide.with(MainActivity.this).load(URL).apply(requestOptions).listener(xxx).into(vh.imageView);
复制代码

不出意外会得到300*600的Bitmap,Bitmap比例依然在被放大,甚至更大了;

如果我们不想让Bitmap被放大,可以用加载原图尺寸的方式,比如设置 override(Target.SIZE_ORIGINAL,Target.SIZE_ORIGINAL) 或者用 SimpleTarget 这样的 Target ,但是加载原图终究不是解决方案,为什么?内存的原因,大部分情况下我们还是希望Glide来对图片进行缩小,加载原图的操作等于是直接把APP往OOM送近了一步;

Bitmap被放大不是Glide发明的,是Android官方带的头,比如同一张图片,放在res中不同drawable文件夹下,得到的尺寸不一样;既然如此,Bitmap被放大肯定是有积极意义的,也难怪Glide很少提及这事;

但是,总会有强迫症患者不想Bitmap被放大,Glide肯定也想到了这一点,具体怎么限制Bitmap不会放大,答案的使用 RequestOptions.downsample()方法 ;

RequestOptions.downsample() 方法接受参数类型为: DownsampleStrategy ,那么我们从 DownsampleStrategy 开始分析;

DownsampleStrategy

DownsampleStrategy 顾名思义就是下采样策略,下采样是图像进行缩小的一种方式;

DownsampleStrategy.java

public abstract class DownsampleStrategy {
//获取缩放比例
public abstract float getScaleFactor(int sourceWidth, int sourceHeight, int requestedWidth,
      int requestedHeight);
      
//获取SampleSize策略
public abstract SampleSizeRounding getSampleSizeRounding(int sourceWidth, int sourceHeight,
      int requestedWidth, int requestedHeight);
}

public enum SampleSizeRounding {
    //内存优先 
    MEMORY,
    //图片质量优先
    QUALITY,
  }
复制代码

DownsampleStrategy 是抽象类,提供两个抽象方法, getScaleFactor() 顾名思义是获取缩放比例的, getSampleSizeRounding() 可能是获取SampleSize的, DownsampleStrategy 真是的实现类在其内部,几个嵌套内部类 FitCenter , CenterOutside , AtLeast , AtMost , None , CenterInside ,除此之外, DownsampleStrategy 还定义对应类的静态变量;

DownsampleStrategy.java

public static final DownsampleStrategy FIT_CENTER = new FitCenter();

 public static final DownsampleStrategy CENTER_OUTSIDE = new CenterOutside();

 public static final DownsampleStrategy AT_LEAST = new AtLeast();
 
 public static final DownsampleStrategy AT_MOST = new AtMost();
 
 public static final DownsampleStrategy CENTER_INSIDE = new CenterInside();
 
 public static final DownsampleStrategy NONE = new None();
 
 public static final DownsampleStrategy DEFAULT = CENTER_OUTSIDE;
复制代码

其中 DEFAULT 应该是Glide默认的策略,它指向的是 CENTER_OUTSIDE ,我们简单看一下 CenterOutside 的逻辑;

CenterOutside.java

private static class CenterOutside extends DownsampleStrategy {

    @Synthetic
    CenterOutside() { }

    @Override
    public float getScaleFactor(int sourceWidth, int sourceHeight, int requestedWidth,
        int requestedHeight) {
        //宽度比例(控件/图片)
      float widthPercentage = requestedWidth / (float) sourceWidth;
      //高度比例(控件/图片)
      float heightPercentage = requestedHeight / (float) sourceHeight;
      //谁大取谁
      return Math.max(widthPercentage, heightPercentage);
    }

    @Override
    public SampleSizeRounding getSampleSizeRounding(int sourceWidth, int sourceHeight,
        int requestedWidth, int requestedHeight) {
      return SampleSizeRounding.QUALITY;
    }
  }
复制代码

主要关注是 CenterOutsidegetScaleFactor() 逻辑,该逻辑非常简单,主要是拿 控件宽高/图片宽高 ,得到尺寸取最大值,我们回到文章开头的那个例子,假设此时控件宽高是300x300,图片宽高100x200:

widthPercentage = 300/100 = 3.0f;

heightPercentage = 300/200 = 1.5f;

Math.max(widthPercentage, heightPercentage) = 3.0f;

从这个推理来看,文章开头那个图片确实会被放大3倍;

文章开头试图找控制缩放比例不超过1的,就是只缩小不放大的,有没有这样的策略,其实是有的,比如 AtLeast , AtMost

AtLeast.java

private static class AtLeast extends DownsampleStrategy {

    @Synthetic
    AtLeast() { }

    @Override
    public float getScaleFactor(int sourceWidth, int sourceHeight, int requestedWidth,
        int requestedHeight) {
      int minIntegerFactor = Math.min(sourceHeight / requestedHeight, sourceWidth / requestedWidth);
      return minIntegerFactor == 0 ? 1f : 1f / Integer.highestOneBit(minIntegerFactor);
    }

    @Override
    public SampleSizeRounding getSampleSizeRounding(int sourceWidth, int sourceHeight,
        int requestedWidth, int requestedHeight) {
      return SampleSizeRounding.QUALITY;
    }
  }
复制代码

AtLeastgetScaleFactor 的逻辑简要分析:

获取图片宽高/布局宽高的比例的最小值,转成int值;

如果这个值等于0,直接返回1,等于0其实就意味着图片宽高/布局宽高至少有一个是小于1的,也就屏蔽了需要放大的情况;

如果 minIntegerFactor 不等一0,肯定是需要缩小的,最后返回 1f / Integer.highestOneBit(minIntegerFactor) ,其中 Integer.highestOneBit(minIntegerFactor) 是取 minIntegerFactor 的二进制形式最左边的最高一位且高位后面全部补零,最后返回int型的结果;

其余的 DownsampleStrategy 实现类就不讲解了, DownsampleStrategy 是负责计算缩放比例和SampleSize策略,那么真正去进行Bitmap计算的是 Downsampler ;

重头戏Downsampler

Downsampler.java

Downsampler 这个类在我们之前分析Decode流程时已经遇到过它,该类的主要职责是从输入流中解析出 Bitmap ,核心功能的入口方法在 decode() ;

public Resource<Bitmap> decode(InputStream is, int requestedWidth, int requestedHeight,
      Options options, DecodeCallbacks callbacks) throws IOException {
    Preconditions.checkArgument(is.markSupported(), "You must provide an InputStream that supports"
        + " mark()");

    byte[] bytesForOptions = byteArrayPool.get(ArrayPool.STANDARD_BUFFER_SIZE_BYTES, byte[].class);
    //获取默认的BitmapFactory
    BitmapFactory.Options bitmapFactoryOptions = getDefaultOptions();
    bitmapFactoryOptions.inTempStorage = bytesForOptions;
    //decodeFormat主要是RGB_8888||RGB_565
    DecodeFormat decodeFormat = options.get(DECODE_FORMAT);
    //获取DownsampleStrategy
    DownsampleStrategy downsampleStrategy = options.get(DownsampleStrategy.OPTION);
    //是否fixedBitmapSize
    boolean fixBitmapToRequestedDimensions = options.get(FIX_BITMAP_SIZE_TO_REQUESTED_DIMENSIONS);
    //是否支持硬件位图
    boolean isHardwareConfigAllowed =
      options.get(ALLOW_HARDWARE_CONFIG) != null && options.get(ALLOW_HARDWARE_CONFIG);

    try {
        //解析出Bitmap
      Bitmap result = decodeFromWrappedStreams(is, bitmapFactoryOptions,
          downsampleStrategy, decodeFormat, isHardwareConfigAllowed, requestedWidth,
          requestedHeight, fixBitmapToRequestedDimensions, callbacks);
     //BitmapResource包装Bitmap
      return BitmapResource.obtain(result, bitmapPool);
    } finally {
      releaseOptions(bitmapFactoryOptions);
      byteArrayPool.put(bytesForOptions);
    }
  }
复制代码

首先调用 getDefaultOptions() 获取默认的 BitmapFactory ;

默认的BitmapFactory

默认的BitmapFactory使用一个队列缓存,初始化设置在 resetOptions() 方法;

private static void resetOptions(BitmapFactory.Options decodeBitmapOptions) {
    decodeBitmapOptions.inTempStorage = null;
    decodeBitmapOptions.inDither = false;
    decodeBitmapOptions.inScaled = false;
    decodeBitmapOptions.inSampleSize = 1;
    decodeBitmapOptions.inPreferredConfig = null;
    decodeBitmapOptions.inJustDecodeBounds = false;
    decodeBitmapOptions.inDensity = 0;
    decodeBitmapOptions.inTargetDensity = 0;
    decodeBitmapOptions.outWidth = 0;
    decodeBitmapOptions.outHeight = 0;
    decodeBitmapOptions.outMimeType = null;
    decodeBitmapOptions.inBitmap = null;
    decodeBitmapOptions.inMutable = true;
  }
复制代码

继续 decode() 方法分析,首先从 Option 中获取 decodeFormatfixBitmapToRequestedDimensionsisHardwareConfigAllowed 等,这个 Option 是从 RequestOptions 传递过来,代表用户的配置;

  • decodeFormat 表示解析格式,RGB_565或ARGB_8888;
  • fixBitmapToRequestedDimensions 表示是否填充尺寸,不进行缩放
  • isHardwareConfigAllowed 是否允许硬件位图,详细了解可以看Glide官方文档: muyangmin.github.io/glide-docs-…
  • BitmapResource 包装 Bitmap 返回; 真正的加载Bitmap逻辑在 decodeFromWrappedStreams() 方法:

decodeFromWrappedStreams

decodeFromWrappedStreams()

private Bitmap decodeFromWrappedStreams(InputStream is,
      BitmapFactory.Options options, DownsampleStrategy downsampleStrategy,
      DecodeFormat decodeFormat, boolean isHardwareConfigAllowed, int requestedWidth,
      int requestedHeight, boolean fixBitmapToRequestedDimensions,
      DecodeCallbacks callbacks) throws IOException {
    long startTime = LogTime.getLogTime();
    //解析出输入流图片尺寸
    int[] sourceDimensions = getDimensions(is, options, callbacks, bitmapPool);
    int sourceWidth = sourceDimensions[0];
    int sourceHeight = sourceDimensions[1];
    //得到mimeType
    String sourceMimeType = options.outMimeType;
    if (sourceWidth == -1 || sourceHeight == -1) {
      isHardwareConfigAllowed = false;
    }
    //获取图片旋转方向
    int orientation = ImageHeaderParserUtils.getOrientation(parsers, is, byteArrayPool);
    int degreesToRotate = TransformationUtils.getExifOrientationDegrees(orientation);
    boolean isExifOrientationRequired = TransformationUtils.isExifOrientationRequired(orientation);
    //判断目标控件尺寸
    int targetWidth = requestedWidth == Target.SIZE_ORIGINAL ? sourceWidth : requestedWidth;
    int targetHeight = requestedHeight == Target.SIZE_ORIGINAL ? sourceHeight : requestedHeight;
    //获取image类型
    ImageType imageType = ImageHeaderParserUtils.getType(parsers, is, byteArrayPool);
    //计算缩放
    calculateScaling();
    //计算其他config
    calculateConfig();

    boolean isKitKatOrGreater = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
    //处理inBitmap
    if ((options.inSampleSize == 1 || isKitKatOrGreater) && shouldUsePool(imageType)) {
      int expectedWidth;
      int expectedHeight;
      if (sourceWidth >= 0 && sourceHeight >= 0
          && fixBitmapToRequestedDimensions && isKitKatOrGreater) {
        expectedWidth = targetWidth;
        expectedHeight = targetHeight;
      } else {
        float densityMultiplier = isScaling(options)
            ? (float) options.inTargetDensity / options.inDensity : 1f;
        int sampleSize = options.inSampleSize;
        int downsampledWidth = (int) Math.ceil(sourceWidth / (float) sampleSize);
        int downsampledHeight = (int) Math.ceil(sourceHeight / (float) sampleSize);
        expectedWidth = Math.round(downsampledWidth * densityMultiplier);
        expectedHeight = Math.round(downsampledHeight * densityMultiplier);
      }
      //设置inBitmap
      if (expectedWidth > 0 && expectedHeight > 0) {
        setInBitmap(options, bitmapPool, expectedWidth, expectedHeight);
      }
    }
    //BitmapFactory.Option配置完毕,调用decodeStream解析
    Bitmap downsampled = decodeStream(is, options, callbacks, bitmapPool);
    callbacks.onDecodeComplete(bitmapPool, downsampled);
    
    Bitmap rotated = null;
    if (downsampled != null) {
      //重新设置density
      downsampled.setDensity(displayMetrics.densityDpi);
      //处理旋转信息
      rotated = TransformationUtils.rotateImageExif(bitmapPool, downsampled, orientation);
      if (!downsampled.equals(rotated)) {
      //添加进BitmapPool
        bitmapPool.put(downsampled);
      }
    }

    return rotated;
  }
复制代码
  • decodeFromWrappedStreams() 方法首先调用 getDimensions() 获取输入流图片尺寸和 mimeType
  • 获取图片的方向和旋转角度;
  • 确认目标控件的宽高,获取图片的ImageType;
  • 调用 calculateScaling() 计算缩放信息;
  • 调用 calculateConfig() 计算其他配置信息;
  • 根据配置,调用 setInBitmap() 设置inBitmap;
  • 上诉流程完成 BitmapFactory.Option 配置,调用 decodeStream() 解析输入流;
  • 得到Bitmap,对Bitmap做最后的操作;

getDimensions()

private static int[] getDimensions(InputStream is, BitmapFactory.Options options,
      DecodeCallbacks decodeCallbacks, BitmapPool bitmapPool) throws IOException {
    //只解析Bounds
    options.inJustDecodeBounds = true;
    decodeStream(is, options, decodeCallbacks, bitmapPool);
     //重置为false;
    options.inJustDecodeBounds = false;
    return new int[] { options.outWidth, options.outHeight };
  }
复制代码

getDimensions() 使用 options.inJustDecodeBounds = true 读取输入流图片信息;

计算缩放

calculateScaling()

private static void calculateScaling(
      ImageType imageType,
      InputStream is,
      DecodeCallbacks decodeCallbacks,
      BitmapPool bitmapPool,
      DownsampleStrategy downsampleStrategy,
      int degreesToRotate,
      int sourceWidth,
      int sourceHeight,
      int targetWidth,
      int targetHeight,
      BitmapFactory.Options options) throws IOException {
    //尺寸不能为0
    if (sourceWidth <= 0 || sourceHeight <= 0) {
      return;
    }

    final float exactScaleFactor;
    //根据方向和角度获取exactScaleFactor
    if (degreesToRotate == 90 || degreesToRotate == 270) {
      exactScaleFactor = downsampleStrategy.getScaleFactor(sourceHeight, sourceWidth,
          targetWidth, targetHeight);
    } else {
      exactScaleFactor =
          downsampleStrategy.getScaleFactor(sourceWidth, sourceHeight, targetWidth, targetHeight);
    }
    //exactScaleFactor不能小于等于0
    if (exactScaleFactor <= 0f) {
      throw new IllegalArgumentException("");
    }
    //获取SampleSizeRounding
    SampleSizeRounding rounding = downsampleStrategy.getSampleSizeRounding(sourceWidth,
        sourceHeight, targetWidth, targetHeight);
    if (rounding == null) {
      throw new IllegalArgumentException("Cannot round with null rounding");
    }
    //获取Bitmap输出宽高
    int outWidth = round(exactScaleFactor * sourceWidth);
    int outHeight = round(exactScaleFactor * sourceHeight);
    //转成int类型的factor
    int widthScaleFactor = sourceWidth / outWidth;
    int heightScaleFactor = sourceHeight / outHeight;
    //根据SampleSizeRounding得到scaleFactor
    int scaleFactor = rounding == SampleSizeRounding.MEMORY
        ? Math.max(widthScaleFactor, heightScaleFactor)
        : Math.min(widthScaleFactor, heightScaleFactor);

    int powerOfTwoSampleSize;
    //不支持下采样
    if (Build.VERSION.SDK_INT <= 23
        && NO_DOWNSAMPLE_PRE_N_MIME_TYPES.contains(options.outMimeType)) {
      powerOfTwoSampleSize = 1;
    } else {
    //在scaleFactor再进行处理,保证是2的指数幂
      powerOfTwoSampleSize = Math.max(1, Integer.highestOneBit(scaleFactor));
      if (rounding == SampleSizeRounding.MEMORY
          && powerOfTwoSampleSize < (1.f / exactScaleFactor)) {
        powerOfTwoSampleSize = powerOfTwoSampleSize << 1;
      }
    }
    //设置到inSampleSize
    options.inSampleSize = powerOfTwoSampleSize;
    int powerOfTwoWidth;
    int powerOfTwoHeight;
    //针对不同图片格式,重新计算采样后的宽高
    if (imageType == ImageType.JPEG) {
    //libjpeg引擎最大采样size=8,skia会进行二次采样
      int nativeScaling = Math.min(powerOfTwoSampleSize, 8);
      powerOfTwoWidth = (int) Math.ceil(sourceWidth / (float) nativeScaling);
      powerOfTwoHeight = (int) Math.ceil(sourceHeight / (float) nativeScaling);
      //如大大于8,skia会对剩下的进行二次采样计算逻辑
      int secondaryScaling = powerOfTwoSampleSize / 8;
      if (secondaryScaling > 0) {
        powerOfTwoWidth = powerOfTwoWidth / secondaryScaling;
        powerOfTwoHeight = powerOfTwoHeight / secondaryScaling;
      }
    } else if (imageType == ImageType.PNG || imageType == ImageType.PNG_A) {
      powerOfTwoWidth = (int) Math.floor(sourceWidth / (float) powerOfTwoSampleSize);
      powerOfTwoHeight = (int) Math.floor(sourceHeight / (float) powerOfTwoSampleSize);
    } else if (imageType == ImageType.WEBP || imageType == ImageType.WEBP_A) {
    //不同版本采样不同的计算方式round或者floor
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        powerOfTwoWidth = Math.round(sourceWidth / (float) powerOfTwoSampleSize);
        powerOfTwoHeight = Math.round(sourceHeight / (float) powerOfTwoSampleSize);
      } else {
        powerOfTwoWidth = (int) Math.floor(sourceWidth / (float) powerOfTwoSampleSize);
        powerOfTwoHeight = (int) Math.floor(sourceHeight / (float) powerOfTwoSampleSize);
      }
    } else if (
        sourceWidth % powerOfTwoSampleSize != 0 || sourceHeight % powerOfTwoSampleSize != 0) {
      int[] dimensions = getDimensions(is, options, decodeCallbacks, bitmapPool);
      powerOfTwoWidth = dimensions[0];
      powerOfTwoHeight = dimensions[1];
    } else {
      powerOfTwoWidth = sourceWidth / powerOfTwoSampleSize;
      powerOfTwoHeight = sourceHeight / powerOfTwoSampleSize;
    }
    //计算采样后进行缩放的比例
    double adjustedScaleFactor = downsampleStrategy.getScaleFactor(
        powerOfTwoWidth, powerOfTwoHeight, targetWidth, targetHeight);
    //计算inTargetDensity和inDensity进行缩放
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    //这一块计算方法没有弄明白
      options.inTargetDensity = adjustTargetDensityForError(adjustedScaleFactor);
      options.inDensity = getDensityMultiplier(adjustedScaleFactor);
    }
    //设置inScaled
    if (isScaling(options)){
      options.inScaled = true;
    } else {
      options.inDensity = options.inTargetDensity = 0;
    }
  }
复制代码

calculateScaling代码看似复杂,但是仔细分析流程还是很清晰的,该方法主要计算 BitmapFactory.FactoryinSampleSizeinTargetDensityinDensityinSampleSize 主要是进行向下采样,采样后自然会对图片进行缩小,但是可能不满足目标缩放比例,所以再配合 inTargetDensityinDensity 进行二次缩放;主要针对两个缩放变量 powerOfTwoSampleSizeadjustedScaleFactor ;具体流程分析:

流程一

  1. 计算 powerOfTwoSampleSize 过程: powerOfTwoSampleSize 是最终赋值给 options.inSampleSize 的;
  • 根据图片角度不同,分别调用 downSampleStategy.getScaleFactor() 方法,得到 exactScaleFactor ;
  • 通过 exactScaleFactor 计算出 outWidthoutHeight ,再重新计算 widthScaleFactorheightScaleFactor (ps:widthScaleFactor和heightScaleFactor是int类型,而exactScaleFactor是浮点型,结果是不一样的);
  • 通过策略得到int类型的比例 scaleFactor
  • 判断是否支持下采样,计算出最终的缩放比例 powerOfTwoSampleSize
  • 赋值给options.inSampleSize

流程二

  1. 计算 adjustedScaleFactor 过程: adjustedScaleFactor 是最终计算 options.inTargetDensityoptions.inDensity 的;
  • 不同格式和版本的下采样逻辑不同,Glide分别实现采样逻辑的计算,然后生成采样后的宽高尺寸 powerOfTwoWidthpowerOfTwoHeight ;
  • 再此调用 downsampleStrategy.getScaleFactor() 得到缩放比 adjustedScaleFactor ;
  • 调用 adjustTargetDensityForError()getDensityMultiplier() 得到 inTargetDensityinDensity 并赋值;
  • 上面 inDensity 依赖 options.inScaled = true ,最终还得判断是否能够进行scale;

计算硬件位图/Bitmap.Config

calculateConfig()

private void calculateConfig(
      InputStream is,
      DecodeFormat format,
      boolean isHardwareConfigAllowed,
      boolean isExifOrientationRequired,
      BitmapFactory.Options optionsWithScaling,
      int targetWidth,
      int targetHeight) {
      //如果支持硬件位图
    if (hardwareConfigState.setHardwareConfigIfAllowed(
        targetWidth,
        targetHeight,
        optionsWithScaling,
        format,
        isHardwareConfigAllowed,
        isExifOrientationRequired)) {
      return;
    }
    //这种情况,直接配置ARGB_8888
    if (format == DecodeFormat.PREFER_ARGB_8888
        || Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN) {
      optionsWithScaling.inPreferredConfig = Bitmap.Config.ARGB_8888;
      return;
    }
    //是否有透明的通道
    boolean hasAlpha = false;
    try {
      hasAlpha = ImageHeaderParserUtils.getType(parsers, is, byteArrayPool).hasAlpha();
    } catch (IOException e) {
    
    }
    //有透明的通道,需要配置成ARGB_8888
    optionsWithScaling.inPreferredConfig =
        hasAlpha ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
    if (optionsWithScaling.inPreferredConfig == Config.RGB_565) {
      optionsWithScaling.inDither = true;//565支持抖动界面
    }
  }
复制代码

calculateConfig() 主要是对 BitmapFactory.Option 的硬件位图和 inPreferredConfig 进行计算;如果用户配置8888直接配置,如果用户配置565,还需要判断是否有透明度通道,如果有透明度通道,依然采用8888解码器;

硬件位图

HardwareConfigState.java

boolean setHardwareConfigIfAllowed(
      int targetWidth,
      int targetHeight,
      BitmapFactory.Options optionsWithScaling,
      DecodeFormat decodeFormat,
      boolean isHardwareConfigAllowed,
      boolean isExifOrientationRequired) {
    if (!isHardwareConfigAllowed
        || Build.VERSION.SDK_INT < Build.VERSION_CODES.O
        || isExifOrientationRequired) {
        Android O以上支持
      return false;
    }
    //对尺寸有要求;
    boolean result =
        targetWidth >= MIN_HARDWARE_DIMENSION
            && targetHeight >= MIN_HARDWARE_DIMENSION
            && isFdSizeBelowHardwareLimit();

    if (result) {
      optionsWithScaling.inPreferredConfig = Bitmap.Config.HARDWARE;
      optionsWithScaling.inMutable = false;
    }
    return result;
  }
复制代码

硬件位图Bitmap.Config.HARDWARE 是一种 Android O 添加的新的位图格式。硬件位图仅在显存 (graphic memory) 里存储像素数据,并对图片仅在屏幕上绘制的场景做了优化。

###设置InBitmap

setInBitmap()

private static void setInBitmap(
      BitmapFactory.Options options, BitmapPool bitmapPool, int width, int height) {
    @Nullable Bitmap.Config expectedConfig = null;
    // Avoid short circuiting, it appears to break on some devices.
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    //硬件位图不设置
      if (options.inPreferredConfig == Config.HARDWARE) {
        return;
      }
      //inJustDecodeBoudes时候有可能可以获取到
      expectedConfig = options.outConfig;
    }

    if (expectedConfig == null) {
      expectedConfig = options.inPreferredConfig;
    }
   
    options.inBitmap = bitmapPool.getDirty(width, height, expectedConfig);
  }
复制代码

设置成硬件位图的不能进行 inBitmap 操作,最终会设置 options.inBitmap ,其中缓存的位图从bitmapPool中获取;

解析输出流

decodeStream()

万事具体只差东风,设置完BitmapFactory.Options之后,正在解析输入流的代码就在 decodeStream() :

private static Bitmap decodeStream(InputStream is, BitmapFactory.Options options,
      DecodeCallbacks callbacks, BitmapPool bitmapPool) throws IOException {
    if (options.inJustDecodeBounds) {
      is.mark(MARK_POSITION);
    } else {
      callbacks.onObtainBounds();
    }
    int sourceWidth = options.outWidth;
    int sourceHeight = options.outHeight;
    String outMimeType = options.outMimeType;
    final Bitmap result;
    TransformationUtils.getBitmapDrawableLock().lock();
    try {
    //真正的解析调用
      result = BitmapFactory.decodeStream(is, null, options);
    } catch (IllegalArgumentException e) {
      if (options.inBitmap != null) {
        try {
        //发生异常要做inBitmap回收
          is.reset();
          bitmapPool.put(options.inBitmap);
          options.inBitmap = null;//不适用inBitmap
          return decodeStream(is, options, callbacks, bitmapPool);
        } catch (IOException resetException) {
          throw bitmapAssertionException;
        }
      }
      throw bitmapAssertionException;
    } finally {
      TransformationUtils.getBitmapDrawableLock().unlock();
    }
    if (options.inJustDecodeBounds) {
      is.reset();
    }
    return result;
  }
复制代码

decodeStream() 核心的方法是调用 BitmapFactory.decodeStream(is, null, options) 做解析工作,剩下的代码都是对异常解析,如果发现异常时 inBitmap 不为空,设置 inBitmapnull 并重试一次;

总结

本文主要是对Glide下采样做了简单介绍,从代码流程上分析,可以分为对BitmapFactory.Options的配置、调用BitmapFactory解析输入流以及对Bitmap最后的处理三个步骤,其中Options配置阶段在计算缩放上最为重要,其中在缩放因子计算阶段,涉及 native 以及 libjpeg 底层的逻辑尤为重要,值得学习;


以上所述就是小编给大家介绍的《Glide4.8源码拆解(四)Bitmap解析之"下采样"浅析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Master Switch

The Master Switch

Tim Wu / Knopf / 2010-11-2 / USD 27.95

In this age of an open Internet, it is easy to forget that every American information industry, beginning with the telephone, has eventually been taken captive by some ruthless monopoly or cartel. Wit......一起来看看 《The Master Switch》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器