RustとWebAssemblyで拡大鏡ツール作ってみた




# はじめに

前回RustとWebAssemblyで電光掲示板ツール作った際に、拡大鏡ツール的なものも作ってみたいと思ったため、作成してみようと思う。🔎

(作り始めてから気が付きました、、、これ、わざわざWasm使わなくても

CSSのscaleなどを使えば簡単に実装できる！！😮

と考えながらも、とりあえず作ってしまったので記事にします💪)

前回作った電光掲示板ツールについて

https://qiita.com/shisojuice/items/0bd4663aed0a3152d1f6


# プロジェクトの作成

まず、画像を取り込み、imgタグに表示する。

取り込む際に、処理しやすいサイズとアスペクト比に画像をクロップしておく。(今回は1280pxベース)


```rust:lib.rs
// 省略

#[wasm_bindgen]
pub fn resize_img(image_data: &[u8]) -> String {
    let img = resize_image(image::load_from_memory(image_data).unwrap());
    let mut buffer = Cursor::new(Vec::new());
    img.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)
}

// 画像のアスペクト比・操作しやすいサイズに設定・調整
fn resize_image(img: DynamicImage) -> DynamicImage {
    let aspect_ratio = img.width() as f32 / img.height() as f32;
    let (new_width, new_height) = if aspect_ratio > 1.0 {
        // 横長の画像の場合
        (1280, (1280.0 / aspect_ratio) as u32)
    } else {
        // 縦長の画像の場合
        ((1280.0 * aspect_ratio) as u32, 1280)
    };
    // リサイズ
    let resized_img = img.resize_exact(new_width, new_height, FilterType::Lanczos3);
    // 1280x1280のキャンバスを作成
    let mut canvas = DynamicImage::new_rgba8(1280, 1280);
    // 白を背景色とする
    let bkg_image= ImageBuffer::from_pixel(1280, 1280, Rgba([255, 255, 255, 255]));
    image::imageops::overlay(&mut canvas, &bkg_image, 0, 0);
    // リサイズ画像をキャンバスの中央に貼付
    let x = (1280 - resized_img.width()) / 2;
    let y = (1280 - resized_img.height()) / 2;
    image::imageops::overlay(&mut canvas, &resized_img, x.into(), y.into());
    canvas
}

// 省略

```

mousemove中にZoom処理を行う。

取り込み時Resize済みの画像と拡大鏡ツールの座標を受け取る。

imgタグが320pxで画像サイズが1280pxのため座標を4倍したものと、

16倍の拡大率を考慮してpixelを設定。

```rust:lib.rs
// 省略

#[wasm_bindgen]
pub fn zoom_img(str_url: String,point_x: u32,point_y: u32) -> String {
    let comma_pos = str_url.find(',').unwrap();
    let str_base64 :String  =Some(&str_url[comma_pos + 1..]).unwrap().to_string();
    let image_data = general_purpose::STANDARD.decode(str_base64).unwrap();
    let img =  image::load_from_memory_with_format(&image_data, ImageFormat::Png).unwrap();
    let scaled_img: ImageBuffer<Rgba<u8>, Vec<u8>> = ImageBuffer::from_fn(
        img.width(),img.height(),
        |i, j| {
            let original_x = point_x * 4 + i / 16;// zoom 16倍
            let original_y = point_y * 4 + j / 16;
            img.get_pixel(original_x, original_y).clone()
        },
    );
    let mut buffer = Cursor::new(Vec::new());
    scaled_img.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側でmousemoveのeventで呼び出して、
その都度拡大表示させたら完成！🔎


# 今回の成果物

デモURL

https://shisojuice.github.io/Rust_Loupe/


デモ画像

![RustとWebAssemblyで拡大鏡ツール作ってみた_001.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/d131f872-12b9-3714-e8d4-2fbafec7cd2f.jpeg)

![RustとWebAssemblyで拡大鏡ツール作ってみた_002.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/105edaca-0f4c-e1f3-8a3e-47a720efa9f1.jpeg)


ソース

https://github.com/shisojuice/Rust_Loupe


# まとめ

今回はRustで画像の拡大処理を処理してみた。📚

おそらくCSSで実装したほうが楽だと思うが、

Rustの勉強中ということもあり、

細かいことは気にせずコーディングを楽しもうと思う💪

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