/** * Decodes a GIF into a CloseableImage. * @param encodedImage encoded image (native byte array holding the encoded bytes and meta data) * @param options the options for the decode * @param bitmapConfig the Bitmap.Config used to generate the output bitmaps * @return a {@link CloseableImage} for the GIF image */ public CloseableImage decodeGif( final EncodedImage encodedImage, final ImageDecodeOptions options, final Bitmap.Config bitmapConfig) { if (sGifAnimatedImageDecoder == null) { throw new UnsupportedOperationException("To encode animated gif please add the dependency " + "to the animated-gif module"); } final CloseableReference<PooledByteBuffer> bytesRef = encodedImage.getByteBufferRef(); Preconditions.checkNotNull(bytesRef); try { final PooledByteBuffer input = bytesRef.get(); AnimatedImage gifImage = sGifAnimatedImageDecoder.decode(input.getNativePtr(), input.size()); return getCloseableImage(options, gifImage, bitmapConfig); } finally { CloseableReference.closeSafely(bytesRef); } }
/** * Decode a WebP into a CloseableImage. * @param encodedImage encoded image (native byte array holding the encoded bytes and meta data) * @param options the options for the decode * @param bitmapConfig the Bitmap.Config used to generate the output bitmaps * @return a {@link CloseableImage} for the WebP image */ public CloseableImage decodeWebP( final EncodedImage encodedImage, final ImageDecodeOptions options, final Bitmap.Config bitmapConfig) { if (sWebpAnimatedImageDecoder == null) { throw new UnsupportedOperationException("To encode animated webp please add the dependency " + "to the animated-webp module"); } final CloseableReference<PooledByteBuffer> bytesRef = encodedImage.getByteBufferRef(); Preconditions.checkNotNull(bytesRef); try { final PooledByteBuffer input = bytesRef.get(); AnimatedImage webPImage = sWebpAnimatedImageDecoder.decode( input.getNativePtr(), input.size()); return getCloseableImage(options, webPImage, bitmapConfig); } finally { CloseableReference.closeSafely(bytesRef); } }
public EncodedImage cloneOrNull() { EncodedImage encodedImage; if (mInputStreamSupplier != null) { encodedImage = new EncodedImage(mInputStreamSupplier, mStreamSize); } else { CloseableReference<PooledByteBuffer> pooledByteBufferRef = CloseableReference.cloneOrNull(mPooledByteBufferRef); try { encodedImage = (pooledByteBufferRef == null) ? null : new EncodedImage(pooledByteBufferRef); } finally { // Close the recently created reference since it will be cloned again in the constructor. CloseableReference.closeSafely(pooledByteBufferRef); } } if (encodedImage != null) { encodedImage.copyMetaDataFrom(this); } return encodedImage; }
/** * Returns an InputStream from the internal InputStream Supplier if it's not null. Otherwise * returns an InputStream for the internal buffer reference if valid and null otherwise. * * <p>The caller has to close the InputStream after using it. */ public InputStream getInputStream() { if (mInputStreamSupplier != null) { return mInputStreamSupplier.get(); } CloseableReference<PooledByteBuffer> pooledByteBufferRef = CloseableReference.cloneOrNull(mPooledByteBufferRef); if (pooledByteBufferRef != null) { try { return new PooledByteBufferInputStream(pooledByteBufferRef.get()); } finally { CloseableReference.closeSafely(pooledByteBufferRef); } } return null; }
/** * Returns first n bytes of encoded image as hexbytes * * @param length the number of bytes to return */ public String getFirstBytesAsHexString(int length) { CloseableReference<PooledByteBuffer> imageBuffer = getByteBufferRef(); if (imageBuffer == null) { return ""; } int imageSize = getSize(); int resultSampleSize = Math.min(imageSize, length); byte[] bytesBuffer = new byte[resultSampleSize]; try { PooledByteBuffer pooledByteBuffer = imageBuffer.get(); if (pooledByteBuffer == null) { return ""; } pooledByteBuffer.read(0, bytesBuffer, 0, resultSampleSize); } finally { imageBuffer.close(); } StringBuilder stringBuilder = new StringBuilder(bytesBuffer.length * 2); for (byte b : bytesBuffer) { stringBuilder.append(String.format("%02X", b)); } return stringBuilder.toString(); }
public CloseableReference<PooledByteBuffer> generate(short width, short height) { PooledByteBufferOutputStream os = null; try { os = mPooledByteBufferFactory.newOutputStream( EMPTY_JPEG_PREFIX.length + EMPTY_JPEG_SUFFIX.length + 4); os.write(EMPTY_JPEG_PREFIX); os.write((byte) (height >> 8)); os.write((byte) (height & 0x00ff)); os.write((byte) (width >> 8)); os.write((byte) (width & 0x00ff)); os.write(EMPTY_JPEG_SUFFIX); return CloseableReference.of(os.toByteBuffer()); } catch (IOException e) { throw new RuntimeException(e); } finally { if (os != null) { os.close(); } } }
/** * 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); } }
/** * Creates a bitmap from encoded bytes. * * @param encodedImage the encoded image with reference to the encoded bytes * @param bitmapConfig the {@link android.graphics.Bitmap.Config} used to create the decoded * Bitmap * @param regionToDecode optional image region to decode. currently not supported. * @return the bitmap * @throws TooManyBitmapsException if the pool is full * @throws java.lang.OutOfMemoryError if the Bitmap cannot be allocated */ @Override public CloseableReference<Bitmap> decodeFromEncodedImage( final EncodedImage encodedImage, Bitmap.Config bitmapConfig, @Nullable Rect regionToDecode) { 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); } }
/** * Creates a bitmap from encoded JPEG bytes. Supports a partial JPEG image. * * @param encodedImage the encoded image with reference to the encoded bytes * @param bitmapConfig the {@link android.graphics.Bitmap.Config} used to create the decoded * Bitmap * @param regionToDecode optional image region to decode. currently not supported. * @param length the number of encoded bytes in the buffer * @return the bitmap * @throws TooManyBitmapsException if the pool is full * @throws java.lang.OutOfMemoryError if the Bitmap cannot be allocated */ @Override public CloseableReference<Bitmap> decodeJPEGFromEncodedImage( final EncodedImage encodedImage, Bitmap.Config bitmapConfig, @Nullable Rect regionToDecode, int length) { BitmapFactory.Options options = getBitmapFactoryOptions( encodedImage.getSampleSize(), bitmapConfig); final CloseableReference<PooledByteBuffer> bytesRef = encodedImage.getByteBufferRef(); Preconditions.checkNotNull(bytesRef); try { Bitmap bitmap = decodeJPEGByteArrayAsPurgeable(bytesRef, length, options); return pinBitmap(bitmap); } finally { CloseableReference.closeSafely(bytesRef); } }
protected Bitmap decodeFileDescriptorAsPurgeable( CloseableReference<PooledByteBuffer> bytesRef, int inputLength, byte[] suffix, BitmapFactory.Options options) { MemoryFile memoryFile = null; try { memoryFile = copyToMemoryFile(bytesRef, inputLength, suffix); FileDescriptor fd = getMemoryFileDescriptor(memoryFile); Bitmap bitmap = sWebpBitmapFactory.decodeFileDescriptor(fd, null, options); return Preconditions.checkNotNull(bitmap, "BitmapFactory returned null"); } catch (IOException e) { throw Throwables.propagate(e); } finally { if (memoryFile != null) { memoryFile.close(); } } }
public static MemoryCache<CacheKey, PooledByteBuffer> get( final CountingMemoryCache<CacheKey, PooledByteBuffer> encodedCountingMemoryCache, final ImageCacheStatsTracker imageCacheStatsTracker) { imageCacheStatsTracker.registerEncodedMemoryCache(encodedCountingMemoryCache); MemoryCacheTracker memoryCacheTracker = new MemoryCacheTracker<CacheKey>() { @Override public void onCacheHit(CacheKey cacheKey) { imageCacheStatsTracker.onMemoryCacheHit(cacheKey); } @Override public void onCacheMiss() { imageCacheStatsTracker.onMemoryCacheMiss(); } @Override public void onCachePut() { imageCacheStatsTracker.onMemoryCachePut(); } }; return new InstrumentedMemoryCache<>(encodedCountingMemoryCache, memoryCacheTracker); }
/** Creates a memory-backed encoded image from the stream. The stream is closed. */ protected EncodedImage getByteBufferBackedEncodedImage( InputStream inputStream, int length) throws IOException { CloseableReference<PooledByteBuffer> ref = null; try { if (length <= 0) { ref = CloseableReference.of(mPooledByteBufferFactory.newByteBuffer(inputStream)); } else { ref = CloseableReference.of(mPooledByteBufferFactory.newByteBuffer(inputStream, length)); } return new EncodedImage(ref); } finally { Closeables.closeQuietly(inputStream); CloseableReference.closeSafely(ref); } }
ProducerFactory createProducerFactory( Context context, ByteArrayPool byteArrayPool, ImageDecoder imageDecoder, ProgressiveJpegConfig progressiveJpegConfig, boolean downsampleEnabled, boolean resizeAndRotateEnabledForNetwork, boolean decodeCancellationEnabled, Supplier<Boolean> experimentalSmartResizingEnabled, ExecutorSupplier executorSupplier, PooledByteBufferFactory pooledByteBufferFactory, MemoryCache<CacheKey, CloseableImage> bitmapMemoryCache, MemoryCache<CacheKey, PooledByteBuffer> encodedMemoryCache, BufferedDiskCache defaultBufferedDiskCache, BufferedDiskCache smallImageBufferedDiskCache, MediaVariationsIndex mediaVariationsIndex, CacheKeyFactory cacheKeyFactory, PlatformBitmapFactory platformBitmapFactory, int bitmapPrepareToDrawMinSizeBytes, int bitmapPrepareToDrawMaxSizeBytes, boolean bitmapPrepareToDrawForPrefetch);
/** * Returns a sequence that can be used for a request for an encoded image from either network or * local files. * * @param imageRequest the request that will be submitted * @return the sequence that should be used to process the request */ public Producer<CloseableReference<PooledByteBuffer>> getEncodedImageProducerSequence( ImageRequest imageRequest) { validateEncodedImageRequest(imageRequest); final Uri uri = imageRequest.getSourceUri(); switch (imageRequest.getSourceUriType()) { case SOURCE_TYPE_NETWORK: return getNetworkFetchEncodedImageProducerSequence(); case SOURCE_TYPE_LOCAL_VIDEO_FILE: case SOURCE_TYPE_LOCAL_IMAGE_FILE: return getLocalFileFetchEncodedImageProducerSequence(); default: throw new IllegalArgumentException( "Unsupported uri scheme for encoded image fetch! Uri is: " + getShortenedUriString(uri)); } }
public ImagePipeline( ProducerSequenceFactory producerSequenceFactory, Set<RequestListener> requestListeners, Supplier<Boolean> isPrefetchEnabledSupplier, MemoryCache<CacheKey, CloseableImage> bitmapMemoryCache, MemoryCache<CacheKey, PooledByteBuffer> encodedMemoryCache, BufferedDiskCache mainBufferedDiskCache, BufferedDiskCache smallImageBufferedDiskCache, CacheKeyFactory cacheKeyFactory, ThreadHandoffProducerQueue threadHandoffProducerQueue, Supplier<Boolean> suppressBitmapPrefetchingSupplier) { mIdCounter = new AtomicLong(); mProducerSequenceFactory = producerSequenceFactory; mRequestListener = new ForwardingRequestListener(requestListeners); mIsPrefetchEnabledSupplier = isPrefetchEnabledSupplier; mBitmapMemoryCache = bitmapMemoryCache; mEncodedMemoryCache = encodedMemoryCache; mMainBufferedDiskCache = mainBufferedDiskCache; mSmallImageBufferedDiskCache = smallImageBufferedDiskCache; mCacheKeyFactory = cacheKeyFactory; mThreadHandoffProducerQueue = threadHandoffProducerQueue; mSuppressBitmapPrefetchingSupplier = suppressBitmapPrefetchingSupplier; }
/** * Returns a DataSource supplier that will on get submit the request for execution and return a * DataSource representing the pending results of the task. * * @param imageRequest the request to submit (what to execute). * @return a DataSource representing pending results and completion of the request */ public Supplier<DataSource<CloseableReference<PooledByteBuffer>>> getEncodedImageDataSourceSupplier( final ImageRequest imageRequest, final Object callerContext) { return new Supplier<DataSource<CloseableReference<PooledByteBuffer>>>() { @Override public DataSource<CloseableReference<PooledByteBuffer>> get() { return fetchEncodedImage(imageRequest, callerContext); } @Override public String toString() { return Objects.toStringHelper(this) .add("uri", imageRequest.getSourceUri()) .toString(); } }; }
@Test public void testFetchAssetResource() throws Exception { PooledByteBuffer pooledByteBuffer = mock(PooledByteBuffer.class); when(mAssetManager.open(eq(TEST_FILENAME), eq(AssetManager.ACCESS_STREAMING))) .thenReturn(new ByteArrayInputStream(new byte[TEST_DATA_LENGTH])); when(mPooledByteBufferFactory.newByteBuffer(any(InputStream.class), eq(TEST_DATA_LENGTH))) .thenReturn(pooledByteBuffer); mLocalAssetFetchProducer.produceResults(mConsumer, mProducerContext); mExecutor.runUntilIdle(); assertEquals( 2, mCapturedEncodedImage.getByteBufferRef() .getUnderlyingReferenceTestOnly().getRefCountTestOnly()); assertSame(pooledByteBuffer, mCapturedEncodedImage.getByteBufferRef().get()); verify(mProducerListener).onProducerStart(mRequestId, PRODUCER_NAME); verify(mProducerListener).onProducerFinishWithSuccess(mRequestId, PRODUCER_NAME, null); verify(mProducerListener).onUltimateProducerReached(mRequestId, PRODUCER_NAME, true); }
@Test public void testQualifiedResourceUri() throws Exception { PooledByteBuffer pooledByteBuffer = mock(PooledByteBuffer.class); when(mPooledByteBufferFactory.newByteBuffer(any(InputStream.class))) .thenReturn(pooledByteBuffer); when(mContentResolver.openInputStream(mContentUri)) .thenReturn(mock(InputStream.class)); mQualifiedResourceFetchProducer.produceResults(mConsumer, mProducerContext); mExecutor.runUntilIdle(); verify(mPooledByteBufferFactory, times(1)).newByteBuffer(any(InputStream.class)); verify(mContentResolver, times(1)).openInputStream(mContentUri); verify(mProducerListener).onProducerStart(REQUEST_ID, PRODUCER_NAME); verify(mProducerListener).onProducerFinishWithSuccess(REQUEST_ID, PRODUCER_NAME, null); }
@Test public void testFetchLocalResource() throws Exception { PooledByteBuffer pooledByteBuffer = mock(PooledByteBuffer.class); when(mPooledByteBufferFactory.newByteBuffer(any(InputStream.class), eq(TEST_DATA_LENGTH))) .thenReturn(pooledByteBuffer); when(mResources.openRawResource(eq(TEST_ID))) .thenReturn(new ByteArrayInputStream(new byte[TEST_DATA_LENGTH])); mLocalResourceFetchProducer.produceResults(mConsumer, mProducerContext); mExecutor.runUntilIdle(); assertEquals( 2, mCapturedEncodedImage.getByteBufferRef() .getUnderlyingReferenceTestOnly().getRefCountTestOnly()); assertSame(pooledByteBuffer, mCapturedEncodedImage.getByteBufferRef().get()); verify(mProducerListener).onProducerStart(mRequestId, PRODUCER_NAME); verify(mProducerListener).onProducerFinishWithSuccess(mRequestId, PRODUCER_NAME, null); verify(mProducerListener).onUltimateProducerReached(mRequestId, PRODUCER_NAME, true); }
@Before public void setUp() { MockitoAnnotations.initMocks(this); mRemoveMetaDataProducer = new RemoveImageTransformMetaDataProducer(mInputProducer); mIntermediateByteBuffer = mock(PooledByteBuffer.class); mFinalByteBuffer = mock(PooledByteBuffer.class); mIntermediateResult = CloseableReference.of(mIntermediateByteBuffer); mFinalResult = CloseableReference.of(mFinalByteBuffer); mRemoveMetaDataConsumer = null; doAnswer( new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { mRemoveMetaDataConsumer = (Consumer<EncodedImage>) invocation.getArguments()[0]; return null; } }).when(mInputProducer).produceResults(any(Consumer.class), any(ProducerContext.class)); mRemoveMetaDataProducer.produceResults(mConsumer, mProducerContext); }
@Before public void setUp() { MockitoAnnotations.initMocks(this); PowerMockito.mockStatic(ImageFormatChecker.class, JfifUtil.class, BitmapUtil.class); mAddMetaDataProducer = new AddImageTransformMetaDataProducer(mInputProducer); mIntermediateResultBufferRef = CloseableReference.of(mock(PooledByteBuffer.class)); mFinalResultBufferRef = CloseableReference.of(mock(PooledByteBuffer.class)); mIntermediateResult = new EncodedImage(mIntermediateResultBufferRef); mFinalResult = new EncodedImage(mFinalResultBufferRef); mAddMetaDataConsumer = null; doAnswer( new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { mAddMetaDataConsumer = (Consumer<EncodedImage>) invocation.getArguments()[0]; return null; } }).when(mInputProducer).produceResults(any(Consumer.class), any(ProducerContext.class)); mAddMetaDataProducer.produceResults(mConsumer, mProducerContext); }
@Test public void testFetchLocalContentUri() throws Exception { PooledByteBuffer pooledByteBuffer = mock(PooledByteBuffer.class); when(mPooledByteBufferFactory.newByteBuffer(any(InputStream.class))) .thenReturn(pooledByteBuffer); when(mContentResolver.openInputStream(mContentUri)).thenReturn(mock(InputStream.class)); mLocalContentUriFetchProducer.produceResults(mConsumer, mProducerContext); mExecutor.runUntilIdle(); assertEquals( 2, mCapturedEncodedImage.getByteBufferRef() .getUnderlyingReferenceTestOnly().getRefCountTestOnly()); assertSame(pooledByteBuffer, mCapturedEncodedImage.getByteBufferRef().get()); verify(mProducerListener).onProducerStart(mRequestId, PRODUCER_NAME); verify(mProducerListener).onUltimateProducerReached(mRequestId, PRODUCER_NAME, true); }
@Test public void testFetchLocalFile() throws Exception { PooledByteBuffer pooledByteBuffer = mock(PooledByteBuffer.class); when(mPooledByteBufferFactory.newByteBuffer(any(InputStream.class), eq(INPUT_STREAM_LENGTH))) .thenReturn(pooledByteBuffer); mLocalFileFetchProducer.produceResults(mConsumer, mProducerContext); mExecutor.runUntilIdle(); assertEquals( 2, mCapturedEncodedImage.getByteBufferRef() .getUnderlyingReferenceTestOnly().getRefCountTestOnly()); assertSame(pooledByteBuffer, mCapturedEncodedImage.getByteBufferRef().get()); verify(mProducerListener).onProducerStart(mRequestId, PRODUCER_NAME); verify(mProducerListener).onProducerFinishWithSuccess(mRequestId, PRODUCER_NAME, null); verify(mProducerListener).onUltimateProducerReached(mRequestId, PRODUCER_NAME, true); }
@Test public void testEncodedMemoryCacheGetNotFoundInputProducerSuccess() { setupEncodedMemoryCacheGetNotFound(); setupInputProducerStreamingSuccess(); mEncodedMemoryCacheProducer.produceResults(mConsumer, mProducerContext); verify(mMemoryCache, never()).cache(mCacheKey, mIntermediateImageReference); ArgumentCaptor<CloseableReference> argumentCaptor = ArgumentCaptor.forClass(CloseableReference.class); verify(mMemoryCache).cache(eq(mCacheKey), argumentCaptor.capture()); CloseableReference<PooledByteBuffer> capturedRef = (CloseableReference<PooledByteBuffer>) argumentCaptor.getValue(); Assert.assertSame( mFinalImageReference.getUnderlyingReferenceTestOnly(), capturedRef.getUnderlyingReferenceTestOnly()); verify(mConsumer).onNewResult(mIntermediateEncodedImage, Consumer.NO_FLAGS); verify(mConsumer).onNewResult(mFinalEncodedImage, Consumer.IS_LAST); Assert.assertTrue(EncodedImage.isValid(mFinalEncodedImageClone)); verify(mProducerListener).onProducerStart(mRequestId, PRODUCER_NAME); Map<String, String> extraMap = ImmutableMap.of(EncodedMemoryCacheProducer.EXTRA_CACHED_VALUE_FOUND, "false"); verify(mProducerListener).onProducerFinishWithSuccess(mRequestId, PRODUCER_NAME, extraMap); verify(mProducerListener, never()) .onUltimateProducerReached(anyString(), anyString(), anyBoolean()); }
private void notifyConsumer( PooledByteBufferOutputStream pooledOutputStream, @Consumer.Status int status, @Nullable BytesRange responseBytesRange, Consumer<EncodedImage> consumer) { CloseableReference<PooledByteBuffer> result = CloseableReference.of(pooledOutputStream.toByteBuffer()); EncodedImage encodedImage = null; try { encodedImage = new EncodedImage(result); encodedImage.setBytesRange(responseBytesRange); encodedImage.parseMetaData(); consumer.onNewResult(encodedImage, status); } finally { EncodedImage.closeSafely(encodedImage); CloseableReference.closeSafely(result); } }
private EncodedImage buildEncodedImage( PooledByteBuffer imageBytes, ExifInterface exifInterface) { Pair<Integer, Integer> dimensions = BitmapUtil.decodeDimensions(new PooledByteBufferInputStream(imageBytes)); int rotationAngle = getRotationAngle(exifInterface); int width = dimensions != null ? dimensions.first : EncodedImage.UNKNOWN_WIDTH; int height = dimensions != null ? dimensions.second : EncodedImage.UNKNOWN_HEIGHT; EncodedImage encodedImage; CloseableReference<PooledByteBuffer> closeableByteBuffer = CloseableReference.of(imageBytes); try { encodedImage = new EncodedImage(closeableByteBuffer); } finally { CloseableReference.closeSafely(closeableByteBuffer); } encodedImage.setImageFormat(DefaultImageFormats.JPEG); encodedImage.setRotationAngle(rotationAngle); encodedImage.setWidth(width); encodedImage.setHeight(height); return encodedImage; }