// 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) }