in imagepipeline/src/main/java/com/facebook/imagepipeline/platform/DefaultDecoder.java [189:296]
private CloseableReference<Bitmap> decodeFromStream(
InputStream inputStream,
BitmapFactory.Options options,
@Nullable Rect regionToDecode,
@Nullable final ColorSpace colorSpace) {
Preconditions.checkNotNull(inputStream);
int targetWidth = options.outWidth;
int targetHeight = options.outHeight;
if (regionToDecode != null) {
targetWidth = regionToDecode.width() / options.inSampleSize;
targetHeight = regionToDecode.height() / options.inSampleSize;
}
@Nullable Bitmap bitmapToReuse = null;
boolean shouldUseHardwareBitmapConfig = false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
shouldUseHardwareBitmapConfig =
mPreverificationHelper != null
&& mPreverificationHelper.shouldUseHardwareBitmapConfig(options.inPreferredConfig);
}
if (regionToDecode == null && shouldUseHardwareBitmapConfig) {
// Cannot reuse bitmaps with Bitmap.Config.HARDWARE
options.inMutable = false;
} else {
if (regionToDecode != null && shouldUseHardwareBitmapConfig) {
// If region decoding was requested we need to fallback to default config
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
}
final int sizeInBytes = getBitmapSize(targetWidth, targetHeight, options);
bitmapToReuse = mBitmapPool.get(sizeInBytes);
if (bitmapToReuse == null) {
throw new NullPointerException("BitmapPool.get returned null");
}
}
// inBitmap can be nullable
//noinspection ConstantConditions
options.inBitmap = bitmapToReuse;
// Performs transformation at load time to target color space.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
options.inPreferredColorSpace =
colorSpace == null ? ColorSpace.get(ColorSpace.Named.SRGB) : colorSpace;
}
Bitmap decodedBitmap = null;
ByteBuffer byteBuffer = mDecodeBuffers.acquire();
if (byteBuffer == null) {
byteBuffer = ByteBuffer.allocate(DecodeBufferHelper.getRecommendedDecodeBufferSize());
}
try {
options.inTempStorage = byteBuffer.array();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
&& regionToDecode != null
&& bitmapToReuse != null) {
BitmapRegionDecoder bitmapRegionDecoder = null;
try {
bitmapToReuse.reconfigure(targetWidth, targetHeight, options.inPreferredConfig);
bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, true);
decodedBitmap = bitmapRegionDecoder.decodeRegion(regionToDecode, options);
} catch (IOException e) {
FLog.e(TAG, "Could not decode region %s, decoding full bitmap instead.", regionToDecode);
} finally {
if (bitmapRegionDecoder != null) {
bitmapRegionDecoder.recycle();
}
}
}
if (decodedBitmap == null) {
decodedBitmap = BitmapFactory.decodeStream(inputStream, null, options);
}
} catch (IllegalArgumentException e) {
if (bitmapToReuse != null) {
mBitmapPool.release(bitmapToReuse);
}
// This is thrown if the Bitmap options are invalid, so let's just try to decode the bitmap
// as-is, which might be inefficient - but it works.
try {
// We need to reset the stream first
inputStream.reset();
Bitmap naiveDecodedBitmap = BitmapFactory.decodeStream(inputStream);
if (naiveDecodedBitmap == null) {
throw e;
}
return CloseableReference.of(naiveDecodedBitmap, SimpleBitmapReleaser.getInstance());
} catch (IOException re) {
// We throw the original exception instead since it's the one causing this workaround in the
// first place.
throw e;
}
} catch (RuntimeException re) {
if (bitmapToReuse != null) {
mBitmapPool.release(bitmapToReuse);
}
throw re;
} finally {
mDecodeBuffers.release(byteBuffer);
}
// If bitmap with Bitmap.Config.HARDWARE was used, `bitmapToReuse` will be null and it's
// expected
if (bitmapToReuse != null && bitmapToReuse != decodedBitmap) {
mBitmapPool.release(bitmapToReuse);
decodedBitmap.recycle();
throw new IllegalStateException();
}
return CloseableReference.of(decodedBitmap, mBitmapPool);
}