文字列を適当な色に変換する。(Java)

文字列をハッシュ化して、
それをRGBコードに利用しようと思います。

ハッシュ化して、色生成するので同じ文字列は同じ色になります。

文字列から自動で色付けしてくれると、
自分で配色を考える必要がありません。

名前ごとに色を用意しないと行けない場合、
名前の数が多くても使えるので便利だと思います。

文字列をハッシュ化

色に必要なのは、RGBの3要素だけあればいいので、
4バイト長のハッシュを生成してくれる
CRC32 というアルゴリズムを使うのが手軽だと思います。

Javaならもちろん標準で利用できます。
import java.util.zip.CRC32;

/**
 * 指定文字列を、ハッシュ化したバイト配列を返す
 */
byte[] hashBytes(String s) {
    CRC32 crc32 = new CRC32();
    crc32.update(s.getBytes());

    return ByteBuffer.allocate(Integer.BYTES)
            .putInt((int) crc32.getValue())
            .array();
}

RGB要素取り出し

ハッシュ化したバイト配列からRGBに使う要素を取り出します。

1バイトが1要素になるので、1バイト余ってしまいますが
それぞれ1バイトずつ取得します。

// ハッシュ化したバイト配列をRGBとして使う
byte[] bytes = hashBytes(s);

int bgR = bytes[0] & 0xff;
int bgG = bytes[1] & 0xff;
int bgB = bytes[2] & 0xff;

String background = String.format("%02x%02x%02x", bgR, bgG, bgB);

Javaのbyte型は、unsignedなどはなく、-128~127の範囲のため、後の計算で扱いにくくなるため、
intに代入しています。

また、byte を intにキャストする場合、
単純にキャストすると符号拡張(上位ビットが符号ビットで埋められる)が発生するため、& 0xff というビット演算を使って、上位ビットがゼロ埋めされるようにしています。

この時点で色コードとして使うことができます。

背景/フォント色を用意する

これまでで、好きな文字列を色コードとして利用することができるようになりました。

それを背景色として使う場合、
フォント色を用意する必要があります。

単純に白や黒でもいいですが、背景色に合わせて見やすい色を選択したいので、反転色(色コードをビット反転したもの)を使うことにします。

反転色でもコントラスト差がない場合、とても見づらくなるので、その場合は、白か黒、見やすい方を採用することとします。

反転色をつくる

反転色は、ビット反転させるだけでできます。

int fcR = ~bgR & 0xff;
int fcG = ~bgG & 0xff;
int fcB = ~bgB & 0xff;

intのnot演算では、上位ビットも反転してしまうので、
必要な1バイト分にマスクして取得します。

明度差を計算する

中央値に近い色の場合は、反転色は変化が少なく見づらくなってしまいます。

また、明度が近いと明暗の差がなく、くっきりと見えなくなります。
明度差(0 - 255)が125未満だと見づらいそうです。

W3CにRGBから明度計算する式が書いてあります。

Color brightness is determined by the following formula:
((Red value X 299) + (Green value X 587) + (Blue value X 114)) / 1000
Note: This algorithm is taken from a formula for converting RGB values to YIQ values. This brightness value gives a perceived brightness for a color.

/**
 * 0 - 255 の明度を返す
 */
int brightness(int r, int g, int b) {
    return ((r * 299) + (g * 587) + (b * 114)) / 1000;
}

明度を求めたら、差が125未満かどうか判定し、
見づらい色であれば、白か黒にしています。

// 明るさ
int bgL = brightness(bgR, bgG, bgB);
int fcL = brightness(fcR, fcG, fcB);

// 背景とフォントの明度差が125未満なら、白または黒にする。(明度差が大きい方を採用)
if (Math.abs(bgL - fcL) < 125) {
    fcR = fcG = fcB = ((0xff - bgL) > bgL) ? 0xff : 0x00;
}

String color = String.format("%02x%02x%02x", fcR, fcG, fcB);

白の明度は、255 (0xff)
黒の明度は、0 (0x00) です。

↑の三項演算は、背景色に対して、白の明度差と黒の明度差がどちらが大きいか判定しています。
(0xff - bgL) > (bgL - 0x00)

サンプル

以下は適当な単語をつかって、背景色とフォント色を生成したものです。
文字色が白か黒になっている箇所は、明度差が少ないために白黒に調整されたということです。

コメントを残す