Glide4.8源码拆解(四)Bitmap解析之"下采样"浅析
前言 Glide归根结底是一个图片加载框架,它一定会涉及到BitmapFactory
相关API把Bitmap
读取到内存;可能大家已经很熟悉如何高效的加载Bitmap(比如使用inSample等),这一章还是要看一看Glide是如何玩转的;
本文主要分析这两个类:
DownsampleStrategy
Downsampler
Glide.with(MainActivity.this).load(URL).listener(new RequestListener() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
return false;
}@Override
public boolean onResourceReady(Drawable resource, Object model, Target 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
;
【Glide4.8源码拆解(四)Bitmap解析之"下采样"浅析】假设我们对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;
}
}
复制代码
主要关注是
CenterOutside
的getScaleFactor()
逻辑,该逻辑非常简单,主要是拿控件宽高/图片宽高
,得到尺寸取最大值,我们回到文章开头的那个例子,假设此时控件宽高是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;
}
}
复制代码
AtLeast
对getScaleFactor
的逻辑简要分析:获取图片宽高/布局宽高的比例的最小值,转成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 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
中获取decodeFormat
,fixBitmapToRequestedDimensions
,isHardwareConfigAllowed
等,这个Option
是从RequestOptions
传递过来,代表用户的配置;
decodeFormat
表示解析格式,RGB_565或ARGB_8888;fixBitmapToRequestedDimensions
表示是否填充尺寸,不进行缩放isHardwareConfigAllowed
是否允许硬件位图,详细了解可以看Glide官方文档:muyangmin.github.io/glide-docs-…- 用
BitmapResource
包装Bitmap
返回; 真正的加载Bitmap逻辑在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做最后的操作;
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.Factory
的inSampleSize
和inTargetDensity
、inDensity
,inSampleSize
主要是进行向下采样,采样后自然会对图片进行缩小,但是可能不满足目标缩放比例,所以再配合inTargetDensity
、inDensity
进行二次缩放;主要针对两个缩放变量powerOfTwoSampleSize
和adjustedScaleFactor
;具体流程分析:流程一
- 计算
powerOfTwoSampleSize
过程:powerOfTwoSampleSize
是最终赋值给options.inSampleSize
的;
- 根据图片角度不同,分别调用
downSampleStategy.getScaleFactor()
方法,得到exactScaleFactor
; - 通过
exactScaleFactor
计算出outWidth
和outHeight
,再重新计算widthScaleFactor
和heightScaleFactor
(ps:widthScaleFactor和heightScaleFactor是int类型,而exactScaleFactor是浮点型,结果是不一样的); - 通过策略得到int类型的比例
scaleFactor
- 判断是否支持下采样,计算出最终的缩放比例
powerOfTwoSampleSize
- 赋值给options.inSampleSize
- 计算
adjustedScaleFactor
过程:adjustedScaleFactor
是最终计算options.inTargetDensity
和options.inDensity
的;
- 不同格式和版本的下采样逻辑不同,Glide分别实现采样逻辑的计算,然后生成采样后的宽高尺寸
powerOfTwoWidth
和powerOfTwoHeight
; - 再此调用
downsampleStrategy.getScaleFactor()
得到缩放比adjustedScaleFactor
; - 调用
adjustTargetDensityForError()
和getDensityMultiplier()
得到inTargetDensity
和inDensity
并赋值; - 上面
inDensity
依赖options.inScaled = true
,最终还得判断是否能够进行scale;
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
不为空,设置inBitmap
为null
并重试一次;总结 本文主要是对Glide下采样做了简单介绍,从代码流程上分析,可以分为对BitmapFactory.Options的配置、调用BitmapFactory解析输入流以及对Bitmap最后的处理三个步骤,其中Options配置阶段在计算缩放上最为重要,其中在缩放因子计算阶段,涉及
native
以及libjpeg
底层的逻辑尤为重要,值得学习;推荐阅读
- Android事件传递源码分析
- Quartz|Quartz 源码解析(四) —— QuartzScheduler和Listener事件监听
- [源码解析]|[源码解析] NVIDIA HugeCTR,GPU版本参数服务器---(3)
- ffmpeg源码分析01(结构体)
- Java程序员阅读源码的小技巧,原来大牛都是这样读的,赶紧看看!
- Vue源码分析—响应式原理(二)
- SwiftUI|SwiftUI iOS 瀑布流组件之仿CollectionView不规则图文混合(教程含源码)
- java|java b2b2c shop 多用户商城系统源码- config 修改配置
- Spring源码解析_属性赋值
- Android下的IO库-Okio源码解析(一)|Android下的IO库-Okio源码解析(一) 入门