symbol.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. // go-qrcode
  2. // Copyright 2014 Tom Harwood
  3. package qrcode
  4. // symbol is a 2D array of bits representing a QR Code symbol.
  5. //
  6. // A symbol consists of size*size modules, with each module normally drawn as a
  7. // black or white square. The symbol also has a border of quietZoneSize modules.
  8. //
  9. // A (fictional) size=2, quietZoneSize=1 QR Code looks like:
  10. //
  11. // +----+
  12. // | |
  13. // | ab |
  14. // | cd |
  15. // | |
  16. // +----+
  17. //
  18. // For ease of implementation, the functions to set/get bits ignore the border,
  19. // so (0,0)=a, (0,1)=b, (1,0)=c, and (1,1)=d. The entire symbol (including the
  20. // border) is returned by bitmap().
  21. //
  22. type symbol struct {
  23. // Value of module at [y][x]. True is set.
  24. module [][]bool
  25. // True if the module at [y][x] is used (to either true or false).
  26. // Used to identify unused modules.
  27. isUsed [][]bool
  28. // Combined width/height of the symbol and quiet zones.
  29. //
  30. // size = symbolSize + 2*quietZoneSize.
  31. size int
  32. // Width/height of the symbol only.
  33. symbolSize int
  34. // Width/height of a single quiet zone.
  35. quietZoneSize int
  36. }
  37. // newSymbol constructs a symbol of size size*size, with a border of
  38. // quietZoneSize.
  39. func newSymbol(size int, quietZoneSize int) *symbol {
  40. var m symbol
  41. m.module = make([][]bool, size+2*quietZoneSize)
  42. m.isUsed = make([][]bool, size+2*quietZoneSize)
  43. for i := range m.module {
  44. m.module[i] = make([]bool, size+2*quietZoneSize)
  45. m.isUsed[i] = make([]bool, size+2*quietZoneSize)
  46. }
  47. m.size = size + 2*quietZoneSize
  48. m.symbolSize = size
  49. m.quietZoneSize = quietZoneSize
  50. return &m
  51. }
  52. // get returns the module value at (x, y).
  53. func (m *symbol) get(x int, y int) (v bool) {
  54. v = m.module[y+m.quietZoneSize][x+m.quietZoneSize]
  55. return
  56. }
  57. // empty returns true if the module at (x, y) has not been set (to either true
  58. // or false).
  59. func (m *symbol) empty(x int, y int) bool {
  60. return !m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize]
  61. }
  62. // set sets the module at (x, y) to v.
  63. func (m *symbol) set(x int, y int, v bool) {
  64. m.module[y+m.quietZoneSize][x+m.quietZoneSize] = v
  65. m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize] = true
  66. }
  67. // set2dPattern sets a 2D array of modules, starting at (x, y).
  68. func (m *symbol) set2dPattern(x int, y int, v [][]bool) {
  69. for j, row := range v {
  70. for i, value := range row {
  71. m.set(x+i, y+j, value)
  72. }
  73. }
  74. }
  75. // bitmap returns the entire symbol, including the quiet zone.
  76. func (m *symbol) bitmap() [][]bool {
  77. module := make([][]bool, len(m.module))
  78. for i := range m.module {
  79. module[i] = m.module[i][:]
  80. }
  81. return module
  82. }
  83. // string returns a pictorial representation of the symbol, suitable for
  84. // printing in a TTY.
  85. func (m *symbol) string() string {
  86. var result string
  87. for _, row := range m.module {
  88. for _, value := range row {
  89. switch value {
  90. case true:
  91. result += " "
  92. case false:
  93. // Unicode 'FULL BLOCK' (U+2588).
  94. result += "██"
  95. }
  96. }
  97. result += "\n"
  98. }
  99. return result
  100. }
  101. // Constants used to weight penalty calculations. Specified by ISO/IEC
  102. // 18004:2006.
  103. const (
  104. penaltyWeight1 = 3
  105. penaltyWeight2 = 3
  106. penaltyWeight3 = 40
  107. penaltyWeight4 = 10
  108. )
  109. // penaltyScore returns the penalty score of the symbol. The penalty score
  110. // consists of the sum of the four individual penalty types.
  111. func (m *symbol) penaltyScore() int {
  112. return m.penalty1() + m.penalty2() + m.penalty3() + m.penalty4()
  113. }
  114. // penalty1 returns the penalty score for "adjacent modules in row/column with
  115. // same colour".
  116. //
  117. // The numbers of adjacent matching modules and scores are:
  118. // 0-5: score = 0
  119. // 6+ : score = penaltyWeight1 + (numAdjacentModules - 5)
  120. func (m *symbol) penalty1() int {
  121. penalty := 0
  122. for x := 0; x < m.symbolSize; x++ {
  123. lastValue := m.get(x, 0)
  124. count := 1
  125. for y := 1; y < m.symbolSize; y++ {
  126. v := m.get(x, y)
  127. if v != lastValue {
  128. count = 1
  129. lastValue = v
  130. } else {
  131. count++
  132. if count == 6 {
  133. penalty += penaltyWeight1 + 1
  134. } else if count > 6 {
  135. penalty++
  136. }
  137. }
  138. }
  139. }
  140. for y := 0; y < m.symbolSize; y++ {
  141. lastValue := m.get(0, y)
  142. count := 1
  143. for x := 1; x < m.symbolSize; x++ {
  144. v := m.get(x, y)
  145. if v != lastValue {
  146. count = 1
  147. lastValue = v
  148. } else {
  149. count++
  150. if count == 6 {
  151. penalty += penaltyWeight1 + 1
  152. } else if count > 6 {
  153. penalty++
  154. }
  155. }
  156. }
  157. }
  158. return penalty
  159. }
  160. // penalty2 returns the penalty score for "block of modules in the same colour".
  161. //
  162. // m*n: score = penaltyWeight2 * (m-1) * (n-1).
  163. func (m *symbol) penalty2() int {
  164. penalty := 0
  165. for y := 1; y < m.symbolSize; y++ {
  166. for x := 1; x < m.symbolSize; x++ {
  167. topLeft := m.get(x-1, y-1)
  168. above := m.get(x, y-1)
  169. left := m.get(x-1, y)
  170. current := m.get(x, y)
  171. if current == left && current == above && current == topLeft {
  172. penalty++
  173. }
  174. }
  175. }
  176. return penalty * penaltyWeight2
  177. }
  178. // penalty3 returns the penalty score for "1:1:3:1:1 ratio
  179. // (dark:light:dark:light:dark) pattern in row/column, preceded or followed by
  180. // light area 4 modules wide".
  181. //
  182. // Existence of the pattern scores penaltyWeight3.
  183. func (m *symbol) penalty3() int {
  184. penalty := 0
  185. for y := 0; y < m.symbolSize; y++ {
  186. var bitBuffer int16 = 0x00
  187. for x := 0; x < m.symbolSize; x++ {
  188. bitBuffer <<= 1
  189. if v := m.get(x, y); v {
  190. bitBuffer |= 1
  191. }
  192. switch bitBuffer & 0x7ff {
  193. // 0b000 0101 1101 or 0b10111010000
  194. // 0x05d or 0x5d0
  195. case 0x05d, 0x5d0:
  196. penalty += penaltyWeight3
  197. bitBuffer = 0xFF
  198. default:
  199. if x == m.symbolSize - 1 && (bitBuffer & 0x7f) == 0x5d {
  200. penalty += penaltyWeight3
  201. bitBuffer = 0xFF
  202. }
  203. }
  204. }
  205. }
  206. for x := 0; x < m.symbolSize; x++ {
  207. var bitBuffer int16 = 0x00
  208. for y := 0; y < m.symbolSize; y++ {
  209. bitBuffer <<= 1
  210. if v := m.get(x, y); v {
  211. bitBuffer |= 1
  212. }
  213. switch bitBuffer & 0x7ff {
  214. // 0b000 0101 1101 or 0b10111010000
  215. // 0x05d or 0x5d0
  216. case 0x05d, 0x5d0:
  217. penalty += penaltyWeight3
  218. bitBuffer = 0xFF
  219. default:
  220. if y == m.symbolSize - 1 && (bitBuffer & 0x7f) == 0x5d {
  221. penalty += penaltyWeight3
  222. bitBuffer = 0xFF
  223. }
  224. }
  225. }
  226. }
  227. return penalty
  228. }
  229. // penalty4 returns the penalty score...
  230. func (m *symbol) penalty4() int {
  231. numModules := m.symbolSize * m.symbolSize
  232. numDarkModules := 0
  233. for x := 0; x < m.symbolSize; x++ {
  234. for y := 0; y < m.symbolSize; y++ {
  235. if v := m.get(x, y); v {
  236. numDarkModules++
  237. }
  238. }
  239. }
  240. numDarkModuleDeviation := numModules/2 - numDarkModules
  241. if numDarkModuleDeviation < 0 {
  242. numDarkModuleDeviation *= -1
  243. }
  244. return penaltyWeight4 * (numDarkModuleDeviation / (numModules / 20))
  245. }