内容简介: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; } } 复制代码
主要关注是 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<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
中获取 decodeFormat
, fixBitmapToRequestedDimensions
, isHardwareConfigAllowed
等,这个 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.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;
计算硬件位图/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
不为空,设置 inBitmap
为 null
并重试一次;
总结
本文主要是对Glide下采样做了简单介绍,从代码流程上分析,可以分为对BitmapFactory.Options的配置、调用BitmapFactory解析输入流以及对Bitmap最后的处理三个步骤,其中Options配置阶段在计算缩放上最为重要,其中在缩放因子计算阶段,涉及 native
以及 libjpeg
底层的逻辑尤为重要,值得学习;
以上所述就是小编给大家介绍的《Glide4.8源码拆解(四)Bitmap解析之"下采样"浅析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- CNN 真的需要下采样(上采样)吗?
- Hive的分桶和采样
- 【信号与系统】05 - 滤波、采样和通信
- 加权随机采样 (Weighted Random Sampling)
- 深度卷积神经网络中的降采样
- SQL Server 查找统计信息的相关采样信息
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。