/* * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ package com.facebook.imagepipeline.platform; import javax.annotation.concurrent.ThreadSafe; import android.annotation.TargetApi; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Build; import com.facebook.common.internal.Preconditions; import com.facebook.common.references.CloseableReference; import com.facebook.imagepipeline.memory.FlexByteArrayPool; import com.facebook.imagepipeline.memory.PooledByteBuffer; import com.facebook.imageutils.JfifUtil; /** * Bitmap Decoder implementation for KitKat * * <p>The MemoryFile trick used in GingerbreadPurgeableDecoder does not work in KitKat. Here, we * instead use Java memory to store the encoded images, but make use of a pool to minimize * allocations. We cannot decode from a stream, as that does not support purgeable decodes. */ @TargetApi(Build.VERSION_CODES.KITKAT) @ThreadSafe public class KitKatPurgeableDecoder extends DalvikPurgeableDecoder { private final FlexByteArrayPool mFlexByteArrayPool; public KitKatPurgeableDecoder(FlexByteArrayPool flexByteArrayPool) { mFlexByteArrayPool = flexByteArrayPool; } /** * Decodes a byteArray into a purgeable bitmap * * @param bytesRef the byte buffer that contains the encoded bytes * @return */ @Override 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); } } /** * Decodes a byteArray containing jpeg encoded bytes into a purgeable bitmap * * <p> Adds a JFIF End-Of-Image marker if needed before decoding. * * @param bytesRef the byte buffer that contains the encoded bytes * @return */ @Override protected Bitmap decodeJPEGByteArrayAsPurgeable( CloseableReference<PooledByteBuffer> bytesRef, int length, BitmapFactory.Options options) { byte[] suffix = endsWithEOI(bytesRef, length) ? null : EOI; final PooledByteBuffer pooledByteBuffer = bytesRef.get(); Preconditions.checkArgument(length <= pooledByteBuffer.size()); // allocate bigger array in case EOI needs to be added final CloseableReference<byte[]> encodedBytesArrayRef = mFlexByteArrayPool.get(length + 2); try { byte[] encodedBytesArray = encodedBytesArrayRef.get(); pooledByteBuffer.read(0, encodedBytesArray, 0, length); if (suffix != null) { putEOI(encodedBytesArray, length); length += 2; } Bitmap bitmap = BitmapFactory.decodeByteArray( encodedBytesArray, 0, length, options); return Preconditions.checkNotNull(bitmap, "BitmapFactory returned null"); } finally { CloseableReference.closeSafely(encodedBytesArrayRef); } } private static void putEOI(byte[] imageBytes, int offset) { // TODO 5884402: remove dependency on JfifUtil imageBytes[offset] = (byte) JfifUtil.MARKER_FIRST_BYTE; imageBytes[offset + 1] = (byte) JfifUtil.MARKER_EOI; } }