D'accord, j'ai donc une variable entière dans mon application. C'est la valeur d'une couleur, définie par un sélecteur de couleur dans mes préférences. Maintenant, je dois utiliser à la fois cette couleur et une version plus foncée de n'importe quelle couleur.
Maintenant, je sais que dans la norme Java il y a une méthode Color.darker (), mais il ne semble pas y avoir d'équivalent dans Android. Quelqu'un connaît-il un équivalent ou des solutions de contournement?
Le plus simple, je pense, serait de convertir en HSV, de faire le noircissement là-bas et de reconvertir:
float[] hsv = new float[3];
int color = getColor();
Color.colorToHSV(color, hsv);
hsv[2] *= 0.8f; // value component
color = Color.HSVToColor(hsv);
Pour alléger, une approche simple peut être de multiplier la composante valeur par quelque chose> 1,0. Cependant, vous devrez restreindre le résultat à la plage [0,0, 1,0]. De plus, la simple multiplication ne va pas éclaircir le noir.
Par conséquent, une meilleure solution consiste à: Réduire la différence de 1,0 du composant de valeur pour alléger:
hsv[2] = 1.0f - 0.8f * (1.0f - hsv[2]);
Ceci est entièrement parallèle à l'approche pour l'assombrissement, en utilisant simplement 1 comme origine au lieu de 0. Il fonctionne pour éclaircir n'importe quelle couleur (même le noir) et n'a pas besoin de serrage. Il pourrait être simplifié pour:
hsv[2] = 0.2f + 0.8f * hsv[2];
Cependant, en raison des effets d'arrondi possibles de l'arithmétique à virgule flottante, je crains que le résultat ne dépasse 1,0 f (peut-être un bit). Mieux vaut s'en tenir à la formule légèrement plus compliquée.
Voici ce que j'ai créé:
/**
* 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 ) );
}
La réponse de Ted pour éclaircir une couleur ne fonctionnait pas pour moi, alors voici une solution qui pourrait aider quelqu'un d'autre:
/**
* 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);
}
La routine Java Color pour assombrir et éclaircir ne nécessite rien de spécial. En fait, c'est simplement la compréhension non contrôlée de la luminosité appliquée aux couleurs pertinentes. Autrement dit, vous pouvez simplement prendre les valeurs rouge, vert, bleu. Multipliez-les par n'importe quel facteur, assurez-vous qu'elles tombent correctement dans la gamme.
Voici le code trouvé dans la classe 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());
}
De toute évidence, à partir de cela, nous pouvons voir comment faire ce processus dans Android. Prenez les valeurs RVB, multipliez-les par un facteur et écrasez-les dans la gamme. (Recodé à partir de zéro pour des raisons de licence).
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)));
}
Notez que cela revient à augmenter la luminosité dans HSB, le B est simplement le facteur le plus lumineux, la teinte est le rapport entre les différentes couleurs et S est la distance qui les sépare. Donc, si nous prenons juste toutes les couleurs et les multiplions par un facteur, nous nous retrouvons avec les mêmes couleurs dans le même mélange avec un peu plus de blanc/noir.
De nombreux espaces de couleurs modernes le font également en calculant la valeur Y à travers les différentes composantes de couleur qui correspondent le mieux à la luminosité. Donc, vous pourriez si vous vouliez convertir une meilleure forme de Y ou de L à travers l'un des espaces de couleurs modernes et les annuler, d'autres espaces de couleurs ont une meilleure forme de gamma en ce qui concerne la contribution de chaque couleur à la luminosité, la légèreté, la valeur réelles, blancheur, noirceur, ou tout ce que cet espace colorimétrique l'appelle. Cela ferait un meilleur travail, mais dans la plupart des cas, c'est solide.
Donc, à l'extrême, vous pouvez le faire en convertissant en Lab, en diminuant le composant L et en le reconvertissant.
Voici le code pour le faire:
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;
}
Mes quelques lignes font la même chose que Color.darken (), voici une image d'un jeu de couleurs échantillon (ces couleurs sont la distance maximale de toutes les couleurs précédentes via CIE-LabD2000, en les utilisant simplement comme un jeu d'échantillons de couleurs robustes.)
Index Color, Color.darker () et my basic darken (), le tout à un facteur de 0,7. (ceux-ci doivent être identiques)
Ensuite, pour ceux qui ont suggéré d'utiliser Lab pour assombrir,
Index Color, Color.darker () et Lab Darker (), le tout avec un FACTEUR de 0,7. (est-ce une amélioration qui vaut le coup?)