--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) {