Compiles
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java b/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
index 4b7ed78..d932b15 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
@@ -5,16 +5,12 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
-import android.graphics.Color;
 import android.graphics.ColorFilter;
 import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Path;
 import android.graphics.PixelFormat;
 import android.graphics.Typeface;
 import android.graphics.drawable.Animatable;
 import android.graphics.drawable.Drawable;
-import android.os.Build;
 import androidx.annotation.FloatRange;
 import androidx.annotation.IntDef;
 import androidx.annotation.IntRange;
@@ -317,7 +313,7 @@
 
     matrix.reset();
     matrix.preScale(scale, scale);
-    compositionLayer.draw(new WrappedCanvas(canvas), matrix, alpha, null, maskIdentityMatrix, matrix);
+    compositionLayer.draw(new WrappedCanvas(canvas), matrix, alpha, null, maskIdentityMatrix);
     L.endSection("Drawable#draw");
 
     if (extraScale > 1) {
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/canvas/ICanvas.java b/lottie/src/main/java/com/airbnb/lottie/animation/canvas/ICanvas.java
index 9f667ab..a836d28 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/canvas/ICanvas.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/canvas/ICanvas.java
@@ -22,6 +22,7 @@
   boolean clipPath(Path path);
   boolean clipRect(RectF rect);
   void drawText(char[] text, int index, int count, float x, float y, Paint paint);
+  void drawRect(float left, float top, float right, float bottom, Paint paint);
   void setMatrix(@Nullable Matrix matrix);
   void concat(@Nullable Matrix matrix);
   void drawBitmap(@NonNull Bitmap bitmap, Rect src, Rect dst, @Nullable Paint paint);
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/canvas/RecordedCanvas.java b/lottie/src/main/java/com/airbnb/lottie/animation/canvas/RecordedCanvas.java
index 7925899..cdcf230 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/canvas/RecordedCanvas.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/canvas/RecordedCanvas.java
@@ -147,6 +147,11 @@
   }
 
   @Override
+  public void drawRect(float left, float top, float right, float bottom, Paint paint) {
+    // TODO
+  }
+
+  @Override
   public void setMatrix(@Nullable Matrix matrix) {
     Log.d("Gabe", "setMatrix\t");
     // TODO
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/canvas/WrappedCanvas.java b/lottie/src/main/java/com/airbnb/lottie/animation/canvas/WrappedCanvas.java
index c444465..8faba94 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/canvas/WrappedCanvas.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/canvas/WrappedCanvas.java
@@ -79,6 +79,11 @@
   }
 
   @Override
+  public void drawRect(float left, float top, float right, float bottom, Paint paint) {
+    canvas.drawRect(left, top, right, bottom, paint);
+  }
+
+  @Override
   public void setMatrix(@Nullable Matrix matrix) {
     canvas.setMatrix(matrix);
   }
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/content/BaseStrokeContent.java b/lottie/src/main/java/com/airbnb/lottie/animation/content/BaseStrokeContent.java
index 98d8031..9cbae87 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/content/BaseStrokeContent.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/content/BaseStrokeContent.java
@@ -144,7 +144,7 @@
   }
 
   @Override
