commit 9c387f9446bcaa297e19274cbf2ebef6dc47c3d5
Author: Yongbin Kim <iam@yongbin.kim>
Date: Sun, 12 Sep 2021 03:43:25 +0900
First Commmit
Signed-off-by: Yongbin Kim <iam@yongbin.kim>
Diffstat:
6 files changed, 130 insertions(+), 0 deletions(-)
diff --git a/README b/README
@@ -0,0 +1,6 @@
+go.lair.cx/identicon
+====================
+
+Profile image generator.
+
+See `examples/main.go` for usage.
diff --git a/examples/main.go b/examples/main.go
@@ -0,0 +1,27 @@
+package main
+
+import (
+ "image/png"
+ "os"
+
+ "go.lair.cx/identicon"
+)
+
+func main() {
+ factory := identicon.NewFactory()
+ generated, err := factory.Generate([]byte("Hello, World!"))
+ if err != nil {
+ panic(err)
+ }
+
+ f, err := os.Create("result.png")
+ if err != nil {
+ panic(err)
+ }
+ defer f.Close()
+
+ err = png.Encode(f, generated)
+ if err != nil {
+ panic(err)
+ }
+}
diff --git a/examples/result.png b/examples/result.png
Binary files differ.
diff --git a/go.mod b/go.mod
@@ -0,0 +1,8 @@
+module go.lair.cx/identicon
+
+go 1.18
+
+require (
+ github.com/segmentio/fasthash v1.0.3
+ golang.org/x/image v0.0.0-20220902085622-e7cb96979f69
+)
diff --git a/go.sum b/go.sum
@@ -0,0 +1,6 @@
+github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM=
+github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY=
+golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 h1:Lj6HJGCSn5AjxRAH2+r35Mir4icalbqku+CLUtjnvXY=
+golang.org/x/image v0.0.0-20220902085622-e7cb96979f69/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
diff --git a/identicon.go b/identicon.go
@@ -0,0 +1,83 @@
+package identicon
+
+import (
+ "image"
+ "image/color"
+
+ "github.com/segmentio/fasthash/fnv1a"
+ "golang.org/x/image/draw"
+)
+
+const (
+ iconWidth = 5
+ iconWidthHalf = 3
+ iconHeight = 5
+)
+
+var defaultPalette = [...]color.RGBA{
+ {0xFF, 0x40, 0x00, 0xFF},
+ {0xFF, 0x93, 0x51, 0xFF},
+ {0xFF, 0xE2, 0x72, 0xFF},
+ {0x9D, 0xE1, 0x6F, 0xFF},
+ {0x53, 0xAF, 0xFF, 0xFF},
+ {0x41, 0x47, 0x96, 0xFF},
+ {0xDE, 0x6F, 0xFF, 0xFF},
+ {0xFF, 0x74, 0xBC, 0xFF},
+}
+
+type Factory struct {
+ // Palette
+ palette []color.RGBA
+}
+
+func NewFactory() *Factory {
+ return &Factory{
+ palette: defaultPalette[:],
+ }
+}
+
+// SetPalette sets the palette.
+func (f *Factory) SetPalette(palette []color.RGBA) {
+ f.palette = palette
+}
+
+// Generate generates a identicon.
+func (f *Factory) Generate(src []byte) (image.Image, error) {
+ // Hash the source.
+ hash := fnv1a.HashBytes64(src)
+
+ // Choose color from palette.
+ markColor := f.palette[int(hash&0xFF)%len(f.palette)]
+ hash = hash >> 8
+
+ // Prepare canvas.
+ canvas := image.NewRGBA(image.Rect(0, 0, iconWidth, iconHeight))
+ draw.Draw(canvas, canvas.Bounds(), &image.Uniform{C: color.White}, image.Point{}, draw.Src)
+
+ // Draw to canvas.
+ for y := 0; y < iconHeight; y++ {
+ for x := 0; x < iconWidthHalf; x++ {
+ if hash>>(8*y+x)&1 > 0 {
+ canvas.SetRGBA(x, y, markColor)
+ canvas.SetRGBA(4-x, y, markColor)
+ }
+ }
+ }
+
+ return canvas, nil
+}
+
+func WrapImage(img image.Image, cw, ch, ix int) image.Image {
+ var (
+ w = iconWidth * ix
+ h = iconHeight * ix
+ x1 = (cw - w) / 2
+ y1 = (ch - h) / 2
+ x2 = x1 + w
+ y2 = y1 + h
+ )
+ wrapper := image.NewRGBA(image.Rect(0, 0, cw, ch))
+ draw.Draw(wrapper, wrapper.Bounds(), &image.Uniform{C: color.White}, image.Point{}, draw.Src)
+ draw.NearestNeighbor.Scale(wrapper, image.Rect(x1, y1, x2, y2), img, img.Bounds(), draw.Src, nil)
+ return wrapper
+}