はじめに
無料個人非商用版があるRustIDE「RustRover」が正式リリースされたついでに、以前から触ってみたかったRustを学習していきたいと思います。
なんとなくですが、学習用にRustとWebAssembly使ってAsciiアートGenerator的なのを作成しようと思います。
RustRoverについて
プロジェクトの作成
wasm-packのテンプレートを作成。
buildのtargetをwebにする。
wasm-packのbuildのtargetをwebにする理由(Gemini1.5proに聞いてみた)
target web を指定すると、以下の様な Web ブラウザ向けの最適化が行われます。
・JavaScript のモジュール形式: Web ブラウザで読み込めるように、ES Modules 形式の JavaScript ファイルが出力されます。
・Web API との連携: web-sys クレートなどを利用して、DOM 操作や fetch API などの Web API を利用するためのコードを含めることができます。
・サイズ最適化: Web ブラウザでのダウンロードを高速化するために、コードサイズが小さく保たれます。
wasmでimageのdataを受け取り、解析して、文字列を返すような関数を作成。
lib.rs
// 省略
#[wasm_bindgen]
pub fn process_image(image_data: &[u8]) -> String {
// 画像データからImageBufferを作成
let mut img = ImageReader::new(std::io::Cursor::new(image_data))
.with_guessed_format()
.unwrap()
.decode()
.unwrap()
.to_rgba8();
// セルのサイズ
let cell_size = 16;
let mut result = String::new();
// セルごとに処理
for y in 0..img.height() / cell_size {
for x in 0..img.width() / cell_size {
// セルを切り出す
let cell = img.sub_image(
x * cell_size,
y * cell_size,
cell_size,
cell_size,
);
// セルを解析
let recognized_char = analyze_cell(cell.to_image());
result.push(recognized_char);
result.push(recognized_char);
}
result.push('\n');
}
result
}
fn analyze_cell(cell: ImageBuffer<Rgba<u8>, Vec<u8>>) -> char {
// 黒ピクセルの数をカウント
let black_pixels = cell.pixels().filter(|p| p[0] == 0 && p[1] == 0 && p[2] == 0 && p[3] != 0).count();
// 黒ピクセル数に応じて文字を判定
match black_pixels {
0..=50 => '-',
_ => '@',
}
}
//略
要所のコード説明(Gemini1.5proに聞いてみた)
・ wasm_bindgen は、RustのコードをWebAssemblyにコンパイルし、JavaScriptから呼び出すことを可能にするためのツールです。
・ std::io::Cursor を使用することで、バイト列を ImageReader が読み込める形式に変換しています。
・ .with_guessed_format() 画像のフォーマットを自動判別します。
・ sub_image メソッドを使って、現在のセルに対応する部分画像を切り出します。
・ p[0] == 0 && p[1] == 0 && p[2] == 0 && p[3] != 0 赤、緑、青成分がすべて0(黒)で、アルファ成分が0以外(透明ではない)のピクセル数をカウントします。
・ 0..=50 => '-', 黒ピクセル数が50以下の場合は、 - を返します。
・ _ => '@', それ以外の場合は、 @ を返します。
生成された成果物のwasmを使い、
キャンバスに書いた絵や文字が出力されるような処理を実装
(お絵描き処理はFabric.jsで実装)
asciiart.js
import init, { process_image } from './rust_asciiart.js';
//略
async function run() {
await init();
document.getElementById("GeneBtn").addEventListener("click", (event) => {
canvasToUint8Array()
.then((uint8Array) => {
console.log(uint8Array);
const result = process_image(uint8Array);
document.getElementById("AsciiArea").innerText = result;
})
.catch((error) => {
console.error('Error converting canvas to Uint8Array:', error);
});
});
}
run();
//略
今回の成果物
デモURL
デモ画像
ソース
まとめ
Rust自体触ったことがなかったがこれを機に学習していきたい。📚
RustRoverははじめてでも使いやすかったので今後も使い倒していきます。💪
今回、Gemini1.5proを利用しながらプログラミングしましたが、改めてAI強いなと思いました。🤖
※間違い等ありましたら、ご指摘いただけると助かります。