Identicon generator in Go
Tags: go, tutorial, programming
Published: 2021-11-06 19:04:41

I wrote a simple [Identicon](https://en.wikipedia.org/wiki/Identicon) generator in Go.

## Usage
```
go run main.go -inputs=abcde
``` 
"abcde" will be hashed, then generates a png file
```
go run main.go -h // check how to use
```


## Code

The directory
```
.
├── avatarme
│   └── avatarme.go
├── go.mod
├── go.sum
└── main.go
```

**main.go**
```go
package main
import (
  "main/avatarme"
)
func main () {
  avatarme := avatarme.Avatarme{}
  avatarme.Generate() // it generates .png file
}
```
**avatarme.go**
```go
package avatarme
import (
    "flag"
    "fmt"
    "os"
    "log"
    "image"
    "image/color"
    "image/draw"
    "image/png"
    "crypto/sha1"
    "crypto/sha256"
    "crypto/sha512"
    "encoding/hex"
    "math"
)
// default value
const (
  SHA1 = iota // 0
  SHA224 // 1
  SHA256 // 2
  SHA384 // 3
  SHA512 // 4
)
const hashAlgDesc string = "0 (SHA-1)\n1 (SHA-224)\n2 (SHA-256)\n3 (SHA-384)\n4 (SHA-512)\n"
const (
  hashAlgDefault = SHA512
  sizeDefault = 50
  mutltiple = 5
  numberHash = 3
)

type Avatarme struct {
  inputs string
  hashAlgPtr, sizePtr *int
}
// package init
func init() {
  flag.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: go run cli.go [options]\n")
    fmt.Fprintf(os.Stderr, "e.g. go run cli.go -hashAlg=2 \n")
    flag.PrintDefaults()
  }
  if len(os.Args) < 2 {
    log.Fatal("expected at least two arguments")
    os.Exit(1)
  }
}
func (opts *Avatarme) SetOpts() {
  flag.StringVar(&opts.inputs, "inputs", "", "inputs desc")
  opts.hashAlgPtr = flag.Int("hashAlg", hashAlgDefault, hashAlgDesc)
  opts.sizePtr = flag.Int("size", sizeDefault, "size desc")
  flag.Parse()
  if *opts.sizePtr < sizeDefault {
    log.Fatalf("size at least 50, but yours is %v", *opts.sizePtr)
  }
}
func (opts *Avatarme) Generate() {
  opts.SetOpts()
  c:= [3]uint8{}
  hash := []byte{}
  text := opts.inputs
  hashAlg := *opts.hashAlgPtr
  // get hash
  switch hashAlg {
    case SHA1:
      _a := sha1.Sum([]byte(text))
      hash= _a[:sha1.Size]
    case SHA224:
      _a := sha256.Sum224([]byte(text))
      hash= _a[:sha256.Size224]
    case SHA256:
      _a := sha256.Sum256([]byte(text))
      hash = _a[:sha256.Size]
    case SHA384:
      _a := sha512.Sum384([]byte(text))
      hash = _a[:sha512.Size384]
    case SHA512:
      _a := sha512.Sum512([]byte(text))
      hash = _a[:sha512.Size]
  }
  // select color (first 3 hash value)
  copy(c[:], hash[:3])
  colorrgb := color.RGBA{c[0], c[1], c[2], 0xff}  //  R, G, B, Alpha
  // create .png
  outFile, err := os.Create("avatarme.png")
  if err != nil {
    log.Fatal(err)
  }
  defer outFile.Close()
  // size is mutltiple of 5 
  size := int(math.Ceil(float64(*opts.sizePtr)/float64(mutltiple))) * mutltiple
  blockSize := size/mutltiple
  img := image.NewRGBA(image.Rect(0, 0, size, size)) // x1,y1,  x2,y2
  whiteColor := color.RGBA{0xff, 0xff, 0xff, 0xff}  //  R, G, B, Alpha
  // backfill entire surface with white
  draw.Draw(img, img.Bounds(), &image.Uniform{whiteColor}, image.ZP, draw.Src)
  hashLen := len(hash)
  // choose first 25 bytes
  for i:=0; i < (mutltiple * numberHash) ; i+=numberHash {
    hashIndex := i % hashLen
    chunks := []byte{}
    if hashIndex + numberHash < hashLen {
      chunks = append(chunks, hash[hashIndex:hashIndex+3]...)
    } else {
      rest := (hashIndex + numberHash) - hashLen
      chunks = append(chunks, hash[hashIndex:hashLen]...)
      chunks = append(chunks, hash[0:rest]...)
    }
    chunks = append(chunks, chunks[1:2]...)
    chunks = append(chunks, chunks[0:1]...)
    origin := i/numberHash * mutltiple
    for index := origin; index < origin+mutltiple; index+=1 {
      // filter out even number
      if chunks[index-origin] % 2 == 1 {
        continue
      }
      horizontal := (index % mutltiple) * blockSize
      vertical := (index / mutltiple) * blockSize
      block := image.Rect(horizontal, vertical, horizontal+blockSize, vertical+blockSize) //  geometry of 2nd rectangle
      draw.Draw(img, block, &image.Uniform{colorrgb}, image.ZP, draw.Src)
    }
  }
  png.Encode(outFile, img)
}
```

## Reference
[Tutorial: Identicon generator in Go](https://bartfokker.com/posts/identicon/)