Использование цвета и color.darker в Android?

Хорошо, поэтому у меня есть целочисленная переменная в моем приложении. Это значение цвета, заданное цветовым подборщиком в моих предпочтениях. Теперь мне нужно использовать как этот цвет, так и более темную версию любого цвета, который может быть.

Теперь я знаю, что в стандартной Java существует метод Color.darker(), но, похоже, в Android нет эквивалента. Кто-нибудь знает об эквиваленте или обходных решениях?

Ответ 1

Простейшим, я думаю, было бы преобразование в HSV, потемнение там и преобразование назад:

float[] hsv = new float[3];
int color = getColor();
Color.colorToHSV(color, hsv);
hsv[2] *= 0.8f; // value component
color = Color.HSVToColor(hsv);

Чтобы облегчить, простым подходом может быть умножение компонента значения на что-то > 1.0. Однако вам нужно будет закрепить результат в диапазоне [0.0, 1.0]. Кроме того, простое умножение не будет осветлять черный.

Поэтому лучшее решение: уменьшить разницу от 1.0 компонента значения, чтобы облегчить:

hsv[2] = 1.0f - 0.8f * (1.0f - hsv[2]);

Это полностью параллельна подходу для потемнения, просто используя 1 как источник вместо 0. Он работает, чтобы осветлить любой цвет (даже черный) и не требует никакого зажима. Это можно упростить:

hsv[2] = 0.2f + 0.8f * hsv[2];

Однако из-за возможных эффектов округления арифметики с плавающей запятой я был бы обеспокоен тем, что результат может превысить 1.0f (возможно, один бит). Лучше придерживаться немного более сложной формулы.

Ответ 2

Вот что я создал:

/**
 * Returns darker version of specified <code>color</code>.
 */
public static int darker (int color, float factor) {
    int a = Color.alpha( color );
    int r = Color.red( color );
    int g = Color.green( color );
    int b = Color.blue( color );

    return Color.argb( a,
            Math.max( (int)(r * factor), 0 ),
            Math.max( (int)(g * factor), 0 ),
            Math.max( (int)(b * factor), 0 ) );
}

Ответ 3

Ответ Ted для облегчения цвета не работал у меня, поэтому вот решение, которое может помочь кому-то другому:

/**
 * Lightens a color by a given factor.
 * 
 * @param color
 *            The color to lighten
 * @param factor
 *            The factor to lighten the color. 0 will make the color unchanged. 1 will make the
 *            color white.
 * @return lighter version of the specified color.
 */
public static int lighter(int color, float factor) {
    int red = (int) ((Color.red(color) * (1 - factor) / 255 + factor) * 255);
    int green = (int) ((Color.green(color) * (1 - factor) / 255 + factor) * 255);
    int blue = (int) ((Color.blue(color) * (1 - factor) / 255 + factor) * 255);
    return Color.argb(Color.alpha(color), red, green, blue);
}

Ответ 4

Процедура Java Color для darken и brighten не требует ничего особенного. Фактически, это просто развернутое понимание того, какая яркость применяется к соответствующим цветам. То есть вы можете просто взять красные, зеленые, синие значения. Умножьте их на любой коэффициент, убедитесь, что они правильно попали в гамму.

Ниже приведен код, найденный в классе Color.

private static final double FACTOR = 0.7;

//...

public Color darker() {
    return new Color(Math.max((int)(getRed()  *FACTOR), 0),
                     Math.max((int)(getGreen()*FACTOR), 0),
                     Math.max((int)(getBlue() *FACTOR), 0),
                     getAlpha());
}

Очевидно, что из этого мы можем увидеть, как это сделать в android. Возьмите значения RGB, умножьте их на коэффициент и обмотайте их в гамму. (Перекодировано с нуля по причинам лицензирования).

public int crimp(int c) {
        return Math.min(Math.max(c, 0), 255);
    }

public int darken(int color) {
        double factor = 0.7;
        return (color & 0xFF000000) | 
                (crimp((int) (((color >> 16) & 0xFF) * factor)) << 16) |
                (crimp((int) (((color >> 8) & 0xFF) * factor)) << 8) |
                (crimp((int) (((color) & 0xFF) * factor)));
    }

