Added the ability to outline masks and mattes (#1658)

diff --git a/LottieSample/src/androidTest/java/com/airbnb/lottie/samples/LottieTest.kt b/LottieSample/src/androidTest/java/com/airbnb/lottie/samples/LottieTest.kt
index 30271c3..6db0e7a 100644
--- a/LottieSample/src/androidTest/java/com/airbnb/lottie/samples/LottieTest.kt
+++ b/LottieSample/src/androidTest/java/com/airbnb/lottie/samples/LottieTest.kt
@@ -108,6 +108,7 @@
             snapshotProdAnimations()
             testNightMode()
             testApplyOpacityToLayer()
+            testOutlineMasksAndMattes()
             snapshotter.finalizeReportAndUpload()
         }
     }
@@ -983,6 +984,16 @@
         }
     }
 
+    private suspend fun testOutlineMasksAndMattes() {
+        withFilmStripView(
+            "Tests/Masks.json",
+            "Outline Masks and Mattes",
+            "Enabled"
+        ) { filmStripView ->
+            filmStripView.setOutlineMasksAndMattes(true)
+        }
+    }
+
     private suspend fun withDrawable(assetName: String, snapshotName: String, snapshotVariant: String, callback: (LottieDrawable) -> Unit) {
         val result = LottieCompositionFactory.fromAssetSync(application, assetName)
         val composition = result.value
diff --git a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerFragment.kt b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerFragment.kt
index 0f92052..944382d 100644
--- a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerFragment.kt
+++ b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerFragment.kt
@@ -179,6 +179,12 @@
             binding.controlBarPlayerControls.lottieVersionView.animateVisible(!it)
         }
 
+        binding.controlBar.masksAndMattesToggle.setOnClickListener { viewModel.toggleOutlineMasksAndMattes() }
+        viewModel.selectSubscribe(PlayerState::outlineMasksAndMattes) {
+            binding.controlBar.masksAndMattesToggle.isActivated = it
+            binding.animationView.setOutlineMasksAndMattes(it)
+        }
+
         binding.controlBar.backgroundColorToggle.setOnClickListener { viewModel.toggleBackgroundColorVisible() }
         binding.controlBarBackgroundColor.closeBackgroundColorButton.setOnClickListener { viewModel.setBackgroundColorVisible(false) }
         viewModel.selectSubscribe(PlayerState::backgroundColorVisible) {
diff --git a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerViewModel.kt b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerViewModel.kt
index a6bce9f..a9ef3ed 100644
--- a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerViewModel.kt
+++ b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerViewModel.kt
@@ -18,6 +18,7 @@
         val controlsVisible: Boolean = true,
         val controlBarVisible: Boolean = true,
         val renderGraphVisible: Boolean = false,
+        val outlineMasksAndMattes: Boolean = false,
         val borderVisible: Boolean = false,
         val backgroundColorVisible: Boolean = false,
         val scaleVisible: Boolean = false,
@@ -64,6 +65,8 @@
 
     fun toggleRenderGraphVisible() = setState { copy(renderGraphVisible = !renderGraphVisible) }
 
+    fun toggleOutlineMasksAndMattes() = setState { copy(outlineMasksAndMattes = !outlineMasksAndMattes) }
+
     fun toggleBorderVisible() = setState { copy(borderVisible = !borderVisible) }
 
     fun toggleBackgroundColorVisible() = setState { copy(backgroundColorVisible = !backgroundColorVisible) }
diff --git a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/views/FilmStripView.kt b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/views/FilmStripView.kt
index 99bb21a..d93961b 100644
--- a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/views/FilmStripView.kt
+++ b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/views/FilmStripView.kt
@@ -40,4 +40,8 @@
     fun setApplyingOpacityToLayersEnabled(isApplyingOpacityToLayersEnabled: Boolean) {
         animationViews.forEach { it.setApplyingOpacityToLayersEnabled(isApplyingOpacityToLayersEnabled) }
     }
+
+    fun setOutlineMasksAndMattes(outline: Boolean) {
+        animationViews.forEach { it.setOutlineMasksAndMattes(outline) }
+    }
 }
