RustとWebAssemblyでFaviconGenerator作ってみた




# はじめに

下記で作成した「RustとWebAssembly使ってAsciiアート」に引き続き同じ構成で

なんとなくですが、学習用にRustとWebAssembly使ってFaviconGenerator的なのを作成しようと思います。


https://qiita.com/shisojuice/items/2413580f72244a4823c7


faviconについて

https://developers.google.com/search/docs/appearance/favicon-in-search?hl=ja


# プロジェクトの作成

wasm-packのテンプレートを作成。

今回は、wasmでimageのdataを受け取り、画像加工しFavicon作成して出力する関数を作成する。

画面のRadioButtonで背景の指定があった場合は、その背景色に合わせて背景を加工する。

背景の判定ロジックは処理を簡単にするため左上Pixelを背景色とした。(どんなアプローチがあるのか勉強したい🐬)

背景色と同じなら指定色にPixelを入替する。

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

 if input_color == "transparent" || input_color == "black" || input_color == "white"{
        let rgb_img = img.to_rgb8();
        // 左上Pixelを背景色とする
        let background_color = rgb_img.get_pixel(0, 0);
        let mut output_img = ImageBuffer::new(rgb_img.width(), rgb_img.height());
        for (y, row) in rgb_img.rows().enumerate() {
            for (x, pixel) in row.enumerate() {
                if pixel == background_color {
                    if input_color == "transparent"{
                        output_img.put_pixel(x as u32, y as u32, Rgba([0, 0, 0, 0]));
                    }
                    if input_color == "black" {
                        output_img.put_pixel(x as u32, y as u32, Rgba([0, 0, 0, 255]));
                    }
                    if input_color == "white"{
                        output_img.put_pixel(x as u32, y as u32, Rgba([255, 255, 255, 255]));
                    }
                } else {
                    output_img.put_pixel(x as u32, y as u32, Rgba([pixel[0], pixel[1], pixel[2], 255]));
                }
            }
        }
        img = DynamicImage::ImageRgba8(output_img);
    }


// 省略

```

各アイコンサイズをループ中にリサイズする。

icoクレートを使い、そのリサイズした画像をIconDirに入れて

マルチアイコン化する。


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

  // ICOエンコーダーを作成
    let mut icon_dir = ico::IconDir::new(ico::ResourceType::Icon);
    // 各アイコンサイズに対してループ処理
    let icon_sizes = vec![16,32,48,96,144,192,240,288];
    for size in icon_sizes {
        let resized_img = img.resize(size, size, FilterType::Lanczos3);
        let rgba = resized_img.to_rgba8().to_vec();
        let icon_img = ico::IconImage::from_rgba_data(size, size, rgba);

        // エンコードした画像をICOディレクトリに追加
        let icon = ico::IconDirEntry::encode(&icon_img).unwrap();
        icon_dir.add_entry(icon);
    }

// 省略

```


正直、下記ダウンロード処理についてはJavascript側で処理しても良いが、、、

せっかくならWasmでWeb-sysクレートを使用してJavascriptAPIを操作してみる。💪

よくJavascriptで行う　a要素　を使用してダウンロードする処理を

RustとWeb-sysクレートで行うと下記のようになる。


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

  　　// Blobを作成
    let blob = Blob::new_with_u8_array_sequence_and_options(&parts,BlobPropertyBag::new().type_("image/vnd.microsoft.icon")).unwrap();
    // BlobのURLを取得
    let url = Url::create_object_url_with_blob(&blob).unwrap();
    // a要素を作成
    let link = window.document().unwrap().create_element("a").unwrap().dyn_into::<web_sys::HtmlAnchorElement>().unwrap();
    link.set_href(&url);
    link.set_download("favicon.ico");
    link.click();

// 省略

```



# 今回の成果物

デモURL

https://shisojuice.github.io/Rust_Favicon/


デモ画像

・元画像

![RustとWebAssemblyでFaviconGenerator作ってみた_001.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/0df7c4d6-9b51-7ee8-256d-7c77a84c2571.jpeg)

![RustとWebAssemblyでFaviconGenerator作ってみた_002.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/25e7b294-4f8a-95d8-fac6-68bd7d8a38f3.jpeg)



※icoはUpload出来なかったため代替でJpgをUploadしています。

左から順に

1.通常 2.透過 3.不透過(背景白) 4.不透過(背景黒)

![RustとWebAssemblyでFaviconGenerator作ってみた_007.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/d0ac318a-7792-6c17-502b-a01b0d5d2ad8.jpeg)


ソース

https://github.com/shisojuice/Rust_Favicon



# まとめ

背景透過のアルゴリズムが思いつかなかったので今回は左上Pixelを参照するロジックにしたが、

画像系のアルゴリズムなども学習していきたいと思う。📚

次回はFaviconのマルチアイコンのCheckerを作成して何サイズが含まれるか確認できるツールを作りたいと思います。💪

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