Skip to content

Commit 8cbe470

Browse files
Andy-Wu25facebook-github-bot
authored andcommitted
Implement setting loop counts for animated images
Reviewed By: kartavya-ramnani Differential Revision: D78413066 fbshipit-source-id: de8cf75054665d4a73189ec9fa9d8ede1942eb1d
1 parent 6f6112a commit 8cbe470

4 files changed

Lines changed: 90 additions & 3 deletions

File tree

‎animated-base/src/main/java/com/facebook/fresco/animation/factory/DefaultBitmapAnimationDrawableFactory.java‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import com.facebook.fresco.animation.drawable.AnimatedDrawable2;
3838
import com.facebook.fresco.animation.drawable.KAnimatedDrawable2;
3939
import com.facebook.fresco.middleware.HasExtraData;
40+
import com.facebook.fresco.vito.options.AnimatedOptions;
4041
import com.facebook.fresco.vito.options.ImageOptions;
4142
import com.facebook.fresco.vito.options.ImageOptionsDrawableFactory;
4243
import com.facebook.fresco.vito.options.RoundingOptions;
@@ -196,6 +197,11 @@ private AnimationBackend createAnimationBackend(
196197
roundingOptions = imageOptions.getRoundingOptions();
197198
}
198199

200+
AnimatedOptions animatedOptions = null;
201+
if (imageOptions != null) {
202+
animatedOptions = imageOptions.getAnimatedOptions();
203+
}
204+
199205
if (mUseNewBitmapRender.get()) {
200206
bitmapFramePreparationStrategy =
201207
new FrameLoaderStrategy(
@@ -218,7 +224,8 @@ private AnimationBackend createAnimationBackend(
218224
mUseNewBitmapRender.get(),
219225
bitmapFramePreparationStrategy,
220226
bitmapFramePreparer,
221-
roundingOptions);
227+
roundingOptions,
228+
animatedOptions);
222229

223230
return AnimationBackendDelegateWithInactivityCheck.createForBackend(
224231
bitmapAnimationBackend, mMonotonicClock, mScheduledExecutorServiceForUiThread);

‎animated-drawable/src/main/java/com/facebook/fresco/animation/bitmap/BitmapAnimationBackend.kt‎

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import com.facebook.fresco.animation.backend.AnimationBackendDelegateWithInactiv
2727
import com.facebook.fresco.animation.backend.AnimationInformation
2828
import com.facebook.fresco.animation.bitmap.preparation.BitmapFramePreparationStrategy
2929
import com.facebook.fresco.animation.bitmap.preparation.BitmapFramePreparer
30+
import com.facebook.fresco.vito.options.AnimatedOptions
3031
import com.facebook.fresco.vito.options.RoundingOptions
3132
import com.facebook.imagepipeline.bitmaps.PlatformBitmapFactory
3233

@@ -38,7 +39,9 @@ import com.facebook.imagepipeline.bitmaps.PlatformBitmapFactory
3839
* [BitmapFrameRenderer] is used to render frames to the bitmaps acquired from the
3940
* [BitmapFrameCache].
4041
*/
41-
class BitmapAnimationBackend(
42+
class BitmapAnimationBackend
43+
@JvmOverloads
44+
constructor(
4245
private val platformBitmapFactory: PlatformBitmapFactory,
4346
private val bitmapFrameCache: BitmapFrameCache,
4447
private val animationInformation: AnimationInformation,
@@ -47,6 +50,7 @@ class BitmapAnimationBackend(
4750
private val bitmapFramePreparationStrategy: BitmapFramePreparationStrategy?,
4851
private val bitmapFramePreparer: BitmapFramePreparer?,
4952
roundingOptions: RoundingOptions? = null,
53+
private val animatedOptions: AnimatedOptions? = null,
5054
) : AnimationBackend, InactivityListener {
5155

5256
private val isCircular: Boolean = roundingOptions?.isCircular == true
@@ -135,7 +139,18 @@ class BitmapAnimationBackend(
135139

136140
override fun getLoopDurationMs(): Int = animationInformation.loopDurationMs
137141

138-
override fun getLoopCount(): Int = animationInformation.loopCount
142+
override fun getLoopCount(): Int {
143+
// If no animated options are set, use the default loop count
144+
if (animatedOptions == null) {
145+
return animationInformation.loopCount
146+
}
147+
148+
return when (animatedOptions.loopCount) {
149+
AnimatedOptions.LOOP_COUNT_INFINITE -> AnimationInformation.LOOP_COUNT_INFINITE
150+
AnimatedOptions.LOOP_COUNT_STATIC -> 1
151+
else -> animatedOptions.loopCount
152+
}
153+
}
139154

140155
override fun drawFrame(parent: Drawable, canvas: Canvas, frameNumber: Int): Boolean {
141156
frameListener?.onDrawFrameStart(this, frameNumber)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
package com.facebook.fresco.vito.options
9+
10+
@Suppress("KtDataClass")
11+
data class AnimatedOptions(
12+
val loopCount: Int,
13+
) {
14+
15+
fun isInfinite(): Boolean = loopCount == LOOP_COUNT_INFINITE
16+
17+
fun isStatic(): Boolean = loopCount == LOOP_COUNT_STATIC
18+
19+
override fun equals(other: Any?): Boolean {
20+
if (this === other) {
21+
return true
22+
}
23+
if (javaClass != other?.javaClass) {
24+
return false
25+
}
26+
27+
val otherOptions = other as? AnimatedOptions ?: return false
28+
29+
return loopCount == otherOptions.loopCount
30+
}
31+
32+
override fun hashCode(): Int {
33+
return loopCount.hashCode()
34+
}
35+
36+
@Suppress("BooleanLiteralArgument")
37+
companion object {
38+
const val LOOP_COUNT_INFINITE: Int = 0
39+
const val LOOP_COUNT_STATIC: Int = 1
40+
val INFINITE: AnimatedOptions = AnimatedOptions(LOOP_COUNT_INFINITE)
41+
val STATIC_FIRST_FRAME: AnimatedOptions = AnimatedOptions(LOOP_COUNT_STATIC)
42+
43+
@JvmStatic fun infinite(): AnimatedOptions = INFINITE
44+
45+
@JvmStatic fun static(): AnimatedOptions = STATIC_FIRST_FRAME
46+
47+
@JvmStatic fun loop(loopCount: Int): AnimatedOptions = AnimatedOptions(loopCount)
48+
}
49+
}

‎vito/options/src/main/java/com/facebook/fresco/vito/options/DecodedImageOptions.kt‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ open class DecodedImageOptions(builder: Builder<*>) : EncodedImageOptions(builde
2424
val postprocessor: Postprocessor? = builder.postprocessor
2525
val imageDecodeOptions: ImageDecodeOptions? = builder.imageDecodeOptions
2626
val roundingOptions: RoundingOptions? = builder.roundingOptions
27+
val animatedOptions: AnimatedOptions? = builder.animatedOptions
2728
val borderOptions: BorderOptions? = builder.borderOptions
2829
val actualImageScaleType: ScalingUtils.ScaleType = builder.actualImageScaleType
2930
val actualImageFocusPoint: PointF? = builder.actualFocusPoint
@@ -49,6 +50,7 @@ open class DecodedImageOptions(builder: Builder<*>) : EncodedImageOptions(builde
4950
!Objects.equal(postprocessor, other.postprocessor) ||
5051
!Objects.equal(imageDecodeOptions, other.imageDecodeOptions) ||
5152
!Objects.equal(roundingOptions, other.roundingOptions) ||
53+
!Objects.equal(animatedOptions, other.animatedOptions) ||
5254
!Objects.equal(borderOptions, other.borderOptions) ||
5355
!Objects.equal(actualImageScaleType, other.actualImageScaleType) ||
5456
!Objects.equal(actualImageFocusPoint, other.actualImageFocusPoint) ||
@@ -68,6 +70,7 @@ open class DecodedImageOptions(builder: Builder<*>) : EncodedImageOptions(builde
6870
result = 31 * result + (postprocessor?.hashCode() ?: 0)
6971
result = 31 * result + (imageDecodeOptions?.hashCode() ?: 0)
7072
result = 31 * result + (roundingOptions?.hashCode() ?: 0)
73+
result = 31 * result + (animatedOptions?.hashCode() ?: 0)
7174
result = 31 * result + (borderOptions?.hashCode() ?: 0)
7275
result = 31 * result + actualImageScaleType.hashCode()
7376
result = 31 * result + (actualImageFocusPoint?.hashCode() ?: 0)
@@ -88,6 +91,7 @@ open class DecodedImageOptions(builder: Builder<*>) : EncodedImageOptions(builde
8891
.add("postprocessor", postprocessor)
8992
.add("imageDecodeOptions", imageDecodeOptions)
9093
.add("roundingOptions", roundingOptions)
94+
.add("animatedOptions", animatedOptions)
9195
.add("borderOptions", borderOptions)
9296
.add("actualImageScaleType", actualImageScaleType)
9397
.add("actualImageFocusPoint", actualImageFocusPoint)
@@ -103,6 +107,7 @@ open class DecodedImageOptions(builder: Builder<*>) : EncodedImageOptions(builde
103107
internal var postprocessor: Postprocessor? = null
104108
internal var imageDecodeOptions: ImageDecodeOptions? = null
105109
internal var roundingOptions: RoundingOptions? = null
110+
internal var animatedOptions: AnimatedOptions? = null
106111
internal var borderOptions: BorderOptions? = null
107112
internal var actualImageScaleType: ScalingUtils.ScaleType = ScalingUtils.ScaleType.CENTER_CROP
108113
internal var actualFocusPoint: PointF? = null
@@ -120,6 +125,7 @@ open class DecodedImageOptions(builder: Builder<*>) : EncodedImageOptions(builde
120125
postprocessor = decodedImageOptions.postprocessor
121126
imageDecodeOptions = decodedImageOptions.imageDecodeOptions
122127
roundingOptions = decodedImageOptions.roundingOptions
128+
animatedOptions = decodedImageOptions.animatedOptions
123129
borderOptions = decodedImageOptions.borderOptions
124130
actualImageScaleType = decodedImageOptions.actualImageScaleType
125131
actualFocusPoint = decodedImageOptions.actualImageFocusPoint
@@ -165,6 +171,16 @@ open class DecodedImageOptions(builder: Builder<*>) : EncodedImageOptions(builde
165171
this.roundingOptions = roundingOptions
166172
}
167173

174+
/**
175+
* Set the animated options to be used or null if default animation behavior should be used.
176+
*
177+
* @param animatedOptions the animated options to use
178+
* @return the builder
179+
*/
180+
fun animated(animatedOptions: AnimatedOptions?): T = modify {
181+
this.animatedOptions = animatedOptions
182+
}
183+
168184
fun borders(borderOptions: BorderOptions?): T = modify { this.borderOptions = borderOptions }
169185

170186
fun scale(actualImageScaleType: ScalingUtils.ScaleType?): T = modify {

0 commit comments

Comments
 (0)