From 703fcf587dc37bbabe77807a5593ebfca9d4ae41 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 19 Apr 2017 16:42:21 +0000 Subject: [PATCH] Continued implementing C library by adding functions to append Reed-Solomon ECC and interleave blocks. --- c/qrcodegen.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 104edc0..4ded8ee 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -39,6 +39,8 @@ static void drawWhiteFunctionModules(uint8_t qrcode[], int version); static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uint8_t qrcode[], int size); static int getAlignmentPatternPositions(int version, uint8_t result[7]); +static void appendErrorCorrection(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]); +static int getNumRawDataModules(int version); static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[], int version); static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], int size, int mask); @@ -48,6 +50,28 @@ static uint8_t finiteFieldMultiply(uint8_t x, uint8_t y); +/*---- Private tables of constants ----*/ + +static const int16_t NUM_ERROR_CORRECTION_CODEWORDS[4][41] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 7, 10, 15, 20, 26, 36, 40, 48, 60, 72, 80, 96, 104, 120, 132, 144, 168, 180, 196, 224, 224, 252, 270, 300, 312, 336, 360, 390, 420, 450, 480, 510, 540, 570, 570, 600, 630, 660, 720, 750}, // Low + {-1, 10, 16, 26, 36, 48, 64, 72, 88, 110, 130, 150, 176, 198, 216, 240, 280, 308, 338, 364, 416, 442, 476, 504, 560, 588, 644, 700, 728, 784, 812, 868, 924, 980, 1036, 1064, 1120, 1204, 1260, 1316, 1372}, // Medium + {-1, 13, 22, 36, 52, 72, 96, 108, 132, 160, 192, 224, 260, 288, 320, 360, 408, 448, 504, 546, 600, 644, 690, 750, 810, 870, 952, 1020, 1050, 1140, 1200, 1290, 1350, 1440, 1530, 1590, 1680, 1770, 1860, 1950, 2040}, // Quartile + {-1, 17, 28, 44, 64, 88, 112, 130, 156, 192, 224, 264, 308, 352, 384, 432, 480, 532, 588, 650, 700, 750, 816, 900, 960, 1050, 1110, 1200, 1260, 1350, 1440, 1530, 1620, 1710, 1800, 1890, 1980, 2100, 2220, 2310, 2430}, // High +}; + +const int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low + {-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium + {-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile + {-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High +}; + + + /*---- Function implementations ----*/ // Public function - see documentation comment in header file. @@ -312,6 +336,66 @@ static int getAlignmentPatternPositions(int version, uint8_t result[7]) { } +// Appends error correction bytes to each block of the given data array, then interleaves bytes +// from the blocks and stores them in the result array. data[0 : rawCodewords - totalEcc] contains +// the input data. data[rawCodewords - totalEcc : rawCodewords] is used as a temporary work area +// and will be clobbered by this function. The final answer is stored in result[0 : rawCodewords]. +static void appendErrorCorrection(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]) { + // Calculate parameter numbers + assert(0 <= (int)ecl && (int)ecl < 4 && 1 <= version && version <= 40); + int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[(int)ecl][version]; + int totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[(int)ecl][version]; + assert(totalEcc % numBlocks == 0); + int blockEccLen = totalEcc / numBlocks; + int rawCodewords = getNumRawDataModules(version) / 8; + int dataLen = rawCodewords - totalEcc; + int numShortBlocks = numBlocks - rawCodewords % numBlocks; + int shortBlockDataLen = rawCodewords / numBlocks - blockEccLen; + + // Split data into blocks and append ECC after all data + uint8_t generator[30]; + calcReedSolomonGenerator(blockEccLen, generator); + for (int i = 0, j = dataLen, k = 0; i < numBlocks; i++) { + int blockLen = shortBlockDataLen; + if (i >= numShortBlocks) + blockLen++; + calcReedSolomonRemainder(&data[k], blockLen, generator, blockEccLen, &data[j]); + j += blockEccLen; + k += blockLen; + } + + // Interleave (not concatenate) the bytes from every block into a single sequence + for (int i = 0, k = 0; i < numBlocks; i++) { + for (int j = 0, l = i; j < shortBlockDataLen; j++, k++, l += numBlocks) + result[l] = data[k]; + if (i >= numShortBlocks) + k++; + } + for (int i = numShortBlocks, l = numBlocks * shortBlockDataLen, k = (numShortBlocks + 1) * shortBlockDataLen; + i < numBlocks; i++, k += shortBlockDataLen + 1, l++) + result[l] = data[k]; + for (int i = 0, k = dataLen; i < numBlocks; i++) { + for (int j = 0, l = dataLen + i; j < blockEccLen; j++, k++, l += numBlocks) + result[l] = data[k]; + } +} + + +// Returns the number of data bits that can be stored in a QR Code of the given version number, after +// all function modules are excluded. This includes remainder bits, so it may not be a multiple of 8. +static int getNumRawDataModules(int version) { + assert(1 <= version && version <= 40); + int result = (16 * version + 128) * version + 64; + if (version >= 2) { + int numAlign = version / 7 + 2; + result -= (25 * numAlign - 10) * numAlign - 55; + if (version >= 7) + result -= 18 * 2; // Subtract version information + } + return result; +} + + // Draws the raw codewords (including data and ECC) onto the given QR Code. This requires the initial state of // the QR Code to be black at function modules and white at codeword modules (including unused remainder bits). static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[], int version) {