やっつけな日常(6) スマホでGo!fmt.Printで今度はフルカラー画像出力

Joseph Halfmoon

Androidスマホ上で、Go言語を学んでおります。前々回、fmt.Printだけでも画像出力ができるということでやってみましたが白黒2値画像でした。今回は、前回使用したPBM形式の兄弟分?、PPM形式をつかってフルカラー画像を出力してみたいと思います。カラー画像が出力できるといろいろやってみられるような。

※末尾に今回実験した Go言語のソース一式を掲げました。

PPM Format

PPM Formatのドキュメントは以下です。

PPM Format Specification

前々回のPBMフォーマットと近縁ですが、カラー化のためにR,G,Bの3要素を持たせないとなりません。またR,G,Bの3要素のMax値も与えなければなりません。

例によって前々回使用したPBMフォーマット用のプログラムをコピペしてチョイ直し(いつもそんなんだな。)作成したのが以下のサンプルプログラムであります(テキスト形式は末尾に貼り付けましたが、スマホの画面上で1画面に収まってしまったので、スクリーンショットも貼り付けました。)

Screenshot_20220413-185907

ちょっと老眼の目には厳しいフォントサイズではありますが、何とか編集も可能。

Go言語の Escape to Heap

goのソースを書いていて気持ちが悪かったのが、関数の中で宣言した変数(ローカル変数)へのポインタを関数の戻り値にして関数外に出しても問題なく使えてしまうところ。上記のコードでも見様見真似で使ってますが、Cとかでそんなことをやったら即アウト、ですよね。

あちこち調べていて分かってきたのが、Goの場合、スコープの外でも使われるということを認識するとその対象をローカルなスタックからヒープに勝手に追い出してくれる(escape、脱出というべきか)ということ。おお、なんということ。勝手に良きように計らってくれると。凄いなGo。年寄りはヒープに追い出してくれているのかどうかイチイチ確かめないとチョイと心配ですが。大丈夫みたい。

実機上での実行結果

今回の実行はコンパイルして行いました。実行ファイル自体の出力は.ppmフォーマットです。Termux上のLinuxアプリなら読める画像フォーマットですが、残念ながらスマホの「表のアプリ」で表示しようとするとエラーになるみたいなので、ImageMagick(要インストール)のconvertコマンドで .png に変換してます。

$ go build samplePPM.go
$ ./samplePPM >samplePPM.ppm
$ convert samplePPM.ppm samplePPM.png

変換した結果のPNG画像の様子が冒頭のアイキャッチ画像に。

カラー画像が描けるとなると夢が広がっちゃうな(ホントか、妄想であろう、自分。)

やっつけな日常(5) スマホでGo!ロジスティック写像のカオスなパターン に戻る

やっつけな日常(6) スマホでGo! fmt.Printで吉例マンデルブロ集合を描く へ進む

※実験に使用した Go 言語のソース全文

package main
import "fmt"

type ppmImage struct {
  width int
  height int
  cmax int
  BUF []int
}

func newPpmImage(w int, h int, cx int) *ppmImage {
  p := ppmImage{width: w, height: h, cmax: cx}
  p.BUF = make([]int, w*h*3)
  return &p
}

func dotXY(img *ppmImage, x int, y int, r int, g int, b int) bool {
  if x > (img.width - 1) {
    return false
  } else if y > (img.height - 1) {
    return false
  } else {
    img.BUF[3*(y*img.width + x)]     = r
    img.BUF[3*(y*img.width + x) + 1] = g
    img.BUF[3*(y*img.width + x) + 2] = b
  }
  return true
}

func writePPM(img *ppmImage) bool {
  fmt.Println("P3")
  fmt.Println(img.width, " ", img.height)
  fmt.Println(img.cmax)
  for y := 0; y < img.height; y++ {
    for x := 0; x < img.width; x++ {
      fmt.Print(  img.BUF[3*(y*img.width + x)], " ")
      fmt.Print(  img.BUF[3*(y*img.width + x) + 1], " ")
      fmt.Println(img.BUF[3*(y*img.width + x) + 2])
    }
  }
  return true
}

func main() {
  XMAX:= 256
  YMAX:= 256
  img := newPpmImage(XMAX, YMAX, 256)
  for x:= 0; x < XMAX; x++ {
    for y:= 0; y < YMAX; y++ {
      dotXY(img, x, y, x, 0, y)
    }
  }
  writePPM(img)
}