Slide 1

Slide 1 text

WASMで画像生成するた めにデータを受け渡す lapis2411

Slide 2

Slide 2 text

今日話す内容 ● Goで書いたwasmに対してデータを渡す方法 ● https://board-game-drafters.vercel.app/produce ○ カードゲームのモックを作成するためのApp(beta) 今日話さない内容 ● 画像生成の詳細 ○ 画像生成といいつつ生成AIみたいなものではなく 単純に入力した文字列からカード画像を作成するものです ● https://github.com/lapis2411/card-generator

Slide 3

Slide 3 text

アプリの概要 文字を各列に入力すると列に対応し た位置に対応した文字サイズで文 字を配置したカード画像を生成 生成したカード画像を並べてPDFと して出力 →印刷して切ればモック(試作品)と して遊べる 将来的にはWeb上で遊べるようにし たい...

Slide 4

Slide 4 text

やりたかったこと 入力された文字列をWASMに渡す 1行のデータを以下のようなJSON形式で受け取りたい (サーバーで実行することも可能なようにJSONでデータ受け 取りたい) { "id":1, "name":"name1", "data":[ {"styleID":1,"text":"name1"}, {"styleID":2,"text":"lu1"}, {"styleID":4,"text":"this is text"}, {"styleID":5,"text":"ld1"}] } 1つのカードに1つのdataがあってその中にスタイル情報(どの位置にどのサイズで 描画するか)に紐づいたテキストが複数ある

Slide 5

Slide 5 text

実現方法1 データをオブジェクトとしてwasmに渡してwasm側でJSONにする こんな構造のオブジェクトを渡してGo側でJSON化、画像生成処理へ渡す [ [ "id": 1, "name": "title1", "data": [ [ "styleID": "title", "text": "タイトル"], [ "styleID": "text", "text": "これは説明文です"], … ] ], … ]

Slide 6

Slide 6 text

実現方法1 Go側の処理 func main() { c := make(chan struct{}, 0) cg := NewCardGenerator() js.Global().Set("cardGenerator", js.ValueOf( map[string]any{ "CreateImages": js.FuncOf(CreateImages), }, )) <-c } func CreateImages(this js.Value, args []js.Value) any { str := js.Global().Get("JSON").Call("stringify", args[0]).String() // 生成処理など }

Slide 7

Slide 7 text

実現方法2 JSONの文字列データをフロントで作成しWASMに渡す 直接文字列を渡すのは大変...? WASMで予め文字列を書き込む領域を確保 → フロント側に渡して文字列データにしたJSONをWASMの領域に直接書き込む 書き込むためにポインタを受け取る (あまりにも壮大なのでもっと上手いやり方あれば教えてください...)

Slide 8

Slide 8 text

実現方法2 書き込む領域のポインタを受け取る処理 JSから呼んで書き込む領域を把握する 確保した領域のどこまでをデータとして扱うか知るために 文字数も先に指定 func (c *CardGenerator) GetBuffer(this js.Value, args []js.Value) any { if len(args) == 0 { return nil } size := args[0].Int() if size <= 0 || inputBufferSize < size { return nil } *c.inputSize = size c.clearBuffer(size) return js.ValueOf(uintptr(unsafe.Pointer(c.cardCSVHead))) }

Slide 9

Slide 9 text

実現方法2 JSでWASMの確保した領域に文字を書き込む const json = JSON.stringify(cards); const proc = async () => { const encoder = new TextEncoder(); const encodedStr = encoder.encode(json); const address = await cardGenerator.GetBuffer (encodedStr.length); const buf = inst?.exports.mem.buffer; // inst はWASM Instance const mem = new Int8Array(buf); const view = mem.subarray(address, address + encodedStr.length); for (let i = 0; i < encodedStr.length; i++) { view[i] = encodedStr[i]; } // @ts-ignore cardGenerator.CreateImages (); };

Slide 10

Slide 10 text

実現方法2 確保した領域から指定サイズだけ読み取り生成ロジックに渡す func (c CardGenerator) CreateImages(this js.Value, args []js.Value) any { json := []byte(c.cardCSV()[:*c.inputSize]) imgs := c.generateImages(json) ... }

Slide 11

Slide 11 text

まとめ 特にこだわりがなければ文字列としてJSONを渡すのではなくObjectとして渡した方が 無難そう どうしても文字列で渡したい場合は確保した領域を超える文字数を書き込まないような バリデーションが必要だったり、考えることが増える