Internet ComputerのBackend Canisterで動的に画像生成してみます。
ここで解説するサンプルは、Frontendから渡した画像サイズをもとにBackendで動的に画像データを生成して返すだけのシンプルなものです。
生成画像はBackend側のデータを利用しないため、厳密にはBackendを呼び出さずともFrontend内で生成可能なのですが、あえてBackendで動的生成させています。
Backendで動的に画像生成する仕組みを応用することで、Blockchainに刻まれたデータに基づいて動的に画像データを生成するといったこともできるでしょう。
1. テンプレート生成
生成した画像を表示するためのFrontendも用意しますので、手っ取り早くテンプレート生成を使うことにします。
コピー $ dfx new --type=rust fractal
$ cd fractal
コマンドが成功すると、以下のようなディレクトリ構造でファイルが生成されていると思います。
コピー fractal
├── Cargo.lock
├── Cargo.toml
├── README.md
├── dfx.json
├── node_modules
├── package-lock.json
├── package.json
├── src
│ ├── fractal_backend
│ │ ├── Cargo.toml
│ │ ├── fractal_backend.did
│ │ └── src
│ │ └── lib.rs
│ └── fractal_frontend
│ ├── assets
│ │ ├── favicon.ico
│ │ ├── logo2.svg
│ │ ├── main.css
│ │ └── sample-asset.txt
│ └── src
│ ├── index.html
│ └── index.js
└── webpack.config.js
2. プロジェクト資材修正
(1) dfx.jsonの編集
テンプレート生成されたものがそのまま使えるのでとくに修正は必要ありません。
(2) didファイルの編集
Backendを呼び出すためのI/Fを定義します。
引数として画像サイズ (x, y) を渡して、pngデータ (バイナリ)を返すことを考えます。サンプルですのでつくりを単純化するため、Backend側からエラーは返さないものとします。
src/fractal_backend/fractal_backend.did
コピー service : {
"fractal": (nat32, nat32) -> (blob) query;
}
(3) Backendプログラムの編集
I/Fに合わせてプログラムを用意します。
今回はBackendで動的に画像を生成する方法の検証が目的ですので、生成する画像にはこだわりません。
使用するimage createのページを見ると『Julia fractals』のコード例がありましたので、今回はこれを使ってみることにしましょう。
-https://crates.io/crates/image
描画ロジックの詳細については聞かないでください。
src/fractal_backend/src/lib.rs
コピー // An example of generating julia fractals.
// https://crates.io/crates/image
use std :: io :: Cursor ;
#[ic_cdk :: query]
fn fractal (imgx : u32 , imgy : u32 ) -> Vec < u8 > {
︙
let mut result = vec! [];
imgbuf . write_to ( &mut Cursor :: new ( &mut result), image :: ImageOutputFormat :: Png ) . unwrap ();
result
}
(4) Rust依存モジュールの追加
Julia fractalsでは計算に複素平面を使用するため、num-complex crateを使います。また、Rust上で画像を扱いpngフォーマットへの変換するために、image crateを追加します。
コピー $ cargo add num-complex
$ cargo add --features png --no-default-features image
プロジェクトルートにあるCargo.tomlはWorkspaceの設定にすぎませんので、
Cargo.toml
コピー [workspace]
members = [
"src/fractal_backend" ,
]
実体があるsrc/fractal_backend/Cargo.tomlに依存ライブラリが追加されます。
src/fractal_backend/Cargo.toml
コピー ︙
image = { version = "0.24.7" , default-features = false , features = [ "png" ] }
num-complex = "0.4.4"
(5) Backend呼び出し用のJavaScriptソース生成
FrontendからBackendを呼び出せるようにJavaScriptソースを生成します。
※コマンドの実体はdfx generate fractal_backend
です。
(6) index.htmlの編集
あくまでもサンプルですので、画像サイズ (x, y) を入力する<input>
タグ、画像生成する<button>
タグ、生成した画像を表示させる<img>
タグ のみのシンプルなものとし、素のHTMLとJavaScriptで記述することにします。
コピー <! DOCTYPE html >
< html >
< head >
< title >fractal</ title >
</ head >
< body >
Size:
< input id = "x" type = "number" min = "100" max = "1000" step = "1" value = "512" >
< input id = "y" type = "number" min = "100" max = "1000" step = "1" value = "512" >
< button id = "button" >Generate</ button >
< br />< br />
< img id = "fractal" >
</ body >
</ html >
(7) index.jsの編集
buttonがクリックされた場合に、Backendのfractal()関数を呼び出し、取得できたpngデータを<img>
タグに表示する例です。
<img>
タグにpngデータを直接流し込むことはできないため、convertToDataUrl()関数を用意してDataUrl形式に変換し、src属性に設定する方法で実現しています。
コピー import { fractal_backend } from "../../declarations/fractal_backend" ;
document .getElementById ( "button" ) .addEventListener ( "click" , async (e) => {
e .preventDefault ();
const x = parseInt ( document .getElementById ( "x" ).value);
const y = parseInt ( document .getElementById ( "y" ).value);
const png = await fractal_backend .fractal (x , y);
const blob = new Blob ([png] , { type : "image/png" });
const url = await convertToDataUrl (blob);
button .removeAttribute ( "disabled" );
document .getElementById ( "fractal" ).src = url;
return false ;
});
convertToDataUrl()関数はDfinityの公式サンプル を流用しています。
コピー // Converts the given blob into a data url such that it can be assigned as a
// target of a link of as an image source.
function convertToDataUrl (blob) {
return new Promise ((resolve , _) => {
const fileReader = new FileReader ();
fileReader .readAsDataURL (blob);
fileReader . onloadend = function () {
resolve ( fileReader .result);
}
});
}
(8) dfxサービス起動
コピー $ dfx start --background --clean
(9) Backend、Frontendのローカル配備
実行例
参考