2011-02-08 6 views
56

Bien, entonces tengo una variable entera en mi aplicación. Es el valor de un color, establecido por un selector de color en mis preferencias. Ahora, necesito usar tanto ese color como una versión más oscura de cualquier color que sea.¿Usando color y color.darker en Android?

Ahora sé que en Java estándar hay un método Color.darker(), pero no parece ser un equivalente en Android. ¿Alguien sabe de un equivalente o alguna solución alternativa?

Respuesta

191

La forma más fácil, creo, sería convertir al HSV, hacer el oscurecimiento allí, y convertir de nuevo:

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

para aligerar, un enfoque simple puede ser la de multiplicar el componente de valor por algo> 1.0 . Sin embargo, tendrá que fijar el resultado al rango [0.0, 1.0]. Además, simplemente multiplicar no va a aclarar negro.

Por lo tanto una mejor solución es: Reducir la diferencia de 1,0 del componente de valor para aligerar:

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

Esto es completamente paralela a la aproximación para el oscurecimiento, simplemente usando 1 como el origen en vez de 0. funciona para aclarar cualquier color (incluso negro) y no necesita ninguna sujeción. Se podría simplificarse a:

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

Sin embargo, debido a los posibles efectos de redondeo de la aritmética de punto flotante, estaría preocupado de que el resultado podría exceder 1.0f (tal vez por un bit). Es mejor seguir con la fórmula un poco más complicada.

+0

Me parece que este efecto es visualmente mucho más fuerte en algunos colores que otros. Por lo tanto, no fue muy útil para mí. –

+0

@ABoschman - Ese es un punto interesante. Puede obtener mejores resultados convirtiendo a [lab color space] (http://en.wikipedia.org/wiki/Lab_color_space) y haciendo el aclarado/oscurecimiento allí aplicando la técnica de mi respuesta al componente de luminosidad. Desafortunadamente, las conversiones entre RGB y el espacio de color del laboratorio no son tan simples (como se discute en el artículo) y tendrá que pasar su propio código de conversión. La ventaja del espacio de color de laboratorio es que (como se discute [aquí] (http://en.wikipedia.org/wiki/Color_difference)) modela más de cerca la percepción del color humano que otros espacios. –

+0

@TedHopp ¿No tendría sentido alterar HSL en lugar de HSV? Uno puede usar android.support.v4.graphics.ColorUtils # RGBToHSL(). –

30

Esto es lo que he creado:

/** 
* 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)); 
} 
+2

con este enfoque, si desea que el color a ser un 10% más oscuro, entonces tendrá que pasar en (1 - Factor) => (1 - 0.1f) => 0.9f –

17

respuesta de Ted para aligerar un color no funcionaba para mí, así que aquí hay una solución que podría ayudar a alguien más:

/** 
* 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

estoy curioso cómo mi sugerencia para aclarar un color era su defecto para usted. ¿Puedes dar detalles? –

+1

Esta es la única respuesta que funcionó para mí. Gracias. – Leebeedev

+0

Funciona perfectamente. –

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

¿Qué es CircleView.shiftColorDown (color)? –

4

La rutina Java Color para oscurecer y aclarar no requiere nada especial. De hecho, es simplemente la comprensión desenrollada de qué brillo se aplica a los colores relevantes. Lo que quiere decir que simplemente puede tomar los valores rojo, verde, azul. Multiplíquelos por cualquier factor, asegúrese de que caigan correctamente en la gama.

El siguiente es el código que se encuentra en la clase 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()); 
} 

Obviamente, a partir de esto podemos ver cómo se hace este proceso en androide. Tome los valores RGB, multiplíquelos por un factor y críjelos en la gama. (Recodificado desde cero por razones de licencia).

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))); 
    } 

hacer la nota que esto es lo mismo que simplemente aumentando el brillo en HSB, el B es simplemente el factor más brillante, el tono es la relación entre los diversos colores, y S es lo lejos que están. Entonces, si simplemente tomamos todos los colores y los multiplicamos por un factor, terminamos con los mismos colores en la misma mezcla con un poco más de blanco/negro en ellos.

Muchos espacios de colores modernos también hacen esto al calcular el valor Y a través de los diversos componentes de color que mejor se aproximan al brillo.Por lo que podría si quisiera convertir a una mejor forma de Y o L a través de cualquiera de los espacios de colores modernos y unconvert ellos, otros espacios de color tienen una mejor forma de gamma con respecto a la cantidad de cada color contribuye a la luminosidad real, ligereza, el valor, blancura, negrura, o lo que sea que el espacio de color lo llame. Esto haría un mejor trabajo, pero para la mayoría de los propósitos, esto es sólido.

Así, en el extremo más-usted puede hacer esto mediante la conversión de laboratorio, disminuyendo el componente L y convertir de nuevo.

Aquí está el código para hacer eso:

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; 
} 

Mis par de líneas allí hacen lo mismo que Color.darken() aquí está una imagen de un conjunto color de la muestra (estos colores son distancia máxima de todo colores anteriores a través de CIE-LabD2000, solo utilizándolos como un sólido conjunto de muestras de color.)

Color de índice, Color.darker(), y mi oscurecimiento básico(), todo a un FACTOR de 0.7. index vs. darker vs. darken (estos deben ser idénticos)

Siguiente para aquellos que sugirió el uso de laboratorio para oscurecer,

Colour Index, Color.darker(), y el laboratorio más oscuro(), todo ello a un factor de 0,7. enter image description here (¿es esta una mejora que vale la pena el tiempo de succión?)

Cuestiones relacionadas