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.

147 lines
3.9 KiB

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