JavaFXでPixelReader/PixelWriterを使う

source image:

output:


source code:

import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
import javafx.scene.image.PixelFormat;
import javafx.scene.image.PixelWriter;
import javafx.stage.Stage;

public class PixelReadWriteTest extends Application {
    public void start(Stage primaryStage) {
        // 元画像
        Image image = new Image("http://d.hatena.ne.jp/images/renew/title.gif");
        
        // ImageをIntBufferに変換
        int[] pixels = this.convertImageToIntBuffer(image);
        int imageWidth = (int) image.getWidth();
        int imageHeight = (int) image.getHeight();
        
        // 白(0xffffffff)だったら黒(0xff000000)にする
        for (int y = 0; y < imageHeight; y++) {
            for (int x = 0; x < imageWidth; x++) {
                int coordinate = x + y * imageWidth;
                if (pixels[coordinate] == 0xffffffff) {
                    pixels[coordinate] = 0xff000000;
                }
            }
        }
        
        // IntBufferの内容をCanvasに描画する
        Canvas canvas = new Canvas(imageWidth, imageHeight);
        GraphicsContext gc = canvas.getGraphicsContext2D();
        PixelWriter pixelWriter = gc.getPixelWriter();
        pixelWriter.setPixels(0, 0, imageWidth, imageHeight, PixelFormat.getIntArgbInstance(), pixels, 0, imageWidth);
        
        Scene scene = new Scene(new Group(canvas), imageWidth, imageHeight);
        primaryStage.setTitle(this.getClass().getName());
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    /** ImageをIntBufferに変換 */
    private int[] convertImageToIntBuffer(Image image) {
        int imageWidth = (int) image.getWidth();
        int imageHeight = (int) image.getHeight();
        int[] intBuffer = new int[imageWidth * imageHeight];
        for (int y = 0; y < imageHeight; y++) {
            for (int x = 0; x < imageWidth; x++) {
                intBuffer[x + y * imageWidth] = image.getPixelReader().getArgb(x, y);
            }
        }
        return intBuffer;
    }
    
    public static void main(String[] args) {
        launch(args);
    }   
}
メモ
  • setPixels() を使わずに、二重のfor文 (xは0〜imageWidth、yは0〜imageHeight) を回して1ピクセルずつ setArgb(int x, int y, int argb) を使ったらクッソ遅かった。
  • setPixels() の引数がやたらと多い。
void setPixels(int x,
             int y,
             int w,
             int h,
             PixelFormat<java.nio.IntBuffer> pixelformat,
             int[] buffer,
             int offset,
             int scanlineStride)

……で、それぞれ、

Parameters:
    x - the X coordinate of the rectangular region to write
    y - the Y coordinate of the rectangular region to write
    w - the width of the rectangular region to write
    h - the height of the rectangular region to write
    pixelformat - the PixelFormat<IntBuffer> object defining the int format to read the pixels from buffer
    buffer - an int array to containing the pixel data to store
    offset - the offset into buffer to read the first pixel data
    scanlineStride - the distance between the pixel data for the start of one row of data in the buffer to the start of the next row of data

……ということらしいです。