image.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package captcha
  5. import (
  6. "bytes"
  7. "image"
  8. "image/color"
  9. "image/png"
  10. "io"
  11. "math"
  12. )
  13. const (
  14. fontWidth = 11
  15. fontHeight = 18
  16. blackChar = 1
  17. // Standard width and height of a captcha image.
  18. stdWidth = 240
  19. stdHeight = 80
  20. // Maximum absolute skew factor of a single digit.
  21. maxSkew = 0.7
  22. // Number of background circles.
  23. circleCount = 20
  24. )
  25. var font = [][]byte{
  26. { // 0
  27. 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
  28. 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
  29. 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0,
  30. 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0,
  31. 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0,
  32. 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  33. 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  34. 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  35. 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  36. 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  37. 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  38. 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  39. 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  40. 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1,
  41. 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0,
  42. 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0,
  43. 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
  44. 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
  45. },
  46. { // 1
  47. 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
  48. 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
  49. 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0,
  50. 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0,
  51. 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0,
  52. 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0,
  53. 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
  54. 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
  55. 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
  56. 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
  57. 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
  58. 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
  59. 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
  60. 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
  61. 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
  62. 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
  63. 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  64. 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  65. },
  66. { // 2
  67. 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0,
  68. 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
  69. 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0,
  70. 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0,
  71. 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
  72. 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
  73. 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
  74. 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
  75. 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
  76. 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0,
  77. 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
  78. 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
  79. 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0,
  80. 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0,
  81. 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
  82. 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
  83. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  84. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  85. },
  86. { // 3
  87. 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0,
  88. 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
  89. 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0,
  90. 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
  91. 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
  92. 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
  93. 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
  94. 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0,
  95. 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
  96. 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0,
  97. 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
  98. 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  99. 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  100. 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  101. 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
  102. 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0,
  103. 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
  104. 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
  105. },
  106. { // 4
  107. 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
  108. 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
  109. 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0,
  110. 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0,
  111. 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,
  112. 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0,
  113. 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0,
  114. 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0,
  115. 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0,
  116. 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0,
  117. 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0,
  118. 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
  119. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  120. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  121. 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
  122. 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
  123. 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
  124. 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
  125. },
  126. { // 5
  127. 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
  128. 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
  129. 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
  130. 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
  131. 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
  132. 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
  133. 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
  134. 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
  135. 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0,
  136. 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
  137. 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  138. 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  139. 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  140. 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  141. 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
  142. 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0,
  143. 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
  144. 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0,
  145. },
  146. { // 6
  147. 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0,
  148. 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0,
  149. 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0,
  150. 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
  151. 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
  152. 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  153. 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0,
  154. 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0,
  155. 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0,
  156. 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1,
  157. 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  158. 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  159. 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  160. 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  161. 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1,
  162. 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0,
  163. 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
  164. 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
  165. },
  166. { // 7
  167. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  168. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  169. 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1,
  170. 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0,
  171. 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
  172. 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0,
  173. 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
  174. 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
  175. 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0,
  176. 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0,
  177. 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0,
  178. 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
  179. 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
  180. 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0,
  181. 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0,
  182. 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0,
  183. 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
  184. 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0,
  185. },
  186. { // 8
  187. 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
  188. 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
  189. 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1,
  190. 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1,
  191. 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1,
  192. 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1,
  193. 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0,
  194. 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
  195. 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
  196. 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0,
  197. 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0,
  198. 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1,
  199. 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  200. 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  201. 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  202. 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0,
  203. 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
  204. 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
  205. },
  206. { // 9
  207. 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
  208. 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
  209. 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0,
  210. 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0,
  211. 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  212. 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  213. 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  214. 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1,
  215. 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1,
  216. 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1,
  217. 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1,
  218. 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  219. 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
  220. 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
  221. 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0,
  222. 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
  223. 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
  224. 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
  225. },
  226. }
  227. type Image struct {
  228. *image.Paletted
  229. numWidth int
  230. numHeight int
  231. dotSize int
  232. }
  233. var prng = &siprng{}
  234. // randIntn returns a pseudorandom non-negative int in range [0, n).
  235. func randIntn(n int) int {
  236. return prng.Intn(n)
  237. }
  238. // randInt returns a pseudorandom int in range [from, to].
  239. func randInt(from, to int) int {
  240. return prng.Intn(to+1-from) + from
  241. }
  242. // randFloat returns a pseudorandom float64 in range [from, to].
  243. func randFloat(from, to float64) float64 {
  244. return (to-from)*prng.Float64() + from
  245. }
  246. func randomPalette() color.Palette {
  247. p := make([]color.Color, circleCount+1)
  248. // Transparent color.
  249. p[0] = color.RGBA{0xFF, 0xFF, 0xFF, 0x00}
  250. // Primary color.
  251. prim := color.RGBA{
  252. uint8(randIntn(129)),
  253. uint8(randIntn(129)),
  254. uint8(randIntn(129)),
  255. 0xFF,
  256. }
  257. p[1] = prim
  258. // Circle colors.
  259. for i := 2; i <= circleCount; i++ {
  260. p[i] = randomBrightness(prim, 255)
  261. }
  262. return p
  263. }
  264. // NewImage returns a new captcha image of the given width and height with the
  265. // given digits, where each digit must be in range 0-9.
  266. func NewImage(digits []byte, width, height int) *Image {
  267. m := new(Image)
  268. m.Paletted = image.NewPaletted(image.Rect(0, 0, width, height), randomPalette())
  269. m.calculateSizes(width, height, len(digits))
  270. // Randomly position captcha inside the image.
  271. maxx := width - (m.numWidth+m.dotSize)*len(digits) - m.dotSize
  272. maxy := height - m.numHeight - m.dotSize*2
  273. var border int
  274. if width > height {
  275. border = height / 5
  276. } else {
  277. border = width / 5
  278. }
  279. x := randInt(border, maxx-border)
  280. y := randInt(border, maxy-border)
  281. // Draw digits.
  282. for _, n := range digits {
  283. m.drawDigit(font[n], x, y)
  284. x += m.numWidth + m.dotSize
  285. }
  286. // Draw strike-through line.
  287. m.strikeThrough()
  288. // Apply wave distortion.
  289. m.distort(randFloat(5, 10), randFloat(100, 200))
  290. // Fill image with random circles.
  291. m.fillWithCircles(circleCount, m.dotSize)
  292. return m
  293. }
  294. // encodedPNG encodes an image to PNG and returns
  295. // the result as a byte slice.
  296. func (m *Image) encodedPNG() []byte {
  297. var buf bytes.Buffer
  298. if err := png.Encode(&buf, m.Paletted); err != nil {
  299. panic(err.Error())
  300. }
  301. return buf.Bytes()
  302. }
  303. // WriteTo writes captcha image in PNG format into the given writer.
  304. func (m *Image) WriteTo(w io.Writer) (int64, error) {
  305. n, err := w.Write(m.encodedPNG())
  306. return int64(n), err
  307. }
  308. func (m *Image) calculateSizes(width, height, ncount int) {
  309. // Goal: fit all digits inside the image.
  310. var border int
  311. if width > height {
  312. border = height / 4
  313. } else {
  314. border = width / 4
  315. }
  316. // Convert everything to floats for calculations.
  317. w := float64(width - border*2)
  318. h := float64(height - border*2)
  319. // fw takes into account 1-dot spacing between digits.
  320. fw := float64(fontWidth + 1)
  321. fh := float64(fontHeight)
  322. nc := float64(ncount)
  323. // Calculate the width of a single digit taking into account only the
  324. // width of the image.
  325. nw := w / nc
  326. // Calculate the height of a digit from this width.
  327. nh := nw * fh / fw
  328. // Digit too high?
  329. if nh > h {
  330. // Fit digits based on height.
  331. nh = h
  332. nw = fw / fh * nh
  333. }
  334. // Calculate dot size.
  335. m.dotSize = int(nh / fh)
  336. // Save everything, making the actual width smaller by 1 dot to account
  337. // for spacing between digits.
  338. m.numWidth = int(nw) - m.dotSize
  339. m.numHeight = int(nh)
  340. }
  341. func (m *Image) drawHorizLine(fromX, toX, y int, colorIdx uint8) {
  342. for x := fromX; x <= toX; x++ {
  343. m.SetColorIndex(x, y, colorIdx)
  344. }
  345. }
  346. func (m *Image) drawCircle(x, y, radius int, colorIdx uint8) {
  347. f := 1 - radius
  348. dfx := 1
  349. dfy := -2 * radius
  350. xo := 0
  351. yo := radius
  352. m.SetColorIndex(x, y+radius, colorIdx)
  353. m.SetColorIndex(x, y-radius, colorIdx)
  354. m.drawHorizLine(x-radius, x+radius, y, colorIdx)
  355. for xo < yo {
  356. if f >= 0 {
  357. yo--
  358. dfy += 2
  359. f += dfy
  360. }
  361. xo++
  362. dfx += 2
  363. f += dfx
  364. m.drawHorizLine(x-xo, x+xo, y+yo, colorIdx)
  365. m.drawHorizLine(x-xo, x+xo, y-yo, colorIdx)
  366. m.drawHorizLine(x-yo, x+yo, y+xo, colorIdx)
  367. m.drawHorizLine(x-yo, x+yo, y-xo, colorIdx)
  368. }
  369. }
  370. func (m *Image) fillWithCircles(n, maxradius int) {
  371. maxx := m.Bounds().Max.X
  372. maxy := m.Bounds().Max.Y
  373. for i := 0; i < n; i++ {
  374. colorIdx := uint8(randInt(1, circleCount-1))
  375. r := randInt(1, maxradius)
  376. m.drawCircle(randInt(r, maxx-r), randInt(r, maxy-r), r, colorIdx)
  377. }
  378. }
  379. func (m *Image) strikeThrough() {
  380. maxx := m.Bounds().Max.X
  381. maxy := m.Bounds().Max.Y
  382. y := randInt(maxy/3, maxy-maxy/3)
  383. amplitude := randFloat(5, 20)
  384. period := randFloat(80, 180)
  385. dx := 2.0 * math.Pi / period
  386. for x := 0; x < maxx; x++ {
  387. xo := amplitude * math.Cos(float64(y)*dx)
  388. yo := amplitude * math.Sin(float64(x)*dx)
  389. for yn := 0; yn < m.dotSize; yn++ {
  390. r := randInt(0, m.dotSize)
  391. m.drawCircle(x+int(xo), y+int(yo)+(yn*m.dotSize), r/2, 1)
  392. }
  393. }
  394. }
  395. func (m *Image) drawDigit(digit []byte, x, y int) {
  396. skf := randFloat(-maxSkew, maxSkew)
  397. xs := float64(x)
  398. r := m.dotSize / 2
  399. y += randInt(-r, r)
  400. for yo := 0; yo < fontHeight; yo++ {
  401. for xo := 0; xo < fontWidth; xo++ {
  402. if digit[yo*fontWidth+xo] != blackChar {
  403. continue
  404. }
  405. m.drawCircle(x+xo*m.dotSize, y+yo*m.dotSize, r, 1)
  406. }
  407. xs += skf
  408. x = int(xs)
  409. }
  410. }
  411. func (m *Image) distort(amplude float64, period float64) {
  412. w := m.Bounds().Max.X
  413. h := m.Bounds().Max.Y
  414. oldm := m.Paletted
  415. newm := image.NewPaletted(image.Rect(0, 0, w, h), oldm.Palette)
  416. dx := 2.0 * math.Pi / period
  417. for x := 0; x < w; x++ {
  418. for y := 0; y < h; y++ {
  419. xo := amplude * math.Sin(float64(y)*dx)
  420. yo := amplude * math.Cos(float64(x)*dx)
  421. newm.SetColorIndex(x, y, oldm.ColorIndexAt(x+int(xo), y+int(yo)))
  422. }
  423. }
  424. m.Paletted = newm
  425. }
  426. func randomBrightness(c color.RGBA, max uint8) color.RGBA {
  427. minc := min3(c.R, c.G, c.B)
  428. maxc := max3(c.R, c.G, c.B)
  429. if maxc > max {
  430. return c
  431. }
  432. n := randIntn(int(max-maxc)) - int(minc)
  433. return color.RGBA{
  434. uint8(int(c.R) + n),
  435. uint8(int(c.G) + n),
  436. uint8(int(c.B) + n),
  437. uint8(c.A),
  438. }
  439. }
  440. func min3(x, y, z uint8) (m uint8) {
  441. m = x
  442. if y < m {
  443. m = y
  444. }
  445. if z < m {
  446. m = z
  447. }
  448. return
  449. }
  450. func max3(x, y, z uint8) (m uint8) {
  451. m = x
  452. if y > m {
  453. m = y
  454. }
  455. if z > m {
  456. m = z
  457. }
  458. return
  459. }