diff --git a/LottieSample/src/main/res/drawable/ic_show_masks_and_mattes.xml b/LottieSample/src/main/res/drawable/ic_show_masks_and_mattes.xml
new file mode 100644
index 0000000..c997121
--- /dev/null
+++ b/LottieSample/src/main/res/drawable/ic_show_masks_and_mattes.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M3,5h2L5,3c-1.1,0 -2,0.9 -2,2zM3,13h2v-2L3,11v2zM7,21h2v-2L7,19v2zM3,9h2L5,7L3,7v2zM13,3h-2v2h2L13,3zM19,3v2h2c0,-1.1 -0.9,-2 -2,-2zM5,21v-2L3,19c0,1.1 0.9,2 2,2zM3,17h2v-2L3,15v2zM9,3L7,3v2h2L9,3zM11,21h2v-2h-2v2zM19,13h2v-2h-2v2zM19,21c1.1,0 2,-0.9 2,-2h-2v2zM19,9h2L21,7h-2v2zM19,17h2v-2h-2v2zM15,21h2v-2h-2v2zM15,5h2L17,3h-2v2zM7,17h10L17,7L7,7v10zM9,9h6v6L9,15L9,9z"/>
+</vector>
diff --git a/LottieSample/src/main/res/layout/control_bar.xml b/LottieSample/src/main/res/layout/control_bar.xml
index 3c1ad37..b44548c 100644
--- a/LottieSample/src/main/res/layout/control_bar.xml
+++ b/LottieSample/src/main/res/layout/control_bar.xml
@@ -21,6 +21,11 @@
             app:src="@drawable/ic_chart"
             app:text="@string/control_bar_render_graph" />
         <com.airbnb.lottie.samples.views.ControlBarItemToggleView
+            android:id="@+id/masksAndMattesToggle"
+            style="@style/ControlBarItem"
+            app:src="@drawable/ic_show_masks_and_mattes"
+            app:text="@string/control_bar_masks_and_mattes" />
+        <com.airbnb.lottie.samples.views.ControlBarItemToggleView
             android:id="@+id/warningsButton"
             style="@style/ControlBarItem" />
         <com.airbnb.lottie.samples.views.ControlBarItemToggleView
diff --git a/LottieSample/src/main/res/values/strings.xml b/LottieSample/src/main/res/values/strings.xml
index da68ca1..ad9fc55 100644
--- a/LottieSample/src/main/res/values/strings.xml
+++ b/LottieSample/src/main/res/values/strings.xml
@@ -46,6 +46,7 @@
     <string name="control_bar_hardware_acceleration">Hardware Acceleration</string>
     <string name="control_bar_render_graph">Render Graph</string>
     <string name="control_bar_scale">Scale</string>
+    <string name="control_bar_masks_and_mattes">Outline Masks</string>
     <string name="control_bar_trim">Trim</string>
     <string name="control_bar_speed">Speed</string>
     <string name="control_bar_key_paths">Show KeyPaths</string>
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java b/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java
index 5bf4b31..70f0a9a 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java
@@ -370,6 +370,16 @@
   }
 
   /**
+   * Enable this to debug slow animations by outlining masks and mattes. The performance overhead of the masks and mattes will
+   * be proportional to the surface area of all of the masks/mattes combined.
+   *
+   * DO NOT leave this enabled in production.
+   */
+  public void setOutlineMasksAndMattes(boolean outline) {
+    lottieDrawable.setOutlineMasksAndMattes(outline);
+  }
+
+  /**
    * Sets the animation from a file in the raw directory.
    * This will load and deserialize the file asynchronously.
    */
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java b/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
index 34faf26..e171b4b 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
@@ -94,6 +94,7 @@
   private CompositionLayer compositionLayer;
   private int alpha = 255;
   private boolean performanceTrackingEnabled;
