Added support for miter limit (#839)

Fixes #797 
diff --git a/After Effects Samples/Miter Limit.aep b/After Effects Samples/Miter Limit.aep
new file mode 100644
index 0000000..edcb488
--- /dev/null
+++ b/After Effects Samples/Miter Limit.aep
Binary files differ
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d11f666..04863a7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
     * The new factory methods make it easier to catch exceptions by separating out success and
     failure handlers. Previously, catching exceptions was impossible and would crash your app.
 * [Sample App] Added the ability to load a file from assets.
+* Added support for miter limit.
 
 # 2.5.7
 * Reapply min/max frame once composition is loaded (#827).
diff --git a/LottieSample/src/main/assets/Tests/MiterLimit.json b/LottieSample/src/main/assets/Tests/MiterLimit.json
new file mode 100644
index 0000000..a3d4258
--- /dev/null
+++ b/LottieSample/src/main/assets/Tests/MiterLimit.json
@@ -0,0 +1 @@
+{"v":"4.11.1","fr":60,"ip":0,"op":180,"w":300,"h":300,"nm":"miter","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,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]],"o":[[0,0]],"v":[[80,153]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.928262987324,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"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":"Shape 2","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0]],"o":[[0,0]],"v":[[105,78]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":13,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.928262987324,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"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":"Shape 1","np":3,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-63.432,-124.568],[37.568,62.568],[-61.568,61.568]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"gs","o":{"a":0,"k":100,"ix":9},"w":{"a":0,"k":13,"ix":10},"g":{"p":3,"k":{"a":0,"k":[0,0,0,1,0.5,0,0,0.5,1,0,0,0,0,0.5,0.5,0.375,1,0.25],"ix":8}},"s":{"a":0,"k":[0,0],"ix":4},"e":{"a":0,"k":[100,0],"ix":5},"t":1,"lc":1,"lj":1,"ml":4,"nm":"Gradient Stroke 1","mn":"ADBE Vector Graphic - G-Stroke","hd":false},{"ty":"st","c":{"a":0,"k":[1,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":13,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[3.568,12.568],"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":3,"cix":2,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0}]}
\ No newline at end of file
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 b4f6a03..2c489ac 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
@@ -15,6 +15,7 @@
 import com.airbnb.lottie.LottieDrawable;
 import com.airbnb.lottie.LottieProperty;
 import com.airbnb.lottie.animation.keyframe.BaseKeyframeAnimation;
+import com.airbnb.lottie.animation.keyframe.FloatKeyframeAnimation;
 import com.airbnb.lottie.animation.keyframe.ValueCallbackKeyframeAnimation;
 import com.airbnb.lottie.model.KeyPath;
 import com.airbnb.lottie.model.animatable.AnimatableFloatValue;
@@ -50,7 +51,7 @@
   @Nullable private BaseKeyframeAnimation<ColorFilter, ColorFilter> colorFilterAnimation;
 
   BaseStrokeContent(final LottieDrawable lottieDrawable, BaseLayer layer, Paint.Cap cap,
-      Paint.Join join, AnimatableIntegerValue opacity, AnimatableFloatValue width,
+      Paint.Join join, float miterLimit, AnimatableIntegerValue opacity, AnimatableFloatValue width,
       List<AnimatableFloatValue> dashPattern, AnimatableFloatValue offset) {
     this.lottieDrawable = lottieDrawable;
     this.layer = layer;
@@ -58,6 +59,7 @@
     paint.setStyle(Paint.Style.STROKE);
     paint.setStrokeCap(cap);
     paint.setStrokeJoin(join);
+    paint.setStrokeMiter(miterLimit);
 
     opacityAnimation = opacity.createAnimation();
     widthAnimation = width.createAnimation();
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 262531a..8f7198a 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
@@ -36,8 +36,8 @@
   public GradientStrokeContent(
       final LottieDrawable lottieDrawable, BaseLayer layer, GradientStroke stroke) {
     super(lottieDrawable, layer, stroke.getCapType().toPaintCap(),
-        stroke.getJoinType().toPaintJoin(), stroke.getOpacity(), stroke.getWidth(),
-        stroke.getLineDashPattern(), stroke.getDashOffset());
+        stroke.getJoinType().toPaintJoin(), stroke.getMiterLimit(), stroke.getOpacity(),
+        stroke.getWidth(), stroke.getLineDashPattern(), stroke.getDashOffset());
 
     name = stroke.getName();
     type = stroke.getGradientType();
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 2c1f077..5542d2e 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
@@ -24,8 +24,8 @@
 
   public StrokeContent(final LottieDrawable lottieDrawable, BaseLayer layer, ShapeStroke stroke) {
     super(lottieDrawable, layer, stroke.getCapType().toPaintCap(),
-        stroke.getJoinType().toPaintJoin(), stroke.getOpacity(), stroke.getWidth(),
-        stroke.getLineDashPattern(), stroke.getDashOffset());
+        stroke.getJoinType().toPaintJoin(), stroke.getMiterLimit(), stroke.getOpacity(),
+        stroke.getWidth(), stroke.getLineDashPattern(), stroke.getDashOffset());
     this.layer = layer;
     name = stroke.getName();
     colorAnimation = stroke.getColor().createAnimation();
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/content/GradientStroke.java b/lottie/src/main/java/com/airbnb/lottie/model/content/GradientStroke.java
index ec249d5..4bf5954 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/content/GradientStroke.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/content/GradientStroke.java
@@ -24,6 +24,7 @@
   private final AnimatableFloatValue width;
   private final ShapeStroke.LineCapType capType;
   private final ShapeStroke.LineJoinType joinType;
+  private final float miterLimit;
   private final List<AnimatableFloatValue> lineDashPattern;
   @Nullable private final AnimatableFloatValue dashOffset;
 
@@ -31,7 +32,8 @@
       AnimatableGradientColorValue gradientColor,
       AnimatableIntegerValue opacity, AnimatablePointValue startPoint,
       AnimatablePointValue endPoint, AnimatableFloatValue width, ShapeStroke.LineCapType capType,
-      ShapeStroke.LineJoinType joinType, List<AnimatableFloatValue> lineDashPattern,
+      ShapeStroke.LineJoinType joinType, float miterLimit,
+      List<AnimatableFloatValue> lineDashPattern,
       @Nullable AnimatableFloatValue dashOffset) {
     this.name = name;
     this.gradientType = gradientType;
@@ -42,6 +44,7 @@
     this.width = width;
     this.capType = capType;
     this.joinType = joinType;
+    this.miterLimit = miterLimit;
     this.lineDashPattern = lineDashPattern;
     this.dashOffset = dashOffset;
   }
