1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
// 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
}
// New 声明一个 Identicon 实例。
// size 表示整个头像的大小;
// back 表示前景色;
// fore 表示所有可能的前景色,会为每个图像随机挑选一个作为其前景色。
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
}
// Make 根据 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
}
// Make 根据 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)
}
|