You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

identicon.go 3.9KB

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