Skip to content

Commit

Permalink
GradientColorKeyframeAnimation does not handle progress outside [0,1] (
Browse files Browse the repository at this point in the history
…airbnb#2427)

- Closes airbnb#2426
- Improve performance by running GammaEvaluator once

Co-authored-by: Sven Obser <[email protected]>
  • Loading branch information
brudaswen and Sven Obser authored Dec 15, 2023
1 parent 664c8e3 commit db452ea
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ public int getSize() {
}

public void lerp(GradientColor gc1, GradientColor gc2, float progress) {
// Fast return in case start and end is the same
// or if progress is at start/end or out of [0,1] bounds
if (gc1.equals(gc2)) {
copyFrom(gc1);
return;
} else if (progress <= 0f) {
copyFrom(gc1);
return;
} else if (progress >= 1f) {
copyFrom(gc2);
return;
}

if (gc1.colors.length != gc2.colors.length) {
throw new IllegalArgumentException("Cannot interpolate between gradients. Lengths vary (" +
gc1.colors.length + " vs " + gc2.colors.length + ")");
Expand Down Expand Up @@ -56,6 +69,25 @@ public GradientColor copyWithPositions(float[] positions) {
return new GradientColor(positions, colors);
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
GradientColor that = (GradientColor) o;
return Arrays.equals(positions, that.positions) && Arrays.equals(colors, that.colors);
}

@Override
public int hashCode() {
int result = Arrays.hashCode(positions);
result = 31 * result + Arrays.hashCode(colors);
return result;
}

private int getColorForPosition(float position) {
int existingIndex = Arrays.binarySearch(positions, position);
if (existingIndex >= 0) {
Expand All @@ -76,4 +108,11 @@ private int getColorForPosition(float position) {
float fraction = (position - startPosition) / (endPosition - startPosition);
return GammaEvaluator.evaluate(fraction, startColor, endColor);
}

private void copyFrom(GradientColor other) {
for (int i = 0; i < other.colors.length; i++) {
positions[i] = other.positions[i];
colors[i] = other.colors[i];
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -189,12 +189,16 @@ int getColorInBetweenColorStops(float position, float opacity, float[] colorStop
float distanceBetweenColors = colorStopPositions[i] - colorStopPositions[i - 1];
float distanceToLowerColor = position - colorStopPositions[i - 1];
float percentage = distanceToLowerColor / distanceBetweenColors;

int upperColor = colorStopColors[i];
int lowerColor = colorStopColors[i - 1];
int intermediateColor = GammaEvaluator.evaluate(percentage, lowerColor, upperColor);

int a = (int) (opacity * 255);
int r = GammaEvaluator.evaluate(percentage, Color.red(lowerColor), Color.red(upperColor));
int g = GammaEvaluator.evaluate(percentage, Color.green(lowerColor), Color.green(upperColor));
int b = GammaEvaluator.evaluate(percentage, Color.blue(lowerColor), Color.blue(upperColor));
int r = Color.red(intermediateColor);
int g = Color.green(intermediateColor);
int b = Color.blue(intermediateColor);

return Color.argb(a, r, g, b);
}
throw new IllegalArgumentException("Unreachable code.");
Expand Down Expand Up @@ -269,7 +273,6 @@ protected static float[] mergeUniqueElements(float[] arrayA, float[] arrayB) {
return mergedNotTruncated;
}


return Arrays.copyOf(mergedNotTruncated, mergedNotTruncated.length - numDuplicates);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,16 @@ private static float EOCF_sRGB(float srgb) {
}

public static int evaluate(float fraction, int startInt, int endInt) {
// Fast return in case start and end is the same
// or if fraction is at start/end or out of [0,1] bounds
if (startInt == endInt) {
return startInt;
} else if (fraction <= 0f) {
return startInt;
} else if (fraction >= 1f) {
return endInt;
}

float startA = ((startInt >> 24) & 0xff) / 255.0f;
float startR = ((startInt >> 16) & 0xff) / 255.0f;
float startG = ((startInt >> 8) & 0xff) / 255.0f;
Expand Down Expand Up @@ -61,4 +68,4 @@ public static int evaluate(float fraction, int startInt, int endInt) {

return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.airbnb.lottie.model.content;

import junit.framework.TestCase;
import org.junit.Test;

import java.util.Arrays;

public class GradientColorTest extends TestCase {

private final GradientColor start = new GradientColor(new float[]{0f, 1f}, new int[]{0xFF000000, 0xFF020202});

private final GradientColor end = new GradientColor(new float[]{0f, 1f}, new int[]{0xFF020202, 0xFF040404});

private final GradientColor gradient = new GradientColor(new float[2], new int[2]);

@Test
public void testLerpWithOutOfBoundsNegativeProgress() {
gradient.lerp(start, end, -42f);
assertEquals(start, gradient);
}

@Test
public void testLerpWithZeroProgress() {
gradient.lerp(start, end, 0f);
assertEquals(start, gradient);
}

@Test
public void testLerpWithHalfProgress() {
gradient.lerp(start, end, 0.5f);
GradientColor half = new GradientColor(new float[]{0f, 1f}, new int[]{0xFF010101, 0xFF030303});
assertEquals(half, gradient);
}

@Test
public void testLerpWithOneProgress() {
gradient.lerp(start, end, 1f);
assertEquals(end, gradient);
}

@Test
public void testLerpWithOutOfBoundsPositiveProgress() {
gradient.lerp(start, end, 42f);
assertEquals(end, gradient);
}
}
Binary file not shown.

0 comments on commit db452ea

Please sign in to comment.