Fresco源码分析六

图片解码部分:
DecodeProducer的内部类ProgressiveDecoder中doDecode()方法对未解码的图片进行解码。
该方法实际上调用了ImageDecoder类的decodeImage()方法,该方法根据图片不同的格式调用不同的解码方法,每种解码方法再调用不同平台的解码方法。(以JPEG为例)
PlatformDecoder是不同平台解码器都应该实现的接口。只有两个基本方法:
1)decodeFromEncodedImage
2)decodeJPEGFromEncodedImage
DalvikPurgeableDecoder:是PlatformDecoder抽象实现类,实现了上面两个方法,用于Gingerbread版本以后。
decodeFromEncodedImage()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public CloseableReference<Bitmap> decodeFromEncodedImage(
final EncodedImage encodedImage,
Bitmap.Config bitmapConfig) {
BitmapFactory.Options options = getBitmapFactoryOptions(
encodedImage.getSampleSize(),
bitmapConfig);
CloseableReference<PooledByteBuffer> bytesRef = encodedImage.getByteBufferRef();

Preconditions.checkNotNull(bytesRef);
try {

Bitmap bitmap = decodeByteArrayAsPurgeable(bytesRef, options);
return pinBitmap(bitmap);
} finally {

CloseableReference.closeSafely(bytesRef);
}

}

关于BitmapFactory.Options的设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private static BitmapFactory.Options getBitmapFactoryOptions(
int sampleSize,
Bitmap.Config bitmapConfig) {
BitmapFactory.Options options = new BitmapFactory.Options();
//是否进行抖动处理
options.inDither = true;
//设置图片压缩质量参数,默认是Bitmap.Config.ARGB_8888
options.inPreferredConfig = bitmapConfig;
//将图片解码成一个可回收(purgeable)的bitmap,bitmap位于ashmem heap中。
options.inPurgeable = true;
//在LOLLIPOP忽略该字段,KITKAT及以下的版本和inPurgeable配合使用,如果inPurgeable字段是false则忽略,如果inPurgeable字段是true,inInputShareable 将决定bitmap是否能够共享一个指向数据源(inputstream, array, etc.)的引用,或者持有一份全拷贝(deep copy)
options.inInputShareable = true;
//缩放指数
options.inSampleSize = sampleSize;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
options.inMutable = true; // no known perf difference; allows postprocessing to work
}
return options;
}

pinBitmap()方法调用了nativePinBitmap()方法:

1
2
3
4
5
6
7
8
9
10
static void Bitmaps_pinBitmap(
JNIEnv* env,

jclass clazz,
jobject bitmap) {
UNUSED(clazz);
int rc = AndroidBitmap_lockPixels(env, bitmap, 0);
if (rc != ANDROID_BITMAP_RESULT_SUCCESS) {
safe_throw_exception(env, "Failed to pin Bitmap");
}
}

实质上执行了AndroidBitmap_lockPixels操作。
关于lockPixels操作,ashmem的pin/unpin操作:Introducing Fresco: A new image library for Android
GingerbreadPurgeableDecoder:继承自DalvikPurgeableDecoder,用于Gingerbread到 Jelly Bean的版本。把encoded bytes拷贝到MemoryFile(ashmem),之后再进行解码,完全不使用Java memory。

1
2
3
memoryFile = copyToMemoryFile(bytesRef, inputLength, suffix);
FileDescriptor fd = getMemoryFileDescriptor(memoryFile);
Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fd, null, options);

KitKatPurgeableDecoder:继承自DalvikPurgeableDecoder,用于KitKat版本,用Java memory存储encoded images,用FlexByteArrayPool提供的CloseableReference代替MemoryFile(ashmem),之后再进行解码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
protected Bitmap decodeByteArrayAsPurgeable(
CloseableReference<PooledByteBuffer> bytesRef,
BitmapFactory.Options options) {
final PooledByteBuffer pooledByteBuffer = bytesRef.get();
final int length = pooledByteBuffer.size();
final CloseableReference<byte[]> encodedBytesArrayRef = mFlexByteArrayPool.get(length);
try {
final byte[] encodedBytesArray = encodedBytesArrayRef.get();
pooledByteBuffer.read(0, encodedBytesArray, 0, length);
Bitmap bitmap = BitmapFactory.decodeByteArray(
encodedBytesArray,
0,
length,
options);
return Preconditions.checkNotNull(bitmap, "BitmapFactory returned null");
} finally {
CloseableReference.closeSafely(encodedBytesArrayRef);
}
}

