De-dupe gradient stops (#2081)

diff --git a/lottie/src/main/java/com/airbnb/lottie/parser/GradientColorParser.java b/lottie/src/main/java/com/airbnb/lottie/parser/GradientColorParser.java
index 0dbfb5b..50a52e3 100644
--- a/lottie/src/main/java/com/airbnb/lottie/parser/GradientColorParser.java
+++ b/lottie/src/main/java/com/airbnb/lottie/parser/GradientColorParser.java
@@ -142,14 +142,12 @@
       }
     }
 
-    int newColorPoints = colorPoints + opacityStops;
-    float[] newPositions = new float[newColorPoints];
+    // Pre-SKIA (Oreo) devices render artifacts when there is two stops in the same position.
+    // As a result, we have to de-dupe the merge color and opacity stop positions.
+    float[] newPositions = mergeUniqueElements(gradientColor.getPositions(), opacityStopPositions);
+    int newColorPoints = newPositions.length;
     int[] newColors = new int[newColorPoints];
 
-    System.arraycopy(gradientColor.getPositions(), 0, newPositions, 0, colorPoints);
-    System.arraycopy(opacityStopPositions, 0, newPositions, colorPoints, opacityStops);
-    Arrays.sort(newPositions);
-
     for (int i = 0; i < newColorPoints; i++) {
       float position = newPositions[i];
       int colorStopIndex = Arrays.binarySearch(colorStopPositions, position);
@@ -223,4 +221,46 @@
     }
     throw new IllegalArgumentException("Unreachable code.");
   }
+
+  /**
+   * Takes two sorted float arrays and merges their elements while removing duplicates.
+   */
+  protected static float[] mergeUniqueElements(float[] arrayA, float[] arrayB) {
+    if (arrayA.length == 0) {
+      return arrayB;
+    } else if (arrayB.length == 0) {
+      return arrayA;
+    }
+
+    int aIndex = 0;
+    int bIndex = 0;
+    int numDuplicates = 0;
+    // This will be the merged list but may be longer than what is needed if there are duplicates.
+    // If there are, the 0 elements at the end need to be truncated.
+    float[] mergedNotTruncated = new float[arrayA.length + arrayB.length];
+    for (int i = 0; i < mergedNotTruncated.length; i++) {
+      final float a = aIndex < arrayA.length ? arrayA[aIndex] : Float.NaN;
+      final float b = bIndex < arrayB.length ? arrayB[bIndex] : Float.NaN;
+
+      if (Float.isNaN(b) || a < b) {
+        mergedNotTruncated[i] = a;
+        aIndex++;
+      } else if (Float.isNaN(a) || b < a) {
+        mergedNotTruncated[i] = b;
+        bIndex++;
+      } else {
+        mergedNotTruncated[i] = a;
+        aIndex++;
+        bIndex++;
+        numDuplicates++;
+      }
+    }
+
+    if (numDuplicates == 0) {
+      return mergedNotTruncated;
+    }
+
+
+    return Arrays.copyOf(mergedNotTruncated, mergedNotTruncated.length - numDuplicates);
+  }
 }
\ No newline at end of file
diff --git a/lottie/src/test/java/com/airbnb/lottie/parser/GradientColorParserTest.java b/lottie/src/test/java/com/airbnb/lottie/parser/GradientColorParserTest.java
new file mode 100644
index 0000000..79682e1
--- /dev/null
+++ b/lottie/src/test/java/com/airbnb/lottie/parser/GradientColorParserTest.java
@@ -0,0 +1,32 @@
+package com.airbnb.lottie.parser;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import org.junit.Test;
+
+public class GradientColorParserTest {
+
+  @Test public void testNoDistinctShort() {
+    assertMerged(new float[]{1}, new float[]{2}, new float[]{1, 2});
+  }
+
+  @Test public void testNoDistinct() {
+    assertMerged(new float[]{1, 2, 3}, new float[]{4, 5, 6}, new float[]{1, 2, 3, 4, 5, 6});
+  }
+
+  @Test public void testWithDistinct() {
+    assertMerged(new float[]{1, 2, 3, 5}, new float[]{4, 5, 6}, new float[]{1, 2, 3, 4, 5, 6});
+  }
+
+  @Test public void testWithDistinctInterleavingValues() {
+    assertMerged(new float[]{2, 4, 5, 6, 8, 10}, new float[]{1, 3, 4, 5, 7, 9}, new float[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
+  }
+
+  @Test public void testIdentical() {
+    assertMerged(new float[]{2, 3}, new float[]{2, 3}, new float[]{2, 3});
+  }
+
+  private void assertMerged(float[] arrayA, float[] arrayB, float[] merged) {
+    assertArrayEquals(merged, GradientColorParser.mergeUniqueElements(arrayA, arrayB), 0f);
+  }
+}
\ No newline at end of file