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.
 
 
 

310 lines
6.8 KiB

  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. // numEmptyModules returns the number of empty modules.
  63. //
  64. // Initially numEmptyModules is symbolSize * symbolSize. After every module has
  65. // been set (to either true or false), the number of empty modules is zero.
  66. func (m *symbol) numEmptyModules() int {
  67. var count int
  68. for y := 0; y < m.symbolSize; y++ {
  69. for x := 0; x < m.symbolSize; x++ {
  70. if !m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize] {
  71. count++
  72. }
  73. }
  74. }
  75. return count
  76. }
  77. // set sets the module at (x, y) to v.
  78. func (m *symbol) set(x int, y int, v bool) {
  79. m.module[y+m.quietZoneSize][x+m.quietZoneSize] = v
  80. m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize] = true
  81. }
  82. // set2dPattern sets a 2D array of modules, starting at (x, y).
  83. func (m *symbol) set2dPattern(x int, y int, v [][]bool) {
  84. for j, row := range v {
  85. for i, value := range row {
  86. m.set(x+i, y+j, value)
  87. }
  88. }
  89. }
  90. // bitmap returns the entire symbol, including the quiet zone.
  91. func (m *symbol) bitmap() [][]bool {
  92. module := make([][]bool, len(m.module))
  93. for i := range m.module {
  94. module[i] = m.module[i][:]
  95. }
  96. return module
  97. }
  98. // string returns a pictorial representation of the symbol, suitable for
  99. // printing in a TTY.
  100. func (m *symbol) string() string {
  101. var result string
  102. for _, row := range m.module {
  103. for _, value := range row {
  104. switch value {
  105. case true:
  106. result += " "
  107. case false:
  108. // Unicode 'FULL BLOCK' (U+2588).
  109. result += "██"
  110. }
  111. }
  112. result += "\n"
  113. }
  114. return result
  115. }
  116. // Constants used to weight penalty calculations. Specified by ISO/IEC
  117. // 18004:2006.
  118. const (
  119. penaltyWeight1 = 3
  120. penaltyWeight2 = 3
  121. penaltyWeight3 = 40
  122. penaltyWeight4 = 10
  123. )
  124. // penaltyScore returns the penalty score of the symbol. The penalty score
  125. // consists of the sum of the four individual penalty types.
  126. func (m *symbol) penaltyScore() int {
  127. return m.penalty1() + m.penalty2() + m.penalty3() + m.penalty4()
  128. }
  129. // penalty1 returns the penalty score for "adjacent modules in row/column with
  130. // same colour".
  131. //
  132. // The numbers of adjacent matching modules and scores are:
  133. // 0-5: score = 0
  134. // 6+ : score = penaltyWeight1 + (numAdjacentModules - 5)
  135. func (m *symbol) penalty1() int {
  136. penalty := 0
  137. for x := 0; x < m.symbolSize; x++ {
  138. lastValue := m.get(x, 0)
  139. count := 1
  140. for y := 1; y < m.symbolSize; y++ {
  141. v := m.get(x, y)
  142. if v != lastValue {
  143. count = 1
  144. lastValue = v
  145. } else {
  146. count++
  147. if count == 6 {
  148. penalty += penaltyWeight1 + 1
  149. } else if count > 6 {
  150. penalty++
  151. }
  152. }
  153. }
  154. }
  155. for y := 0; y < m.symbolSize; y++ {
  156. lastValue := m.get(0, y)
  157. count := 1
  158. for x := 1; x < m.symbolSize; x++ {
  159. v := m.get(x, y)
  160. if v != lastValue {
  161. count = 1
  162. lastValue = v
  163. } else {
  164. count++
  165. if count == 6 {
  166. penalty += penaltyWeight1 + 1
  167. } else if count > 6 {
  168. penalty++
  169. }
  170. }
  171. }
  172. }
  173. return penalty
  174. }
  175. // penalty2 returns the penalty score for "block of modules in the same colour".
  176. //
  177. // m*n: score = penaltyWeight2 * (m-1) * (n-1).
  178. func (m *symbol) penalty2() int {
  179. penalty := 0
  180. for y := 1; y < m.symbolSize; y++ {
  181. for x := 1; x < m.symbolSize; x++ {
  182. topLeft := m.get(x-1, y-1)
  183. above := m.get(x, y-1)
  184. left := m.get(x-1, y)
  185. current := m.get(x, y)
  186. if current == left && current == above && current == topLeft {
  187. penalty++
  188. }
  189. }
  190. }
  191. return penalty * penaltyWeight2
  192. }
  193. // penalty3 returns the penalty score for "1:1:3:1:1 ratio
  194. // (dark:light:dark:light:dark) pattern in row/column, preceded or followed by
  195. // light area 4 modules wide".
  196. //
  197. // Existence of the pattern scores penaltyWeight3.
  198. func (m *symbol) penalty3() int {
  199. penalty := 0
  200. for y := 0; y < m.symbolSize; y++ {
  201. var bitBuffer int16 = 0x00
  202. for x := 0; x < m.symbolSize; x++ {
  203. bitBuffer <<= 1
  204. if v := m.get(x, y); v {
  205. bitBuffer |= 1
  206. }
  207. switch bitBuffer & 0x7ff {
  208. // 0b000 0101 1101 or 0b10111010000
  209. // 0x05d or 0x5d0
  210. case 0x05d, 0x5d0:
  211. penalty += penaltyWeight3
  212. bitBuffer = 0xFF
  213. default:
  214. if x == m.symbolSize-1 && (bitBuffer&0x7f) == 0x5d {
  215. penalty += penaltyWeight3
  216. bitBuffer = 0xFF
  217. }
  218. }
  219. }
  220. }
  221. for x := 0; x < m.symbolSize; x++ {
  222. var bitBuffer int16 = 0x00
  223. for y := 0; y < m.symbolSize; y++ {
  224. bitBuffer <<= 1
  225. if v := m.get(x, y); v {
  226. bitBuffer |= 1
  227. }
  228. switch bitBuffer & 0x7ff {
  229. // 0b000 0101 1101 or 0b10111010000
  230. // 0x05d or 0x5d0
  231. case 0x05d, 0x5d0:
  232. penalty += penaltyWeight3
  233. bitBuffer = 0xFF
  234. default:
  235. if y == m.symbolSize-1 && (bitBuffer&0x7f) == 0x5d {
  236. penalty += penaltyWeight3
  237. bitBuffer = 0xFF
  238. }
  239. }
  240. }
  241. }
  242. return penalty
  243. }
  244. // penalty4 returns the penalty score...
  245. func (m *symbol) penalty4() int {
  246. numModules := m.symbolSize * m.symbolSize
  247. numDarkModules := 0
  248. for x := 0; x < m.symbolSize; x++ {
  249. for y := 0; y < m.symbolSize; y++ {
  250. if v := m.get(x, y); v {
  251. numDarkModules++
  252. }
  253. }
  254. }
  255. numDarkModuleDeviation := numModules/2 - numDarkModules
  256. if numDarkModuleDeviation < 0 {
  257. numDarkModuleDeviation *= -1
  258. }
  259. return penaltyWeight4 * (numDarkModuleDeviation / (numModules / 20))
  260. }