/** * 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); } }
@Test public void testSmallImageDiskCacheGetSuccessful() { when(mImageRequest.getCacheChoice()).thenReturn(ImageRequest.CacheChoice.SMALL); setupDiskCacheGetSuccess(mSmallImageBufferedDiskCache); mDiskCacheReadProducer.produceResults(mConsumer, mProducerContext); verify(mConsumer).onNewResult(mFinalEncodedImage, Consumer.IS_LAST); verify(mProducerListener).onProducerStart(mRequestId, PRODUCER_NAME); ArgumentCaptor<HashMap> captor = ArgumentCaptor.forClass(HashMap.class); verify(mProducerListener).onProducerFinishWithSuccess( eq(mRequestId), eq(PRODUCER_NAME), captor.capture()); Map<String, String> resultMap = captor.getValue(); assertEquals("true", resultMap.get(DiskCacheReadProducer.EXTRA_CACHED_VALUE_FOUND)); assertEquals( "0", resultMap.get(DiskCacheReadProducer.ENCODED_IMAGE_SIZE)); verify(mProducerListener).onUltimateProducerReached(mRequestId, PRODUCER_NAME, true); Assert.assertFalse(EncodedImage.isValid(mFinalEncodedImage)); }
@Override protected void onNewResultImpl(@Nullable EncodedImage newResult, @Status int status) { // try to determine if the last result should be transformed if (mShouldTranscodeWhenFinished == TriState.UNSET && newResult != null) { mShouldTranscodeWhenFinished = shouldTranscode(newResult); } // just propagate result if it shouldn't be transformed if (mShouldTranscodeWhenFinished == TriState.NO) { getConsumer().onNewResult(newResult, status); return; } if (isLast(status)) { if (mShouldTranscodeWhenFinished == TriState.YES && newResult != null) { transcodeLastResult(newResult, getConsumer(), mContext); } else { getConsumer().onNewResult(newResult, status); } } }
/** * 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 testFirstProducerResultNotGoodEnough() { EncodedImage firstProducerEncodedImage = new EncodedImage( mFirstProducerFinalResult.getByteBufferRef()); firstProducerEncodedImage.setRotationAngle(-1); firstProducerEncodedImage.setWidth(WIDTH / 2); firstProducerEncodedImage.setHeight(HEIGHT / 2); mFirstProducerConsumer.onNewResult(firstProducerEncodedImage, Consumer.IS_LAST); verify(mConsumer).onNewResult(firstProducerEncodedImage, Consumer.NO_FLAGS); EncodedImage intermediateEncodedImage = new EncodedImage( mIntermediateResult.getByteBufferRef()); intermediateEncodedImage.setRotationAngle(-1); intermediateEncodedImage.setWidth(WIDTH / 2); intermediateEncodedImage.setHeight(HEIGHT / 2); mSecondProducerConsumer.onNewResult(intermediateEncodedImage, Consumer.NO_FLAGS); verify(mConsumer).onNewResult(intermediateEncodedImage, Consumer.NO_FLAGS); EncodedImage secondProducerEncodedImage = new EncodedImage( mSecondProducerFinalResult.getByteBufferRef()); secondProducerEncodedImage.setRotationAngle(-1); secondProducerEncodedImage.setWidth(WIDTH / 2); secondProducerEncodedImage.setHeight(HEIGHT / 2); mSecondProducerConsumer.onNewResult(secondProducerEncodedImage, Consumer.IS_LAST); verify(mConsumer).onNewResult(secondProducerEncodedImage, Consumer.IS_LAST); }
@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()); }
/** * 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); } }
/** * bitmap cache get -> * background thread hand-off -> multiplex -> bitmap cache -> decode -> * branch on separate images * -> thumbnail resize and rotate -> thumbnail branch * -> local content thumbnail creation * -> exif thumbnail creation * -> local image resize and rotate -> add meta data producer -> multiplex -> encoded cache -> * (webp transcode) -> local content uri fetch. */ private synchronized Producer<CloseableReference<CloseableImage>> getLocalContentUriFetchSequence() { if (mLocalContentUriFetchSequence == null) { LocalContentUriFetchProducer localContentUriFetchProducer = mProducerFactory.newLocalContentUriFetchProducer(); ThumbnailProducer<EncodedImage>[] thumbnailProducers = new ThumbnailProducer[2]; thumbnailProducers[0] = mProducerFactory.newLocalContentUriThumbnailFetchProducer(); thumbnailProducers[1] = mProducerFactory.newLocalExifThumbnailProducer(); mLocalContentUriFetchSequence = newBitmapCacheGetToLocalTransformSequence( localContentUriFetchProducer, thumbnailProducers); } return mLocalContentUriFetchSequence; }
/** * Options returned by this method are configured with mDecodeBuffer which is GuardedBy("this") */ private static BitmapFactory.Options getDecodeOptionsForStream( EncodedImage encodedImage, Bitmap.Config bitmapConfig) { final BitmapFactory.Options options = new BitmapFactory.Options(); // Sample size should ONLY be different than 1 when downsampling is enabled in the pipeline options.inSampleSize = encodedImage.getSampleSize(); options.inJustDecodeBounds = true; // fill outWidth and outHeight BitmapFactory.decodeStream(encodedImage.getInputStream(), null, options); if (options.outWidth == -1 || options.outHeight == -1) { throw new IllegalArgumentException(); } options.inJustDecodeBounds = false; options.inDither = true; options.inPreferredConfig = bitmapConfig; options.inMutable = true; return options; }
/** * Decodes image. * * @param encodedImage input image (encoded bytes plus meta data) * @param length if image type supports decoding incomplete image then determines where the image * data should be cut for decoding. * @param qualityInfo quality information for the image * @param options options that cange decode behavior */ @Override public CloseableImage decode( final EncodedImage encodedImage, final int length, final QualityInfo qualityInfo, final ImageDecodeOptions options) { if (options.customImageDecoder != null) { return options.customImageDecoder.decode(encodedImage, length, qualityInfo, options); } ImageFormat imageFormat = encodedImage.getImageFormat(); if (imageFormat == null || imageFormat == ImageFormat.UNKNOWN) { imageFormat = ImageFormatChecker.getImageFormat_WrapIOException( encodedImage.getInputStream()); encodedImage.setImageFormat(imageFormat); } if (mCustomDecoders != null) { ImageDecoder decoder = mCustomDecoders.get(imageFormat); if (decoder != null) { return decoder.decode(encodedImage, length, qualityInfo, options); } } return mDefaultDecoder.decode(encodedImage, length, qualityInfo, options); }
/** * Determine if an valid entry for the key exists in the staging area. */ public synchronized boolean containsKey(CacheKey key) { Preconditions.checkNotNull(key); if (!mMap.containsKey(key)) { return false; } EncodedImage storedEncodedImage = mMap.get(key); synchronized (storedEncodedImage) { if (!EncodedImage.isValid(storedEncodedImage)) { // Reference is not valid, this means that someone cleared reference while it was still in // use. Log error // TODO: 3697790 mMap.remove(key); FLog.w( TAG, "Found closed reference %d for key %s (%d)", System.identityHashCode(storedEncodedImage), key.getUriString(), System.identityHashCode(key)); return false; } return true; } }
/** * Performs key-value loop up in staging area and file cache. * Any error manifests itself as a miss, i.e. returns false. * @param key * @return true if the image is found in staging area or File cache, false if not found */ private boolean checkInStagingAreaAndFileCache(final CacheKey key) { EncodedImage result = mStagingArea.get(key); if (result != null) { result.close(); FLog.v(TAG, "Found image for %s in staging area", key.getUriString()); mImageCacheStatsTracker.onStagingAreaHit(key); return true; } else { FLog.v(TAG, "Did not find image for %s in staging area", key.getUriString()); mImageCacheStatsTracker.onStagingAreaMiss(); try { return mFileCache.hasKey(key); } catch (Exception exception) { return false; } } }
private void mockThumbnailFile() throws Exception { PowerMockito.whenNew(File.class) .withArguments(THUMBNAIL_FILE_NAME) .thenReturn(mThumbnailFile); when(mThumbnailFile.exists()).thenReturn(true); when(mThumbnailFile.length()).thenReturn(THUMBNAIL_FILE_SIZE); PowerMockito.whenNew(FileInputStream.class) .withArguments(THUMBNAIL_FILE_NAME) .thenReturn(mock(FileInputStream.class)); EncodedImage encodedImage = mock(EncodedImage.class); when(encodedImage.getSize()).thenReturn((int) THUMBNAIL_FILE_SIZE); PowerMockito.whenNew(EncodedImage.class) .withAnyArguments() .thenReturn(encodedImage); }
@Override public void produceResults(Consumer<EncodedImage> consumer, ProducerContext context) { context.getListener() .onProducerStart(context.getId(), PRODUCER_NAME); final FetchState fetchState = mNetworkFetcher.createFetchState(consumer, context); mNetworkFetcher.fetch( fetchState, new NetworkFetcher.Callback() { @Override public void onResponse(InputStream response, int responseLength) throws IOException { NetworkFetchProducer.this.onResponse(fetchState, response, responseLength); } @Override public void onFailure(Throwable throwable) { NetworkFetchProducer.this.onFailure(fetchState, throwable); } @Override public void onCancellation() { NetworkFetchProducer.this.onCancellation(fetchState); } }); }
@Test public void testOnNewResultNotLast_DimensionsNotFound() { int rotationAngle = 180; int orientation = 1; when(ImageFormatChecker.getImageFormat_WrapIOException(any(InputStream.class))) .thenReturn(DefaultImageFormats.JPEG); when(JfifUtil.getAutoRotateAngleFromOrientation(orientation)).thenReturn(rotationAngle); when(JfifUtil.getOrientation(any(InputStream.class))).thenReturn(orientation); when(BitmapUtil.decodeDimensions(any(InputStream.class))).thenReturn(null); mAddMetaDataConsumer.onNewResult(mIntermediateResult, Consumer.NO_FLAGS); ArgumentCaptor<EncodedImage> argumentCaptor = ArgumentCaptor.forClass(EncodedImage.class); verify(mConsumer).onNewResult(argumentCaptor.capture(), eq(Consumer.NO_FLAGS)); EncodedImage encodedImage = argumentCaptor.getValue(); assertTrue(EncodedImage.isValid(encodedImage)); assertEquals(-1, encodedImage.getRotationAngle()); assertEquals(-1, encodedImage.getWidth()); assertEquals(-1, encodedImage.getHeight()); }
@Test public void testDoesNotTransformIfImageRotationAngleUnkown() { whenResizingEnabled(); whenRequestSpecificRotation(RotationOptions.NO_ROTATION); provideIntermediateResult( DefaultImageFormats.JPEG, 800, 800, EncodedImage.UNKNOWN_ROTATION_ANGLE, ExifInterface.ORIENTATION_UNDEFINED); verifyIntermediateResultPassedThroughUnchanged(); provideFinalResult( DefaultImageFormats.JPEG, 800, 800, EncodedImage.UNKNOWN_ROTATION_ANGLE, ExifInterface.ORIENTATION_UNDEFINED); verifyFinalResultPassedThroughUnchanged(); verifyZeroJpegTranscoderInteractions(); }
/** * background-thread hand-off -> multiplex -> encoded cache -> * disk cache -> (webp transcode) -> local file fetch */ private synchronized Producer<EncodedImage> getBackgroundLocalFileFetchToEncodeMemorySequence() { if (mBackgroundLocalFileFetchToEncodedMemorySequence == null) { final LocalFileFetchProducer localFileFetchProducer = mProducerFactory.newLocalFileFetchProducer(); final Producer<EncodedImage> toEncodedMultiplexProducer = newEncodedCacheMultiplexToTranscodeSequence(localFileFetchProducer); mBackgroundLocalFileFetchToEncodedMemorySequence = mProducerFactory.newBackgroundThreadHandoffProducer( toEncodedMultiplexProducer, mThreadHandoffProducerQueue); } return mBackgroundLocalFileFetchToEncodedMemorySequence; }
private void verifyInputProducerProduceResultsWithNewConsumer(boolean allowIntermediateResult) { verify(mInputProducer) .produceResults(mConsumerCaptor.capture(), mProducerContextCaptor.capture()); Consumer<EncodedImage> consumer = mConsumerCaptor.getValue(); assertThat(consumer).isInstanceOf(MediaVariationsConsumer.class); assertThat(((MediaVariationsConsumer) consumer).getConsumer()).isSameAs(mConsumer); SettableProducerContext referenceContext = new SettableProducerContext(mProducerContext); referenceContext.setIsIntermediateResultExpected(allowIntermediateResult); ProducerContext capturedContext = mProducerContextCaptor.getValue(); assertEquals(referenceContext.getCallerContext(), capturedContext.getCallerContext()); assertEquals(referenceContext.getId(), capturedContext.getId()); assertEquals(referenceContext.getImageRequest(), capturedContext.getImageRequest()); assertEquals(referenceContext.getListener(), capturedContext.getListener()); assertEquals( referenceContext.getLowestPermittedRequestLevel(), capturedContext.getLowestPermittedRequestLevel()); assertEquals(referenceContext.getPriority(), capturedContext.getPriority()); assertEquals( referenceContext.isIntermediateResultExpected(), capturedContext.isIntermediateResultExpected()); }
@Override protected void onNewResultImpl(EncodedImage newResult, @Status int status) { ImageRequest request = mProducerContext.getImageRequest(); boolean isLast = isLast(status); boolean isGoodEnough = ThumbnailSizeChecker.isImageBigEnough(newResult, request.getResizeOptions()); if (newResult != null && (isGoodEnough || request.getLocalThumbnailPreviewsEnabled())) { if (isLast && isGoodEnough) { getConsumer().onNewResult(newResult, status); } else { int alteredStatus = turnOffStatusFlag(status, IS_LAST); getConsumer().onNewResult(newResult, alteredStatus); } } if (isLast && !isGoodEnough) { EncodedImage.closeSafely(newResult); mInputProducer2.produceResults(getConsumer(), mProducerContext); } }
private @Nullable EncodedImage getCameraImage(Uri uri) throws IOException { Cursor cursor = mContentResolver.query(uri, PROJECTION, null, null, null); if (cursor == null) { return null; } try { if (cursor.getCount() == 0) { return null; } cursor.moveToFirst(); final String pathname = cursor.getString(cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA)); if (pathname != null) { return getEncodedImage(new FileInputStream(pathname), getLength(pathname)); } } finally { cursor.close(); } return null; }
@Test public void testSchedule_Last_Queued() { mJobScheduler.updateJob(fakeEncodedImage(), Consumer.IS_LAST); assertTrue(mJobScheduler.scheduleJob()); EncodedImage encodedImage2 = fakeEncodedImage(); mJobScheduler.updateJob(encodedImage2, Consumer.IS_LAST); assertEquals(JobScheduler.JobState.QUEUED, mJobScheduler.mJobState); assertTrue(mJobScheduler.scheduleJob()); assertEquals(0, mTestScheduledExecutorService.getPendingCount()); assertEquals(1, mTestExecutorService.getPendingCount()); assertEquals(0, mTestJobRunnable.jobs.size()); mFakeClockForTime.incrementBy(1234); mFakeClockForWorker.incrementBy(1234); mFakeClockForScheduled.incrementBy(1234); assertEquals(1, mTestJobRunnable.jobs.size()); assertJobsEqual(mTestJobRunnable.jobs.get(0), encodedImage2, Consumer.IS_LAST); }
public void produceResults( final Consumer<EncodedImage> consumer, final ProducerContext producerContext) { final ImageRequest imageRequest = producerContext.getImageRequest(); if (!imageRequest.isDiskCacheEnabled()) { maybeStartInputProducer(consumer, producerContext); return; } producerContext.getListener().onProducerStart(producerContext.getId(), PRODUCER_NAME); final CacheKey cacheKey = mCacheKeyFactory.getEncodedCacheKey(imageRequest, producerContext.getCallerContext()); final boolean isSmallRequest = (imageRequest.getCacheChoice() == CacheChoice.SMALL); final BufferedDiskCache preferredCache = isSmallRequest ? mSmallImageBufferedDiskCache : mDefaultBufferedDiskCache; final AtomicBoolean isCancelled = new AtomicBoolean(false); final Task<EncodedImage> diskLookupTask = preferredCache.get(cacheKey, isCancelled); final Continuation<EncodedImage, Void> continuation = onFinishDiskReads(consumer, producerContext); diskLookupTask.continueWith(continuation); subscribeTaskForRequestCancellation(isCancelled, producerContext); }
private static boolean shouldProcess(EncodedImage encodedImage, @Consumer.Status int status) { // the last result should always be processed, whereas // an intermediate result should be processed only if valid return BaseConsumer.isLast(status) || BaseConsumer.statusHasFlag(status, Consumer.IS_PLACEHOLDER) || EncodedImage.isValid(encodedImage); }
@Override public ImageDecoder getGifDecoder(final Bitmap.Config bitmapConfig) { return new ImageDecoder() { @Override public CloseableImage decode( EncodedImage encodedImage, int length, QualityInfo qualityInfo, ImageDecodeOptions options) { return getAnimatedImageFactory().decodeGif(encodedImage, options, bitmapConfig); } }; }
private void startInputProducer( Consumer<EncodedImage> consumerOfPartialDiskCacheProducer, ProducerContext producerContext, CacheKey partialImageCacheKey, @Nullable EncodedImage partialResultFromCache) { Consumer<EncodedImage> consumer = new PartialDiskCacheConsumer( consumerOfPartialDiskCacheProducer, mDefaultBufferedDiskCache, partialImageCacheKey, mPooledByteBufferFactory, mByteArrayPool, partialResultFromCache); mInputProducer.produceResults(consumer, producerContext); }
@Before public void setUp() { MockitoAnnotations.initMocks(this); mEncodedMemoryCacheProducer = new EncodedMemoryCacheProducer(mMemoryCache, mCacheKeyFactory, mInputProducer); mPooledByteBuffer1 = mock(PooledByteBuffer.class); mPooledByteBuffer2 = mock(PooledByteBuffer.class); mFinalImageReference = CloseableReference.of(mPooledByteBuffer1); mIntermediateImageReference = CloseableReference.of(mPooledByteBuffer2); mFinalImageReferenceClone = mFinalImageReference.clone(); mFinalEncodedImage = new EncodedImage(mFinalImageReference); mIntermediateEncodedImage = new EncodedImage(mIntermediateImageReference); mFinalEncodedImageClone = new EncodedImage(mFinalImageReferenceClone); List<CacheKey> list = new ArrayList<>(); list.add(new SimpleCacheKey("http://dummy.uri")); mCacheKey = new MultiCacheKey(list); when(mCacheKeyFactory.getEncodedCacheKey(mImageRequest, mCallerContext)).thenReturn(mCacheKey); when(mMemoryCache.cache(mCacheKey, mFinalImageReference)).thenReturn(mFinalImageReferenceClone); when(mProducerContext.getImageRequest()).thenReturn(mImageRequest); when(mProducerContext.getCallerContext()).thenReturn(mCallerContext); when(mProducerContext.getListener()).thenReturn(mProducerListener); when(mProducerListener.requiresExtraMap(mRequestId)).thenReturn(true); when(mProducerContext.getId()).thenReturn(mRequestId); when(mProducerContext.getLowestPermittedRequestLevel()) .thenReturn(ImageRequest.RequestLevel.FULL_FETCH); }
@Test public void testImageWithInsufficientHeightWhenNoResizeOptions() { for (int rotation = 0; rotation < 360; rotation += 90) { EncodedImage mockImage = mockImage( BIG_ENOUGH_SIZE_FOR_NO_RESIZE_OPTIONS, BIG_ENOUGH_SIZE_FOR_NO_RESIZE_OPTIONS - 1, rotation); assertFalse(ThumbnailSizeChecker.isImageBigEnough(mockImage, null)); } }
/** * encoded cache multiplex -> encoded cache -> (disk cache) -> (webp transcode) * @param inputProducer producer providing the input to the transcode * @return encoded cache multiplex to webp transcode sequence */ private Producer<EncodedImage> newEncodedCacheMultiplexToTranscodeSequence( Producer<EncodedImage> inputProducer) { if (WebpSupportStatus.sIsWebpSupportRequired && (!mWebpSupportEnabled || WebpSupportStatus.sWebpBitmapFactory == null)) { inputProducer = mProducerFactory.newWebpTranscodeProducer(inputProducer); } inputProducer = newDiskCacheSequence(inputProducer); EncodedMemoryCacheProducer encodedMemoryCacheProducer = mProducerFactory.newEncodedMemoryCacheProducer(inputProducer); return mProducerFactory.newEncodedCacheKeyMultiplexProducer(encodedMemoryCacheProducer); }
/** * Clears the currently set job. * * <p> In case the currently set job has been scheduled but not started yet, the job won't be * executed. */ public void clearJob() { EncodedImage oldEncodedImage; synchronized (this) { oldEncodedImage = mEncodedImage; mEncodedImage = null; mStatus = 0; } EncodedImage.closeSafely(oldEncodedImage); }
@Test public void testClear() throws Exception { EncodedImage encodedImage = fakeEncodedImage(); mJobScheduler.updateJob(encodedImage, Consumer.IS_LAST); mJobScheduler.clearJob(); assertEquals(null, mJobScheduler.mEncodedImage); encodedImage.close(); assertNull(encodedImage.getByteBufferRef()); }
@Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mExecutor = new TestExecutorService(new FakeClock()); mLocalContentUriFetchProducer = new LocalContentUriFetchProducer( mExecutor, mPooledByteBufferFactory, mContentResolver ); mContentUri = Uri.fromFile(mock(File.class)); mProducerContext = new SettableProducerContext( mImageRequest, mRequestId, mProducerListener, mock(Object.class), ImageRequest.RequestLevel.FULL_FETCH, false, true, Priority.MEDIUM); when(mImageRequest.getSourceUri()).thenReturn(mContentUri); doAnswer( new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { mCapturedEncodedImage = EncodedImage.cloneOrNull((EncodedImage) invocation.getArguments()[0]); return null; } }) .when(mConsumer) .onNewResult(notNull(EncodedImage.class), anyInt()); }
@Override public void saveCachedVariant( String mediaId, ImageRequest.CacheChoice cacheChoice, CacheKey cacheKey, EncodedImage encodedImage) { // no-op }
private static EncodedImage mockEncodedImage(int width, int height, int rotationAngle) { EncodedImage mockImage = mock(EncodedImage.class); when(mockImage.getWidth()).thenReturn(width); when(mockImage.getHeight()).thenReturn(height); when(mockImage.getRotationAngle()).thenReturn(rotationAngle); return mockImage; }
/** * Removes key-value from the StagingArea. Both key and value must match. * @param key * @param encodedImage value corresponding to key * @return true if item was removed */ public synchronized boolean remove(final CacheKey key, final EncodedImage encodedImage) { Preconditions.checkNotNull(key); Preconditions.checkNotNull(encodedImage); Preconditions.checkArgument(EncodedImage.isValid(encodedImage)); final EncodedImage oldValue = mMap.get(key); if (oldValue == null) { return false; } CloseableReference<PooledByteBuffer> oldRef = oldValue.getByteBufferRef(); CloseableReference<PooledByteBuffer> ref = encodedImage.getByteBufferRef(); try { if (oldRef == null || ref == null || oldRef.get() != ref.get()) { return false; } mMap.remove(key); } finally { CloseableReference.closeSafely(ref); CloseableReference.closeSafely(oldRef); EncodedImage.closeSafely(oldValue); } logStats(); return true; }
public WebpTranscodeConsumer( final Consumer<EncodedImage> consumer, final ProducerContext context) { super(consumer); mContext = context; mShouldTranscodeWhenFinished = TriState.UNSET; }
@Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mCloseableReference = CloseableReference.of(mPooledByteBuffer); mEncodedImage = new EncodedImage(mCloseableReference); List<CacheKey> keys = new ArrayList<>(); keys.add(new SimpleCacheKey("http://test.uri")); keys.add(new SimpleCacheKey("http://tyrone.uri")); keys.add(new SimpleCacheKey("http://ian.uri")); mCacheKey = new MultiCacheKey(keys); mIsCancelled = new AtomicBoolean(false); FakeClock fakeClock = new FakeClock(); mReadPriorityExecutor = new TestExecutorService(fakeClock); mWritePriorityExecutor = new TestExecutorService(fakeClock); when(mBinaryResource.openStream()).thenReturn(mInputStream); when(mBinaryResource.size()).thenReturn(123L); when(mByteBufferFactory.newByteBuffer(same(mInputStream), eq(123))) .thenReturn(mPooledByteBuffer); mockStatic(StagingArea.class); when(StagingArea.getInstance()).thenReturn(mStagingArea); mBufferedDiskCache = new BufferedDiskCache( mFileCache, mByteBufferFactory, mPooledByteStreams, mReadPriorityExecutor, mWritePriorityExecutor, mImageCacheStatsTracker); }
private void assertConsumerReceivesImage() { ArgumentCaptor<EncodedImage> resultCaptor = ArgumentCaptor.forClass(EncodedImage.class); verify(mConsumer).onNewResult(resultCaptor.capture(), eq(Consumer.IS_LAST)); assertNotNull(resultCaptor.getValue()); assertEquals(THUMBNAIL_FILE_SIZE, resultCaptor.getValue().getSize()); verifyNoMoreInteractions(mConsumer); }
public ResizeAndRotateProducer( Executor executor, PooledByteBufferFactory pooledByteBufferFactory, boolean resizingEnabled, Producer<EncodedImage> inputProducer, boolean useDownsamplingRatio) { mExecutor = Preconditions.checkNotNull(executor); mPooledByteBufferFactory = Preconditions.checkNotNull(pooledByteBufferFactory); mResizingEnabled = resizingEnabled; mInputProducer = Preconditions.checkNotNull(inputProducer); mUseDownsamplingRatio = useDownsamplingRatio; }
public EncodedMemoryCacheProducer newEncodedMemoryCacheProducer( Producer<EncodedImage> inputProducer) { return new EncodedMemoryCacheProducer( mEncodedMemoryCache, mCacheKeyFactory, inputProducer); }