Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

De-dupe gradient stops #2081

Merged
merged 2 commits into from May 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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);
}
}