GraphicsUtil.getPixel()
は、任意の座標の色を取得するメソッドです。
GraphicsUtil.getPixel()
と GraphicsUtil.setPixel()
メソッドを組み合わせて使用することで、
Java アプリケーション上で複雑な画像の加工が実現できます。
GraphicsUtil.getPixel()
を使用する場合、以下の点で注意が必要です。
GraphicsUtil.setPixel() で描画した後に、同じ座標の色を GraphicsUtil.getPixel() で取得すると、 描画した色と取得した色の値が異なる場合があります。
これは、オフスクリーンに描画した時点で、描画色が減色されている場合があるためです。
たとえば、赤 5 ビット、青 5 ビット、緑 5 ビット の 15 ビットカラーで保持しているオフスクリーンの場合、
GraphicsUtil.setPixel(g, x, y, 0xffffff);
int color = GraphicsUtil.getPixel(g, x, y);
上記のコードを実行すると color
に 0xf8f8f8
が代入されているかもしれません。
減色された色を 0x00RRGGBB 形式で返す場合、一般的には不足したビットを 0 で補った値を返すと考えられますが、 実際にどのように変換されるかは機器に依存します。
GraphicsUtil.getPixel()
で取得した色は、減色されている場合があります。
そのため、GraphicsUtil.getPixel()
で取得した色を任意の色と比較する場合は、
あらかじめ有効なビットを調べておき、無効なビットをマスクして比較する必要があります。
オフスクリーンに描画した色が何ビットで保持されるかは、System.getProperty()
メソッドに
"jscl.system.display.colordepth"
を渡して呼び出すと取得できます。
たとえば、赤 5 ビット、青 5 ビット、緑 5 ビットの 15 ビットカラーで保持している機器の場合、
String str = System.getProperty("jscl.system.display.colordepth");
上記のコードを実行すると str には "555"
が代入されます。
GraphicsUtil.getPixel()
の使用例として、
閉領域フィルのサンプルコードを以下に示します。
このサンプルコードでは、
(GraphicsUtil.getPixel(g, x, y) & mask_pattern_) == color
の正否で閉領域の境界色を判定しています。
mask_pattern_
は
色の有効なビットを得るためのマスクパターンで、
System.getProperty("jscl.system.display.colordepth")
の値から作成しています。
color
は最初の seed_fill()
呼び出しでマスクしているので、「境界色 color
」と
「GraphicsUtil.getPixel()
で取得した色」の両方を有効なビットだけで比較していることになります。
このように有効なビットだけを比較することで、 オフスクリーンへ減色して描画を行う機器でも境界色を検出することができます。
private int mask_pattern_;
private byte[] work_ = null;
/**
* color を境界とした,座標(x, y) を含む閉領域を塗りつぶします.
*/
public void fill(Graphics g, int x, int y, int color) {
/* R, G, B の有効なビット数を取得します. */
int bit_depth_r = 0;
int bit_depth_g = 0;
int bit_depth_b = 0;
try {
String str = System.getProperty("jscl.system.display.colordepth");
int color_depth = Integer.parseInt(str);
if ((str.length() >= 3) && (color_depth >= 0) && (color_depth <= 999)) {
bit_depth_r = ((int) str.charAt(0)) - '0';
bit_depth_g = ((int) str.charAt(1)) - '0';
bit_depth_b = ((int) str.charAt(2)) - '0';
}
} catch (NumberFormatException e) {
e.printStackTrace();
}
/* 色を比較するときに使用するマスクパターンを作成します. */
mask_pattern_ = ((1 << bit_depth_r) - 1) << (24 - bit_depth_r)
| ((1 << bit_depth_g) - 1) << (16 - bit_depth_g)
| ((1 << bit_depth_b) - 1) << ( 8 - bit_depth_b);
/* 既に描画したピクセルを保持するワークエリアを初期化します. */
int work_size = (g.getClipWidth() * g.getClipHeight() + 7) / 8;
work_ = new byte[work_size];
for (int i = 0; i < work_size; i++) {
work_[i] = 0;
}
/* 指定座標から塗りつぶしを開始します.
* 境界色 color を mask_pattern_ でマスクしないと,
* GraphicsUtil.getPixel() で取得した色との比較で一致しなくなります。*/
seed_fill(g, x, y, color & mask_pattern_);
work_ = null;
}
/**
* 指定座標(x, y) が既に描画されたか否かを調べます.
*/
private boolean is_already_drawn(Graphics g, int x, int y) {
x -= g.getClipX();
y -= g.getClipY();
if ((x < 0) || (y < 0) || (x >= g.getClipWidth()) || (y >= g.getClipHeight())) {
return true; /* クリップ外は描画済みとする. */
}
int pixel_index = y * g.getClipWidth() + x;
int byte_index = pixel_index / 8;
int byte_offset = pixel_index % 8;
if ((work_[byte_index] & (1 << byte_offset)) != 0) {
return true; /* 既に描画済み. */
}
/* 今回描画するので描画済みにする. */
work_[byte_index] |= (1 << byte_offset);
return false;
}
/**
* 実際に塗りつぶしを行うメソッドです.
*/
private void seed_fill(Graphics g, int x, int y, int color) {
if ((is_already_drawn(g, x, y))
|| ((GraphicsUtil.getPixel(g, x, y) & mask_pattern_) == color)) {
/* 既に描画済みの座標,あるいは境界色の場合ここで戻る. */
return;
}
GraphicsUtil.setPixel(g, x, y);
/* 左方向へ塗りつぶす. */
int minx = x;
while (true) {
minx--;
if ((is_already_drawn(g, minx, y))
|| ((GraphicsUtil.getPixel(g, minx, y) & mask_pattern_) == color)) {
break;
}
GraphicsUtil.setPixel(g, minx, y);
}
minx++;
/* 右方向へ塗りつぶす. */
int maxx = x;
while (true) {
maxx++;
if ((is_already_drawn(g, maxx, y))
|| ((GraphicsUtil.getPixel(g, maxx, y) & mask_pattern_) == color)) {
break;
}
GraphicsUtil.setPixel(g, maxx, y);
}
/* 上と下のピクセルを塗りつぶす. */
for (; minx < maxx; minx++) {
seed_fill(g, minx, y - 1, color);
seed_fill(g, minx, y + 1, color);
}
}