Skip to content

Commit 5273ec6

Browse files
authored
Fix floating point errors calculating keyframe end progress (#2588)
This fixed a really tricky bug that led to the wrong keyframe being used due to a floating point rounding error. Given specific composition start frames and the existing floating point rounding, you could wind up with the following situation: Keyframe 1 has an endFrame of 48 and an endProgress of 0.095051385 Keyframe 2 has a startFrame of 48 and a startProgress of 0.09505139 The Keyframe.containsProgress check intentionally leaves the upper end of the range open to make it unambiguous that the progress on the boundary of two keyframes should use the latter one. However, due to this floating point error, there was a gap and if the progress == the endProgress of the first keyframe, it wouldn't match either. I was able to reconstruct this specific scenario with a unit test and confirmed that this fixed it. However, it is not impossible that there are other scenarios in which this could happen. However, I would rather avoid allocating doubles for everything which is more expensive unless we find a specific repro again
1 parent 255352b commit 5273ec6

File tree

2 files changed

+42
-2
lines changed

2 files changed

+42
-2
lines changed

lottie/src/main/java/com/airbnb/lottie/value/Keyframe.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,8 @@ public float getEndProgress() {
125125
} else {
126126
float startProgress = getStartProgress();
127127
float durationFrames = endFrame - startFrame;
128-
float durationProgress = durationFrames / composition.getDurationFrames();
129-
endProgress = startProgress + durationProgress;
128+
double durationProgress = durationFrames / (double) composition.getDurationFrames();
129+
endProgress = (float) (startProgress + durationProgress);
130130
}
131131
}
132132
return endProgress;
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.airbnb.lottie.value;
2+
3+
import static org.junit.Assert.*;
4+
5+
import android.graphics.Rect;
6+
import android.view.animation.LinearInterpolator;
7+
import androidx.collection.LongSparseArray;
8+
import androidx.collection.SparseArrayCompat;
9+
import com.airbnb.lottie.LottieComposition;
10+
import org.junit.Test;
11+
12+
import java.util.ArrayList;
13+
import java.util.HashMap;
14+
15+
public class KeyframeTest {
16+
17+
@Test
18+
public void testStartFrame() {
19+
LottieComposition composition = new LottieComposition();
20+
composition.init(
21+
new Rect(),
22+
0f,
23+
504.99f,
24+
60f,
25+
new ArrayList<>(),
26+
new LongSparseArray<>(),
27+
new HashMap<>(),
28+
new HashMap<>(),
29+
1f,
30+
new SparseArrayCompat<>(),
31+
new HashMap<>(),
32+
new ArrayList<>(),
33+
0,
34+
0
35+
);
36+
Keyframe<Float> keyframe1 = new Keyframe<>(composition, 200f, 321f, new LinearInterpolator(), 28f, 48f);
37+
Keyframe<Float> keyframe2 = new Keyframe<>(composition, 321f, 300f, new LinearInterpolator(), 48f, 56f);
38+
assertEquals(keyframe2.getStartProgress(), keyframe1.getEndProgress(), 0f);
39+
}
40+
}

0 commit comments

Comments
 (0)