-  public void draw(ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix, Matrix matteMatrix) {
+  public void draw(ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix) {
     L.beginSection("StrokeContent#draw");
     int alpha = (int) ((parentAlpha / 255f * opacityAnimation.getValue() / 100f) * 255);
     paint.setAlpha(clamp(alpha, 0, 255));
@@ -166,7 +166,7 @@
 
 
       if (pathGroup.trimPath != null) {
-        applyTrimPath(canvas, pathGroup, parentMatrix, mask, maskMatrix, matteMatrix);
+        applyTrimPath(canvas, pathGroup, parentMatrix, mask, maskMatrix);
       } else {
         L.beginSection("StrokeContent#buildPath");
         path.reset();
@@ -178,7 +178,7 @@
           // Stroke has to use Canvas.clipPath so that it actually clips the stroke rather than creating
           // a new path of the content combined with the stroke. Doing that would cause Lottie to draw a stroke
           // on the mask edge that wasn't part of the original path.
-          Path maskPath = mask.getMaskPath(path, maskMatrix, matteMatrix, parentMatrix, false);
+          Path maskPath = mask.getMaskPath(path, maskMatrix, false);
           maskPath.computeBounds(maskBounds, false);
           path.computeBounds(pathBounds, false);
           pathBounds.inset(-strokeWidth, -strokeWidth);
@@ -198,7 +198,7 @@
     L.endSection("StrokeContent#draw");
   }
 
-  private void applyTrimPath(ICanvas canvas, PathGroup pathGroup, Matrix parentMatrix, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix, Matrix matteMatrix) {
+  private void applyTrimPath(ICanvas canvas, PathGroup pathGroup, Matrix parentMatrix, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix) {
     L.beginSection("StrokeContent#applyTrimPath");
     if (pathGroup.trimPath == null) {
       L.endSection("StrokeContent#applyTrimPath");
@@ -238,7 +238,7 @@
         float endValue = Math.min((endLength - totalLength) / length, 1);
         Utils.applyTrimPathIfNeeded(trimPathPath, startValue, endValue, 0);
         if (mask != null) {
-          mask.applyToPath(path, maskMatrix, matteMatrix, parentMatrix);
+          mask.applyToPath(path, maskMatrix);
         }
         canvas.drawPath(trimPathPath, paint);
       } else
@@ -247,7 +247,7 @@
           // Do nothing
         } else if (currentLength + length <= endLength && startLength < currentLength) {
           if (mask != null) {
-            mask.applyToPath(path, maskMatrix, matteMatrix, parentMatrix);
+            mask.applyToPath(path, maskMatrix);
           }
           canvas.drawPath(trimPathPath, paint);
         } else {
@@ -265,7 +265,7 @@
           }
           Utils.applyTrimPathIfNeeded(trimPathPath, startValue, endValue, 0);
           if (mask != null) {
-            mask.applyToPath(path, maskMatrix, matteMatrix, parentMatrix);
+            mask.applyToPath(path, maskMatrix);
           }
           canvas.drawPath(trimPathPath, paint);
         }
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/content/ContentGroup.java b/lottie/src/main/java/com/airbnb/lottie/animation/content/ContentGroup.java
index 9cd4013..1d2e8bc 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/content/ContentGroup.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/content/ContentGroup.java
@@ -174,7 +174,7 @@
     return paths;
   }
 
-  @Override public void draw(ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix, Matrix matteMatrix) {
+  @Override public void draw(ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix) {
     matrix.set(parentMatrix);
     int alpha;
     if (transformAnimation != null) {
@@ -189,7 +189,7 @@
     for (int i = contents.size() - 1; i >= 0; i--) {
       Object content = contents.get(i);
       if (content instanceof DrawingContent) {
-        ((DrawingContent) content).draw(canvas, matrix, alpha, mask, maskMatrix, matteMatrix);
+        ((DrawingContent) content).draw(canvas, matrix, alpha, mask, maskMatrix);
       }
     }
   }
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/content/DrawingContent.java b/lottie/src/main/java/com/airbnb/lottie/animation/content/DrawingContent.java
index b5a5410..69694d5 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/content/DrawingContent.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/content/DrawingContent.java
@@ -7,6 +7,6 @@
 import com.airbnb.lottie.animation.keyframe.MaskKeyframeAnimation;
 
 public interface DrawingContent extends Content {
-  void draw(ICanvas canvas, Matrix parentMatrix, int alpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix, Matrix matteMatrix);
+  void draw(ICanvas canvas, Matrix parentMatrix, int alpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix);
   void getBounds(RectF outBounds, Matrix parentMatrix);
 }
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/content/FillContent.java b/lottie/src/main/java/com/airbnb/lottie/animation/content/FillContent.java
index dd8b073..c9e295d 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/content/FillContent.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/content/FillContent.java
@@ -74,7 +74,7 @@
     return name;
   }
 
-  @Override public void draw(ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix, Matrix matteMatrix) {
+  @Override public void draw(ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix) {
     L.beginSection("FillContent#draw");
     paint.setColor(colorAnimation.getValue());
     int alpha = (int) ((parentAlpha / 255f * opacityAnimation.getValue() / 100f) * 255);
@@ -90,7 +90,7 @@
     }
 
     if (mask != null) {
-      mask.applyToPath(path, maskMatrix, matteMatrix, parentMatrix);
+      mask.applyToPath(path, maskMatrix);
     }
 
     canvas.drawPath(path, paint);
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/content/GradientFillContent.java b/lottie/src/main/java/com/airbnb/lottie/animation/content/GradientFillContent.java
index 65a01f3..0bf8996 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/content/GradientFillContent.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/content/GradientFillContent.java
@@ -95,7 +95,7 @@
     }
   }
 
-  @Override public void draw(ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix, Matrix matteMatrix) {
+  @Override public void draw(ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix) {
     L.beginSection("GradientFillContent#draw");
     path.reset();
     for (int i = 0; i < paths.size(); i++) {
@@ -122,7 +122,7 @@
     paint.setAlpha(clamp(alpha, 0, 255));
 
     if (mask != null) {
-      mask.applyToPath(path, maskMatrix, matteMatrix, parentMatrix);
+      mask.applyToPath(path, maskMatrix);
     }
 
     canvas.drawPath(path, paint);
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/content/GradientStrokeContent.java b/lottie/src/main/java/com/airbnb/lottie/animation/content/GradientStrokeContent.java
index a4d963f..6db8e94 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/content/GradientStrokeContent.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/content/GradientStrokeContent.java
@@ -58,7 +58,7 @@
     layer.addAnimation(endPointAnimation);
   }
 
-  @Override public void draw(ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix, Matrix matteMatrix) {
+  @Override public void draw(ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix) {
     getBounds(boundsRect, parentMatrix);
     if (type == GradientType.Linear) {
       paint.setShader(getLinearGradient());
@@ -66,7 +66,7 @@
       paint.setShader(getRadialGradient());
     }
 
-    super.draw(canvas, parentMatrix, parentAlpha, mask, maskMatrix, matteMatrix);
+    super.draw(canvas, parentMatrix, parentAlpha, mask, maskMatrix);
   }
 
   @Override public String getName() {
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/content/RepeaterContent.java b/lottie/src/main/java/com/airbnb/lottie/animation/content/RepeaterContent.java
index 9dbb840..58cfb01 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/content/RepeaterContent.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/content/RepeaterContent.java
@@ -102,7 +102,7 @@
     return path;
   }
 
-  @Override public void draw(ICanvas canvas, Matrix parentMatrix, int alpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix, Matrix matteMatrix) {
+  @Override public void draw(ICanvas canvas, Matrix parentMatrix, int alpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix) {
     float copies = this.copies.getValue();
     float offset = this.offset.getValue();
     //noinspection ConstantConditions
@@ -113,7 +113,7 @@
       matrix.set(parentMatrix);
       matrix.preConcat(transform.getMatrixForRepeater(i + offset));
       float newAlpha = alpha * MiscUtils.lerp(startOpacity, endOpacity, i / copies);
-      contentGroup.draw(canvas, matrix, (int) newAlpha, mask, maskMatrix, parentMatrix);
+      contentGroup.draw(canvas, matrix, (int) newAlpha, mask, maskMatrix);
     }
   }
 
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/content/StrokeContent.java b/lottie/src/main/java/com/airbnb/lottie/animation/content/StrokeContent.java
index 1cd69ed..40e9781 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/content/StrokeContent.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/content/StrokeContent.java
@@ -34,12 +34,12 @@
     layer.addAnimation(colorAnimation);
   }
 
-  @Override public void draw(ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix, Matrix matteMatrix) {
+  @Override public void draw(ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix) {
     paint.setColor(colorAnimation.getValue());
     if (colorFilterAnimation != null) {
       paint.setColorFilter(colorFilterAnimation.getValue());
     }
-    super.draw(canvas, parentMatrix, parentAlpha, mask, maskMatrix, matteMatrix);
+    super.draw(canvas, parentMatrix, parentAlpha, mask, maskMatrix);
   }
 
   @Override public String getName() {
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/MaskKeyframeAnimation.java b/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/MaskKeyframeAnimation.java
index 1746d37..82fbb36 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/MaskKeyframeAnimation.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/MaskKeyframeAnimation.java
@@ -3,15 +3,9 @@
 import android.graphics.Matrix;
 import android.graphics.Path;
 
-import android.graphics.RectF;
-import androidx.annotation.Nullable;
-import com.airbnb.lottie.animation.canvas.RecordedCanvas;
 import com.airbnb.lottie.model.animatable.AnimatableIntegerValue;
 import com.airbnb.lottie.model.content.Mask;
 import com.airbnb.lottie.model.content.ShapeData;
-import com.airbnb.lottie.model.layer.BaseLayer;
-import com.airbnb.lottie.model.layer.Layer;
-import com.airbnb.lottie.utils.Utils;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -24,14 +18,9 @@
    * Reusable path for calculating masks.
    */
   private final Path masksPath = new Path();
-  private final Path mattesPath = new Path();
   private final Path combinedPath = new Path();
   private final Path addPath = new Path();
   private final Path subtractPath = new Path();
-  @Nullable
-  public BaseLayer matteLayer;
-  @Nullable
-  private Layer.MatteType matteType;
 
   public MaskKeyframeAnimation(List<Mask> masks) {
     this.masks = masks;
@@ -44,11 +33,6 @@
     }
   }
 
-  public void setMatteLayer(@Nullable BaseLayer matteLayer, Layer.MatteType matteType) {
-    this.matteLayer = matteLayer;
-    this.matteType = matteType;
-  }
-
   public List<Mask> getMasks() {
     return masks;
   }
@@ -61,12 +45,12 @@
     return opacityAnimations;
   }
 
-  public void applyToPath(Path contentPath, Matrix maskMatrix, Matrix matteMatrix, Matrix parentMatrix) {
-    getMaskPath(contentPath, maskMatrix, matteMatrix, parentMatrix, true);
+  public void applyToPath(Path contentPath, Matrix maskMatrix) {
+    getMaskPath(contentPath, maskMatrix, true);
   }
 
-  public Path getMaskPath(Path contentPath, Matrix maskMatrix, Matrix matteMatrix, Matrix parentMatrix, boolean applyToPath) {
-    if (getMaskAnimations().isEmpty() && matteLayer == null) {
+  public Path getMaskPath(Path contentPath, Matrix maskMatrix, boolean applyToPath) {
+    if (getMaskAnimations().isEmpty()) {
       return contentPath;
     }
 
@@ -91,53 +75,7 @@
     }
     masksPath.close();
 
-    mattesPath.reset();
-//    mattesPath.setFillType(Path.FillType.WINDING);
-    if (matteLayer != null && matteType != null) {
-      RectF contentBounds = Utils.getBounds(contentPath);
-      RecordedCanvas canvas = new RecordedCanvas((int) contentBounds.right, (int) contentBounds.bottom);
-
-
-      matteLayer.draw(canvas, matteMatrix, 255, null, new Matrix(), new Matrix());
-      List<Path> mattePaths = canvas.getPaths();
-      if (matteType == Layer.MatteType.Add) {
-        for (int i = 0; i < mattePaths.size(); i++) {
-          Path mattePath = new Path(mattePaths.get(i));
-          addPath.set(contentPath);
-          addPath.op(mattePath, Path.Op.INTERSECT);
-          mattesPath.op(addPath, Path.Op.UNION);
-        }
-      } else if (matteType == Layer.MatteType.Invert) {
-        for (int i = 0; i < mattePaths.size(); i++) {
-          Path mattePath = new Path(mattePaths.get(i));
-          subtractPath.set(contentPath);
-          mattePath.transform(matteMatrix);
-          subtractPath.op(mattePath, Path.Op.DIFFERENCE);
-        }
-        if (mattesPath.isEmpty()) {
-          mattesPath.addPath(subtractPath);
-        } else {
-          mattesPath.op(subtractPath, Path.Op.INTERSECT);
-        }
-
-
-//        subtractPath.set(contentPath);
-//        subtractPath.op(mattePath, Path.Op.DIFFERENCE);
-//        if (combinedPath.isEmpty()) {
-//          combinedPath.addPath(contentPath);
-//        }
-//        combinedPath.op(subtractPath, Path.Op.INTERSECT);
-      }
-
-    }
-
-    if (!masksPath.isEmpty() && !mattesPath.isEmpty()) {
-      combinedPath.op(masksPath, mattesPath, Path.Op.INTERSECT);
-    } else if (!masksPath.isEmpty()) {
-      combinedPath.set(masksPath);
-    } else if (mattesPath != null) {
-      combinedPath.set(mattesPath);
-    }
+    combinedPath.set(masksPath);
     if (applyToPath) {
       contentPath.set(combinedPath);
     }
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 9bfacae..6f4fa41 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,6 +1,8 @@
 package com.airbnb.lottie.model.layer;
 
+import android.annotation.SuppressLint;
 import android.graphics.*;
+import android.os.Build;
 import androidx.annotation.CallSuper;
 import androidx.annotation.FloatRange;
 import androidx.annotation.Nullable;
@@ -18,7 +20,6 @@
 import com.airbnb.lottie.animation.keyframe.TransformKeyframeAnimation;
 import com.airbnb.lottie.model.KeyPath;
 import com.airbnb.lottie.model.KeyPathElement;
-import com.airbnb.lottie.model.content.Mask;
 import com.airbnb.lottie.value.LottieValueCallback;
 
 import java.util.ArrayList;
@@ -28,6 +29,15 @@
 public abstract class BaseLayer
     implements DrawingContent, PathContent, PathsContent, BaseKeyframeAnimation.AnimationListener, KeyPathElement {
 
+  /**
+   * These flags were in Canvas but they were deprecated and removed.
+   * TODO: test removing these on older versions of Android.
+   */
+  private static final int CLIP_SAVE_FLAG = 0x02;
+  private static final int CLIP_TO_LAYER_SAVE_FLAG = 0x10;
+  private static final int MATRIX_SAVE_FLAG = 0x01;
+  private static final int SAVE_FLAGS = CLIP_SAVE_FLAG | CLIP_TO_LAYER_SAVE_FLAG | MATRIX_SAVE_FLAG;
+
   @Nullable
   static BaseLayer forModel(
     Layer layerModel, LottieDrawable drawable, LottieComposition composition) {
@@ -55,6 +65,12 @@
 
   private final Path path = new Path();
   private final Matrix matrix = new Matrix();
+  private final Paint contentPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+  private final Paint mattePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+  private final Paint clearPaint = new Paint();
+  private final RectF rect = new RectF();
+  private final RectF matteBoundsRect = new RectF();
+  private final RectF tempMaskBoundsRect = new RectF();
   private final String drawTraceName;
   final Matrix boundsMatrix = new Matrix();
   final LottieDrawable lottieDrawable;
@@ -72,6 +88,12 @@
     this.lottieDrawable = lottieDrawable;
     this.layerModel = layerModel;
     drawTraceName = layerModel.getName() + "#draw";
+    clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+    if (layerModel.getMatteType() == Layer.MatteType.Invert) {
+      mattePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
+    } else {
+      mattePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
+    }
 
     this.transform = layerModel.getTransform().createAnimation();
     transform.addListener(this);
@@ -100,12 +122,6 @@
   }
 
   void setMatteLayer(@Nullable BaseLayer matteLayer) {
-    if (mask != null) {
-      mask.setMatteLayer(matteLayer, layerModel.getMatteType());
-    } else {
-      mask = new MaskKeyframeAnimation(Collections.<Mask>emptyList());
-      mask.setMatteLayer(matteLayer, layerModel.getMatteType());
-    }
     this.matteLayer = matteLayer;
   }
 
@@ -138,6 +154,17 @@
     lottieDrawable.invalidateSelf();
   }
 
+  @SuppressLint("WrongConstant")
+  private void saveLayerCompat(ICanvas canvas, RectF rect, Paint paint, boolean all) {
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+      // This method was deprecated in API level 26 and not recommended since 22, but its
+      // 2-parameter replacement is only available starting at API level 21.
+      canvas.saveLayer(rect, paint, all ? Canvas.ALL_SAVE_FLAG : SAVE_FLAGS);
+    } else {
+      canvas.saveLayer(rect, paint);
+    }
+  }
+
   public void addAnimation(BaseKeyframeAnimation<?, ?> newAnimation) {
     animations.add(newAnimation);
   }
@@ -148,7 +175,7 @@
   }
 
   @Override
-  public void draw(ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix, Matrix matteMatrix) {
+  public void draw(ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix) {
     L.beginSection(drawTraceName);
     if (!visible) {
       L.endSection(drawTraceName);
@@ -164,12 +191,53 @@
     L.endSection("Layer#parentMatrix");
     int alpha = (int)
         ((parentAlpha / 255f * (float) transform.getOpacity().getValue() / 100f) * 255);
-    matrix.preConcat(transform.getMatrix());
-    L.beginSection("Layer#drawLayer");
-    // TODO: figure out the right mask to use.
     MaskKeyframeAnimation layerMask = this.mask != null ? this.mask : mask;
-    drawLayer(canvas, matrix, alpha, layerMask, matrix, matteMatrix);
+    if (!hasMatteOnThisLayer()) {
+      matrix.preConcat(transform.getMatrix());
+      L.beginSection("Layer#drawLayer");
+      drawLayer(canvas, matrix, alpha, layerMask, matrix);
+      L.endSection("Layer#drawLayer");
+      recordRenderTime(L.endSection(drawTraceName));
+      return;
+    }
+
+    L.beginSection("Layer#computeBounds");
+    rect.set(0, 0, 0, 0);
+    getBounds(rect, matrix);
+    intersectBoundsWithMatte(rect, matrix);
+
+    matrix.preConcat(transform.getMatrix());
+
+    rect.set(0, 0, canvas.getWidth(), canvas.getHeight());
+    L.endSection("Layer#computeBounds");
+
+    L.beginSection("Layer#saveLayer");
+    saveLayerCompat(canvas, rect, contentPaint, true);
+    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, layerMask, matrix);
     L.endSection("Layer#drawLayer");
+
+    if (hasMatteOnThisLayer()) {
+      L.beginSection("Layer#drawMatte");
+      L.beginSection("Layer#saveLayer");
+      saveLayerCompat(canvas, rect, mattePaint, false);
+      L.endSection("Layer#saveLayer");
+      clearCanvas(canvas);
+      //noinspection ConstantConditions
+      matteLayer.draw(canvas, parentMatrix, alpha, layerMask, matrix);
+      L.beginSection("Layer#restoreLayer");
+      canvas.restore();
+      L.endSection("Layer#restoreLayer");
+      L.endSection("Layer#drawMatte");
+    }
+
+    L.beginSection("Layer#restoreLayer");
+    canvas.restore();
+    L.endSection("Layer#restoreLayer");
     recordRenderTime(L.endSection(drawTraceName));
   }
 
@@ -180,10 +248,35 @@
   private void recordRenderTime(float ms) {
     lottieDrawable.getComposition()
         .getPerformanceTracker().recordRenderTime(layerModel.getName(), ms);
-
   }
 
-  abstract void drawLayer(ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix, Matrix matteMatrix);
+  private void clearCanvas(ICanvas canvas) {
+    L.beginSection("Layer#clearLayer");
+    // If we don't pad the clear draw, some phones leave a 1px border of the graphics buffer.
+    canvas.drawRect(rect.left - 1, rect.top - 1, rect.right + 1, rect.bottom + 1, clearPaint);
+    L.endSection("Layer#clearLayer");
+  }
+
+  private void intersectBoundsWithMatte(RectF rect, Matrix matrix) {
+    if (!hasMatteOnThisLayer()) {
+      return;
+    }
+    if (layerModel.getMatteType() == Layer.MatteType.Invert) {
+      // We can't trim the bounds if the mask is inverted since it extends all the way to the
+      // composition bounds.
+      return;
+    }
+    //noinspection ConstantConditions
+    matteLayer.getBounds(matteBoundsRect, matrix);
+    rect.set(
+        Math.max(rect.left, matteBoundsRect.left),
+        Math.max(rect.top, matteBoundsRect.top),
+        Math.min(rect.right, matteBoundsRect.right),
+        Math.min(rect.bottom, matteBoundsRect.bottom)
+    );
+  }
+
+  abstract void drawLayer(ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix);
 
   boolean hasMasksOnThisLayer() {
     return mask != null && !mask.getMaskAnimations().isEmpty();
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 f5fba64..0d8ba44 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,7 +88,7 @@
     }
   }
 
-  @Override void drawLayer(ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix, Matrix matteMatrix) {
+  @Override void drawLayer(ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix) {
     L.beginSection("CompositionLayer#draw");
     canvas.save();
     newClipRect.set(0, 0, layerModel.getPreCompWidth(), layerModel.getPreCompHeight());
@@ -101,7 +101,7 @@
       }
       if (nonEmptyClip) {
         BaseLayer layer = layers.get(i);
-        layer.draw(canvas, parentMatrix, parentAlpha, mask, maskMatrix, parentMatrix);
+        layer.draw(canvas, parentMatrix, parentAlpha, mask, maskMatrix);
       }
     }
     canvas.restore();
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/layer/ImageLayer.java b/lottie/src/main/java/com/airbnb/lottie/model/layer/ImageLayer.java
index 3a6ffa4..33c48e0 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/layer/ImageLayer.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/layer/ImageLayer.java
@@ -30,7 +30,7 @@
     super(lottieDrawable, layerModel);
   }
 
-  @Override public void drawLayer(@NonNull ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix, Matrix matteMatrix) {
+  @Override public void drawLayer(@NonNull ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix) {
     Bitmap bitmap = getBitmap();
     if (bitmap == null || bitmap.isRecycled()) {
       return;
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/layer/NullLayer.java b/lottie/src/main/java/com/airbnb/lottie/model/layer/NullLayer.java
index 4f9f80d..57c1ee8 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/layer/NullLayer.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/layer/NullLayer.java
@@ -19,7 +19,7 @@
     super(lottieDrawable, layerModel);
   }
 
-  @Override void drawLayer(ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix, Matrix matteMatrix) {
+  @Override void drawLayer(ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix) {
     // Do nothing.
   }
 
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/layer/ShapeLayer.java b/lottie/src/main/java/com/airbnb/lottie/model/layer/ShapeLayer.java
index 39a8356..1f96406 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/layer/ShapeLayer.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/layer/ShapeLayer.java
@@ -32,8 +32,8 @@
     contentGroup.setContents(Collections.<Content>emptyList(), Collections.<Content>emptyList());
   }
 
-  @Override void drawLayer(@NonNull ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix, Matrix matteMatrix) {
-    contentGroup.draw(canvas, parentMatrix, parentAlpha, mask, maskMatrix, matteMatrix);
+  @Override void drawLayer(@NonNull ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix) {
+    contentGroup.draw(canvas, parentMatrix, parentAlpha, mask, maskMatrix);
   }
 
   @Override public void getBounds(RectF outBounds, Matrix parentMatrix) {
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/layer/SolidLayer.java b/lottie/src/main/java/com/airbnb/lottie/model/layer/SolidLayer.java
index daf3950..111d793 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/layer/SolidLayer.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/layer/SolidLayer.java
@@ -38,7 +38,7 @@
     paint.setColor(layerModel.getSolidColor());
   }
 
-  @Override public void drawLayer(ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix, Matrix matteMatrix) {
+  @Override public void drawLayer(ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix) {
     int backgroundAlpha = Color.alpha(layerModel.getSolidColor());
     if (backgroundAlpha == 0) {
       return;
@@ -72,7 +72,7 @@
 
       if (mask != null) {
         // TODO: use the right matte matrix
-        mask.applyToPath(path, maskMatrix, parentMatrix, new Matrix());
+        mask.applyToPath(path, maskMatrix);
       }
 
       canvas.drawPath(path, paint);
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/layer/TextLayer.java b/lottie/src/main/java/com/airbnb/lottie/model/layer/TextLayer.java
index 26b0a95..a73ee68 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/layer/TextLayer.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/layer/TextLayer.java
@@ -1,6 +1,5 @@
 package com.airbnb.lottie.model.layer;
 
-import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Matrix;
 import android.graphics.Paint;
@@ -95,7 +94,7 @@
     return Collections.emptyList();
   }
 
-  @Override void drawLayer(ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix, Matrix matteMatrix) {
+  @Override void drawLayer(ICanvas canvas, Matrix parentMatrix, int parentAlpha, @Nullable MaskKeyframeAnimation mask, Matrix maskMatrix) {
     canvas.save();
     if (!lottieDrawable.useTextGlyphs()) {
       canvas.setMatrix(parentMatrix);
@@ -235,7 +234,7 @@
 
     if (mask != null) {
       // TODO: use the right matte matrix
-      mask.applyToPath(path, maskMatrix, matteMatrix, new Matrix());
+      mask.applyToPath(path, maskMatrix);
     }
 
     canvas.drawPath(path, paint);