summaryrefslogtreecommitdiff
path: root/genqrcode/tests/test_mask.c
diff options
context:
space:
mode:
Diffstat (limited to 'genqrcode/tests/test_mask.c')
-rw-r--r--genqrcode/tests/test_mask.c411
1 files changed, 411 insertions, 0 deletions
diff --git a/genqrcode/tests/test_mask.c b/genqrcode/tests/test_mask.c
new file mode 100644
index 0000000000..7ef7123251
--- /dev/null
+++ b/genqrcode/tests/test_mask.c
@@ -0,0 +1,411 @@
+#include <stdio.h>
+#include <string.h>
+#include "common.h"
+#include "../mask.h"
+#include "../qrspec.h"
+#include "decoder.h"
+
+static char dot[2] = {'_', '#'};
+static char *maskPatterns[8] = {
+ /* (i + j) mod 2 = 0 */
+ "#_#_#_"
+ "_#_#_#"
+ "#_#_#_"
+ "_#_#_#"
+ "#_#_#_"
+ "_#_#_#",
+ /* i mod 2 = 0 */
+ "######"
+ "______"
+ "######"
+ "______"
+ "######"
+ "______",
+ /* j mod 3 = 0 */
+ "#__#__"
+ "#__#__"
+ "#__#__"
+ "#__#__"
+ "#__#__"
+ "#__#__",
+ /* (i + j) mod 3 = 0 */
+ "#__#__"
+ "__#__#"
+ "_#__#_"
+ "#__#__"
+ "__#__#"
+ "_#__#_",
+ /* ((i div 2) + (j div 3)) mod 2 = 0 */
+ "###___"
+ "###___"
+ "___###"
+ "___###"
+ "###___"
+ "###___",
+ /* (ij) mod 2 + (ij) mod 3 = 0 */
+ "######"
+ "#_____"
+ "#__#__"
+ "#_#_#_"
+ "#__#__"
+ "#_____",
+ /* ((ij) mod 2 + (ij) mod 3) mod 2 = 0 */
+ "######"
+ "###___"
+ "##_##_"
+ "#_#_#_"
+ "#_##_#"
+ "#___##",
+ /* ((ij) mod 3 + (i+j) mod 2) mod 2 = 0 */
+ "#_#_#_"
+ "___###"
+ "#___##"
+ "_#_#_#"
+ "###___"
+ "_###__"
+};
+
+static void print_mask(int mask)
+{
+ const unsigned int w = 6;
+ unsigned char frame[w * w], *masked, *p;
+ int x, y;
+
+ memset(frame, 0, w * w);
+ masked = Mask_makeMaskedFrame(w, frame, mask);
+ p = masked;
+ for(y=0; y<w; y++) {
+ for(x=0; x<w; x++) {
+ putchar(dot[*p&1]);
+ p++;
+ }
+ printf("\n");
+ }
+ printf("\n");
+
+ free(masked);
+}
+
+static void print_masks(void)
+{
+ int i;
+
+ puts("\nPrinting mask patterns.");
+ for(i=0; i<8; i++) {
+ print_mask(i);
+ }
+}
+
+static int test_mask(int mask)
+{
+ const int w = 6;
+ unsigned char frame[w * w], *masked, *p;
+ char *q;
+ int x, y;
+ int err = 0;
+
+ memset(frame, 0, w * w);
+ masked = Mask_makeMaskedFrame(w, frame, mask);
+ p = masked;
+ q = maskPatterns[mask];
+ for(y=0; y<w; y++) {
+ for(x=0; x<w; x++) {
+ if(dot[*p&1] != *q) {
+ err++;
+ }
+ p++;
+ q++;
+ }
+ }
+
+ free(masked);
+ return err;
+}
+
+static void test_masks(void)
+{
+ int i;
+
+ testStart("Mask pattern checks");
+ for(i=0; i<8; i++) {
+ assert_zero(test_mask(i), "Mask pattern %d incorrect.\n", i);
+ }
+ testFinish();
+}
+
+#define N1 (3)
+#define N2 (3)
+#define N3 (40)
+#define N4 (10)
+
+static void test_eval(void)
+{
+ unsigned char *frame;
+ unsigned int w = 6;
+ int penalty;
+
+ frame = (unsigned char *)malloc(w * w);
+
+ testStart("Test mask evaluation (all white)");
+ memset(frame, 0, w * w);
+ penalty = Mask_evaluateSymbol(w, frame);
+ testEndExp(penalty == ((N1 + 1)*w*2 + N2 * (w - 1) * (w - 1)));
+
+ testStart("Test mask evaluation (all black)");
+ memset(frame, 1, w * w);
+ penalty = Mask_evaluateSymbol(w, frame);
+ testEndExp(penalty == ((N1 + 1)*w*2 + N2 * (w - 1) * (w - 1)));
+
+ free(frame);
+}
+
+/* .#.#.#.#.#
+ * #.#.#.#.#.
+ * ..##..##..
+ * ##..##..##
+ * ...###...#
+ * ###...###.
+ * ....####..
+ * ####....##
+ * .....#####
+ * #####.....
+ */
+static void test_eval2(void)
+{
+ unsigned char *frame;
+ unsigned int w = 10;
+ int penalty;
+ unsigned int x;
+
+ frame = (unsigned char *)malloc(w * w);
+
+ testStart("Test mask evaluation (run length penalty check)");
+ for(x=0; x<w; x++) {
+ frame[ x] = x & 1;
+ frame[w + x] = (x & 1) ^ 1;
+ frame[w*2 + x] = (x / 2) & 1;
+ frame[w*3 + x] = ((x / 2) & 1) ^ 1;
+ frame[w*4 + x] = (x / 3) & 1;
+ frame[w*5 + x] = ((x / 3) & 1) ^ 1;
+ frame[w*6 + x] = (x / 4) & 1;
+ frame[w*7 + x] = ((x / 4) & 1) ^ 1;
+ frame[w*8 + x] = (x / 5) & 1;
+ frame[w*9 + x] = ((x / 5) & 1) ^ 1;
+ }
+ penalty = Mask_evaluateSymbol(w, frame);
+ testEndExp(penalty == N1 * 4 + N2 * 4);
+
+ free(frame);
+}
+
+static void test_calcN2(void)
+{
+ unsigned char frame[64];
+ int width;
+ int penalty;
+ int x, y;
+
+ testStart("Test mask evaluation (2x2 block check)");
+ width = 4;
+ for(y = 0; y < width; y++) {
+ for(x = 0; x < width; x++) {
+ frame[y * width + x] = ((x & 2) ^ (y & 2)) >> 1;
+ }
+ }
+ penalty = Mask_calcN2(width, frame);
+ assert_equal(penalty, N2 * 4, "Calculation of N2 penalty is wrong: %d, expected %d", penalty, N2 * 4);
+
+ width = 4;
+ for(y = 0; y < width; y++) {
+ for(x = 0; x < width; x++) {
+ frame[y * width + x] = (((x + 1) & 2) ^ (y & 2)) >> 1;
+ }
+ }
+ penalty = Mask_calcN2(width, frame);
+ assert_equal(penalty, N2 * 2, "Calculation of N2 penalty is wrong: %d, expected %d", penalty, N2 * 2);
+
+ width = 6;
+ for(y = 0; y < width; y++) {
+ for(x = 0; x < width; x++) {
+ frame[y * width + x] = (x / 3) ^ (y / 3);
+ }
+ }
+ penalty = Mask_calcN2(width, frame);
+ assert_equal(penalty, N2 * 16, "Calculation of N2 penalty is wrong: %d, expected %d", penalty, N2 * 16);
+
+ testFinish();
+}
+
+static void test_eval3(void)
+{
+ unsigned char *frame;
+ int w = 15;
+ int penalty;
+ int x, y;
+ static unsigned char pattern[7][15] = {
+ {0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0}, // N3x1
+ {1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1}, // N3x1
+ {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1}, // N3x1
+ {1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0}, // 0
+ {1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1}, // N3x2
+ {1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0}, // N3 + (N1+1)
+ {1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1} // (N1+1)
+ };
+
+ frame = (unsigned char *)malloc(w * w);
+
+ testStart("Test mask evaluation (1:1:3:1:1 check)");
+
+ for(y=0; y<5; y++) {
+ for(x=0; x<w; x++) {
+ frame[w*y*2 + x] = pattern[y][x];
+ frame[w*(y*2+1) + x] = pattern[y][x]^1;
+ }
+ }
+ for(x=0; x<w; x++) {
+ frame[w*10 + x] = x & 1;
+ }
+ for(y=5; y<7; y++) {
+ for(x=0; x<w; x++) {
+ frame[w*(y*2+1) + x] = pattern[y][x];
+ frame[w*(y*2+2) + x] = pattern[y][x]^1;
+ }
+ }
+ /*
+ for(y=0; y<w; y++) {
+ for(x=0; x<w; x++) {
+ printf("%s", frame[w*y+x]?"##":"..");
+ }
+ printf("\n");
+ }
+ */
+ penalty = Mask_evaluateSymbol(w, frame);
+ testEndExp(penalty == N3 * 10 + (N1 + 1) * 4);
+
+ free(frame);
+}
+
+static void test_format(void)
+{
+ unsigned char *frame, *masked;
+ int version, mask, width, dmask;
+ QRecLevel level, dlevel;
+ QRcode *code;
+ int ret;
+
+ testStart("Checking format info.");
+ for(version=1; version<=QRSPEC_VERSION_MAX; version++) {
+ frame = QRspec_newFrame(version);
+ width = QRspec_getWidth(version);
+ for(level=QR_ECLEVEL_L; level<=QR_ECLEVEL_H; level++) {
+ for(mask=0; mask<8; mask++) {
+ masked = Mask_makeMask(width, frame, mask, level);
+ code = QRcode_new(version, width, masked);
+ ret = QRcode_decodeFormat(code, &dlevel, &dmask);
+ assert_zero(ret, "Something wrong in format info.\n");
+ assert_equal(dlevel, level, "Decoded level is wrong: %d, expected %d", dlevel, level);
+ assert_equal(dmask, mask, "Decoded mask is wrong: %d, expected %d", dlevel, level);
+ QRcode_free(code);
+ }
+ }
+ free(frame);
+ }
+ testFinish();
+}
+
+static void test_calcRunLength(void)
+{
+ int width = 5;
+ unsigned char frame[width * width];
+ int runLength[width + 1];
+ int i, j;
+ int length;
+ static unsigned char pattern[6][5] = {
+ {0, 1, 0, 1, 0},
+ {1, 0, 1, 0, 1},
+ {0, 0, 0, 0, 0},
+ {1, 1, 1, 1, 1},
+ {0, 0, 1, 1, 1},
+ {1, 1, 0, 0, 0}
+ };
+ static int expected[6][7] = {
+ { 1, 1, 1, 1, 1, 0, 5},
+ {-1, 1, 1, 1, 1, 1, 6},
+ { 5, 0, 0, 0, 0, 0, 1},
+ {-1, 5, 0, 0, 0, 0, 2},
+ { 2, 3, 0, 0, 0, 0, 2},
+ {-1, 2, 3, 0, 0, 0, 3}
+ };
+
+ testStart("Test runlength calc function");
+ for(i=0; i<6; i++) {
+ length = Mask_calcRunLengthH(width, pattern[i], runLength);
+ assert_equal(expected[i][6], length, "Length incorrect: %d, expected %d.\n", length, expected[i][6]);
+ assert_zero(memcmp(runLength, expected[i], sizeof(int) * expected[i][6]), "Run length does not match: pattern %d, horizontal access.\n", i);
+ for(j=0; j<width; j++) {
+ frame[j * width] = pattern[i][j];
+ }
+ length = Mask_calcRunLengthV(width, frame, runLength);
+ assert_equal(expected[i][6], length, "Length incorrect: %d, expected %d.\n", length, expected[i][6]);
+ assert_zero(memcmp(runLength, expected[i], sizeof(int) * expected[i][6]), "Run length does not match: pattern %d, vertical access.\n", i);
+ }
+ testFinish();
+}
+
+static void test_calcN1N3(void)
+{
+ int runLength[26];
+ int length;
+ int penalty;
+ int i;
+ static unsigned char pattern[][16] = {
+ {1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, N3},
+ {0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, N3},
+ {1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0},
+ {1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, N3},
+ {1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, N3},
+ {1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, N3 * 2},
+ };
+
+ static unsigned char pattern2[][19] = {
+ {1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, N3 + N1 + 1},
+ {0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, N3 + N1 + 1},
+ {1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, N1 + 1},
+ {1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, N3 + N1 + 1},
+ {1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, N3 + N1 + 1}
+ };
+
+ testStart("Test N3 penalty calculation");
+ for(i=0; i<6; i++) {
+ length = Mask_calcRunLengthH(15, pattern[i], runLength);
+ penalty = Mask_calcN1N3(length, runLength);
+ assert_equal(pattern[i][15], penalty, "N3 penalty is wrong: %d, expected %d\n", penalty, pattern[i][15]);
+ }
+ for(i=0; i<5; i++) {
+ length = Mask_calcRunLengthH(18, pattern2[i], runLength);
+ penalty = Mask_calcN1N3(length, runLength);
+ assert_equal(pattern2[i][18], penalty, "N3 penalty is wrong: %d, expected %d\n", penalty, pattern2[i][18]);
+ }
+ testFinish();
+}
+
+int main(int argc, char **argv)
+{
+ int tests = 9;
+ testInit(tests);
+ test_masks();
+ test_eval();
+ test_eval2();
+ test_eval3();
+ test_format();
+ test_calcN2();
+ test_calcRunLength();
+ test_calcN1N3();
+ testReport(tests);
+
+ if(argc > 1) {
+ print_masks();
+ }
+
+ return 0;
+}