github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/identicon/identicon.go (about)

     1  // Copyright 2015 by caixw, All rights reserved.
     2  // Use of this source code is governed by a MIT
     3  // license that can be found in the LICENSE file.
     4  
     5  package identicon
     6  
     7  import (
     8  	"crypto/md5"
     9  	"fmt"
    10  	"image"
    11  	"image/color"
    12  )
    13  
    14  const (
    15  	minSize       = 16 // 图片的最小尺寸
    16  	maxForeColors = 32 // 在New()函数中可以指定的最大颜色数量
    17  )
    18  
    19  // Identicon 用于产生统一尺寸的头像。
    20  // 可以根据用户提供的数据,经过一定的算法,自动产生相应的图案和颜色。
    21  type Identicon struct {
    22  	foreColors []color.Color
    23  	backColor  color.Color
    24  	size       int
    25  	rect       image.Rectangle
    26  }
    27  
    28  // 声明一个Identicon实例。
    29  // size表示整个头像的大小。
    30  // back表示前景色。
    31  // fore表示所有可能的前景色,会为每个图像随机挑选一个作为其前景色。
    32  // NOTE:前景色不要与背景色太相近。
    33  func New(size int, back color.Color, fore ...color.Color) (*Identicon, error) {
    34  	if len(fore) == 0 || len(fore) > maxForeColors {
    35  		return nil, fmt.Errorf("前景色数量必须介于[1]~[%v]之间,当前为[%v]", maxForeColors, len(fore))
    36  	}
    37  
    38  	if size < minSize {
    39  		return nil, fmt.Errorf("参数size的值(%v)不能小于%v", size, minSize)
    40  	}
    41  
    42  	return &Identicon{
    43  		foreColors: fore,
    44  		backColor:  back,
    45  		size:       size,
    46  
    47  		// 画布坐标从0开始,其长度应该是size-1
    48  		rect: image.Rect(0, 0, size, size),
    49  	}, nil
    50  }
    51  
    52  // 根据data数据产生一张唯一性的头像图片。
    53  func (i *Identicon) Make(data []byte) image.Image {
    54  	h := md5.New()
    55  	h.Write(data)
    56  	sum := h.Sum(nil)
    57  
    58  	// 第一个方块
    59  	index := int(sum[0]+sum[1]+sum[2]+sum[3]) % len(blocks)
    60  	b1 := blocks[index]
    61  
    62  	// 第二个方块
    63  	index = int(sum[4]+sum[5]+sum[6]+sum[7]) % len(blocks)
    64  	b2 := blocks[index]
    65  
    66  	// 中间方块
    67  	index = int(sum[8]+sum[9]+sum[10]+sum[11]) % len(centerBlocks)
    68  	c := centerBlocks[index]
    69  
    70  	// 旋转角度
    71  	angle := int(sum[12]+sum[13]+sum[14]) % 4
    72  
    73  	// 根据最后一个字段,获取前景颜色
    74  	index = int(sum[15]) % len(i.foreColors)
    75  
    76  	p := image.NewPaletted(i.rect, []color.Color{i.backColor, i.foreColors[index]})
    77  	drawBlocks(p, i.size, c, b1, b2, angle)
    78  	return p
    79  }
    80  
    81  // 根据data数据产生一张唯一性的头像图片。
    82  // size 头像的大小。
    83  // back, fore头像的背景和前景色。
    84  func Make(size int, back, fore color.Color, data []byte) (image.Image, error) {
    85  	if size < minSize {
    86  		return nil, fmt.Errorf("参数size的值(%v)不能小于%v", size, minSize)
    87  	}
    88  
    89  	h := md5.New()
    90  	h.Write(data)
    91  	sum := h.Sum(nil)
    92  
    93  	// 第一个方块
    94  	index := int(sum[0]+sum[1]+sum[2]+sum[3]) % len(blocks)
    95  	b1 := blocks[index]
    96  
    97  	// 第二个方块
    98  	index = int(sum[4]+sum[5]+sum[6]+sum[7]) % len(blocks)
    99  	b2 := blocks[index]
   100  
   101  	// 中间方块
   102  	index = int(sum[8]+sum[9]+sum[10]+sum[11]) % len(centerBlocks)
   103  	c := centerBlocks[index]
   104  
   105  	// 旋转角度
   106  	angle := int(sum[12]+sum[13]+sum[14]+sum[15]) % 4
   107  
   108  	// 画布坐标从0开始,其长度应该是size-1
   109  	p := image.NewPaletted(image.Rect(0, 0, size, size), []color.Color{back, fore})
   110  	drawBlocks(p, size, c, b1, b2, angle)
   111  	return p, nil
   112  }
   113  
   114  // 将九个方格都填上内容。
   115  // p为画板。
   116  // c为中间方格的填充函数。
   117  // b1,b2为边上8格的填充函数。
   118  // angle为b1,b2的起始旋转角度。
   119  func drawBlocks(p *image.Paletted, size int, c, b1, b2 blockFunc, angle int) {
   120  	// 每个格子的长宽。先转换成float,再计算!
   121  	blockSize := float64(size) / 3
   122  	twoBlockSize := 2 * blockSize
   123  
   124  	incr := func() { // 增加angle的值,但不会大于3
   125  		angle++
   126  		if angle > 3 {
   127  			angle = 0
   128  		}
   129  	}
   130  
   131  	c(p, blockSize, blockSize, blockSize, 0)
   132  
   133  	b1(p, 0, 0, blockSize, angle)
   134  	b2(p, blockSize, 0, blockSize, angle)
   135  
   136  	incr()
   137  	b1(p, twoBlockSize, 0, blockSize, angle)
   138  	b2(p, twoBlockSize, blockSize, blockSize, angle)
   139  
   140  	incr()
   141  	b1(p, twoBlockSize, twoBlockSize, blockSize, angle)
   142  	b2(p, blockSize, twoBlockSize, blockSize, angle)
   143  
   144  	incr()
   145  	b1(p, 0, twoBlockSize, blockSize, angle)
   146  	b2(p, 0, blockSize, blockSize, angle)
   147  }