Home About Contact
Rust , WebAssembly

Rust + Wasm でキャンバスに画像を描画

Pikachu and Triangle

Rust + Wasm で三角形をキャンバスに描画からの続きです。 簡単なコードメモなのであしからず。 詳細は、この本 「RustとWebAssemblyによるゲーム開発」を読もう。 (ステマとかではない。)

この Rust + Wasm で三角形をキャンバスに描画で 書いたコードに続けて記述していきます。

まず、 このピカチューらしき画像を描画するので、プロジェクトディレクトリに pikachu.png というファイル名でコピーしておきます。

Pikachu

現在のプロジェクト構造はこれです:

.
├── Cargo.toml
├── index.html
├── pikachu.png
└── src
    └── lib.rs

src/lib.rs に pikachu.png をキャンバスに描画するコードを追記:

    wasm_bindgen_futures::spawn_local(async move {
        let (success_tx, success_rx) = futures::channel::oneshot::channel::<()>();
        let image = web_sys::HtmlImageElement::new().unwrap();
        let callback = Closure::once(move || {
            success_tx.send(());
        });
    
        image.set_onload(Some(callback.as_ref().unchecked_ref()));
        image.set_src("pikachu.png");

        success_rx.await;
        ctx.draw_image_with_html_image_element(&image, 0.0, 0.0);
    });

async があるからわかるとおり非同期を使わないと画像をロードして表示できない。 詳しくは「RustとWebAssemblyによるゲーム開発」を読もう。

コード全体では次のようになります:

use wasm_bindgen::prelude::*;

#[wasm_bindgen(start)]
pub fn main_js() -> Result<(), JsValue> {
    let window   = web_sys::window().unwrap();
    let document = window.document().unwrap();
    let canvas   = document.get_element_by_id("canvas").unwrap().dyn_into::<web_sys::HtmlCanvasElement>().unwrap();
    let ctx      = canvas.get_context("2d").unwrap().unwrap().dyn_into::<web_sys::CanvasRenderingContext2d>().unwrap();

    let fill_color_str = format!("rgb({}, {}, {})", 238, 232, 213);
    let stroke_color_str = format!("rgb({}, {}, {})", 203, 75, 22);

    ctx.set_fill_style_str(&fill_color_str);
    ctx.set_stroke_style_str(&stroke_color_str);
    ctx.set_line_width(2.0);

    ctx.move_to(150.0, 0.0);
    ctx.begin_path();
    ctx.line_to(0.0, 300.0);
    ctx.line_to(300.0, 300.0);
    ctx.line_to(150.0, 0.0);
    ctx.close_path();
    ctx.stroke();
    ctx.fill();

    wasm_bindgen_futures::spawn_local(async move {
        let (success_tx, success_rx) = futures::channel::oneshot::channel::<()>();
        let image = web_sys::HtmlImageElement::new().unwrap();
        let callback = Closure::once(move || {
            success_tx.send(());
        });
    
        image.set_onload(Some(callback.as_ref().unchecked_ref()));
        image.set_src("pikachu.png");

        success_rx.await;
        ctx.draw_image_with_html_image_element(&image, 0.0, 0.0);
    });

    Ok(())
}

三角形を描画したコードのあとに画像をロードしてキャンバスに描画するコードを追加しました。

早速ビルドしてみる:

$ wasm-pack build --target web

いろいろエラーが出る。まず簡単なところから解決する:

could not find `HtmlImageElement` in `web_sys`

と言われるので Cargo.toml の該当個所に HtmlImageElement を追加:

[dependencies.web-sys]
version = "0.3.55"
features = ["Window", "Document", "HtmlCanvasElement", "CanvasRenderingContext2d", "HtmlImageElement"]

再びビルド wasm-pack build --target web 今度は:

failed to resolve: use of unresolved module or unlinked crate `futures`

futures がない、と言われた。

Cargo.toml に追記:

[dependencies]
wasm-bindgen = "0.2"
futures = "0.3.17"
wasm-bindgen-futures = "0.4.28"

dependencies セクションに futures, wasm-bindgen-futures を追加しました。

これでビルド wasm-pack build --target web すると警告がいくつか出ましたがビルドは成功しました。

web サーバーを起動して意図通りできたか確認します。

$ python3 -m http.server 8000

Pikachu and Triangle

できました。