I wrote a simple 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
package main import ( "main/avatarme" ) func main () { avatarme := avatarme.Avatarme{} avatarme.Generate() // it generates .png file }
avatarme.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) }
Top comments (1)
More: github.com/tingwei628/pgo