Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?

Conversion of the evaluate method in Android's ArgbEvaluator from Java to Kotlin.

On the original version, it could take longer to understand what the code is doing. This is because we start working with lower level operations, like extracting the components of the color.

On the Kotlin version, more significant details, like the fact that we are creating a new color, live closer to the function definition. More details about how each operation works can be found by digging into the code if needed. But if we don't need that information, it's dramatically easier to understand what the function does.

Also, on the Java code, it's easier to miss the fact that we are doing a different manipulation to alpha, since it's already relative, to the other componets of the color. On the Kotlin side, it easier to spot the difference.

package org.virustrend.android.utils
import androidx.annotation.ColorInt
import androidx.core.graphics.alpha
import androidx.core.graphics.blue
import androidx.core.graphics.green
import androidx.core.graphics.red
import kotlin.math.pow
import kotlin.math.roundToInt
@ColorInt
fun Float.toColorBetween(start: Int, end: Int): Int =
colorOf(
alpha = componentBetween(start, end) { alpha.toRelative() }.toAbsolute(),
red = componentBetween(start, end) { red.toRelative().toLinear() }.toRGB().toAbsolute(),
green = componentBetween(start, end) { green.toRelative().toLinear() }.toRGB().toAbsolute(),
blue = componentBetween(start, end) { blue.toRelative().toLinear() }.toRGB().toAbsolute()
)
@ColorInt
fun colorOf(alpha: Int, red: Int, green: Int, blue: Int): Int =
(alpha shl 24) or (red shl 16) or (green shl 8) or blue
private fun Float.toLinear(): Float = pow(2.2f)
private fun Float.toRGB(): Float = pow(1.0f / 2.2f)
private fun Int.toRelative(): Float = (this.toFloat() / 255.0f)
private fun Float.toAbsolute(): Int = (this * 255f).roundToInt()
private inline fun Float.componentBetween(
from: Int,
to: Int,
block: Int.() -> Float
): Float = interpolate(from.block(), to.block())
private fun Float.interpolate(from: Float, to: Float): Float =
from + this * (to - from)
public Object evaluate(float fraction, Object startValue, Object endValue) {
int startInt = (Integer) startValue;
float startA = ((startInt >> 24) & 0xff) / 255.0f;
float startR = ((startInt >> 16) & 0xff) / 255.0f;
float startG = ((startInt >> 8) & 0xff) / 255.0f;
float startB = ( startInt & 0xff) / 255.0f;
int endInt = (Integer) endValue;
float endA = ((endInt >> 24) & 0xff) / 255.0f;
float endR = ((endInt >> 16) & 0xff) / 255.0f;
float endG = ((endInt >> 8) & 0xff) / 255.0f;
float endB = ( endInt & 0xff) / 255.0f;
// convert from sRGB to linear
startR = (float) Math.pow(startR, 2.2);
startG = (float) Math.pow(startG, 2.2);
startB = (float) Math.pow(startB, 2.2);
endR = (float) Math.pow(endR, 2.2);
endG = (float) Math.pow(endG, 2.2);
endB = (float) Math.pow(endB, 2.2);
// compute the interpolated color in linear space
float a = startA + fraction * (endA - startA);
float r = startR + fraction * (endR - startR);
float g = startG + fraction * (endG - startG);
float b = startB + fraction * (endB - startB);
// convert back to sRGB in the [0..255] range
a = a * 255.0f;
r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f;
g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f;
b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f;
return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment