今回のお題は、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に置いておいた。