--wip-- [skip ci]
diff --git a/issue-repro/src/main/java/com/airbnb/lottie/issues/IssueReproActivity.kt b/issue-repro/src/main/java/com/airbnb/lottie/issues/IssueReproActivity.kt
index 495438c..8cd1885 100755
--- a/issue-repro/src/main/java/com/airbnb/lottie/issues/IssueReproActivity.kt
+++ b/issue-repro/src/main/java/com/airbnb/lottie/issues/IssueReproActivity.kt
@@ -1,7 +1,12 @@
package com.airbnb.lottie.issues
+import android.animation.Animator
+import android.animation.Animator.AnimatorListener
+import android.animation.AnimatorListenerAdapter
import android.os.Bundle
+import android.util.Log
import androidx.appcompat.app.AppCompatActivity
+import com.airbnb.lottie.L
import com.airbnb.lottie.issues.databinding.IssueReproActivityBinding
class IssueReproActivity : AppCompatActivity() {
@@ -10,5 +15,13 @@
val binding = IssueReproActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
// Reproduce any issues here.
+ binding.animationView.addAnimatorListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationRepeat(animation: Animator) {
+ Log.d("Gabe", "Draw %.1f".format(
+ L.drawTimeNs.getAndSet(0L) / 1_000_000f,
+ ))
+ }
+ })
+
}
}
diff --git a/issue-repro/src/main/res/layout/issue_repro_activity.xml b/issue-repro/src/main/res/layout/issue_repro_activity.xml
index 05ea4fb..9e29817 100755
--- a/issue-repro/src/main/res/layout/issue_repro_activity.xml
+++ b/issue-repro/src/main/res/layout/issue_repro_activity.xml
@@ -9,7 +9,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
- app:lottie_rawRes="@raw/heart"
+ app:lottie_rawRes="@raw/matte"
app:lottie_autoPlay="true"
app:lottie_loop="true"/>
</FrameLayout>
\ No newline at end of file
diff --git a/issue-repro/src/main/res/raw/matte.json b/issue-repro/src/main/res/raw/matte.json
new file mode 100644
index 0000000..e094c12
--- /dev/null
+++ b/issue-repro/src/main/res/raw/matte.json
@@ -0,0 +1 @@
+{"v":"5.10.2","fr":60,"ip":0,"op":60,"w":756,"h":756,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","td":1,"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},"t":0,"s":[372,358,0],"to":[56.833,58.167,0],"ti":[-56.833,-58.167,0]},{"t":59,"s":[713,707,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[381.742,381.742],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","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":[-164.035,-154.496],"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":"Rectangle 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":1,"nm":"Purple Solid 1","tt":1,"tp":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[378,378,0],"ix":2,"l":2},"a":{"a":0,"k":[378,378,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"sw":756,"sh":756,"sc":"#a100ff","ip":0,"op":60,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/lottie/src/main/java/com/airbnb/lottie/L.java b/lottie/src/main/java/com/airbnb/lottie/L.java
index a27d7da..861348b 100644
--- a/lottie/src/main/java/com/airbnb/lottie/L.java
+++ b/lottie/src/main/java/com/airbnb/lottie/L.java
@@ -15,6 +15,7 @@
import com.airbnb.lottie.utils.LottieTrace;
import java.io.File;
+import java.util.concurrent.atomic.AtomicLong;
@RestrictTo(RestrictTo.Scope.LIBRARY)
public class L {
@@ -33,6 +34,10 @@
private static volatile NetworkCache networkCache;
private static ThreadLocal<LottieTrace> lottieTrace;
+ public static boolean renderNode = true;
+
+ public static AtomicLong drawTimeNs = new AtomicLong(0L);
+
private L() {
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java b/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
index e493288..2d39708 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
@@ -680,7 +680,10 @@
renderAndDrawAsBitmap(canvas, compositionLayer);
canvas.restore();
} else {
+ long start = System.nanoTime();
compositionLayer.draw(canvas, matrix, alpha);
+ long end = System.nanoTime();
+ L.drawTimeNs.getAndAdd(end - start);
}
isDirty = false;
} catch (InterruptedException e) {
@@ -1560,6 +1563,7 @@
return;
}
+ long start = System.nanoTime();
renderingMatrix.reset();
Rect bounds = getBounds();
if (!bounds.isEmpty()) {
@@ -1571,6 +1575,8 @@
renderingMatrix.preTranslate(bounds.left, bounds.top);
}
compositionLayer.draw(canvas, renderingMatrix, alpha);
+ long end = System.nanoTime();
+ L.drawTimeNs.getAndAdd(end - start);
}
/**
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/layer/BaseLayer.java b/lottie/src/main/java/com/airbnb/lottie/model/layer/BaseLayer.java
index 5775c18..fa8327d 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/layer/BaseLayer.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/layer/BaseLayer.java
@@ -1,5 +1,6 @@
package com.airbnb.lottie.model.layer;
+import android.annotation.SuppressLint;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -8,7 +9,9 @@
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
+import android.graphics.RecordingCanvas;
import android.graphics.RectF;
+import android.graphics.RenderNode;
import android.os.Build;
import androidx.annotation.CallSuper;
@@ -110,6 +113,9 @@
final TransformKeyframeAnimation transform;
private boolean visible = true;
+ @SuppressLint("NewApi")
+ private final RenderNode renderNode = new RenderNode("BaseLayer");
+
private boolean outlineMasksAndMattes;
@Nullable private Paint outlineMasksAndMattesPaint;
@@ -290,38 +296,46 @@
// On older devices, drawing to an offscreen buffer of <1px would draw back as a black bar.
// https://github.com/airbnb/lottie-android/issues/1625
if (rect.width() >= 1f && rect.height() >= 1f) {
- L.beginSection("Layer#saveLayer");
- contentPaint.setAlpha(255);
- Utils.saveLayerCompat(canvas, rect, contentPaint);
- L.endSection("Layer#saveLayer");
-
- // Clear the off screen buffer. This is necessary for some phones.
- clearCanvas(canvas);
- L.beginSection("Layer#drawLayer");
- drawLayer(canvas, matrix, alpha);
- L.endSection("Layer#drawLayer");
-
- if (hasMasksOnThisLayer()) {
- applyMasks(canvas, matrix);
- }
-
- if (hasMatteOnThisLayer()) {
- L.beginSection("Layer#drawMatte");
+ if (renderNode.hasDisplayList()) {
+ canvas.drawRenderNode(renderNode);
+ } else {
+ renderNode.setPosition(0, 0, canvas.getWidth(), canvas.getHeight());
+ RecordingCanvas recordingCanvas = renderNode.beginRecording();
+ Canvas canvas2 = L.renderNode ? recordingCanvas : canvas;
L.beginSection("Layer#saveLayer");
- Utils.saveLayerCompat(canvas, rect, mattePaint, SAVE_FLAGS);
+ contentPaint.setAlpha(255);
+ Utils.saveLayerCompat(canvas2, rect, contentPaint);
L.endSection("Layer#saveLayer");
- clearCanvas(canvas);
- //noinspection ConstantConditions
- matteLayer.draw(canvas, parentMatrix, alpha);
- L.beginSection("Layer#restoreLayer");
- canvas.restore();
- L.endSection("Layer#restoreLayer");
- L.endSection("Layer#drawMatte");
- }
- L.beginSection("Layer#restoreLayer");
- canvas.restore();
- L.endSection("Layer#restoreLayer");
+ // Clear the off screen buffer. This is necessary for some phones.
+ clearCanvas(canvas2);
+ L.beginSection("Layer#drawLayer");
+ drawLayer(canvas2, matrix, alpha);
+ L.endSection("Layer#drawLayer");
+
+ if (hasMasksOnThisLayer()) {
+ applyMasks(canvas2, matrix);
+ }
+
+ if (hasMatteOnThisLayer()) {
+ L.beginSection("Layer#drawMatte");
+ L.beginSection("Layer#saveLayer");
+ Utils.saveLayerCompat(canvas2, rect, mattePaint, SAVE_FLAGS);
+ L.endSection("Layer#saveLayer");
+ clearCanvas(canvas2);
+ //noinspection ConstantConditions
+ matteLayer.draw(canvas2, parentMatrix, alpha);
+ L.beginSection("Layer#restoreLayer");
+ canvas2.restore();
+ L.endSection("Layer#restoreLayer");
+ L.endSection("Layer#drawMatte");
+ }
+
+ L.beginSection("Layer#restoreLayer");
+ canvas2.restore();
+ L.endSection("Layer#restoreLayer");
+ renderNode.endRecording();
+ }
}
if (outlineMasksAndMattes && outlineMasksAndMattesPaint != null) {