Support animations without end values in each keyframe (#1104)
diff --git a/LottieSample/src/androidTest/java/com/airbnb/lottie/LottieTest.kt b/LottieSample/src/androidTest/java/com/airbnb/lottie/LottieTest.kt
index 43c44d9..a9692b8 100644
--- a/LottieSample/src/androidTest/java/com/airbnb/lottie/LottieTest.kt
+++ b/LottieSample/src/androidTest/java/com/airbnb/lottie/LottieTest.kt
@@ -178,6 +178,7 @@
filmStripView.setComposition(composition)
canvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR)
withContext(Dispatchers.Main) {
+ log("Drawing $name")
filmStripView.draw(canvas)
}
filmStripViewPool.release(filmStripView)
@@ -592,6 +593,7 @@
callback(drawable)
val bitmap = bitmapPool.acquire(drawable.intrinsicWidth, drawable.intrinsicHeight)
val canvas = Canvas(bitmap)
+ log("Drawing $assetName")
drawable.draw(canvas)
snapshotter.record(bitmap, snapshotName, snapshotVariant)
activity.recordSnapshot(snapshotName, snapshotVariant)
@@ -621,6 +623,7 @@
animationViewContainer.layout(0, 0, animationViewContainer.measuredWidth, animationViewContainer.measuredHeight)
val bitmap = bitmapPool.acquire(animationView.width, animationView.height)
val canvas = Canvas(bitmap)
+ log("Drawing $assetName")
animationView.draw(canvas)
animationViewPool.release(animationView)
snapshotter.record(bitmap, snapshotName, snapshotVariant)
diff --git a/LottieSample/src/main/assets/Tests/TransformWithoutEndValues.json b/LottieSample/src/main/assets/Tests/TransformWithoutEndValues.json
new file mode 100755
index 0000000..8b11e82
--- /dev/null
+++ b/LottieSample/src/main/assets/Tests/TransformWithoutEndValues.json
@@ -0,0 +1,251 @@
+{
+ "v": "4.8.0",
+ "fr": 30,
+ "ip": 0,
+ "op": 31,
+ "w": 1024,
+ "h": 768,
+ "nm": "new_json",
+ "ddd": 0,
+ "assets": [],
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 1,
+ "ty": 4,
+ "nm": "Shape Layer 2",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": 0.833,
+ "y": 0.833
+ },
+ "o": {
+ "x": 0.167,
+ "y": 0.167
+ },
+ "n": "0p833_0p833_0p167_0p167",
+ "t": 0,
+ "s": [
+ 594,
+ 474,
+ 0
+ ],
+ "to": [
+ 25.5,
+ 25.8333339691162,
+ 0
+ ],
+ "ti": [
+ -25.5,
+ -25.8333339691162,
+ 0
+ ]
+ },
+ {
+ "t": 24,
+ "s": [
+ 747,
+ 629,
+ 0
+ ]
+ }
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100,
+ 100
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ -8.5,
+ -23.5
+ ],
+ [
+ -8.5,
+ 23.5
+ ],
+ [
+ -59.5,
+ 23.5
+ ],
+ [
+ -59.5,
+ -23.5
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "Path 1",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0,
+ 0,
+ 1
+ ],
+ "ix": 4
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 5
+ },
+ "r": 1,
+ "bm": 0,
+ "nm": "Fill 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 3
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 6
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 7
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 4
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 5
+ },
+ "nm": "Transform"
+ }
+ ],
+ "nm": "Group 1",
+ "np": 2,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 0,
+ "op": 248,
+ "st": 0,
+ "bm": 0
+ }
+ ],
+ "markers": []
+}
\ No newline at end of file
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/PathKeyframe.java b/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/PathKeyframe.java
index cc8f3b9..d9091e2 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/PathKeyframe.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/PathKeyframe.java
@@ -11,17 +11,23 @@
public class PathKeyframe extends Keyframe<PointF> {
@Nullable private Path path;
+ private final Keyframe<PointF> pointKeyFrame;
+
public PathKeyframe(LottieComposition composition, Keyframe<PointF> keyframe) {
super(composition, keyframe.startValue, keyframe.endValue, keyframe.interpolator,
keyframe.startFrame, keyframe.endFrame);
+ this.pointKeyFrame = keyframe;
+ createPath();
+ }
+ public void createPath() {
// This must use equals(float, float) because PointF didn't have an equals(PathF) method
// until KitKat...
boolean equals = endValue != null && startValue != null &&
startValue.equals(endValue.x, endValue.y);
//noinspection ConstantConditions
if (endValue != null && !equals) {
- path = Utils.createPath(startValue, endValue, keyframe.pathCp1, keyframe.pathCp2);
+ path = Utils.createPath(startValue, endValue, pointKeyFrame.pathCp1, pointKeyFrame.pathCp2);
}
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/parser/AnimatableTransformParser.java b/lottie/src/main/java/com/airbnb/lottie/parser/AnimatableTransformParser.java
index 0249dd4..39154c5 100644
--- a/lottie/src/main/java/com/airbnb/lottie/parser/AnimatableTransformParser.java
+++ b/lottie/src/main/java/com/airbnb/lottie/parser/AnimatableTransformParser.java
@@ -74,6 +74,8 @@
rotation = AnimatableValueParser.parseFloat(reader, composition, false);
if (rotation.getKeyframes().isEmpty()) {
rotation.getKeyframes().add(new Keyframe(composition, 0f, 0f, null, 0f, composition.getEndFrame()));
+ } else if (rotation.getKeyframes().get(0).startValue == null) {
+ rotation.getKeyframes().set(0, new Keyframe(composition, 0f, 0f, null, 0f, composition.getEndFrame()));
}
break;
case "o":
diff --git a/lottie/src/main/java/com/airbnb/lottie/parser/KeyframesParser.java b/lottie/src/main/java/com/airbnb/lottie/parser/KeyframesParser.java
index 64af1c5..24d91bc 100644
--- a/lottie/src/main/java/com/airbnb/lottie/parser/KeyframesParser.java
+++ b/lottie/src/main/java/com/airbnb/lottie/parser/KeyframesParser.java
@@ -4,6 +4,7 @@
import android.util.JsonToken;
import com.airbnb.lottie.LottieComposition;
+import com.airbnb.lottie.animation.keyframe.PathKeyframe;
import com.airbnb.lottie.value.Keyframe;
import java.io.IOException;
@@ -33,8 +34,7 @@
if (reader.peek() == JsonToken.NUMBER) {
// For properties in which the static value is an array of numbers.
- keyframes.add(
- KeyframeParser.parse(reader, composition, scale, valueParser, false));
+ keyframes.add(KeyframeParser.parse(reader, composition, scale, valueParser, false));
} else {
while (reader.hasNext()) {
keyframes.add(KeyframeParser.parse(reader, composition, scale, valueParser, true));
@@ -59,14 +59,22 @@
* The json doesn't include end frames. The data can be taken from the start frame of the next
* keyframe though.
*/
- public static void setEndFrames(List<? extends Keyframe<?>> keyframes) {
+ public static <T> void setEndFrames(List<? extends Keyframe<T>> keyframes) {
int size = keyframes.size();
for (int i = 0; i < size - 1; i++) {
// In the json, the keyframes only contain their starting frame.
- keyframes.get(i).endFrame = keyframes.get(i + 1).startFrame;
+ Keyframe<T> keyframe = keyframes.get(i);
+ Keyframe<T> nextKeyframe = keyframes.get(i + 1);
+ keyframe.endFrame = nextKeyframe.startFrame;
+ if (keyframe.endValue == null && nextKeyframe.startValue != null) {
+ keyframe.endValue = nextKeyframe.startValue;
+ if (keyframe instanceof PathKeyframe) {
+ ((PathKeyframe) keyframe).createPath();
+ }
+ }
}
Keyframe<?> lastKeyframe = keyframes.get(size - 1);
- if (lastKeyframe.startValue == null) {
+ if ((lastKeyframe.startValue == null || lastKeyframe.endValue == null) && keyframes.size() > 1) {
// The only purpose the last keyframe has is to provide the end frame of the previous
// keyframe.
//noinspection SuspiciousMethodCalls
diff --git a/lottie/src/main/java/com/airbnb/lottie/value/Keyframe.java b/lottie/src/main/java/com/airbnb/lottie/value/Keyframe.java
index da3ea82..fa58a43 100644
--- a/lottie/src/main/java/com/airbnb/lottie/value/Keyframe.java
+++ b/lottie/src/main/java/com/airbnb/lottie/value/Keyframe.java
@@ -13,7 +13,7 @@
@Nullable private final LottieComposition composition;
@Nullable public final T startValue;
- @Nullable public final T endValue;
+ @Nullable public T endValue;
@Nullable public final Interpolator interpolator;
public final float startFrame;
@Nullable public Float endFrame;