Обратите внимание, что это то же самое, что только увеличение яркости в HSB, B - просто самый яркий фактор, оттенок - это соотношение между различными цветами, а S - насколько они далеко друг от друга. Поэтому, если мы просто возьмем все цвета и умножим их на коэффициент, мы получим те же цвета в одном и том же миксе с немного более белым/черным в них.

Многие современные цветовые пространства также делают это с вычислением значения Y через различные цветовые компоненты, которые наилучшим образом приближают яркость. Таким образом, вы могли бы, если бы хотели преобразовать в лучшую форму Y или L через любой из современных цветовых пространств и преобразовать их, другие цветовые пространства имеют лучшую форму гамма в отношении того, насколько каждый цвет способствует реальной яркости, легкости, белизну, черноту или что-то другое, что цветовое пространство вызывает это. Это сделало бы лучшую работу, но для большинства целей это твердое.

Итак, в крайнем случае вы можете сделать это, перейдя в Lab, уменьшив L-компонент и преобразовывая его.

Здесь код для этого:

static int darken(int color) {
    double factor = 0.7;
    double[] returnarray = new double[3];
    convertRGBsRGB(returnarray, ((color >> 16) & 0xFF), ((color >> 8) & 0xFF), (color & 0xFF));
    convertRGBXYZ(returnarray,returnarray[0], returnarray[1], returnarray[2]);
    convertXYZLab(returnarray,returnarray[0], returnarray[1], returnarray[2]);
    returnarray[0] *= factor;
    convertLabXYZ(returnarray,returnarray[0], returnarray[1], returnarray[2]);
    convertXYZRGB(returnarray,returnarray[0], returnarray[1], returnarray[2]);
    return (color & 0xFF000000) | convertsRGBRGB(returnarray);
}
static void convertRGBsRGB(double[] returnarray, int R, int G, int B) {
    double var_R = (((double) R) / 255.0d);                     //RGB from 0 to 255
    double var_G = (((double) G) / 255.0d);
    double var_B = (((double) B) / 255.0d);
    returnarray[0] = var_R;
    returnarray[1] = var_G;
    returnarray[2] = var_B;
}
static int convertsRGBRGB(double[] sRGB) {
    int red = (int) (sRGB[0] * 255);
    int green = (int) (sRGB[1] * 255);
    int blue = (int) (sRGB[2] * 255);
    red = crimp(red);
    green = crimp(green);
    blue = crimp(blue);
    return (red << 16) | (green << 8) | blue;
}
public static int crimp(int v) {
    if (v > 0xff) {
        v = 0xff;
    }
    if (v < 0) {
        v = 0;
    }
    return v;
}

public static final double ref_X = 95.047; //ref_X =  95.047   Observer= 2°, Illuminant= D65
public static final double ref_Y = 100.000; //ref_Y = 100.000
public static final double ref_Z = 108.883;//ref_Z = 108.883
static void convertRGBXYZ(double[] returnarray, double var_R, double var_G, double var_B) {

    if (var_R > 0.04045) {
        var_R = Math.pow(((var_R + 0.055) / 1.055), 2.4);

    } else {
        var_R = var_R / 12.92;

    }
    if (var_G > 0.04045) {
        var_G = Math.pow(((var_G + 0.055) / 1.055), 2.4);

    } else {
        var_G = var_G / 12.92;

    }
    if (var_B > 0.04045) {
        var_B = Math.pow(((var_B + 0.055) / 1.055), 2.4);

    } else {
        var_B = var_B / 12.92;
    }
    var_R = var_R * 100;
    var_G = var_G * 100;
    var_B = var_B * 100; //Observer. = 2°, Illuminant = D65
    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;

    returnarray[0] = X;
    returnarray[1] = Y;
    returnarray[2] = Z;
}

