/*
* 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.fresco.animation.factory;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.net.Uri;
import com.facebook.cache.common.CacheKey;
import com.facebook.common.internal.Supplier;
import com.facebook.common.time.MonotonicClock;
import com.facebook.drawee.backends.pipeline.DrawableFactory;
import com.facebook.fresco.animation.backend.AnimationBackend;
import com.facebook.fresco.animation.backend.AnimationBackendDelegateWithInactivityCheck;
import com.facebook.fresco.animation.bitmap.BitmapAnimationBackend;
import com.facebook.fresco.animation.bitmap.BitmapFrameCache;
import com.facebook.fresco.animation.bitmap.BitmapFrameRenderer;
import com.facebook.fresco.animation.bitmap.cache.FrescoFrameCache;
import com.facebook.fresco.animation.bitmap.cache.KeepLastFrameCache;
import com.facebook.fresco.animation.bitmap.cache.NoOpCache;
import com.facebook.fresco.animation.bitmap.preparation.BitmapFramePreparationStrategy;
import com.facebook.fresco.animation.bitmap.preparation.BitmapFramePreparer;
import com.facebook.fresco.animation.bitmap.preparation.DefaultBitmapFramePreparer;
import com.facebook.fresco.animation.bitmap.preparation.FixedNumberBitmapFramePreparationStrategy;
import com.facebook.fresco.animation.bitmap.wrapper.AnimatedDrawableBackendAnimationInformation;
import com.facebook.fresco.animation.bitmap.wrapper.AnimatedDrawableBackendFrameRenderer;
import com.facebook.fresco.animation.drawable.AnimatedDrawable2;
import com.facebook.imagepipeline.animated.base.AnimatedDrawableBackend;
import com.facebook.imagepipeline.animated.base.AnimatedImage;
import com.facebook.imagepipeline.animated.base.AnimatedImageResult;
import com.facebook.imagepipeline.animated.impl.AnimatedDrawableBackendProvider;
import com.facebook.imagepipeline.animated.impl.AnimatedFrameCache;
import com.facebook.imagepipeline.bitmaps.PlatformBitmapFactory;
import com.facebook.imagepipeline.cache.CountingMemoryCache;
import com.facebook.imagepipeline.image.CloseableAnimatedImage;
import com.facebook.imagepipeline.image.CloseableImage;
/**
* Animation factory for {@link AnimatedDrawable2}.
*
* This factory is similar to {@link ExperimentalAnimationFactory} but it wraps around the new
* {@link BitmapAnimationBackend} and does not rely on
* {@link com.facebook.imagepipeline.animated.base.AnimatedDrawableCachingBackend}
* to do the caching.
*/
public class ExperimentalBitmapAnimationDrawableFactory implements DrawableFactory {
public static final int CACHING_STRATEGY_NO_CACHE = 0;
public static final int CACHING_STRATEGY_FRESCO_CACHE = 1;
public static final int CACHING_STRATEGY_FRESCO_CACHE_NO_REUSING = 2;
public static final int CACHING_STRATEGY_KEEP_LAST_CACHE = 3;
private final AnimatedDrawableBackendProvider mAnimatedDrawableBackendProvider;
private final ScheduledExecutorService mScheduledExecutorServiceForUiThread;
private final ExecutorService mExecutorServiceForFramePreparing;
private final MonotonicClock mMonotonicClock;
private final PlatformBitmapFactory mPlatformBitmapFactory;
private final CountingMemoryCache<CacheKey, CloseableImage> mBackingCache;
private final Supplier<Integer> mCachingStrategySupplier;
private final Supplier<Integer> mNumberOfFramesToPrepareSupplier;
public ExperimentalBitmapAnimationDrawableFactory(
AnimatedDrawableBackendProvider animatedDrawableBackendProvider,
ScheduledExecutorService scheduledExecutorServiceForUiThread,
ExecutorService executorServiceForFramePreparing,
MonotonicClock monotonicClock,
PlatformBitmapFactory platformBitmapFactory,
CountingMemoryCache<CacheKey, CloseableImage> backingCache,
Supplier<Integer> cachingStrategySupplier,
Supplier<Integer> numberOfFramesToPrepareSupplier) {
mAnimatedDrawableBackendProvider = animatedDrawableBackendProvider;
mScheduledExecutorServiceForUiThread = scheduledExecutorServiceForUiThread;
mExecutorServiceForFramePreparing = executorServiceForFramePreparing;
mMonotonicClock = monotonicClock;
mPlatformBitmapFactory = platformBitmapFactory;
mBackingCache = backingCache;
mCachingStrategySupplier = cachingStrategySupplier;
mNumberOfFramesToPrepareSupplier = numberOfFramesToPrepareSupplier;
}
@Override
public boolean supportsImageType(CloseableImage image) {
return image instanceof CloseableAnimatedImage;
}
@Override
public AnimatedDrawable2 createDrawable(CloseableImage image) {
return new AnimatedDrawable2(
createAnimationBackend(
((CloseableAnimatedImage) image).getImageResult()));
}
private AnimationBackend createAnimationBackend(AnimatedImageResult animatedImageResult) {
AnimatedDrawableBackend animatedDrawableBackend =
createAnimatedDrawableBackend(animatedImageResult);
BitmapFrameCache bitmapFrameCache = createBitmapFrameCache(animatedImageResult);
BitmapFrameRenderer bitmapFrameRenderer =
new AnimatedDrawableBackendFrameRenderer(bitmapFrameCache, animatedDrawableBackend);
int numberOfFramesToPrefetch = mNumberOfFramesToPrepareSupplier.get();
BitmapFramePreparationStrategy bitmapFramePreparationStrategy = null;
BitmapFramePreparer bitmapFramePreparer = null;
if (numberOfFramesToPrefetch > 0) {
bitmapFramePreparationStrategy = new FixedNumberBitmapFramePreparationStrategy();
bitmapFramePreparer = createBitmapFramePreparer(bitmapFrameRenderer);
}
BitmapAnimationBackend bitmapAnimationBackend = new BitmapAnimationBackend(
mPlatformBitmapFactory,
bitmapFrameCache,
new AnimatedDrawableBackendAnimationInformation(animatedDrawableBackend),
bitmapFrameRenderer,
bitmapFramePreparationStrategy,
bitmapFramePreparer);
return AnimationBackendDelegateWithInactivityCheck.createForBackend(
bitmapAnimationBackend,
mMonotonicClock,
mScheduledExecutorServiceForUiThread);
}
private BitmapFramePreparer createBitmapFramePreparer(BitmapFrameRenderer bitmapFrameRenderer) {
return new DefaultBitmapFramePreparer(
mPlatformBitmapFactory,
bitmapFrameRenderer,
Bitmap.Config.ARGB_8888,
mExecutorServiceForFramePreparing);
}
private AnimatedDrawableBackend createAnimatedDrawableBackend(
AnimatedImageResult animatedImageResult) {
AnimatedImage animatedImage = animatedImageResult.getImage();
Rect initialBounds = new Rect(0, 0, animatedImage.getWidth(), animatedImage.getHeight());
return mAnimatedDrawableBackendProvider.get(animatedImageResult, initialBounds);
}
private BitmapFrameCache createBitmapFrameCache(AnimatedImageResult animatedImageResult) {
switch (mCachingStrategySupplier.get()) {
case CACHING_STRATEGY_FRESCO_CACHE:
return new FrescoFrameCache(createAnimatedFrameCache(animatedImageResult), true);
case CACHING_STRATEGY_FRESCO_CACHE_NO_REUSING:
return new FrescoFrameCache(createAnimatedFrameCache(animatedImageResult), false);
case CACHING_STRATEGY_KEEP_LAST_CACHE:
return new KeepLastFrameCache();
case CACHING_STRATEGY_NO_CACHE:
default:
return new NoOpCache();
}
}
private AnimatedFrameCache createAnimatedFrameCache(
final AnimatedImageResult animatedImageResult) {
return new AnimatedFrameCache(
new AnimationFrameCacheKey(animatedImageResult.hashCode()),
mBackingCache);
}
public static class AnimationFrameCacheKey implements CacheKey {
private static final String URI_PREFIX = "anim://";
private final String mAnimationUriString;
public AnimationFrameCacheKey(int imageId) {
mAnimationUriString = URI_PREFIX + imageId;
}
@Override
public boolean containsUri(Uri uri) {
return uri.toString().startsWith(mAnimationUriString);
}
@Override
public String getUriString() {
return mAnimationUriString;
}
}
}