arc の日記

はてなダイアリーから引っ越してきました。さらに新しい記事は https://junkato.jp/ja/blog/ で書いています。

ピクセルごとの色を直接指定してウィンドウ表示するための基底クラスAbstractRawFrame

AbstractFrameの記事で、Javaで単なるウィンドウを表示する方法を書きましたが、今回はその画面上でピクセルごとの色値を直接指定してみます。これは、Processing畑の人向けに一言でいうと、loadPixels();→画素配列の操作→updatePixels();をJavaでやってみる簡単な例になっています。

他に、同じようなことをしている応用例がBufferedImageを画素加工処理付きで高速描画 - kaisehのブログにありました。

AbstractRawFrame

まず、AbstractFrameを継承した抽象クラスを定義します。

import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;


/** 画素値を直接操作して描画するウィンドウ */
public abstract class AbstractRawFrame extends AbstractFrame {
	private BufferedImage image;
	private int[] data;

	@Override
	public void initialize(int width, int height) {
		image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
		data = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
		super.initialize(width, height);
	}

	@Override
	public void paint2D(Graphics2D g) {
		updatePixels(data);
		g.drawImage(image, 0, 0, null);
	}

	/** 画素配列に対する操作 */
	public abstract void updatePixels(int[] pixels);

}

int型でデータを保持するBufferedImageを作ったあと、その実データを表すint型配列をメンバ変数dataに取っておきます。paint2D(Graphics2D g)は、抽象メソッドupdatePixels(int pixels)でピクセル値を指定する実データを更新したのち、画像を表示します。

子クラスは、抽象メソッドupdatePixels(int pixels)を実装し、画素値を表すint型配列pixelsを受け取って適切に操作することが求められます。

砂嵐みたいな何か

使用例; 各ピクセル値をランダムに0xff000000(黒)か0xffffffff(白)に設定。

import javax.swing.SwingUtilities;


public class SampleRawPaint extends AbstractRawFrame {

	public static void main(String[] args) {

		// Swingがらみの処理はEvent Dispatch Thread上でやる
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				new SampleRawPaint();
			}
		});

	}

	public SampleRawPaint() {
		initialize(640, 480);
	}

	@Override
	public void updatePixels(int[] pixels) {
		int offset = 0;
		for (int y = 0; y < 480; y ++) {
			for (int x = 0; x < 640; x ++) {
				pixels[offset ++] = Math.random() >= 0.5 ?
						0xff000000 : 0xffffffff;
			}
		}
	}

}

こんな砂嵐が見えるはずです。

一定時間ごとに表示を更新したいなら前の記事みたくnew Timer(1000, this).start();すればいいんですが、実際にやってみたら目がちかちかしたのでとりあえず一枚絵を表示するだけにしました。

今回の例では色値をARGBで指定しており、ARGBの最上位8ビットは透明度を表すので、色値は必ず0xffで始まる32ビットになります。例えば色値に0x000000を与えた場合、黒色を期待してしまいますが、最上位8ビットが0なので完全な透明扱いとなり、何も描画されません。