さて、アプリケーションに整数変数があります。これは色の値であり、私の好みでカラーピッカーによって設定されます。今、私はその色とそれが存在する可能性のある色の暗いバージョンの両方を使用する必要があります。
今では標準でJava Color.darker()メソッドがありますが、Androidには同等のものはないようです。同等または回避策を知っている人はいますか?
最も簡単な方法は、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]);
これは、0の代わりに1を原点として使用する、暗くするアプローチと完全に平行です。任意の色(黒を含む)を明るくするために機能し、クランプを必要としません。次のように簡略化できます。
hsv[2] = 0.2f + 0.8f * hsv[2];
ただし、浮動小数点演算の丸め効果が考えられるため、結果が1.0f(おそらく1ビット)を超える可能性があることを心配します。少し複雑な式に固執する方が良い。
これが私が作成したものです。
/**
* 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 ) );
}
色を明るくするという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);
}
Java暗くしたり明るくしたりするためのカラールーチンは、特別なものを必要としません。実際、関連する色にどの明るさが適用されているかを単純に理解するだけです。赤、緑、青の値を任意の係数で乗算し、色域に正しく収まるようにします。
以下は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の要因。 (これらは同一でなければなりません)
次に、Labを使用して暗くすることを提案した人のために、
インデックスカラー、Color.darker()、およびLab Darker()、すべて0.7のFACTORで。 (これは時間をかけて価値がある改善ですか?)