@@ -90,6 +93,10 @@
     return dashOffset;
   }
 
+  public float getMiterLimit() {
+    return miterLimit;
+  }
+
   @Override public Content toContent(LottieDrawable drawable, BaseLayer layer) {
     return new GradientStrokeContent(drawable, layer, this);
   }
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/content/ShapeStroke.java b/lottie/src/main/java/com/airbnb/lottie/model/content/ShapeStroke.java
index 0ebb4ee..e68601e 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/content/ShapeStroke.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/content/ShapeStroke.java
@@ -58,11 +58,12 @@
   private final AnimatableFloatValue width;
   private final LineCapType capType;
   private final LineJoinType joinType;
+  private final float miterLimit;
 
   public ShapeStroke(String name, @Nullable AnimatableFloatValue offset,
       List<AnimatableFloatValue> lineDashPattern, AnimatableColorValue color,
       AnimatableIntegerValue opacity, AnimatableFloatValue width, LineCapType capType,
-      LineJoinType joinType) {
+      LineJoinType joinType, float miterLimit) {
     this.name = name;
     this.offset = offset;
     this.lineDashPattern = lineDashPattern;
@@ -71,6 +72,7 @@
     this.width = width;
     this.capType = capType;
     this.joinType = joinType;
+    this.miterLimit = miterLimit;
   }
 
   @Override public Content toContent(LottieDrawable drawable, BaseLayer layer) {
@@ -108,4 +110,8 @@
   public LineJoinType getJoinType() {
     return joinType;
   }
+
+  public float getMiterLimit() {
+    return miterLimit;
+  }
 }
diff --git a/lottie/src/main/java/com/airbnb/lottie/parser/GradientStrokeParser.java b/lottie/src/main/java/com/airbnb/lottie/parser/GradientStrokeParser.java
index c6a518c..cf4d097 100644
--- a/lottie/src/main/java/com/airbnb/lottie/parser/GradientStrokeParser.java
+++ b/lottie/src/main/java/com/airbnb/lottie/parser/GradientStrokeParser.java
@@ -31,6 +31,7 @@
     ShapeStroke.LineCapType capType = null;
     ShapeStroke.LineJoinType joinType = null;
     AnimatableFloatValue offset = null;
+    float miterLimit = 0f;
 
 
     List<AnimatableFloatValue> lineDashPattern = new ArrayList<>();
@@ -78,6 +79,9 @@
         case "lj":
           joinType = ShapeStroke.LineJoinType.values()[reader.nextInt() - 1];
           break;
+        case "ml":
+          miterLimit = (float) reader.nextDouble();
+          break;
         case "d":
           reader.beginArray();
           while (reader.hasNext()) {
@@ -117,6 +121,6 @@
 
     return new GradientStroke(
         name, gradientType, color, opacity, startPoint, endPoint, width, capType, joinType,
-        lineDashPattern, offset);
+        miterLimit, lineDashPattern, offset);
   }
 }
diff --git a/lottie/src/main/java/com/airbnb/lottie/parser/ShapeStrokeParser.java b/lottie/src/main/java/com/airbnb/lottie/parser/ShapeStrokeParser.java
index 9ae96b3..656bb4d 100644
--- a/lottie/src/main/java/com/airbnb/lottie/parser/ShapeStrokeParser.java
+++ b/lottie/src/main/java/com/airbnb/lottie/parser/ShapeStrokeParser.java
@@ -25,6 +25,7 @@
     ShapeStroke.LineCapType capType = null;
     ShapeStroke.LineJoinType joinType = null;
     AnimatableFloatValue offset = null;
+    float miterLimit = 0f;
 
     List<AnimatableFloatValue> lineDashPattern = new ArrayList<>();
 
@@ -48,6 +49,9 @@
         case "lj":
           joinType = ShapeStroke.LineJoinType.values()[reader.nextInt() - 1];
           break;
+        case "ml":
+          miterLimit =  (float) reader.nextDouble();
+          break;
         case "d":
           reader.beginArray();
           while (reader.hasNext()) {
@@ -92,6 +96,6 @@
     }
 
     return new ShapeStroke(
-        name, offset, lineDashPattern, color, opacity, width, capType, joinType);
+        name, offset, lineDashPattern, color, opacity, width, capType, joinType, miterLimit);
   }
 }