package com.moez.QKSMS.common; import android.animation.TypeEvaluator; /** * Evaluator used for animating between colors in the CIE-LCh color space * <p> * For reading see www.stuartdenman.com/improved-color-blending */ public class CIELChEvaluator implements TypeEvaluator<Integer> { /** * Converting RGB to CIE-LCh is expensive, so since we're only going to be using * an instance of this evaluator to evaluate between two colors, we'll calculate * the CIE-LCh values during construction rather than during frames in the * animation */ private final ColorCIELCh mStartColor; private final ColorCIELCh mEndColor; private final int mStartInt; private final int mEndInt; public CIELChEvaluator(int startColor, int endColor) { mStartInt = startColor; mEndInt = endColor; mStartColor = convertRgbToCIELCH(startColor); mEndColor = convertRgbToCIELCH(endColor); } public Integer evaluate(float fraction) { return evaluate(fraction, 0, 0); } @Override public Integer evaluate(float fraction, Integer ignored, Integer ignored2) { if (mStartInt == mEndInt) { return mStartInt; } // CIELCH to CIELAB double L = mStartColor.L * (1 - fraction) + mEndColor.L * fraction; double C = mStartColor.C * (1 - fraction) + mEndColor.C * fraction; double H = mStartColor.H * (1 - fraction) + mEndColor.H * fraction; double a = Math.cos(Math.toRadians(H)) * C; double b = Math.sin(Math.toRadians(H)) * C; // CIELAB to XYZ double var_Y = (L + 16) / 116.0; double var_X = a / 500 + var_Y; double var_Z = var_Y - b / 200.0; var_Y = Math.pow(var_Y, 3) > 0.008856 ? Math.pow(var_Y, 3) : (var_Y - 16 / 116.0) / 7.787; var_X = Math.pow(var_X, 3) > 0.008856 ? Math.pow(var_X, 3) : (var_X - 16 / 116.0) / 7.787; var_Z = Math.pow(var_Z, 3) > 0.008856 ? Math.pow(var_Z, 3) : (var_Z - 16 / 116.0) / 7.787; double X = 95.047 * var_X; double Y = 100.000 * var_Y; double Z = 108.883 * var_Z; // XYZ TO RGB double var_X2 = X / 100.0; double var_Y2 = Y / 100.0; double var_Z2 = Z / 100.0; double var_R = var_X2 * 3.2406 + var_Y2 * -1.5372 + var_Z2 * -0.4986; double var_G = var_X2 * -0.9689 + var_Y2 * 1.8758 + var_Z2 * 0.0415; double var_B = var_X2 * 0.0557 + var_Y2 * -0.2040 + var_Z2 * 1.0570; var_R = var_R > 0.0031308 ? 1.055 * Math.pow(var_R, 1 / 2.4) - 0.055 : 12.92 * var_R; var_G = var_G > 0.0031308 ? 1.055 * Math.pow(var_G, 1 / 2.4) - 0.055 : 12.92 * var_G; var_B = var_B > 0.0031308 ? 1.055 * Math.pow(var_B, 1 / 2.4) - 0.055 : 12.92 * var_B; double R = var_R * 255; double G = var_G * 255; double B = var_B * 255; int red = (int) Math.round(R); int green = (int) Math.round(G); int blue = (int) Math.round(B); red = Math.min(255, Math.max(0, red)); green = Math.min(255, Math.max(0, green)); blue = Math.min(255, Math.max(0, blue)); return (0xff << 24) | (red << 16) | (green << 8) | (blue << 0); } private ColorCIELCh convertRgbToCIELCH(int rgb) { // RGB TO XYZ int r = 0xff & (rgb >> 16); int g = 0xff & (rgb >> 8); int b = 0xff & (rgb >> 0); double var_R = r / 255.0; double var_G = g / 255.0; double var_B = b / 255.0; var_R = var_R > 0.04045 ? Math.pow((var_R + 0.055) / 1.055, 2.4) : var_R / 12.92; var_G = var_G > 0.04045 ? Math.pow((var_G + 0.055) / 1.055, 2.4) : var_G / 12.92; var_B = var_B > 0.04045 ? Math.pow((var_B + 0.055) / 1.055, 2.4) : var_B / 12.92; var_R = var_R * 100; var_G = var_G * 100; var_B = var_B * 100; double X = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805; double Y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722; double Z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505; // XYZ TO CIELAB double var_X = X / 95.047; double var_Y = Y / 100.000; double var_Z = Z / 108.883; var_X = var_X > 0.008856 ? Math.pow(var_X, 1 / 3.0) : (7.787 * var_X) + (16 / 116.0); var_Y = var_Y > 0.008856 ? Math.pow(var_Y, 1 / 3.0) : (7.787 * var_Y) + (16 / 116.0); var_Z = var_Z > 0.008856 ? Math.pow(var_Z, 1 / 3.0) : (7.787 * var_Z) + (16 / 116.0); double CIELAB_L = (116 * var_Y) - 16; double CIELAB_A = 500 * (var_X - var_Y); double CIELAB_B = 200 * (var_Y - var_Z); // CIELAB TO CIELCH double var_H = Math.atan2(CIELAB_B, CIELAB_A); var_H = var_H > 0 ? (var_H / Math.PI) * 180.0 : 360 - Math.toDegrees(Math.abs(var_H)); double C = Math.hypot(CIELAB_A, CIELAB_B); double H = var_H; return new ColorCIELCh(CIELAB_L, C, H); } private static class ColorCIELCh { public final double L, C, H; public ColorCIELCh(double l, double C, double H) { L = l; this.C = C; this.H = H; } @Override public String toString() { return "{L:" + L + ", C:" + C + ", H:" + H + "}"; } } }