diff options
Diffstat (limited to 'modules/identicon/identicon.go')
-rw-r--r-- | modules/identicon/identicon.go | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/modules/identicon/identicon.go b/modules/identicon/identicon.go new file mode 100644 index 00000000..c0b1db3b --- /dev/null +++ b/modules/identicon/identicon.go @@ -0,0 +1,147 @@ +// Copyright 2015 by caixw, All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + +package identicon + +import ( + "crypto/md5" + "fmt" + "image" + "image/color" +) + +const ( + minSize = 16 // 图片的最小尺寸 + maxForeColors = 32 // 在New()函数中可以指定的最大颜色数量 +) + +// Identicon 用于产生统一尺寸的头像。 +// 可以根据用户提供的数据,经过一定的算法,自动产生相应的图案和颜色。 +type Identicon struct { + foreColors []color.Color + backColor color.Color + size int + rect image.Rectangle +} + +// 声明一个Identicon实例。 +// size表示整个头像的大小。 +// back表示前景色。 +// fore表示所有可能的前景色,会为每个图像随机挑选一个作为其前景色。 +// NOTE:前景色不要与背景色太相近。 +func New(size int, back color.Color, fore ...color.Color) (*Identicon, error) { + if len(fore) == 0 || len(fore) > maxForeColors { + return nil, fmt.Errorf("前景色数量必须介于[1]~[%v]之间,当前为[%v]", maxForeColors, len(fore)) + } + + if size < minSize { + return nil, fmt.Errorf("参数size的值(%v)不能小于%v", size, minSize) + } + + return &Identicon{ + foreColors: fore, + backColor: back, + size: size, + + // 画布坐标从0开始,其长度应该是size-1 + rect: image.Rect(0, 0, size, size), + }, nil +} + +// 根据data数据产生一张唯一性的头像图片。 +func (i *Identicon) Make(data []byte) image.Image { + h := md5.New() + h.Write(data) + sum := h.Sum(nil) + + // 第一个方块 + index := int(sum[0]+sum[1]+sum[2]+sum[3]) % len(blocks) + b1 := blocks[index] + + // 第二个方块 + index = int(sum[4]+sum[5]+sum[6]+sum[7]) % len(blocks) + b2 := blocks[index] + + // 中间方块 + index = int(sum[8]+sum[9]+sum[10]+sum[11]) % len(centerBlocks) + c := centerBlocks[index] + + // 旋转角度 + angle := int(sum[12]+sum[13]+sum[14]) % 4 + + // 根据最后一个字段,获取前景颜色 + index = int(sum[15]) % len(i.foreColors) + + p := image.NewPaletted(i.rect, []color.Color{i.backColor, i.foreColors[index]}) + drawBlocks(p, i.size, c, b1, b2, angle) + return p +} + +// 根据data数据产生一张唯一性的头像图片。 +// size 头像的大小。 +// back, fore头像的背景和前景色。 +func Make(size int, back, fore color.Color, data []byte) (image.Image, error) { + if size < minSize { + return nil, fmt.Errorf("参数size的值(%v)不能小于%v", size, minSize) + } + + h := md5.New() + h.Write(data) + sum := h.Sum(nil) + + // 第一个方块 + index := int(sum[0]+sum[1]+sum[2]+sum[3]) % len(blocks) + b1 := blocks[index] + + // 第二个方块 + index = int(sum[4]+sum[5]+sum[6]+sum[7]) % len(blocks) + b2 := blocks[index] + + // 中间方块 + index = int(sum[8]+sum[9]+sum[10]+sum[11]) % len(centerBlocks) + c := centerBlocks[index] + + // 旋转角度 + angle := int(sum[12]+sum[13]+sum[14]+sum[15]) % 4 + + // 画布坐标从0开始,其长度应该是size-1 + p := image.NewPaletted(image.Rect(0, 0, size, size), []color.Color{back, fore}) + drawBlocks(p, size, c, b1, b2, angle) + return p, nil +} + +// 将九个方格都填上内容。 +// p为画板。 +// c为中间方格的填充函数。 +// b1,b2为边上8格的填充函数。 +// angle为b1,b2的起始旋转角度。 +func drawBlocks(p *image.Paletted, size int, c, b1, b2 blockFunc, angle int) { + // 每个格子的长宽。先转换成float,再计算! + blockSize := float64(size) / 3 + twoBlockSize := 2 * blockSize + + incr := func() { // 增加angle的值,但不会大于3 + angle++ + if angle > 3 { + angle = 0 + } + } + + c(p, blockSize, blockSize, blockSize, 0) + + b1(p, 0, 0, blockSize, angle) + b2(p, blockSize, 0, blockSize, angle) + + incr() + b1(p, twoBlockSize, 0, blockSize, angle) + b2(p, twoBlockSize, blockSize, blockSize, angle) + + incr() + b1(p, twoBlockSize, twoBlockSize, blockSize, angle) + b2(p, blockSize, twoBlockSize, blockSize, angle) + + incr() + b1(p, 0, twoBlockSize, blockSize, angle) + b2(p, 0, blockSize, blockSize, angle) +} |