1 package com.airbnb.lottie.model.content; 2 3 import com.airbnb.lottie.utils.GammaEvaluator; 4 import com.airbnb.lottie.utils.MiscUtils; 5 6 import java.util.Arrays; 7 8 9 public class GradientColor { 10 private final float[] positions; 11 private final int[] colors; 12 GradientColor(float[] positions, int[] colors)13 public GradientColor(float[] positions, int[] colors) { 14 this.positions = positions; 15 this.colors = colors; 16 } 17 getPositions()18 public float[] getPositions() { 19 return positions; 20 } 21 getColors()22 public int[] getColors() { 23 return colors; 24 } 25 getSize()26 public int getSize() { 27 return colors.length; 28 } 29 lerp(GradientColor gc1, GradientColor gc2, float progress)30 public void lerp(GradientColor gc1, GradientColor gc2, float progress) { 31 // Fast return in case start and end is the same 32 // or if progress is at start/end or out of [0,1] bounds 33 if (gc1.equals(gc2)) { 34 copyFrom(gc1); 35 return; 36 } else if (progress <= 0f) { 37 copyFrom(gc1); 38 return; 39 } else if (progress >= 1f) { 40 copyFrom(gc2); 41 return; 42 } 43 44 if (gc1.colors.length != gc2.colors.length) { 45 throw new IllegalArgumentException("Cannot interpolate between gradients. Lengths vary (" + 46 gc1.colors.length + " vs " + gc2.colors.length + ")"); 47 } 48 49 for (int i = 0; i < gc1.colors.length; i++) { 50 positions[i] = MiscUtils.lerp(gc1.positions[i], gc2.positions[i], progress); 51 colors[i] = GammaEvaluator.evaluate(progress, gc1.colors[i], gc2.colors[i]); 52 } 53 54 // Not all keyframes that this GradientColor are used for will have the same length. 55 // AnimatableGradientColorValue.ensureInterpolatableKeyframes may add extra positions 56 // for some keyframes but not others to ensure that it is interpolatable. 57 // If there are extra positions here, just duplicate the last value in the gradient. 58 for (int i = gc1.colors.length; i < positions.length; i++) { 59 positions[i] = positions[gc1.colors.length - 1]; 60 colors[i] = colors[gc1.colors.length - 1]; 61 } 62 } 63 copyWithPositions(float[] positions)64 public GradientColor copyWithPositions(float[] positions) { 65 int[] colors = new int[positions.length]; 66 for (int i = 0; i < positions.length; i++) { 67 colors[i] = getColorForPosition(positions[i]); 68 } 69 return new GradientColor(positions, colors); 70 } 71 72 @Override equals(Object o)73 public boolean equals(Object o) { 74 if (this == o) { 75 return true; 76 } 77 if (o == null || getClass() != o.getClass()) { 78 return false; 79 } 80 GradientColor that = (GradientColor) o; 81 return Arrays.equals(positions, that.positions) && Arrays.equals(colors, that.colors); 82 } 83 84 @Override hashCode()85 public int hashCode() { 86 int result = Arrays.hashCode(positions); 87 result = 31 * result + Arrays.hashCode(colors); 88 return result; 89 } 90 getColorForPosition(float position)91 private int getColorForPosition(float position) { 92 int existingIndex = Arrays.binarySearch(positions, position); 93 if (existingIndex >= 0) { 94 return colors[existingIndex]; 95 } 96 // binarySearch returns -insertionPoint - 1 if it is not found. 97 int insertionPoint = -(existingIndex + 1); 98 if (insertionPoint == 0) { 99 return colors[0]; 100 } else if (insertionPoint == colors.length - 1) { 101 return colors[colors.length - 1]; 102 } 103 float startPosition = positions[insertionPoint - 1]; 104 float endPosition = positions[insertionPoint]; 105 int startColor = colors[insertionPoint - 1]; 106 int endColor = colors[insertionPoint]; 107 108 float fraction = (position - startPosition) / (endPosition - startPosition); 109 return GammaEvaluator.evaluate(fraction, startColor, endColor); 110 } 111 copyFrom(GradientColor other)112 private void copyFrom(GradientColor other) { 113 for (int i = 0; i < other.colors.length; i++) { 114 positions[i] = other.positions[i]; 115 colors[i] = other.colors[i]; 116 } 117 } 118 } 119