Skip to content

Commit

Permalink
De-dupe gradient stops (#2081)
Browse files Browse the repository at this point in the history
  • Loading branch information
gpeal committed May 27, 2022
1 parent a054ca2 commit 3a429d8
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 6 deletions.
Expand Up @@ -142,14 +142,12 @@ private GradientColor addOpacityStopsToGradientIfNeeded(GradientColor gradientCo
}
}

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);
Expand Down Expand Up @@ -223,4 +221,46 @@ private int getColorInBetweenOpacityStops(float position, int color, float[] opa
}
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);
}
}
@@ -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);
}
}

0 comments on commit 3a429d8

Please sign in to comment.