public PipelineDraweeController newController( Supplier<DataSource<CloseableReference<CloseableImage>>> dataSourceSupplier, String id, CacheKey cacheKey, Object callerContext, @Nullable ImmutableList<DrawableFactory> customDrawableFactories) { Preconditions.checkState(mResources != null, "init() not called"); // Field values passed as arguments so that any subclass of PipelineDraweeControllerFactory // can simply override internalCreateController() and return a custom Drawee controller PipelineDraweeController controller = internalCreateController( mResources, mDeferredReleaser, mAnimatedDrawableFactory, mUiThreadExecutor, mMemoryCache, mDrawableFactories, customDrawableFactories, dataSourceSupplier, id, cacheKey, callerContext); if (mDebugOverlayEnabledSupplier != null) { controller.setDrawDebugOverlay(mDebugOverlayEnabledSupplier.get()); } return controller; }
protected PipelineDraweeController internalCreateController( Resources resources, DeferredReleaser deferredReleaser, DrawableFactory animatedDrawableFactory, Executor uiThreadExecutor, MemoryCache<CacheKey, CloseableImage> memoryCache, @Nullable ImmutableList<DrawableFactory> globalDrawableFactories, @Nullable ImmutableList<DrawableFactory> customDrawableFactories, Supplier<DataSource<CloseableReference<CloseableImage>>> dataSourceSupplier, String id, CacheKey cacheKey, Object callerContext) { PipelineDraweeController controller = new PipelineDraweeController( resources, deferredReleaser, animatedDrawableFactory, uiThreadExecutor, memoryCache, dataSourceSupplier, id, cacheKey, callerContext, globalDrawableFactories); controller.setCustomDrawableFactories(customDrawableFactories); return controller; }
@Override public Drawable createDrawable(CloseableImage closeableImage) { if (closeableImage instanceof CloseableStaticBitmap) { CloseableStaticBitmap closeableStaticBitmap = (CloseableStaticBitmap) closeableImage; Drawable bitmapDrawable = new BitmapDrawable(mResources, closeableStaticBitmap.getUnderlyingBitmap()); if (!hasTransformableRotationAngle(closeableStaticBitmap) && !hasTransformableExifOrientation(closeableStaticBitmap)) { // Return the bitmap drawable directly as there's nothing to transform in it return bitmapDrawable; } else { return new OrientedDrawable( bitmapDrawable, closeableStaticBitmap.getRotationAngle(), closeableStaticBitmap.getExifOrientation()); } } else if (mAnimatedDrawableFactory != null && mAnimatedDrawableFactory.supportsImageType(closeableImage)) { return mAnimatedDrawableFactory.createDrawable(closeableImage); } return null; }
public PipelineDraweeController( Resources resources, DeferredReleaser deferredReleaser, DrawableFactory animatedDrawableFactory, Executor uiThreadExecutor, MemoryCache<CacheKey, CloseableImage> memoryCache, Supplier<DataSource<CloseableReference<CloseableImage>>> dataSourceSupplier, String id, CacheKey cacheKey, Object callerContext, @Nullable ImmutableList<DrawableFactory> globalDrawableFactories) { super(deferredReleaser, uiThreadExecutor, id, callerContext); mResources = resources; mAnimatedDrawableFactory = animatedDrawableFactory; mMemoryCache = memoryCache; mCacheKey = cacheKey; mGlobalDrawableFactories = globalDrawableFactories; init(dataSourceSupplier); }
@Override public synchronized void onFrameRendered( int frameNumber, CloseableReference<Bitmap> bitmapReference, @BitmapAnimationBackend.FrameType int frameType) { Preconditions.checkNotNull(bitmapReference); // Close up prepared references. removePreparedReference(frameNumber); // Create the new image reference and cache it. CloseableReference<CloseableImage> closableReference = null; try { closableReference = createImageReference(bitmapReference); if (closableReference != null) { CloseableReference.closeSafely(mLastRenderedItem); mLastRenderedItem = mAnimatedFrameCache.cache(frameNumber, closableReference); } } finally { CloseableReference.closeSafely(closableReference); } }
/** * Converts the given image reference to a bitmap reference * and closes the original image reference. * * @param closeableImage the image to convert. It will be closed afterwards and will be invalid * @return the closeable bitmap reference to be used */ @VisibleForTesting @Nullable static CloseableReference<Bitmap> convertToBitmapReferenceAndClose( final @Nullable CloseableReference<CloseableImage> closeableImage) { try { if (CloseableReference.isValid(closeableImage) && closeableImage.get() instanceof CloseableStaticBitmap) { CloseableStaticBitmap closeableStaticBitmap = (CloseableStaticBitmap) closeableImage.get(); if (closeableStaticBitmap != null) { // We return a clone of the underlying bitmap reference that has to be manually closed // and then close the passed CloseableStaticBitmap in order to preserve correct // cache size calculations. return closeableStaticBitmap.cloneUnderlyingBitmapReference(); } } // Not a bitmap reference, so we return null return null; } finally { CloseableReference.closeSafely(closeableImage); } }
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; }
/** * bitmap cache get -> * background thread hand-off -> bitmap cache -> decode -> resize and rotate -> (webp transcode) * -> data fetch. */ private synchronized Producer<CloseableReference<CloseableImage>> getDataFetchSequence() { if (mDataFetchSequence == null) { Producer<EncodedImage> inputProducer = mProducerFactory.newDataFetchProducer(); if (WebpSupportStatus.sIsWebpSupportRequired && (!mWebpSupportEnabled || WebpSupportStatus.sWebpBitmapFactory == null)) { inputProducer = mProducerFactory.newWebpTranscodeProducer(inputProducer); } inputProducer = mProducerFactory.newAddImageTransformMetaDataProducer(inputProducer); inputProducer = mProducerFactory.newResizeAndRotateProducer( inputProducer, true, mUseDownsamplingRatio); mDataFetchSequence = newBitmapCacheGetToDecodeSequence(inputProducer); } return mDataFetchSequence; }
@Test public void testNonStaticBitmapIsPassedOn() { SingleUsePostprocessorConsumer postprocessorConsumer = produceResults(); CloseableAnimatedImage sourceCloseableAnimatedImage = mock(CloseableAnimatedImage.class); CloseableReference<CloseableImage> sourceCloseableImageRef = CloseableReference.<CloseableImage>of(sourceCloseableAnimatedImage); postprocessorConsumer.onNewResult(sourceCloseableImageRef, Consumer.IS_LAST); sourceCloseableImageRef.close(); mTestExecutorService.runUntilIdle(); mInOrder.verify(mConsumer).onNewResult(any(CloseableReference.class), eq(Consumer.IS_LAST)); mInOrder.verifyNoMoreInteractions(); assertEquals(1, mResults.size()); CloseableReference<CloseableImage> res0 = mResults.get(0); assertTrue(CloseableReference.isValid(res0)); assertSame(sourceCloseableAnimatedImage, res0.get()); res0.close(); verify(sourceCloseableAnimatedImage).close(); }
@Test public void testNonStaticBitmapIsPassedOn() { RepeatedPostprocessorConsumer postprocessorConsumer = produceResults(); RepeatedPostprocessorRunner repeatedPostprocessorRunner = getRunner(); CloseableAnimatedImage sourceCloseableAnimatedImage = mock(CloseableAnimatedImage.class); CloseableReference<CloseableImage> sourceCloseableImageRef = CloseableReference.<CloseableImage>of(sourceCloseableAnimatedImage); postprocessorConsumer.onNewResult(sourceCloseableImageRef, Consumer.IS_LAST); sourceCloseableImageRef.close(); mTestExecutorService.runUntilIdle(); mInOrder.verify(mConsumer).onNewResult(any(CloseableReference.class), eq(Consumer.NO_FLAGS)); mInOrder.verifyNoMoreInteractions(); assertEquals(1, mResults.size()); CloseableReference<CloseableImage> res0 = mResults.get(0); assertTrue(CloseableReference.isValid(res0)); assertSame(sourceCloseableAnimatedImage, res0.get()); res0.close(); performCancelAndVerifyOnCancellation(); verify(sourceCloseableAnimatedImage).close(); }
private void verifyNewResultProcessed(int index, Bitmap destBitmap) { mInOrder.verify(mProducerListener).onProducerStart(mRequestId, PostprocessorProducer.NAME); mInOrder.verify(mPostprocessor).process(mSourceBitmap, mPlatformBitmapFactory); mInOrder.verify(mProducerListener).requiresExtraMap(mRequestId); mInOrder.verify(mProducerListener) .onProducerFinishWithSuccess(mRequestId, PostprocessorProducer.NAME, mExtraMap); mInOrder.verify(mConsumer).onNewResult(any(CloseableReference.class), eq(Consumer.NO_FLAGS)); mInOrder.verifyNoMoreInteractions(); assertEquals(index + 1, mResults.size()); CloseableReference<CloseableImage> res0 = mResults.get(index); assertTrue(CloseableReference.isValid(res0)); assertSame(destBitmap, ((CloseableStaticBitmap) res0.get()).getUnderlyingBitmap()); res0.close(); verify(mBitmapResourceReleaser).release(destBitmap); }
@Test public void testFetchDecodedImage() { Producer<CloseableReference<CloseableImage>> decodedSequence = mock(Producer.class); when(mProducerSequenceFactory.getDecodedImageProducerSequence(mImageRequest)) .thenReturn(decodedSequence); DataSource<CloseableReference<CloseableImage>> dataSource = mImagePipeline.fetchDecodedImage(mImageRequest, mCallerContext); assertFalse(dataSource.isFinished()); verify(mRequestListener1).onRequestStart(mImageRequest, mCallerContext, "0", false); verify(mRequestListener2).onRequestStart(mImageRequest, mCallerContext, "0", false); ArgumentCaptor<ProducerContext> producerContextArgumentCaptor = ArgumentCaptor.forClass(ProducerContext.class); verify(decodedSequence) .produceResults(any(Consumer.class), producerContextArgumentCaptor.capture()); assertTrue(producerContextArgumentCaptor.getValue().isIntermediateResultExpected()); assertEquals(producerContextArgumentCaptor.getValue().getPriority(), Priority.HIGH); }
private Drawable createDrawableFromFetchedResult(Context context, CloseableImage image) { if (image instanceof CloseableStaticBitmap) { CloseableStaticBitmap closeableStaticBitmap = (CloseableStaticBitmap) image; BitmapDrawable bitmapDrawable = createBitmapDrawable(context, closeableStaticBitmap.getUnderlyingBitmap()); return (closeableStaticBitmap.getRotationAngle() != 0 && closeableStaticBitmap.getRotationAngle() != -1 ? new OrientedDrawable(bitmapDrawable, closeableStaticBitmap.getRotationAngle()) : bitmapDrawable); } else if (image instanceof CloseableAnimatedImage) { AnimatedDrawableFactory animatedDrawableFactory = Fresco.getImagePipelineFactory().getAnimatedFactory().getAnimatedDrawableFactory(context); if (animatedDrawableFactory != null) { AnimatedDrawable animatedDrawable = (AnimatedDrawable) animatedDrawableFactory.create(image); if (animatedDrawable != null) { return animatedDrawable; } } } throw new UnsupportedOperationException("Unrecognized image class: " + image); }
/** * Returns a sequence that can be used for a request for a decoded image. * * @param imageRequest the request that will be submitted * @return the sequence that should be used to process the request */ public Producer<CloseableReference<CloseableImage>> getDecodedImageProducerSequence( ImageRequest imageRequest) { Producer<CloseableReference<CloseableImage>> pipelineSequence = getBasicDecodedImageSequence(imageRequest); if (imageRequest.getPostprocessor() != null) { pipelineSequence = getPostprocessorSequence(pipelineSequence); } if (mUseBitmapPrepareToDraw) { pipelineSequence = getBitmapPrepareSequence(pipelineSequence); } return pipelineSequence; }
@Test public void testDontCacheIntermediateImageAsAlreadyHaveFullQuality() { setupBitmapMemoryCacheGetNotFound(); setupInputProducerStreamingSuccess(); CloseableImage closeableImage = mock(CloseableImage.class); when(closeableImage.getQualityInfo()).thenReturn(ImmutableQualityInfo.FULL_QUALITY); CloseableReference<CloseableImage> closeableImageRef = CloseableReference.of(closeableImage); setupBitmapMemoryCacheGetSuccessOnSecondRead(closeableImageRef); mBitmapMemoryCacheProducer.produceResults(mConsumer, mProducerContext); verify(mMemoryCache, never()).cache(mBitmapMemoryCacheKey, mIntermediateImageReference); verify(mMemoryCache).cache(mBitmapMemoryCacheKey, mFinalImageReference); verify(mConsumer).onNewResult(closeableImageRef, Consumer.NO_FLAGS); verify(mConsumer).onNewResult(mFinalImageReferenceClone, Consumer.IS_LAST); Assert.assertTrue(!mFinalImageReferenceClone.isValid()); Assert.assertEquals( 0, closeableImageRef.getUnderlyingReferenceTestOnly().getRefCountTestOnly()); verify(mProducerListener).onProducerStart(mRequestId, PRODUCER_NAME); Map<String, String> extraMap = ImmutableMap.of(BitmapMemoryCacheProducer.EXTRA_CACHED_VALUE_FOUND, "false"); verify(mProducerListener).onProducerFinishWithSuccess(mRequestId, PRODUCER_NAME, extraMap); verify(mProducerListener, never()) .onUltimateProducerReached(anyString(), anyString(), anyBoolean()); }
public static MemoryCache<CacheKey, CloseableImage> get( final CountingMemoryCache<CacheKey, CloseableImage> bitmapCountingMemoryCache, final ImageCacheStatsTracker imageCacheStatsTracker) { imageCacheStatsTracker.registerBitmapMemoryCache(bitmapCountingMemoryCache); MemoryCacheTracker memoryCacheTracker = new MemoryCacheTracker<CacheKey>() { @Override public void onCacheHit(CacheKey cacheKey) { imageCacheStatsTracker.onBitmapCacheHit(cacheKey); } @Override public void onCacheMiss() { imageCacheStatsTracker.onBitmapCacheMiss(); } @Override public void onCachePut() { imageCacheStatsTracker.onBitmapCachePut(); } }; return new InstrumentedMemoryCache<>(bitmapCountingMemoryCache, memoryCacheTracker); }
@Override public void produceResults( final Consumer<CloseableReference<CloseableImage>> consumer, ProducerContext context) { final ProducerListener listener = context.getListener(); final Postprocessor postprocessor = context.getImageRequest().getPostprocessor(); final PostprocessorConsumer basePostprocessorConsumer = new PostprocessorConsumer(consumer, listener, context.getId(), postprocessor, context); final Consumer<CloseableReference<CloseableImage>> postprocessorConsumer; if (postprocessor instanceof RepeatedPostprocessor) { postprocessorConsumer = new RepeatedPostprocessorConsumer( basePostprocessorConsumer, (RepeatedPostprocessor) postprocessor, context); } else { postprocessorConsumer = new SingleUsePostprocessorConsumer(basePostprocessorConsumer); } mInputProducer.produceResults(postprocessorConsumer, context); }
@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); }
private void submitPostprocessing() { mExecutor.execute( new Runnable() { @Override public void run() { CloseableReference<CloseableImage> closeableImageRef; int status; synchronized (PostprocessorConsumer.this) { // instead of cloning and closing the reference, we do a more efficient move. closeableImageRef = mSourceImageRef; status = mStatus; mSourceImageRef = null; mIsDirty = false; } if (CloseableReference.isValid(closeableImageRef)) { try { doPostprocessing(closeableImageRef, status); } finally { CloseableReference.closeSafely(closeableImageRef); } } clearRunningAndStartIfDirty(); } }); }
private void doPostprocessing( CloseableReference<CloseableImage> sourceImageRef, int status) { Preconditions.checkArgument(CloseableReference.isValid(sourceImageRef)); if (!shouldPostprocess(sourceImageRef.get())) { maybeNotifyOnNewResult(sourceImageRef, status); return; } mListener.onProducerStart(mRequestId, NAME); CloseableReference<CloseableImage> destImageRef = null; try { try { destImageRef = postprocessInternal(sourceImageRef.get()); } catch (Exception e) { mListener.onProducerFinishWithFailure( mRequestId, NAME, e, getExtraMap(mListener, mRequestId, mPostprocessor)); maybeNotifyOnFailure(e); return; } mListener.onProducerFinishWithSuccess( mRequestId, NAME, getExtraMap(mListener, mRequestId, mPostprocessor)); maybeNotifyOnNewResult(destImageRef, status); } finally { CloseableReference.closeSafely(destImageRef); } }
/** * Submits a request for execution and returns a DataSource representing the pending decoded * image(s). * <p>The returned DataSource must be closed once the client has finished with it. * * @param imageRequest the request to submit * @param callerContext the caller context for image request * @param lowestPermittedRequestLevelOnSubmit the lowest request level permitted for image request * @return a DataSource representing the pending decoded image(s) */ public DataSource<CloseableReference<CloseableImage>> fetchDecodedImage( ImageRequest imageRequest, Object callerContext, ImageRequest.RequestLevel lowestPermittedRequestLevelOnSubmit) { try { Producer<CloseableReference<CloseableImage>> producerSequence = mProducerSequenceFactory.getDecodedImageProducerSequence(imageRequest); return submitFetchRequest( producerSequence, imageRequest, lowestPermittedRequestLevelOnSubmit, callerContext); } catch (Exception exception) { return DataSources.immediateFailedDataSource(exception); } }
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);
@Test public void testFetchFromBitmapCacheDueToMethodCall() { Producer<CloseableReference<CloseableImage>> bitmapCacheSequence = mock(Producer.class); when(mProducerSequenceFactory.getDecodedImageProducerSequence(mImageRequest)) .thenReturn(bitmapCacheSequence); mImagePipeline.fetchImageFromBitmapCache(mImageRequest, mCallerContext); verify(mRequestListener1).onRequestStart(mImageRequest, mCallerContext, "0", false); verify(mRequestListener2).onRequestStart(mImageRequest, mCallerContext, "0", false); ArgumentCaptor<ProducerContext> producerContextArgumentCaptor = ArgumentCaptor.forClass(ProducerContext.class); verify(bitmapCacheSequence) .produceResults(any(Consumer.class), producerContextArgumentCaptor.capture()); assertTrue(producerContextArgumentCaptor.getValue().isIntermediateResultExpected()); assertEquals(producerContextArgumentCaptor.getValue().getPriority(), Priority.HIGH); assertEquals( producerContextArgumentCaptor.getValue().getLowestPermittedRequestLevel(), ImageRequest.RequestLevel.BITMAP_MEMORY_CACHE); }
@Override public CloseableImage decode( EncodedImage encodedImage, int length, QualityInfo qualityInfo, ImageDecodeOptions options) { InputStream encodedInputStream = null; try { encodedInputStream = encodedImage.getInputStream(); return new CloseableKeyframesImage( KFImageDeserializer.deserialize(encodedInputStream)); } catch (IOException e) { e.printStackTrace(); return null; } finally { Closeables.closeQuietly(encodedInputStream); } }
@Test public void testFetchDecodedImageWithRequestLevel() { Producer<CloseableReference<CloseableImage>> decodedSequence = mock(Producer.class); when(mProducerSequenceFactory.getDecodedImageProducerSequence(mImageRequest)) .thenReturn(decodedSequence); DataSource<CloseableReference<CloseableImage>> dataSource = mImagePipeline.fetchDecodedImage( mImageRequest, mCallerContext, ImageRequest.RequestLevel.DISK_CACHE); assertFalse(dataSource.isFinished()); verify(mRequestListener1).onRequestStart(mImageRequest, mCallerContext, "0", false); verify(mRequestListener2).onRequestStart(mImageRequest, mCallerContext, "0", false); ArgumentCaptor<ProducerContext> producerContextArgumentCaptor = ArgumentCaptor.forClass(ProducerContext.class); verify(decodedSequence) .produceResults(any(Consumer.class), producerContextArgumentCaptor.capture()); assertTrue(producerContextArgumentCaptor.getValue().isIntermediateResultExpected()); assertEquals(producerContextArgumentCaptor.getValue().getPriority(), Priority.HIGH); assertEquals( producerContextArgumentCaptor.getValue().getLowestPermittedRequestLevel(), ImageRequest.RequestLevel.DISK_CACHE); }
/** * swallow result producer -> inputProducer */ private synchronized Producer<Void> getDecodedImagePrefetchSequence( Producer<CloseableReference<CloseableImage>> inputProducer) { if (!mCloseableImagePrefetchSequences.containsKey(inputProducer)) { SwallowResultProducer<CloseableReference<CloseableImage>> swallowResultProducer = mProducerFactory.newSwallowResultProducer(inputProducer); mCloseableImagePrefetchSequences.put(inputProducer, swallowResultProducer); } return mCloseableImagePrefetchSequences.get(inputProducer); }
@Test public void testLocalResourceFullFetch() { PowerMockito.when(mImageRequest.getSourceUriType()) .thenReturn(SOURCE_TYPE_LOCAL_RESOURCE); Producer<CloseableReference<CloseableImage>> producer = mProducerSequenceFactory.getDecodedImageProducerSequence(mImageRequest); assertSame(producer, mProducerSequenceFactory.mLocalResourceFetchSequence); }
@Before public void setUp() { MockitoAnnotations.initMocks(this); mBitmapMemoryCacheProducer = new BitmapMemoryCacheProducer(mMemoryCache, mCacheKeyFactory, mInputProducer); mBitmapMemoryCacheKey = mock(BitmapMemoryCacheKey.class); mCloseableImage1 = mock(CloseableImage.class); when(mCloseableImage1.getQualityInfo()).thenReturn(ImmutableQualityInfo.FULL_QUALITY); mCloseableImage2 = mock(CloseableImage.class); when(mCloseableImage2.getQualityInfo()) .thenReturn(ImmutableQualityInfo.of(INTERMEDIATE_SCAN_2, true, false)); mFinalImageReference = CloseableReference.of(mCloseableImage1); mIntermediateImageReference = CloseableReference.of(mCloseableImage2); mFinalImageReferenceClone = mFinalImageReference.clone(); mIntermediateImageReferenceClone = mIntermediateImageReference.clone(); when(mMemoryCache.cache(mBitmapMemoryCacheKey, mIntermediateImageReference)) .thenReturn(mIntermediateImageReferenceClone); when(mMemoryCache.cache(mBitmapMemoryCacheKey, mFinalImageReference)) .thenReturn(mFinalImageReferenceClone); when(mProducerContext.getImageRequest()).thenReturn(mImageRequest); when(mProducerContext.getListener()).thenReturn(mProducerListener); when(mProducerContext.getId()).thenReturn(mRequestId); when(mProducerContext.getLowestPermittedRequestLevel()) .thenReturn(ImageRequest.RequestLevel.FULL_FETCH); when(mProducerContext.getCallerContext()).thenReturn(PRODUCER_NAME); when(mProducerListener.requiresExtraMap(mRequestId)).thenReturn(true); when(mCacheKeyFactory.getBitmapCacheKey(mImageRequest, PRODUCER_NAME)) .thenReturn(mBitmapMemoryCacheKey); }
/** * Initializes this controller with the new data source supplier, id and caller context. This * allows for reusing of the existing controller instead of instantiating a new one. This method * should be called when the controller is in detached state. * * @param dataSourceSupplier data source supplier * @param id unique id for this controller * @param callerContext tag and context for this controller */ public void initialize( Supplier<DataSource<CloseableReference<CloseableImage>>> dataSourceSupplier, String id, CacheKey cacheKey, Object callerContext, @Nullable ImmutableList<DrawableFactory> customDrawableFactories) { super.initialize(id, callerContext); init(dataSourceSupplier); mCacheKey = cacheKey; setCustomDrawableFactories(customDrawableFactories); }
/** * post-processor producer -> copy producer -> inputProducer */ private synchronized Producer<CloseableReference<CloseableImage>> getPostprocessorSequence( Producer<CloseableReference<CloseableImage>> inputProducer) { if (!mPostprocessorSequences.containsKey(inputProducer)) { PostprocessorProducer postprocessorProducer = mProducerFactory.newPostprocessorProducer(inputProducer); PostprocessedBitmapMemoryCacheProducer postprocessedBitmapMemoryCacheProducer = mProducerFactory.newPostprocessorBitmapMemoryCacheProducer(postprocessorProducer); mPostprocessorSequences.put(inputProducer, postprocessedBitmapMemoryCacheProducer); } return mPostprocessorSequences.get(inputProducer); }
private void setupInputProducerNotFound() { final List<CloseableReference<CloseableImage>> empty = new ArrayList<CloseableReference<CloseableImage>>(1); empty.add(null); doAnswer( new ProduceResultsNewResultAnswer(empty)) .when(mInputProducer).produceResults(any(Consumer.class), eq(mProducerContext)); }
/** * Returns whether the image is stored in the bitmap memory cache. * * @param imageRequest the imageRequest for the image to be looked up. * @return true if the image was found in the bitmap memory cache, false otherwise. */ public boolean isInBitmapMemoryCache(final ImageRequest imageRequest) { if (imageRequest == null) { return false; } final CacheKey cacheKey = mCacheKeyFactory.getBitmapCacheKey(imageRequest, null); CloseableReference<CloseableImage> ref = mBitmapMemoryCache.get(cacheKey); try { return CloseableReference.isValid(ref); } finally { CloseableReference.closeSafely(ref); } }
@Override public Void answer(InvocationOnMock invocation) throws Throwable { Consumer consumer = (Consumer) invocation.getArguments()[0]; Iterator<CloseableReference<CloseableImage>> iterator = mResults.iterator(); while (iterator.hasNext()) { CloseableReference<CloseableImage> result = iterator.next(); consumer.onNewResult(result, BaseConsumer.simpleStatusForIsLast(!iterator.hasNext())); } return null; }
@Override public Void answer(InvocationOnMock invocation) throws Throwable { Consumer consumer = (Consumer) invocation.getArguments()[0]; Iterator<CloseableReference<CloseableImage>> iterator = mResults.iterator(); while (iterator.hasNext()) { CloseableReference<CloseableImage> result = iterator.next(); consumer.onNewResult( result, iterator.hasNext() ? mStatusFlags : Consumer.IS_LAST | mStatusFlags); } return null; }
@Override protected CloseableReference<CloseableImage> getCachedImage() { if (mMemoryCache == null || mCacheKey == null) { return null; } // We get the CacheKey CloseableReference<CloseableImage> closeableImage = mMemoryCache.get(mCacheKey); if (closeableImage != null && !closeableImage.get().getQualityInfo().isOfFullQuality()) { closeableImage.close(); return null; } return closeableImage; }
@Override public synchronized void onFramePrepared( int frameNumber, CloseableReference<Bitmap> bitmapReference, @BitmapAnimationBackend.FrameType int frameType) { Preconditions.checkNotNull(bitmapReference); CloseableReference<CloseableImage> closableReference = null; try { closableReference = createImageReference(bitmapReference); if (closableReference == null) { return; } CloseableReference<CloseableImage> newReference = mAnimatedFrameCache.cache(frameNumber, closableReference); if (CloseableReference.isValid(newReference)) { CloseableReference<CloseableImage> oldReference = mPreparedPendingFrames.get(frameNumber); CloseableReference.closeSafely(oldReference); // For performance reasons, we don't clone the reference and close the original one // but cache the reference directly. mPreparedPendingFrames.put(frameNumber, newReference); FLog.v( TAG, "cachePreparedFrame(%d) cached. Pending frames: %s", frameNumber, mPreparedPendingFrames); } } finally { CloseableReference.closeSafely(closableReference); } }
private synchronized void removePreparedReference(int frameNumber) { CloseableReference<CloseableImage> existingPendingReference = mPreparedPendingFrames.get(frameNumber); if (existingPendingReference != null) { mPreparedPendingFrames.delete(frameNumber); CloseableReference.closeSafely(existingPendingReference); FLog.v( TAG, "removePreparedReference(%d) removed. Pending frames: %s", frameNumber, mPreparedPendingFrames); } }
@Override public CloseableImage decode( EncodedImage encodedImage, int length, QualityInfo qualityInfo, ImageDecodeOptions options) { try { // Read the file as a string String text = new String(ByteStreams.toByteArray(encodedImage.getInputStream())); // Check if the string matches "<color>#" if (!text.startsWith(COLOR_TAG + "#")) { return null; } // Parse the int value between # and < int startIndex = COLOR_TAG.length() + 1; int endIndex = text.lastIndexOf('<'); int color = Integer.parseInt(text.substring(startIndex, endIndex), 16); // Add the alpha component so that we actually see the color color = ColorUtils.setAlphaComponent(color, 255); // Return the CloseableImage return new CloseableColorImage(color); } catch (IOException e) { e.printStackTrace(); } // Return nothing if an error occurred return null; }
@Nullable private static CloseableReference<CloseableImage> createImageReference( CloseableReference<Bitmap> bitmapReference) { // The given CloseableStaticBitmap will be cached and then released by the resource releaser // of the closeable reference CloseableImage closeableImage = new CloseableStaticBitmap(bitmapReference, ImmutableQualityInfo.FULL_QUALITY, 0); return CloseableReference.of(closeableImage); }