RustとWebAssembly使って電光掲示板ツール作ってみた



# はじめに

前回ドット絵作成ツールを作った際に、文字から画像を生成するにはどうすればよいのか気になったので、

今回は文字から画像を生成してそれを画面に流す感じの電光掲示板的なツールを作ってみようと思う。

前回のドット絵ツールについて

https://qiita.com/shisojuice/items/dd4c99bdab0c4cef2387


# プロジェクトの作成

Rustで文字から画像を生成するために、下記のように、文字とそのFontデータを受け取る。

scaleやLayout時の座標などを設定する。

glyphから適当なImageにpixelを書き出す。

最後に、Base64の文字列を返す。


glyph.pixel_bounding_boxについて(Gemini1.5Proより)

「glyph.pixel_bounding_box」は、特定のグリフ（文字の形）をレンダリングするために必要なピクセルの最小境界ボックスを表します。

とのこと

```rust:lib.rs

// 省略

#[wasm_bindgen]
pub fn str_to_png(str:&str,win_h: u32,font_data:&[u8])  -> String  {
    let text = str;
    let font = rusttype::Font::try_from_vec(Vec::from(font_data)).unwrap();
    let scale = Scale::uniform((win_h as f64 * 0.8) as f32);

    let v_metrics = font.v_metrics(scale);
    let glyphs: Vec<_> = font
        .layout(text, scale, point(10.0, 10.0 + v_metrics.ascent))
        .collect();

    let glyphs_height = (v_metrics.ascent - v_metrics.descent).ceil() as u32;
    let glyphs_width = {
        let min_x = glyphs
            .first()
            .map(|g| g.pixel_bounding_box().unwrap().min.x)
            .unwrap();
        let max_x = glyphs
            .last()
            .map(|g| g.pixel_bounding_box().unwrap().max.x)
            .unwrap();
        (max_x - min_x) as u32
    };

    let mut image = DynamicImage::new_rgba8(glyphs_width + (win_h as f64 * 0.1) as u32, glyphs_height + (win_h as f64 * 0.1) as u32).to_rgba8();

    for glyph in glyphs {
        if let Some(bounding_box) = glyph.pixel_bounding_box() {
            glyph.draw(|x, y, v| {
                image.put_pixel(
                    x + bounding_box.min.x as u32,
                    y + bounding_box.min.y as u32,
                    Rgba([255, 255, 0, (v * 255.0) as u8]),
                )
            });
        }
    }

    let mut buffer = Cursor::new(Vec::new());
    image.write_to(&mut buffer, ImageFormat::Png).unwrap();
    let base64_string = general_purpose::STANDARD.encode(buffer.get_ref());
    // データURL形式で返す
    format!("data:image/png;base64,{}", base64_string)

}

// 省略

```

Javascriptで上記の関数からBase64のImageを受けとり、

それを別Windowで開き、Clickしたら自動でScrollするように実装する。

また、Scrollの最終地点でScrollの初期位置に戻るようにしておく。

```Javascript

// 省略(flow.html内のScriptタグ)

  const speed = getUrlParameter('speed');
    const main = document.getElementById('main');
    let scrollPosition = 0;
    document.getElementById("flowImg").src = localStorage.getItem("img_src");
    window.addEventListener("click", () => {
      const image_w = document.getElementById("flowImg").offsetWidth;
      setInterval(() => {
        scrollPosition += Number(speed);
        main.scrollTo({
          top: 0,
          left: scrollPosition
        });
      }, 10);
    })
    main.addEventListener('scroll', () => {
      if (scrollPosition > main.scrollWidth - main.clientWidth - 5) {
        scrollPosition = 0;
      }
    });

// 省略

```

# 今回の成果物

デモURL

https://shisojuice.github.io/Rust_WordsFlow/


デモ画像

設定画面

![RustとWebAssembly使って電光掲示板ツール作ってみた_001.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/c893b12c-ca2f-ca94-5c33-e116d694f40b.jpeg)

流す文字を表示する画面(下記は静止画ですが、実際には横方向に自動で流れる)

![RustとWebAssembly使って電光掲示板ツール作ってみた_002.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/2cf39045-d864-a45f-3efe-4caaaa53e90d.jpeg)


ソース

https://github.com/shisojuice/Rust_WordsFlow


# まとめ

今回はRustを使って文字から画像を生成してみた。📑

fontを操作した実装はRust以外の言語でも経験がなかったので勉強になった。💪

下記のクレートを利用したが投稿時点では直近で更新がされていないようなので注意です。

https://crates.io/crates/rusttype/0.9.3


※間違い等ありましたら、ご指摘いただけると助かります。