QRBitMatrixParser.cpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. /*
  2. * Copyright 2016 Nu-book Inc.
  3. * Copyright 2016 ZXing authors
  4. * Copyright 2023 gitlost
  5. */
  6. // SPDX-License-Identifier: Apache-2.0
  7. #include "QRBitMatrixParser.h"
  8. #include "BitArray.h"
  9. #include "BitMatrix.h"
  10. #include "ByteArray.h"
  11. #include "QRDataMask.h"
  12. #include "QRFormatInformation.h"
  13. #include "QRVersion.h"
  14. #include <utility>
  15. namespace ZXing::QRCode {
  16. static bool getBit(const BitMatrix& bitMatrix, int x, int y, bool mirrored = false)
  17. {
  18. return mirrored ? bitMatrix.get(y, x) : bitMatrix.get(x, y);
  19. }
  20. const Version* ReadVersion(const BitMatrix& bitMatrix, Type type)
  21. {
  22. assert(Version::HasValidSize(bitMatrix));
  23. int number = Version::Number(bitMatrix);
  24. switch (type) {
  25. case Type::Micro: return Version::Micro(number);
  26. case Type::rMQR: return Version::rMQR(number);
  27. case Type::Model1: return Version::Model1(number);
  28. case Type::Model2: return Version::Model2(number);
  29. }
  30. return nullptr;
  31. }
  32. FormatInformation ReadFormatInformation(const BitMatrix& bitMatrix)
  33. {
  34. if (Version::HasValidSize(bitMatrix, Type::Micro)) {
  35. // Read top-left format info bits
  36. int formatInfoBits = 0;
  37. for (int x = 1; x < 9; x++)
  38. AppendBit(formatInfoBits, getBit(bitMatrix, x, 8));
  39. for (int y = 7; y >= 1; y--)
  40. AppendBit(formatInfoBits, getBit(bitMatrix, 8, y));
  41. return FormatInformation::DecodeMQR(formatInfoBits);
  42. }
  43. if (Version::HasValidSize(bitMatrix, Type::rMQR)) {
  44. // Read top-left format info bits
  45. uint32_t formatInfoBits1 = 0;
  46. for (int y = 3; y >= 1; y--)
  47. AppendBit(formatInfoBits1, getBit(bitMatrix, 11, y));
  48. for (int x = 10; x >= 8; x--)
  49. for (int y = 5; y >= 1; y--)
  50. AppendBit(formatInfoBits1, getBit(bitMatrix, x, y));
  51. // Read bottom-right format info bits
  52. uint32_t formatInfoBits2 = 0;
  53. const int width = bitMatrix.width();
  54. const int height = bitMatrix.height();
  55. for (int x = 3; x <= 5; x++)
  56. AppendBit(formatInfoBits2, getBit(bitMatrix, width - x, height - 6));
  57. for (int x = 6; x <= 8; x++)
  58. for (int y = 2; y <= 6; y++)
  59. AppendBit(formatInfoBits2, getBit(bitMatrix, width - x, height - y));
  60. return FormatInformation::DecodeRMQR(formatInfoBits1, formatInfoBits2);
  61. }
  62. // Read top-left format info bits
  63. int formatInfoBits1 = 0;
  64. for (int x = 0; x < 6; x++)
  65. AppendBit(formatInfoBits1, getBit(bitMatrix, x, 8));
  66. // .. and skip a bit in the timing pattern ...
  67. AppendBit(formatInfoBits1, getBit(bitMatrix, 7, 8));
  68. AppendBit(formatInfoBits1, getBit(bitMatrix, 8, 8));
  69. AppendBit(formatInfoBits1, getBit(bitMatrix, 8, 7));
  70. // .. and skip a bit in the timing pattern ...
  71. for (int y = 5; y >= 0; y--)
  72. AppendBit(formatInfoBits1, getBit(bitMatrix, 8, y));
  73. // Read the top-right/bottom-left pattern including the 'Dark Module' from the bottom-left
  74. // part that has to be considered separately when looking for mirrored symbols.
  75. // See also FormatInformation::DecodeQR
  76. int dimension = bitMatrix.height();
  77. int formatInfoBits2 = 0;
  78. for (int y = dimension - 1; y >= dimension - 8; y--)
  79. AppendBit(formatInfoBits2, getBit(bitMatrix, 8, y));
  80. for (int x = dimension - 8; x < dimension; x++)
  81. AppendBit(formatInfoBits2, getBit(bitMatrix, x, 8));
  82. return FormatInformation::DecodeQR(formatInfoBits1, formatInfoBits2);
  83. }
  84. static ByteArray ReadQRCodewords(const BitMatrix& bitMatrix, const Version& version, const FormatInformation& formatInfo)
  85. {
  86. BitMatrix functionPattern = version.buildFunctionPattern();
  87. ByteArray result;
  88. result.reserve(version.totalCodewords());
  89. uint8_t currentByte = 0;
  90. bool readingUp = true;
  91. int bitsRead = 0;
  92. int dimension = bitMatrix.height();
  93. // Read columns in pairs, from right to left
  94. for (int x = dimension - 1; x > 0; x -= 2) {
  95. // Skip whole column with vertical timing pattern.
  96. if (x == 6)
  97. x--;
  98. // Read alternatingly from bottom to top then top to bottom
  99. for (int row = 0; row < dimension; row++) {
  100. int y = readingUp ? dimension - 1 - row : row;
  101. for (int col = 0; col < 2; col++) {
  102. int xx = x - col;
  103. // Ignore bits covered by the function pattern
  104. if (!functionPattern.get(xx, y)) {
  105. // Read a bit
  106. AppendBit(currentByte,
  107. GetDataMaskBit(formatInfo.dataMask, xx, y) != getBit(bitMatrix, xx, y, formatInfo.isMirrored));
  108. // If we've made a whole byte, save it off
  109. if (++bitsRead % 8 == 0)
  110. result.push_back(std::exchange(currentByte, 0));
  111. }
  112. }
  113. }
  114. readingUp = !readingUp; // switch directions
  115. }
  116. if (Size(result) != version.totalCodewords())
  117. return {};
  118. return result;
  119. }
  120. static ByteArray ReadQRCodewordsModel1(const BitMatrix& bitMatrix, const Version& version, const FormatInformation& formatInfo)
  121. {
  122. ByteArray result;
  123. result.reserve(version.totalCodewords());
  124. int dimension = bitMatrix.height();
  125. int columns = dimension / 4 + 1 + 2;
  126. for (int j = 0; j < columns; j++) {
  127. if (j <= 1) { // vertical symbols on the right side
  128. int rows = (dimension - 8) / 4;
  129. for (int i = 0; i < rows; i++) {
  130. if (j == 0 && i % 2 == 0 && i > 0 && i < rows - 1) // extension
  131. continue;
  132. int x = (dimension - 1) - (j * 2);
  133. int y = (dimension - 1) - (i * 4);
  134. uint8_t currentByte = 0;
  135. for (int b = 0; b < 8; b++) {
  136. AppendBit(currentByte, GetDataMaskBit(formatInfo.dataMask, x - b % 2, y - (b / 2))
  137. != getBit(bitMatrix, x - b % 2, y - (b / 2), formatInfo.isMirrored));
  138. }
  139. result.push_back(currentByte);
  140. }
  141. } else if (columns - j <= 4) { // vertical symbols on the left side
  142. int rows = (dimension - 16) / 4;
  143. for (int i = 0; i < rows; i++) {
  144. int x = (columns - j - 1) * 2 + 1 + (columns - j == 4 ? 1 : 0); // timing
  145. int y = (dimension - 1) - 8 - (i * 4);
  146. uint8_t currentByte = 0;
  147. for (int b = 0; b < 8; b++) {
  148. AppendBit(currentByte, GetDataMaskBit(formatInfo.dataMask, x - b % 2, y - (b / 2))
  149. != getBit(bitMatrix, x - b % 2, y - (b / 2), formatInfo.isMirrored));
  150. }
  151. result.push_back(currentByte);
  152. }
  153. } else { // horizontal symbols
  154. int rows = dimension / 2;
  155. for (int i = 0; i < rows; i++) {
  156. if (j == 2 && i >= rows - 4) // alignment & finder
  157. continue;
  158. if (i == 0 && j % 2 == 1 && j + 1 != columns - 4) // extension
  159. continue;
  160. int x = (dimension - 1) - (2 * 2) - (j - 2) * 4;
  161. int y = (dimension - 1) - (i * 2) - (i >= rows - 3 ? 1 : 0); // timing
  162. uint8_t currentByte = 0;
  163. for (int b = 0; b < 8; b++) {
  164. AppendBit(currentByte, GetDataMaskBit(formatInfo.dataMask, x - b % 4, y - (b / 4))
  165. != getBit(bitMatrix, x - b % 4, y - (b / 4), formatInfo.isMirrored));
  166. }
  167. result.push_back(currentByte);
  168. }
  169. }
  170. }
  171. result[0] &= 0xf; // ignore corner
  172. if (Size(result) != version.totalCodewords())
  173. return {};
  174. return result;
  175. }
  176. static ByteArray ReadMQRCodewords(const BitMatrix& bitMatrix, const QRCode::Version& version, const FormatInformation& formatInfo)
  177. {
  178. BitMatrix functionPattern = version.buildFunctionPattern();
  179. // D3 in a Version M1 symbol, D11 in a Version M3-L symbol and D9
  180. // in a Version M3-M symbol is a 2x2 square 4-module block.
  181. // See ISO 18004:2006 6.7.3.
  182. bool hasD4mBlock = version.versionNumber() % 2 == 1;
  183. int d4mBlockIndex =
  184. version.versionNumber() == 1 ? 3 : (formatInfo.ecLevel == QRCode::ErrorCorrectionLevel::Low ? 11 : 9);
  185. ByteArray result;
  186. result.reserve(version.totalCodewords());
  187. uint8_t currentByte = 0;
  188. bool readingUp = true;
  189. int bitsRead = 0;
  190. int dimension = bitMatrix.height();
  191. // Read columns in pairs, from right to left
  192. for (int x = dimension - 1; x > 0; x -= 2) {
  193. // Read alternatingly from bottom to top then top to bottom
  194. for (int row = 0; row < dimension; row++) {
  195. int y = readingUp ? dimension - 1 - row : row;
  196. for (int col = 0; col < 2; col++) {
  197. int xx = x - col;
  198. // Ignore bits covered by the function pattern
  199. if (!functionPattern.get(xx, y)) {
  200. // Read a bit
  201. AppendBit(currentByte,
  202. GetDataMaskBit(formatInfo.dataMask, xx, y, true) != getBit(bitMatrix, xx, y, formatInfo.isMirrored));
  203. ++bitsRead;
  204. // If we've made a whole byte, save it off; save early if 2x2 data block.
  205. if (bitsRead == 8 || (bitsRead == 4 && hasD4mBlock && Size(result) == d4mBlockIndex - 1)) {
  206. result.push_back(std::exchange(currentByte, 0));
  207. bitsRead = 0;
  208. }
  209. }
  210. }
  211. }
  212. readingUp = !readingUp; // switch directions
  213. }
  214. if (Size(result) != version.totalCodewords())
  215. return {};
  216. return result;
  217. }
  218. static ByteArray ReadRMQRCodewords(const BitMatrix& bitMatrix, const Version& version, const FormatInformation& formatInfo)
  219. {
  220. BitMatrix functionPattern = version.buildFunctionPattern();
  221. ByteArray result;
  222. result.reserve(version.totalCodewords());
  223. uint8_t currentByte = 0;
  224. bool readingUp = true;
  225. int bitsRead = 0;
  226. const int width = bitMatrix.width();
  227. const int height = bitMatrix.height();
  228. // Read columns in pairs, from right to left
  229. for (int x = width - 1 - 1; x > 0; x -= 2) { // Skip right edge alignment
  230. // Read alternatingly from bottom to top then top to bottom
  231. for (int row = 0; row < height; row++) {
  232. int y = readingUp ? height - 1 - row : row;
  233. for (int col = 0; col < 2; col++) {
  234. int xx = x - col;
  235. // Ignore bits covered by the function pattern
  236. if (!functionPattern.get(xx, y)) {
  237. // Read a bit
  238. AppendBit(currentByte,
  239. GetDataMaskBit(formatInfo.dataMask, xx, y) != getBit(bitMatrix, xx, y, formatInfo.isMirrored));
  240. // If we've made a whole byte, save it off
  241. if (++bitsRead % 8 == 0)
  242. result.push_back(std::exchange(currentByte, 0));
  243. }
  244. }
  245. }
  246. readingUp = !readingUp; // switch directions
  247. }
  248. if (Size(result) != version.totalCodewords())
  249. return {};
  250. return result;
  251. }
  252. ByteArray ReadCodewords(const BitMatrix& bitMatrix, const Version& version, const FormatInformation& formatInfo)
  253. {
  254. switch (version.type()) {
  255. case Type::Micro: return ReadMQRCodewords(bitMatrix, version, formatInfo);
  256. case Type::rMQR: return ReadRMQRCodewords(bitMatrix, version, formatInfo);
  257. case Type::Model1: return ReadQRCodewordsModel1(bitMatrix, version, formatInfo);
  258. case Type::Model2: return ReadQRCodewords(bitMatrix, version, formatInfo);
  259. }
  260. return {};
  261. }
  262. } // namespace ZXing::QRCode