ArtDecoder继承自PlatformDecoder,用于Lollipop或者更高的版本,直接通过EncodedImage.getInputStream()方法获取到EncodedImage的流进行解码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public CloseableReference<Bitmap> decodeFromEncodedImage(
EncodedImage encodedImage,
Bitmap.Config bitmapConfig) {
final BitmapFactory.Options options = getDecodeOptionsForStream(encodedImage, bitmapConfig);
boolean retryOnFail=options.inPreferredConfig != Bitmap.Config.ARGB_8888;
try {
return decodeStaticImageFromStream(encodedImage.getInputStream(), options);
} catch (RuntimeException re) {
if (retryOnFail) {
return decodeFromEncodedImage(encodedImage, Bitmap.Config.ARGB_8888);
}
throw re;
}
}

关于decodeStaticImageFromStream()方法:
1)inBitmap和对象池(BitmapPool继承自BasePool,BasePool实现了Pool)
options.inBitmap设置复用的内存块,从而不需要重新给这个bitmap申请一块新的内存。
BitmapPool是bitmap对象池,返回相同大小(byte)的bitmap对象,从而使设置了options.inBitmap属性的bitmap对象复用这个内存空间。

1
2
3
4
5
final Bitmap bitmapToReuse = mBitmapPool.get(sizeInBytes);
if (bitmapToReuse == null) {
throw new NullPointerException("BitmapPool.get returned null");
}
options.inBitmap = bitmapToReuse;

2)options.inTempStorage是流解码的缓冲空间,官方建议16K。
mDecodeBuffers是 SynchronizedPool的对象,同样是对象池。

1
2
3
4
5
6
7
8
Bitmap decodedBitmap;
ByteBuffer byteBuffer = mDecodeBuffers.acquire();
if (byteBuffer == null) {
byteBuffer = ByteBuffer.allocate(DECODE_BUFFER_SIZE);
}

try {
options.inTempStorage = byteBuffer.array();
decodedBitmap = BitmapFactory.decodeStream(inputStream, null, options);

这个很重要!关于对象池,inBitmap

ResizeAndRotateProducer只处理JPEG图片的缩放和旋转(EXIF)。
主要代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
PooledByteBufferOutputStream outputStream = mPooledByteBufferFactory.newOutputStream();
int numerator = getScaleNumerator(imageRequest, encodedImage);
is = encodedImage.getInputStream();
JpegTranscoder.transcodeJpeg(
is,
outputStream,
getRotationAngle(imageRequest, encodedImage),
numerator,
DEFAULT_JPEG_QUALITY);
CloseableReference<PooledByteBuffer> ref =
CloseableReference.of(outputStream.toByteBuffer());
ret = new EncodedImage(ref);

JpegTranscoder位于nativecode文件夹是和native代码沟通的Java类,同样jni\imagepipeline含有JpegTranscoder.cpp文件。
transcodeJpeg()方法调用native的nativeTranscodeJpeg()方法,在JpegTranscoder.cpp里对应着JpegTranscoder_transcodeJpeg()方法。
这个方法的具体实现在jpeg_codec.cpp,该文件包含了jpeglib.h头文件,实际上是调用了libjpeg-turbo-1.3.1这个C++开源库实现的。
transcodeJpeg的五个参数分别是:
未解码图片的输入流,未解码图片的输出流,旋转角度,尺寸压缩比例参数,质量压缩比例参数。
其他图片类型如PNG,WEBP的相关处理也都是用到了其他的C++开源库实现的。

获取一张图片的完整过程

文章目录