static void convertXYZLab(double[] returnarray, double X, double Y, double Z) {
    double var_X = X / ref_X;
    double var_Y = Y / ref_Y;
    double var_Z = Z / ref_Z;

    if (var_X > 0.008856) {
        var_X = Math.cbrt(var_X);

    } else {
        var_X = (7.787 * var_X) + (16.0d / 116.0d);

    }
    if (var_Y > 0.008856) {
        var_Y = Math.cbrt(var_Y);

    } else {
        var_Y = (7.787 * var_Y) + (16.0d / 116.0d);

    }
    if (var_Z > 0.008856) {
        var_Z = Math.cbrt(var_Z);

    } else {
        var_Z = (7.787 * var_Z) + (16.0d / 116.0d);
    }
    double CIE_L = (116 * var_Y) - 16;
    double CIE_a = 500 * (var_X - var_Y);
    double CIE_b = 200 * (var_Y - var_Z);
    returnarray[0] = CIE_L;
    returnarray[1] = CIE_a;
    returnarray[2] = CIE_b;
}

static void convertLabXYZ(double[] returnarray, double CIE_L, double CIE_a, double CIE_b) {
    double var_Y = (CIE_L + 16) / 116;
    double var_X = CIE_a / 500 + var_Y;
    double var_Z = var_Y - CIE_b / 200;

    if ((var_Y * var_Y * var_Y) > 0.008856) {
        var_Y = (var_Y * var_Y * var_Y);

    } else {
        var_Y = (((var_Y - 16) / 116)) / 7.787;
    }
    if ((var_X * var_X * var_X) > 0.008856) {
        var_X = (var_X * var_X * var_X);
    } else {
        var_X = ((var_X - 16) / 116) / 7.787;

    }
    if ((var_Z * var_Z * var_Z) > 0.008856) {
        var_Z = (var_Z * var_Z * var_Z);
    } else {
        var_Z = ((var_Z - 16) / 116) / 7.787;
    }

    double X = ref_X * var_X; //ref_X =  95.047     Observer= 2°, Illuminant= D65
    double Y = ref_Y * var_Y; //ref_Y = 100.000
    double Z = ref_Z * var_Z; //ref_Z = 108.883
    returnarray[0] = X;
    returnarray[1] = Y;
    returnarray[2] = Z;
}

static void convertXYZRGB(double[] returnarray, double X, double Y, double Z) {
    double var_X = X / 100; //X from 0 to  95.047      (Observer = 2°, Illuminant = D65)
    double var_Y = Y / 100; //Y from 0 to 100.000
    double var_Z = Z / 100; //Z from 0 to 108.883

    double var_R = (var_X * 3.2406) + (var_Y * -1.5372) + (var_Z * -0.4986);
    double var_G = (var_X * -0.9689) + (var_Y * 1.8758) + (var_Z * 0.0415);
    double var_B = (var_X * 0.0557) + (var_Y * -0.2040) + (var_Z * 1.0570);

    if (var_R > 0.0031308) {
        var_R = 1.055 * (Math.pow(var_R, (1f / 2.4f))) - 0.055;
    } else {
        var_R = 12.92 * var_R;
    }
    if (var_G > 0.0031308) {
        var_G = 1.055 * (Math.pow(var_G, (1f / 2.4f))) - 0.055;

    } else {
        var_G = 12.92 * var_G;

    }
    if (var_B > 0.0031308) {
        var_B = 1.055 * (Math.pow(var_B, (1f / 2.4f))) - 0.055;

    } else {
        var_B = 12.92 * var_B;

    }
    returnarray[0] = var_R;
    returnarray[1] = var_G;
    returnarray[2] = var_B;
}

В моих парах строки есть то же, что и Color.darken(), изображение образцового набора цветов (эти цвета являются максимальным расстоянием от всех предыдущих цветов через CIE-LabD2000, просто используя их в качестве надежных наборов цветов. )

Цвет указателя, Color.darker() и мой основной darken(), все в ФАКТОР 0.7. index vs. darker vs. darken (они должны быть идентичными)

Далее для тех, кто предложил использовать Lab для затемнения,

Цвет указателя, Color.darker() и Lab Darker(), все в ФАКТОРЕ 0,7. введите описание изображения здесь (это улучшение стоит времени сосать?)

Ответ 5

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        getWindow().setStatusBarColor(CircleView.shiftColorDown(color));
    }// color is 'int' value