/* * 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.wrapper; import javax.annotation.Nullable; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.Drawable; import com.facebook.common.references.CloseableReference; import com.facebook.fresco.animation.backend.AnimationBackend; import com.facebook.fresco.animation.backend.AnimationBackendDelegateWithInactivityCheck; import com.facebook.imagepipeline.animated.base.AnimatedDrawableCachingBackend; /** * Animation backend that wraps around {@link AnimatedDrawableCachingBackend}. */ public class AnimatedDrawableCachingBackendWrapper implements AnimationBackend, AnimationBackendDelegateWithInactivityCheck.InactivityListener { private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG); private AnimatedDrawableCachingBackend mAnimatedDrawableCachingBackend; @Nullable private volatile CloseableReference<Bitmap> mLastDrawnFrame; @Nullable private Rect mBounds; public AnimatedDrawableCachingBackendWrapper( AnimatedDrawableCachingBackend animatedDrawableCachingBackend) { mAnimatedDrawableCachingBackend = animatedDrawableCachingBackend; } @Override public int getFrameCount() { return mAnimatedDrawableCachingBackend.getFrameCount(); } @Override public int getFrameDurationMs(int frameNumber) { return mAnimatedDrawableCachingBackend.getDurationMsForFrame(frameNumber); } @Override public int getLoopCount() { return mAnimatedDrawableCachingBackend.getLoopCount(); } @Override public boolean drawFrame(Drawable parent, Canvas canvas, int frameNumber) { // Render order (first available will be drawn): correct frame, last drawn frame, preview frame if (drawBitmap( mAnimatedDrawableCachingBackend.getBitmapForFrame(frameNumber), canvas, mBounds)) { return true; } else if (mLastDrawnFrame != null) { drawBitmap(mLastDrawnFrame, canvas, mBounds); } else if (drawBitmap( mAnimatedDrawableCachingBackend.getPreviewBitmap(), canvas, mBounds)) { return true; } return false; } @Override public void setAlpha(int alpha) { mPaint.setAlpha(alpha); } @Override public void setColorFilter(ColorFilter colorFilter) { mPaint.setColorFilter(colorFilter); } @Override public void setBounds(@Nullable Rect bounds) { if (bounds != null && bounds.equals(mBounds)) { return; } mBounds = bounds; AnimatedDrawableCachingBackend newBackend = mAnimatedDrawableCachingBackend.forNewBounds(bounds); if (newBackend != mAnimatedDrawableCachingBackend) { mAnimatedDrawableCachingBackend.dropCaches(); } mAnimatedDrawableCachingBackend = newBackend; CloseableReference.closeSafely(mLastDrawnFrame); mLastDrawnFrame = null; } @Override public int getSizeInBytes() { return mAnimatedDrawableCachingBackend.getMemoryUsage(); } @Override public void clear() { if (mAnimatedDrawableCachingBackend != null) { mAnimatedDrawableCachingBackend.dropCaches(); } CloseableReference.closeSafely(mLastDrawnFrame); mLastDrawnFrame = null; } @Override public int getIntrinsicWidth() { return mAnimatedDrawableCachingBackend.getWidth(); } @Override public int getIntrinsicHeight() { return mAnimatedDrawableCachingBackend.getHeight(); } @Override public void onInactive() { clear(); } private boolean drawBitmap( CloseableReference<Bitmap> bitmapReference, Canvas canvas, @Nullable Rect bounds) { if (!CloseableReference.isValid(bitmapReference)) { return false; } if (bounds == null) { canvas.drawBitmap(bitmapReference.get(), 0, 0, mPaint); } else { canvas.drawBitmap(bitmapReference.get(), null, bounds, mPaint); } // Close the previous frame if (bitmapReference != mLastDrawnFrame) { CloseableReference.closeSafely(mLastDrawnFrame); mLastDrawnFrame = bitmapReference; } return true; } }