るいもの戯れ言

今回のお題は、2値(白黒)+alpha(透過付き)のgifファイルを読み込み、その中の黒を赤として描画したいというもの。要は「選択状態」の描画を赤色でしたい。だけど元の画像ファイルは2値(白黒)だよ。という状況。

Pixbufは、2値の画像読み込んだ場合でも、中ではRGBAそれぞれに8bitを割り当てているようだ。なので、ピクセル操作は思ったより簡単だった。


fn to_red(pixbuf: &Pixbuf) {
    assert!(
        pixbuf.get_colorspace() == gdk_pixbuf_sys::GDK_COLORSPACE_RGB,
        "Unsupported color space: {}", pixbuf.get_colorspace()
    );
    assert!(pixbuf.get_has_alpha(), "This image does not have alpha channel");
    let n_channels = pixbuf.get_n_channels();
    let w = pixbuf.get_width();
    let h = pixbuf.get_height();
    let rowstride = pixbuf.get_rowstride();
    let mut buf: &mut [u8] = unsafe {
        pixbuf.get_pixels()
    };

    for y in 0..h {
        for x in 0..w {
            let offset = (y * rowstride + x * n_channels) as usize;
            let r = buf[offset];
            let g = buf[offset + 1];
            let b = buf[offset + 2];
            let a = buf[offset + 3];
            if r == 0 && g == 0 && b == 0 {
                buf[offset] = 255;
            }
        }
    }
}

一応colorspaceと、alpha付きであることをチェック。


    assert!(
        pixbuf.get_colorspace() == gdk_pixbuf_sys::GDK_COLORSPACE_RGB,
        "Unsupported color space: {}", pixbuf.get_colorspace()
    );
    assert!(pixbuf.get_has_alpha(), "This image does not have alpha channel");

あと、注意としてパディングされている可能性があるので、rowstrideにyを掛けてやる必要があるようだ。get_pixelsは、&mut [u8]が返ってくるので、大胆にもそのまま中をいじれてしまう。RGB全てが0(黒)なら、Rだけ255にしてやる。


    for y in 0..h {
        for x in 0..w {
            let offset = (y * rowstride + x * n_channels) as usize;
            let r = buf[offset];
            let g = buf[offset + 1];
            let b = buf[offset + 2];
            let a = buf[offset + 3];
            if r == 0 && g == 0 && b == 0 {
                buf[offset] = 255;
            }
        }
    }

コードは、GuiHubに置いておいた。