[ネタ]Excel方眼紙の上に好きな画像を正方形にペイントするツールをGoで作ってみた

# 概要

日本の文化圏にいれば、どこかで「[Excel方眼紙](https://ja.wikipedia.org/wiki/Excel%E6%96%B9%E7%9C%BC%E7%B4%99)」に出会うことがあるでしょう。
このエントリでは、Excel方眼紙にお気に入りの絵をペイントして眺めてみるツールを作ってみました。

|![lenna_rendered.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65002/6a54c551-808c-06c2-f30f-574b75813b68.png)|
|:---|
|図：[コンピュータで画像系のサンプルでよく使われるLenna様](https://en.wikipedia.org/wiki/Lenna)をペイント|

# TL;DR;

リポジトリはこちら[https://github.com/hrkt/xlspaint](https://github.com/hrkt/xlspaint)です。
ご自身でビルドするか、[リリースのページから実行形式xlspaint.exeとテンプレのtemplate_256x256.xlsxを取得](https://github.com/hrkt/xlspaint/releases/tag/0.0.1)して、ペイントしたい画像を同じディレクトリに置き、下記のように実行します。

```console
xlspaint 方眼紙に描きたいファイル名
```

実行例：

```console
>xlspaint.exe sampledata\Lenna_512x512.png
format:png
512
2020/03/06 06:21:19 start redering
................................................................................................................................................................................................................................................................
2020/03/06 06:21:19 redering done.

```

成功すると、「元のファイル名」に拡張し「.xlsx」がついているファイルが出来上がります。
Excelのフォーマットが読み込めるプログラムで、心ゆくまでご鑑賞ください。

# コード

大まかな流れは、下記のとおりです。

1. 元画像ファイルを読み込む
2. 元画像を正方形に切り取る（短い方の辺の長さで、中心によせて）
3. さらにリサイズする（256x256に）
4. 色が多すぎると大変なので、色の数を限るため、パレットを作る
5. 変換された画像をもとに、Worksheetのセルを塗りつぶす
6. ブックのファイルを書きだす


```golang

package main

import (
	"flag"
	"fmt"
	"image"
	"image/color"
	"log"
	"path/filepath"

	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"
	"os"

	"github.com/nfnt/resize"
	"github.com/oliamb/cutter"
	"github.com/soniakeys/quant/median"
	"github.com/tealeg/xlsx"
)

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func main() {
	flag.Parse()

	if 0 == len(flag.Args()) {
		fmt.Println("Usage: xlspaint [imagefilename]")
		os.Exit(1)
	}
	imgFilename := flag.Args()[0]

	imgFile, _ := os.Open(imgFilename)
	defer imgFile.Close()

	srcImg, fmtName, err := image.Decode(imgFile)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("format:" + fmtName)

	length := min(srcImg.Bounds().Max.X, srcImg.Bounds().Max.Y)
	fmt.Println(length)

        // crop
	croppedImg, err := cutter.Crop(srcImg, cutter.Config{
		Width:  length,
		Height: length,
		Mode:   cutter.Centered,
	})

        //resize
	resizedImg := resize.Resize(256, 0, croppedImg, resize.Lanczos3)

        //make palette
	p := median.Quantizer(256).Quantize(make(color.Palette, 0, 256), srcImg)
	palletedImg := image.NewPaletted(srcImg.Bounds(), p)
	for y := resizedImg.Bounds().Min.Y; y < resizedImg.Bounds().Max.Y; y++ {
		for x := resizedImg.Bounds().Min.X; x < resizedImg.Bounds().Max.X; x++ {
			palletedImg.Set(x, y, resizedImg.At(x, y))
		}
	}

	excelFileName := "template_256x256.xlsx"
	xlFile, err := xlsx.OpenFile(excelFileName)
	if err != nil {
		os.Exit(1)
	}
	sheet := xlFile.Sheets[0]
	log.Println("start redering")
        //read pixels from image and paint cells
	for row := 0; row < 256; row++ {
		fmt.Print(".")
		for col := 0; col < 256; col++ {
			style := xlsx.NewStyle()
			r, g, b, _ := palletedImg.At(col, row).RGBA()
			colorStr := fmt.Sprintf("FF%02x%02x%02x", r>>8, g>>8, b>>8)
			style.Fill = *xlsx.NewFill("solid", colorStr, colorStr)
			cell := sheet.Rows[row].Cells[col]
			cell.SetStyle(style)
		}
	}
	fmt.Println("")
	log.Println("redering done.")
	err = xlFile.Save(filepath.Base(imgFilename) + ".xlsx")
	if err != nil {
		fmt.Printf(err.Error())
	}
	os.Exit(0)
}

```


# おわりに

ふと思い立ったのでやり始めたら、（Golangはあまり得意ではないのですが）あれこれ調べながら3時間くらいで書けました。

# 補足

テンプレの「template_256x256.xlsx」はお好きな形に編集できます。