Android-Fresco系列6 图片解码
文章将会被同步至微信公众号:Android部落格
先看看流程图:
一、MultiplexProducer
从EncodedMemoryCacheProducer传递来的数据,来到了 MultiplexProducer.ForwardingConsumer中。
1) 初始化
回去看看producer初始化的地方看看其中初始化顺序:
EncodedCacheKeyMultiplexProducer:MultiplexProducer->AddImageTransformMetaDataProducer->ResizeAndRotateProducer)
因为在ProducerSequenceFactory类的newEncodedCacheMultiplexToTranscodeSequence方法中,最后新建了一个EncodedCacheKeyMultiplexProducer对象,他继承自MultiplexProducer类,并且在其构造函数中初始化了一个Map:Map
,而Multiplexer类有一个多路复用的逻辑,将相同的结果传递给多个消费者,并管理取消和持有最后的中间结果。
看看这个Map中保存了哪几个Consumer。要知道Consumer都定义在Producer中,直观的就是这几个Map中put了几个Producer:
//ProducerSequenceFactory.getCommonNetworkFetchToEncodedMemorySequenceProducer<EncodedImage> inputProducer = newEncodedCacheMultiplexToTranscodeSequence();mCommonNetworkFetchToEncodedMemorySequence = ProducerFactory.newAddImageTransformMetaDataProducer(inputProducer);mCommonNetworkFetchToEncodedMemorySequence = mProducerFactory.newResizeAndRotateProducer(mCommonNetworkFetchToEncodedMemorySequence,);
第一行执行新建一个MultiplexProducer,第二行将producer封装到AddImageTransformMetaDataConsumer,第三行将上一步生成的mCommonNetworkFetchToEncodedMemorySequence封装到ResizeAndRotateProducer。
2) produceResults
再来看看MultiplexProducer的produceResults方法:
//MultiplexProducer@Overridepublic void produceResults(Consumer<T> consumer, ProducerContext context) { do { createdNewMultiplexer = false; synchronized (this) { multiplexer = getExistingMultiplexer(key); if (multiplexer == null) { multiplexer = createAndPutNewMultiplexer(key); createdNewMultiplexer = true; } } //addNewConsumer may call consumer's onNewResult method immediately. For this reason // we release "this" lock. If multiplexer is removed from mMultiplexers in the meantime, // which is not very probable, then addNewConsumer will fail and we will be able to retry. } while (!multiplexer.addNewConsumer(consumer, context)); if (createdNewMultiplexer) { multiplexer.startInputProducerIfHasAttachedConsumers(); }}
还是按照上面的步骤,第一步的时候multiplexer为空成立,执行createAndPutNewMultiplexer方法创建一个新的Multiplexer,而后将传递过来的consumer放到Pair
第一个传递过来的consumer是AbstractProducerToDataSourceAdapter中的内部类BaseConsumer,因为在这个类的构造函数中通过createConsumer函数创建一个。
第二个传递过来的是AddImageTransformMetaDataProducer的Consumer,因为他在上面的第二步的时候被这个Producer包裹。
同时在startInputProducerIfHasAttachedConsumers方法中,内部创建了一个ForwardingConsumer,之后将其往下传递。
//MultiplexProducer.Multiplexerprivate void startInputProducerIfHasAttachedConsumers() { mForwardingConsumer = new ForwardingConsumer(); mInputProducer.produceResults( forwardingConsumer, multiplexProducerContext);}
produceResults往下执行就到了我们上一篇文章分析的EncodedMemoryCacheProducer中,然后EncodedMemoryCacheProducer处理完数据之后就到这里来了。
3) Consumer
好了,这里开始要分析数据了,接收消息的是自己的亲儿子ForwardingConsumer,看看他是怎么处理的:
//MultiplexProducer.Multiplexer.ForwardingConsumerprivate class ForwardingConsumer extends BaseConsumer<T> { @Override protected void onNewResultImpl(T newResult, @Status int status) { Multiplexer.this.onNextResult(this, newResult, status);}
原来这玩意不干活,通过遍历,将数据分发给已经保存到的consumer,从后往前发消息:
public void onNextResult(){ iterator = mConsumerContextPairs.iterator(); while (iterator.hasNext()) { Pair<Consumer<T>, ProducerContext> pair = iterator.next(); synchronized (pair) { pair.first.onNewResult(closeableObject, status); } }}
就这样消息来到了AddImageTransformMetaDataProducer中。
三、AddImageTransformMetaDataProducer
1) onNewResultImpl
这个Producer中的处理很简单:
//AddImageTransformMetaDataProducer@Overrideprotected void onNewResultImpl(EncodedImage newResult, @Status int status) { if (newResult == null) { getConsumer().onNewResult(null, status); return; } if (!EncodedImage.isMetaDataAvailable(newResult)) { newResult.parseMetaData(); } getConsumer().onNewResult(newResult, status);}
数据为空直接交给上一个消费者;如果图片数据的元数据不可用就解析,然后再交给上一个消费者。
那么上一个消费者是谁呢?看看他下一个初始化的是ResizeAndRotateProducer,因为这个producer包裹AddImageTransformMetaDataProducer对象。
四、 ResizeAndRotateProducer
1) produceResults
在produceResults中定义了一个TransformingConsumer消费者,消费数据之前定义了JobScheduler:
//ResizeAndRotateProducerJobScheduler.JobRunnable job = new JobScheduler.JobRunnable() { @Override public void run(EncodedImage encodedImage, @Status int status) { doTransform( encodedImage, status, Preconditions.checkNotNull( mImageTranscoderFactory.createImageTranscoder( encodedImage.getImageFormat(), mIsResizingEnabled))); }};mJobScheduler = new JobScheduler(mExecutor, job, MIN_TRANSFORM_INTERVAL_MS);
这个JobScheduler主要做转码操作。
转码工厂对象mImageTranscoderFactory的初始化是在ImagePipelineFactory中通过getImageTranscoderFactory定义的。不过mNativeCodeDisabled默认是false,看来这个对象对应的应该是MultiImageTranscoderFactory类:
//ImagePipelineFactoryprivate ImageTranscoderFactory getImageTranscoderFactory() { if (mImageTranscoderFactory == null) { if (mConfig.getImageTranscoderFactory() == null && mConfig.getImageTranscoderType() == null && mConfig.getExperiments().isNativeCodeDisabled()) { mImageTranscoderFactory = new SimpleImageTranscoderFactory(mConfig.getExperiments().getMaxBitmapSize()); } else { mImageTranscoderFactory = new MultiImageTranscoderFactory( mConfig.getExperiments().getMaxBitmapSize(), mConfig.getExperiments().getUseDownsamplingRatioForResizing(), mConfig.getImageTranscoderFactory(), mConfig.getImageTranscoderType()); } } return mImageTranscoderFactory;}
2) onNewResultImpl
精简一下代码,只贴一些关键的代码:
//ResizeAndRotateProducer@Overrideprotected void onNewResultImpl(@Nullable EncodedImage newResult, @Status int status) { ImageFormat imageFormat = newResult.getImageFormat(); TriState shouldTransform = shouldTransform(mProducerContext.getImageRequest(),newResult,Preconditions.checkNotNull( mImageTranscoderFactory.createImageTranscoder(imageFormat, mIsResizingEnabled))); if(shouldTransform != "YES"){ forwardNewResult(newResult, status, imageFormat); } else { if (isLast || mProducerContext.isIntermediateResultExpected()/*false*/) { mJobScheduler.scheduleJob(); } }}
1. shouldTransform
判断是否应该转码:
//ResizeAndRotateProducerprivate static TriState shouldTransform( ImageRequest request, EncodedImage encodedImage, ImageTranscoder imageTranscoder) { if (encodedImage == null || encodedImage.getImageFormat() == ImageFormat.UNKNOWN) { return TriState.UNSET; } if (!imageTranscoder.canTranscode(encodedImage.getImageFormat())) { return TriState.NO; } return TriState.valueOf( shouldRotate(request.getRotationOptions(), encodedImage) || imageTranscoder.canResize( encodedImage, request.getRotationOptions(), request.getResizeOptions()));}
传递进来的参数imageTranscoder来自mImageTranscoderFactory.createImageTranscoder
,对应的是MultiImageTranscoderFactory的createImageTranscoder方法:
//MultiImageTranscoderFactory@Overridepublic ImageTranscoder createImageTranscoder(ImageFormat imageFormat, boolean isResizingEnabled) { ImageTranscoder imageTranscoder = getNativeImageTranscoder(imageFormat, isResizingEnabled); return imageTranscoder == null ? getSimpleImageTranscoder(imageFormat, isResizingEnabled) : imageTranscoder;}
因为MultiImageTranscoderFactory初始化的时候,mPrimaryImageTranscoderFactory和mImageTranscoderType参数默认为null,所以最终调用的是getNativeImageTranscoder方法获取到了转码处理类。
这个类是:
//NativeImageTranscoderFactory@Nullableprivate ImageTranscoder getNativeImageTranscoder( ImageFormat imageFormat, boolean isResizingEnabled) { return NativeImageTranscoderFactory.getNativeImageTranscoderFactory( mMaxBitmapSize, mUseDownSamplingRatio) .createImageTranscoder(imageFormat, isResizingEnabled);}//NativeImageTranscoderFactorypublic static ImageTranscoderFactory getNativeImageTranscoderFactory(){ ImageTranscoderFactory imageTranscoderFactory = (ImageTranscoderFactory) Class.forName("com.facebook.imagepipeline.nativecode.NativeJpegTranscoderFactory") .getConstructor(Integer.TYPE, Boolean.TYPE) .newInstance(maxBitmapSize, useDownSamplingRatio);} //NativeJpegTranscoderFactorypublic ImageTranscoder createImageTranscoder(ImageFormat imageFormat, boolean isResizingEnabled) { if (imageFormat != DefaultImageFormats.JPEG) { return null; } return new NativeJpegTranscoder(isResizingEnabled, mMaxBitmapSize, mUseDownSamplingRatio);}
2. canTranscode
终于找到了最终转码的类NativeJpegTranscoder,在这里做一些是否能够转码的判断canTranscode:
//NativeJpegTranscoderpublic boolean canTranscode(ImageFormat imageFormat) { return imageFormat == DefaultImageFormats.JPEG;}
只有JPEG格式才转码。
3. shouldRotate
两个条件:1,当图片本身旋转角度不等于0且用户设置的允许在原图片上做旋转操作;2,图片方向上下,左右,左下,右下翻转。
(JpegTranscoderUtils.getRotationAngle(rotationOptions, encodedImage) != 0 ||INVERTED_EXIF_ORIENTATIONS.contains(encodedImage.getExifOrientation())
4. canResize
imageTranscoder.canResize( encodedImage, request.getRotationOptions(), request.getResizeOptions())
imageTranscoder对应的是NativeJpegTranscoder,通过canResize判断是否需要调整:
//NativeJpegTranscoderpublic boolean canResize(){ return JpegTranscoderUtils.getSoftwareNumerator( rotationOptions, resizeOptions, encodedImage, mResizingEnabled) < JpegTranscoderUtils.SCALE_DENOMINATOR;}
SCALE_DENOMINATOR是调整分母,等于8(为什么是8?估计与编码数据位有关系,此处不深究),getSoftwareNumerator函数是获取分子,当分子小于8的时候就需要调整。
//JpegTranscoderUtilspublic static int getSoftwareNumerator(){ final int rotationAngle = getRotationAngle(rotationOptions, encodedImage); int exifOrientation = ExifInterface.ORIENTATION_UNDEFINED; if (INVERTED_EXIF_ORIENTATIONS.contains(encodedImage.getExifOrientation())) { exifOrientation = getForceRotatedInvertedExifOrientation(rotationOptions, encodedImage); } final boolean swapDimensions = rotationAngle == 90 || rotationAngle == 270 || exifOrientation == ExifInterface.ORIENTATION_TRANSPOSE || exifOrientation == ExifInterface.ORIENTATION_TRANSVERSE; final int widthAfterRotation = swapDimensions ? encodedImage.getHeight() : encodedImage.getWidth(); final int heightAfterRotation = swapDimensions ? encodedImage.getWidth() : encodedImage.getHeight(); float ratio = determineResizeRatio(resizeOptions, widthAfterRotation, heightAfterRotation); int numerator = roundNumerator(ratio, resizeOptions.roundUpFraction); if (numerator > SCALE_DENOMINATOR) { return SCALE_DENOMINATOR; } return (numerator < 1) ? 1 : numerator;}
-
1.先计算旋转角度,图片本身已经旋转的角度加上用户设置的旋转角度
-
2.获取翻转角度,结合用户设置的旋转角度和图片本身的翻转角度计算
-
3.结合旋转角度和翻转角度判断是否需要交换分辨率,就是宽度和高度互换
-
4.计算宽高的调整比率:
public static float determineResizeRatio(ResizeOptions resizeOptions, int width, int height) { if (resizeOptions == null) { return 1.0f; } final float widthRatio = ((float) resizeOptions.width) / width; final float heightRatio = ((float) resizeOptions.height) / height; float ratio = Math.max(widthRatio, heightRatio); if (width * ratio > resizeOptions.maxBitmapSize) { ratio = resizeOptions.maxBitmapSize / width; } if (height * ratio > resizeOptions.maxBitmapSize) { ratio = resizeOptions.maxBitmapSize / height; } return ratio;}
将用户设置的宽高与当前已经获取的宽高分别相除取最大,然后将限制的最大裁剪图片大小(默认2048)与生成的裁剪宽高对比,大于的话,取比值返回。
- 5.通过roundNumerator方法向上取整
public static int roundNumerator(float maxRatio, float roundUpFraction) { return (int) (roundUpFraction + maxRatio * SCALE_DENOMINATOR);}
roundUpFraction默认等于2.0f/3,然后将上一步取到的比率 * 8 加上默认取整分数最后强转成int成为分子。
- 6.最终判断分子小于1返回1,大于1返回分子的值,然后用这个分子值判断是否小于8,小于8的话就要调整图片了。
回到TransformingConsumer的shouldTransform方法中,只要图片格式是JPEG,需要调整分辨率或旋转图片就需要转码了。
5. 转码
如果需要转码调整角度或宽高的话,就需要执行TransformingConsumer类构造函数中的JobScheduler了,其中定义了一个转码函数doTransform:
//TransformingConsumerprivate void doTransform(EncodedImage encodedImage, @Status int status, ImageTranscoder imageTranscoder) { ImageRequest imageRequest = mProducerContext.getImageRequest(); PooledByteBufferOutputStream outputStream = mPooledByteBufferFactory.newOutputStream(); EncodedImage ret; ImageTranscodeResult result = imageTranscoder.transcode( encodedImage, outputStream, imageRequest.getRotationOptions(), imageRequest.getResizeOptions(), null, DEFAULT_JPEG_QUALITY ); CloseableReference<PooledByteBuffer> ref = CloseableReference.of(outputStream.toByteBuffer()); ret = new EncodedImage(ref); ret.setImageFormat(JPEG); ret.parseMetaData(); getConsumer().onNewResult(ret, status);}
转码的时候传入了原编码图片,图片的stream数据流,旋转参数,宽高调整参数。
transcode在NativeJpegTranscoder类中定义:
//NativeJpegTranscoder@Overridepublic ImageTranscodeResult transcode(final EncodedImage encodedImage,final OutputStream outputStream,@Nullable RotationOptions rotationOptions,@Nullable final ResizeOptions resizeOptions,@Nullable ImageFormat outputFormat,@Nullable Integer quality)throws IOException { final int downsampleRatio = DownsampleUtil.determineSampleSize( rotationOptions, resizeOptions, encodedImage, mMaxBitmapSize); final int softwareNumerator = JpegTranscoderUtils.getSoftwareNumerator( rotationOptions, resizeOptions, encodedImage, mResizingEnabled); final int downsampleNumerator = JpegTranscoderUtils.calculateDownsampleNumerator(downsampleRatio); final int numerator; if (mUseDownsamplingRatio) { numerator = downsampleNumerator; } else { numerator = softwareNumerator; } InputStream is = encodedImage.getInputStream(); if (INVERTED_EXIF_ORIENTATIONS.contains(encodedImage.getExifOrientation())) { // Use exif orientation to rotate since we can't use the rotation angle for // inverted exif orientations final int exifOrientation = JpegTranscoderUtils.getForceRotatedInvertedExifOrientation(rotationOptions, encodedImage); transcodeJpegWithExifOrientation(is, outputStream, exifOrientation, numerator, quality); } else { // Use actual rotation angle in degrees to rotate final int rotationAngle = JpegTranscoderUtils.getRotationAngle(rotationOptions, encodedImage); transcodeJpeg(is, outputStream, rotationAngle, numerator, quality); }}
上面这一部分的代码最终在jni中实现了JPEG图片的旋转和伸缩变换,
java端定义的方法是transcodeJpegWithExifOrientation或nativeTranscodeJpeg。
代码执行路径是在:
fresco\native-imagetranscoder\src\main\jni\native-imagetranscoder\JpegTranscoder.cpp的JpegTranscoder_transcodeJpegWithExifOrientation和JpegTranscoder_transcodeJpeg
。
详细的变换过程此处不详细解读。
可以参考:
https://blog.csdn.net/abcjennifer/article/details/8074492
https://blog.csdn.net/abcjennifer/article/details/8074492
回到doTransform方法,我们传递进去转码的outputStream被处理完成之后,获取bytebuffer数据然后就被重新封装进CloseableReference对象,再封装到EncodedImage里面去,重新读取原数据信息,并接着执行onNewResult数据向上传递。
五、DecodeProducer
数据接下来传递到了这个消费者,同样的看看produceResults和Consumer吧。
1) produceResults
//DecodeProducer@Override public void produceResults(){ ProgressiveDecoder progressiveDecoder; if (!UriUtil.isNetworkUri(imageRequest.getSourceUri())) { progressiveDecoder = new LocalImagesProgressiveDecoder( consumer, producerContext, mDecodeCancellationEnabled, mMaxBitmapSize); } else { ProgressiveJpegParser jpegParser = new ProgressiveJpegParser(mByteArrayPool); progressiveDecoder = new NetworkImagesProgressiveDecoder( consumer, producerContext, jpegParser, mProgressiveJpegConfig, mDecodeCancellationEnabled, mMaxBitmapSize); } }
针对在线图片和本地图片分为两个Consumer,NetworkImagesProgressiveDecoder和LocalImagesProgressiveDecoder,因为这两个类继承自ProgressiveDecoder,ProgressiveDecoder类继承自DelegatingConsumer。
1. ProgressiveDecoder
NetworkImagesProgressiveDecoder和LocalImagesProgressiveDecoder这两个类中干的活不是太多,主要在父类ProgressiveDecoder中:
//ProgressiveDecoderpublic ProgressiveDecoder(){ JobRunnable job = new JobRunnable() { @Override public void run(EncodedImage encodedImage, @Status int status) { if (!UriUtil.isNetworkUri(request.getSourceUri())) { //重新计算采样大小 encodedImage.setSampleSize( DownsampleUtil.determineSampleSize( request.getRotationOptions(), request.getResizeOptions(), encodedImage, maxBitmapSize) ); } doDecode(encodedImage, status); } } mJobScheduler = new JobScheduler(mExecutor, job, mImageDecodeOptions.minDecodeIntervalMs);}
同样的,在构造函数中定义了一个JobScheduler,在数据返回到这里的时候执行解码。
2) onNewResultImpl
接收数据的地方:
//ProgressiveDecoder@Overridepublic void onNewResultImpl(EncodedImage newResult, @Status int status) {final boolean isPlaceholder = statusHasFlag(status, IS_PLACEHOLDER); if (isLast || isPlaceholder || mProducerContext.isIntermediateResultExpected()) { mJobScheduler.scheduleJob(); }}
执行scheduleJob方法,开始执行在构造函数中定义的JobScheduler。
3) doDecode
开始解码。
//ProgressiveDecoderprivate void doDecode(EncodedImage encodedImage, @Status int status) { CloseableImage image = mImageDecoder.decode(encodedImage, length, quality, mImageDecodeOptions); handleResult(image, status);}
看看mImageDecoder是怎么初始化的。
这个对象是在ImagePipelineFactory类的getImageDecoder函数中初始化的:
//ImagePipelineFactoryprivate ImageDecoder getImageDecoder() { final AnimatedFactory animatedFactory = getAnimatedFactory(); ImageDecoder gifDecoder = null; ImageDecoder webPDecoder = null; if (animatedFactory != null) { gifDecoder = animatedFactory.getGifDecoder(mConfig.getBitmapConfig()); webPDecoder = animatedFactory.getWebPDecoder(mConfig.getBitmapConfig()); } mImageDecoder = new DefaultImageDecoder(gifDecoder, webPDecoder, getPlatformDecoder()); return mImageDecoder;}
可见mImageDecoder对象对应的是DefaultImageDecoder类,在构造的时候,第三个参数getPlatformDecoder用于选择平台的解码类:
//ImagePipelineFactorypublic PlatformDecoder getPlatformDecoder() { if (mPlatformDecoder == null) { mPlatformDecoder = PlatformDecoderFactory.buildPlatformDecoder( mConfig.getPoolFactory(), mConfig.getExperiments().isGingerbreadDecoderEnabled()); } return mPlatformDecoder;}
直接看buildPlatformDecoder方法吧,看看不同的Android版本选择了不同的解码类:
//PlatformDecoderFactorypublic static PlatformDecoder buildPlatformDecoder( PoolFactory poolFactory, boolean gingerbreadDecoderEnabled) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { int maxNumThreads = poolFactory.getFlexByteArrayPoolMaxNumThreads(); return new OreoDecoder( poolFactory.getBitmapPool(), maxNumThreads, new Pools.SynchronizedPool<>(maxNumThreads)); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { int maxNumThreads = poolFactory.getFlexByteArrayPoolMaxNumThreads(); return new ArtDecoder( poolFactory.getBitmapPool(), maxNumThreads, new Pools.SynchronizedPool<>(maxNumThreads)); } else { if (gingerbreadDecoderEnabled && Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { return new GingerbreadPurgeableDecoder(); } else { return new KitKatPurgeableDecoder(poolFactory.getFlexByteArrayPool()); } }}
当前我的测试机器是Android O,就以OreoDecoder为例往下走。
最后一个参数maxNumThreads的值等于:public static final int DEFAULT_MAX_NUM_THREADS = Runtime.getRuntime().availableProcessors();
就是可用的处理器个数。
好了,回到decode方法,已经知道mImageDecoder对象对应的是DefaultImageDecoder类,那么decode方法定义在DefaultImageDecoder类中:
//DefaultImageDecoder@Overridepublic CloseableImage decode( final EncodedImage encodedImage, final int length, final QualityInfo qualityInfo, final ImageDecodeOptions options) { return mDefaultDecoder.decode(encodedImage, length, qualityInfo, options);}
mDefaultDecoder在这个类中定义:
//DefaultImageDecoderprivate final ImageDecoder mDefaultDecoder = new ImageDecoder() { @Override public CloseableImage decode( EncodedImage encodedImage, int length, QualityInfo qualityInfo, ImageDecodeOptions options) { ImageFormat imageFormat = encodedImage.getImageFormat(); if (imageFormat == DefaultImageFormats.JPEG) { return decodeJpeg(encodedImage, length, qualityInfo, options); } else if (imageFormat == DefaultImageFormats.GIF) { return decodeGif(encodedImage, length, qualityInfo, options); } else if (imageFormat == DefaultImageFormats.WEBP_ANIMATED) { return decodeAnimatedWebp(encodedImage, length, qualityInfo, options); } else if (imageFormat == ImageFormat.UNKNOWN) { throw new DecodeException("unknown image format", encodedImage); } return decodeStaticImage(encodedImage, options); }};
4) 解码JPEG
以JPEG为例,执行decodeJpeg方法:
//DefaultImageDecoderpublic CloseableStaticBitmap decodeJpeg( final EncodedImage encodedImage, int length, QualityInfo qualityInfo, ImageDecodeOptions options) { CloseableReference<Bitmap> bitmapReference = mPlatformDecoder.decodeJPEGFromEncodedImageWithColorSpace( encodedImage, options.bitmapConfig, null, length, options.colorSpace); try { maybeApplyTransformation(options.bitmapTransformation, bitmapReference); return new CloseableStaticBitmap( bitmapReference, qualityInfo, encodedImage.getRotationAngle(), encodedImage.getExifOrientation()); } finally { bitmapReference.close(); }}
此处mPlatformDecoder对应的就是Android O版本的解码类OreoDecoder,在OreoDecoder类中没有decodeJPEGFromEncodedImageWithColorSpace方法,其定义在父类DefaultDecoder中:
//DefaultDecoder @Overridepublic CloseableReference<Bitmap> decodeJPEGFromEncodedImageWithColorSpace( EncodedImage encodedImage, Bitmap.Config bitmapConfig, @Nullable Rect regionToDecode, int length, @Nullable final ColorSpace colorSpace) { boolean isJpegComplete = encodedImage.isCompleteAt(length); final BitmapFactory.Options options = getDecodeOptionsForStream(encodedImage, bitmapConfig); InputStream jpegDataStream = encodedImage.getInputStream(); return decodeFromStream(jpegDataStream, options, regionToDecode, colorSpace);}
1.读取jpeg的尾部数据判断数据是否完整
2.获取解码参数,getDecodeOptionsForStream:
//DefaultDecoderprivate static BitmapFactory.Options getDecodeOptionsForStream( EncodedImage encodedImage, Bitmap.Config bitmapConfig) { final BitmapFactory.Options options = new BitmapFactory.Options(); // Sample size should ONLY be different than 1 when downsampling is enabled in the pipeline options.inSampleSize = encodedImage.getSampleSize(); options.inJustDecodeBounds = true; // fill outWidth and outHeight BitmapFactory.decodeStream(encodedImage.getInputStream(), null, options); if (options.outWidth == -1 || options.outHeight == -1) { throw new IllegalArgumentException(); } options.inJustDecodeBounds = false; options.inDither = true; options.inPreferredConfig = bitmapConfig; options.inMutable = true; return options;}
https://developer.android.com/reference/android/graphics/BitmapFactory.Options
- inSampleSize
采样大小,根据设定的宽高和图片的宽高综合,获取方法在DownsampleUtil.determineSampleSize中。
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.
- inJustDecodeBounds
不为像素数据分配内存,但是可以查询Bitmap的数据
- inDither
是否抖动处理
https://blog.csdn.net/love_xsq/article/details/50387260
- inPreferredConfig
颜色模式选择,如果设置的话,就优先按照设置的来,否则按照图片已有的颜色模式设置。sdk默认设置的是ARGB_8888。
- inMutable
返回的bitmap是否可变,设置成可变之后,可以针对返回的Bitmap做一些修改。
If set, decode methods will always return a mutable Bitmap instead of an immutable one. This can be used for instance to programmatically apply effects to a Bitmap loaded through BitmapFactory.
Can not be set simultaneously with inPreferredConfig = Bitmap.Config.HARDWARE, because hardware bitmaps are always immutable.
5) decodeFromStream
开始解码了:
//DefaultDecoderprivate CloseableReference<Bitmap> decodeFromStream( InputStream inputStream, BitmapFactory.Options options, @Nullable Rect regionToDecode, @Nullable final ColorSpace colorSpace) { final int sizeInBytes = getBitmapSize(targetWidth, targetHeight, options); @Nullable Bitmap bitmapToReuse = null; bitmapToReuse = mBitmapPool.get(sizeInBytes); options.inBitmap = bitmapToReuse; Bitmap decodedBitmap = null; ByteBuffer byteBuffer = mDecodeBuffers.acquire(); if (byteBuffer == null) { byteBuffer = ByteBuffer.allocate(DECODE_BUFFER_SIZE); } if (regionToDecode != null && bitmapToReuse != null) { BitmapRegionDecoder bitmapRegionDecoder = null; bitmapToReuse.reconfigure(targetWidth, targetHeight, options.inPreferredConfig); bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, true); decodedBitmap = bitmapRegionDecoder.decodeRegion(regionToDecode, options); } if (decodedBitmap == null) { decodedBitmap = BitmapFactory.decodeStream(inputStream, null, options); } return CloseableReference.of(decodedBitmap, mBitmapPool);}
1.获取图片所有像素点的byte总数:宽 x 高 x config位(ARGB = 4位)
2.通过byte大小获取分配一个可复用的Bitmap
mBitmapPool的初始化是:
//PlatformDecoderFactoryreturn new OreoDecoder( poolFactory.getBitmapPool(), maxNumThreads, new Pools.SynchronizedPool<>(maxNumThreads));
这里的BitmapPool默认是BucketsBitmapPool,继承自BasePool类。看看get方法,定义在BasePool中:
//BasePool public V get(int size) { int bucketedSize = getBucketedSize(size); Bucket<V> bucket = getBucket(bucketedSize); if (bucket != null) { // find an existing value that we can reuse V value = getValue(bucket); if (value != null) { bucketedSize = getBucketedSizeForValue(value); sizeInBytes = getSizeInBytes(bucketedSize); mUsed.increment(sizeInBytes); mFree.decrement(sizeInBytes); return value; } } sizeInBytes = getSizeInBytes(bucketedSize); mUsed.increment(sizeInBytes); V value = alloc(bucketedSize); mInUseValues.add(value) trimToSoftCap(); return value;}//BasePoolsynchronized Bucket<V> getBucket(int bucketedSize) { // get an existing bucket Bucket<V> bucket = mBuckets.get(bucketedSize); if (bucket != null || !mAllowNewBuckets) { return bucket; } Bucket<V> newBucket = newBucket(bucketedSize); mBuckets.put(bucketedSize, newBucket); return newBucket;}//BasePoolBucket<V> newBucket(int bucketedSize) { return new Bucket<V>( /*itemSize*/ getSizeInBytes(bucketedSize), /*maxLength*/ Integer.MAX_VALUE, /*inUseLength*/ 0, mPoolParams.fixBucketsReinitialization);}
贴了三个函数,粗略讲一下这三个函数干了啥事情:
- Bucket是一个系统已分配对象的管理类,里面封装了对象的大小(itemSize),已分配对象被释放后的队列(freeList)等参数,泛型类型是Bitmap
- mBuckets是一个以Bucket的大小为key,Buckey为value的SparseArray。同时在BasePool中有两个Counter,用于计算空闲和已使用的空间大小:
mFree = new Counter();mUsed = new Counter();
当我们从 mBuckets里面找到一个相同大小的Bucket,就将mUsed加上这个Bucket大小,同时将mFree减去这个大小。
每次分配之前要调用canAllocate检查我们已经分配的大小是否超过限制,是的话就要调用trimToSize调整大小。
另外每次分配完了之后还要调用trimToSoftCap方法检查已经分配的值,超过的话也要调用trimToSize调整。
- 从上面的代码可以看出,来了一个复用分配请求的时候,先从mBuckets里面去找有没有相同大小的Bucket,有的话,就直接返回Bucket的value,否则的话新建一个alloc:
//BucketsBitmapPool@Overrideprotected Bitmap alloc(int size) { return Bitmap.createBitmap( 1, (int) Math.ceil(size / (double) BitmapUtil.RGB_565_BYTES_PER_PIXEL), Bitmap.Config.RGB_565);}
宽度为1,高度为像素位大小 / 2,像素位参数设置为RGB_565。
3.将bitmapToReuse设置到可复用选项
options.inBitmap = bitmapToReuse;
。
4.如果设置的编码区域不为空(默认为null),就按照设定区域编码,否则的话执行BitmapFactory.decodeStream生成一个decodedBitmap。
5.返回CloseableReference.of(decodedBitmap, mBitmapPool)
。
解析到这里仿佛已经忘了开始的地方,在DefaultImageDecoder的decodeJpeg方法中,我们返回了解码后的数据给到CloseableReference bitmapReference对象,然后将这个对象封装到了CloseableStaticBitmap对象中。
//DefaultImageDecoder:decodeJpegreturn new CloseableStaticBitmap( bitmapReference, qualityInfo, encodedImage.getRotationAngle(), encodedImage.getExifOrientation());
接下来这个数据会返回到DecodeProducer.ProgressiveDecoder的doDecode方法中,在调用decode方法之后,我们实际获取到了CloseableStaticBitmap对象,然后执行handleResult(image, status);
:
//ProgressiveDecoderprivate void handleResult(final CloseableImage decodedImage, final @Status int status) { CloseableReference<CloseableImage> decodedImageRef = CloseableReference.of(decodedImage); getConsumer().onNewResult(decodedImageRef, status);}
好了,数据接着往上传递给下一个消费者。
微信公众号:
更多相关文章
- 锦囊篇|一文摸懂SharedPreferences和MMKV(一)
- Data Storage -- Using Databases[SDK翻译]
- Android(安卓)View深入学习(三),View的绘制(Draw)过程
- Android中按返回键怎么退出一个用程序
- Android使用ksoap2连接webserver(JAX-WS)访问数据库
- Android---58---初学GPS定位
- Activity状态转换和Activity栈
- android sqlite 日期查询
- android > ListView > 加载 网络/assets图片