+  private boolean outlineMasksAndMattes;
   private boolean isApplyingOpacityToLayersEnabled;
   private boolean isExtraScaleEnabled = true;
   /**
@@ -249,6 +250,19 @@
     }
   }
 
+  /**
+   * Enable this to debug slow animations by outlining masks and mattes. The performance overhead of the masks and mattes will
+   * be proportional to the surface area of all of the masks/mattes combined.
+   *
+   * DO NOT leave this enabled in production.
+   */
+  void setOutlineMasksAndMattes(boolean outline) {
+    outlineMasksAndMattes = outline;
+    if (compositionLayer != null) {
+      compositionLayer.setOutlineMasksAndMattes(outline);
+    }
+  }
+
   @Nullable
   public PerformanceTracker getPerformanceTracker() {
     if (composition != null) {
@@ -296,6 +310,9 @@
   private void buildCompositionLayer() {
     compositionLayer = new CompositionLayer(
         this, LayerParser.parse(composition), composition.getLayers(), composition);
+    if (outlineMasksAndMattes) {
+      compositionLayer.setOutlineMasksAndMattes(true);
+    }
   }
 
   public void clearComposition() {
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 df33cf6..9f12706 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
@@ -97,6 +97,9 @@
   final TransformKeyframeAnimation transform;
   private boolean visible = true;
 
+  private boolean outlineMasksAndMattes;
+  @Nullable private Paint outlineMasksAndMattesPaint;
+
   BaseLayer(LottieDrawable lottieDrawable, Layer layerModel) {
     this.lottieDrawable = lottieDrawable;
     this.layerModel = layerModel;
@@ -125,6 +128,19 @@
     setupInOutAnimations();
   }
 
+  /**
+   * Enable this to debug slow animations by outlining masks and mattes. The performance overhead of the masks and mattes will
+   * be proportional to the surface area of all of the masks/mattes combined.
+   *
+   * DO NOT leave this enabled in production.
+   */
+  void setOutlineMasksAndMattes(boolean outline) {
+    if (outline && outlineMasksAndMattesPaint == null) {
+      outlineMasksAndMattesPaint = new LPaint();
+    }
+    outlineMasksAndMattes = outline;
+  }
+
   @Override
   public void onValueChanged() {
     invalidateSelf();
@@ -229,13 +245,6 @@
     L.beginSection("Layer#computeBounds");
     getBounds(rect, matrix, false);
 
-    // Uncomment this to draw matte outlines.
-    /* Paint paint = new LPaint();
-    paint.setColor(Color.RED);
-    paint.setStyle(Paint.Style.STROKE);
-    paint.setStrokeWidth(3);
-    canvas.drawRect(rect, paint); */
-
     intersectBoundsWithMatte(rect, parentMatrix);
 
     matrix.preConcat(transform.getMatrix());
@@ -282,6 +291,16 @@
       L.endSection("Layer#restoreLayer");
     }
 
+    if (outlineMasksAndMattes && outlineMasksAndMattesPaint != null) {
+      outlineMasksAndMattesPaint.setStyle(Paint.Style.STROKE);
+      outlineMasksAndMattesPaint.setColor(0xFFFC2803);
+      outlineMasksAndMattesPaint.setStrokeWidth(4);
+      canvas.drawRect(rect, outlineMasksAndMattesPaint);
+      outlineMasksAndMattesPaint.setStyle(Paint.Style.FILL);
+      outlineMasksAndMattesPaint.setColor(0x50EBEBEB);
+      canvas.drawRect(rect, outlineMasksAndMattesPaint);
+    }
+
     recordRenderTime(L.endSection(drawTraceName));
   }
 
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/layer/CompositionLayer.java b/lottie/src/main/java/com/airbnb/lottie/model/layer/CompositionLayer.java
index a98d0cb..33361ed 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/layer/CompositionLayer.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/layer/CompositionLayer.java
@@ -88,6 +88,13 @@
     }
   }
 
+  @Override public void setOutlineMasksAndMattes(boolean outline) {
+    super.setOutlineMasksAndMattes(outline);
+    for (BaseLayer layer : layers) {
+      layer.setOutlineMasksAndMattes(outline);
+    }
+  }
+
   @Override void drawLayer(Canvas canvas, Matrix parentMatrix, int parentAlpha) {
     L.beginSection("CompositionLayer#draw");
     newClipRect.set(0, 0, layerModel.getPreCompWidth(), layerModel.getPreCompHeight());