diff --git a/game.project b/game.project index f0fc1f6..53e5763 100644 --- a/game.project +++ b/game.project @@ -3,6 +3,7 @@ title = QRCode version = 0.1 dependencies = https://github.com/defold/extension-camera/archive/master.zip,https://github.com/andsve/dirtylarry/archive/master.zip custom_resources = main/images/defold.png +include_dirs = qrcode [bootstrap] main_collection = /main/main.collectionc diff --git a/main/main.script b/main/main.script index 5373c84..a1986af 100644 --- a/main/main.script +++ b/main/main.script @@ -22,12 +22,6 @@ local function start_scan(self) type=resource.TEXTURE_TYPE_2D, format=resource.TEXTURE_FORMAT_RGB, num_mip_maps=1 } - - self.debugtextureheader = {width=self.camerainfo.width, - height=self.camerainfo.height, - type=resource.TEXTURE_TYPE_2D, - format=resource.TEXTURE_FORMAT_RGB, - num_mip_maps=1 } end else print("could not start camera capture") @@ -43,10 +37,10 @@ local function end_scan(self) end local function update_scan(self) - + if self.cameraframe ~= nil then - local pathmodelcamera = go.get("#sprite", "texture0") - resource.set_texture(pathmodelcamera, self.cameratextureheader, self.cameraframe) + local texturepath = go.get("#sprite", "texture0") + resource.set_texture(texturepath, self.cameratextureheader, self.cameraframe) local text = qrcode.scan(self.cameraframe, self.camerainfo.width, self.camerainfo.height, self.flip) @@ -58,8 +52,57 @@ local function update_scan(self) end end +local function rescale_texture_old(src_buf, src_size, dst_buf, dst_size) + local src = buffer.get_stream(src_buf, hash("rgb")) + local dst = buffer.get_stream(dst_buf, hash("rgb")) + print("src", src_size, src_size, src_size*src_size*3) + print("dst", dst_size, dst_size, dst_size*dst_size*3) + for dy=0,dst_size-1 do + local v = dy / dst_size + for dx=0,dst_size-1 do + local u = dx / dst_size + local dst_index = (dy * dst_size + dx) * 3 + 1 + local src_index = (math.floor(v * src_size) * src_size + math.floor(u * src_size)) * 3 + 1 + print(dx, dy, dst_index, src_index) + dst[dst_index + 0] = src[src_index + 0] + dst[dst_index + 1] = src[src_index + 1] + dst[dst_index + 2] = src[src_index + 2] + end + end +end + + +local function rescale_texture(src_buf, src_size, dst_buf, dst_size) + local src = buffer.get_stream(src_buf, hash("rgb")) + local dst = buffer.get_stream(dst_buf, hash("rgb")) + print("src", src_size, src_size, src_size*src_size*3) + print("dst", dst_size, dst_size, dst_size*dst_size*3) + for dy=0,dst_size-1 do + local v = dy / dst_size + for dx=0,dst_size-1 do + local u = dx / dst_size + local dst_index = (dy * dst_size + dx) * 3 + 1 + local src_index = (math.floor(v * src_size) * src_size + math.floor(u * src_size)) * 3 + 1 + print(dx, dy, dst_index, src_index) + dst[dst_index + 0] = src[src_index + 0] + dst[dst_index + 1] = src[src_index + 1] + dst[dst_index + 2] = src[src_index + 2] + end + end +end + + local function generate_qrcode(self, text) - --self.qrcode = + local qrcode, qrsize = qrcode.generate(text) + + local qrcodetextureheader = {width=qrsize, + height=qrsize, + type=resource.TEXTURE_TYPE_2D, + format=resource.TEXTURE_FORMAT_LUMINANCE, + num_mip_maps=1 } + + local texturepath = go.get("#sprite", "texture0") + resource.set_texture(texturepath, qrcodetextureheader, qrcode) end function init(self) @@ -68,11 +111,11 @@ function init(self) local screen_height = sys.get_config("display.height", 800) local scale_width = screen_width / logosize local scale_height = screen_height / logosize - + go.set("#sprite", "scale", vmath.vector3(scale_width, scale_height, 1) ) - + start_scan(self) - + if qrcode == nil then print("could not find qrcode module") end @@ -88,13 +131,11 @@ function update(self, dt) if self.mode == "SCAN" then update_scan(self) - end + end end function on_input(self, action_id, action) if action_id == hash("click") or action_id == hash("touch") then - --pprint(action) - --label.set_text("logo#qrcode", "...") print("foo") end end @@ -107,7 +148,7 @@ function on_message(self, message_id, message) pprint(self.cameraframe) else end_scan(self) - generate_qrcode(self) + generate_qrcode(self, message.text) end self.mode = message.mode end diff --git a/qrcode/src/generator/BitBuffer.cpp b/qrcode/src/generator/BitBuffer.cpp deleted file mode 100644 index 4b48cb7..0000000 --- a/qrcode/src/generator/BitBuffer.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * QR Code generator library (C++) - * - * Copyright (c) Project Nayuki - * https://www.nayuki.io/page/qr-code-generator-library - * - * (MIT License) - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - The Software is provided "as is", without warranty of any kind, express or - * implied, including but not limited to the warranties of merchantability, - * fitness for a particular purpose and noninfringement. In no event shall the - * authors or copyright holders be liable for any claim, damages or other - * liability, whether in an action of contract, tort or otherwise, arising from, - * out of or in connection with the Software or the use or other dealings in the - * Software. - */ - -#include -#include "BitBuffer.hpp" - - -qrcodegen::BitBuffer::BitBuffer() : - data(), - bitLength(0) {} - - -int qrcodegen::BitBuffer::getBitLength() const { - return bitLength; -} - - -std::vector qrcodegen::BitBuffer::getBytes() const { - return data; -} - - -void qrcodegen::BitBuffer::appendBits(uint32_t val, int len) { - if (len < 0 || len > 32 || (len < 32 && (val >> len) != 0)) - throw "Value out of range"; - size_t newBitLen = bitLength + len; - while (data.size() * 8 < newBitLen) - data.push_back(0); - for (int i = len - 1; i >= 0; i--, bitLength++) // Append bit by bit - data.at(bitLength >> 3) |= ((val >> i) & 1) << (7 - (bitLength & 7)); -} - - -void qrcodegen::BitBuffer::appendData(const QrSegment &seg) { - size_t newBitLen = bitLength + seg.bitLength; - while (data.size() * 8 < newBitLen) - data.push_back(0); - for (int i = 0; i < seg.bitLength; i++, bitLength++) { // Append bit by bit - int bit = (seg.data.at(i >> 3) >> (7 - (i & 7))) & 1; - data.at(bitLength >> 3) |= bit << (7 - (bitLength & 7)); - } -} diff --git a/qrcode/src/generator/BitBuffer.hpp b/qrcode/src/generator/BitBuffer.hpp deleted file mode 100644 index 0a11a98..0000000 --- a/qrcode/src/generator/BitBuffer.hpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * QR Code generator library (C++) - * - * Copyright (c) Project Nayuki - * https://www.nayuki.io/page/qr-code-generator-library - * - * (MIT License) - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - The Software is provided "as is", without warranty of any kind, express or - * implied, including but not limited to the warranties of merchantability, - * fitness for a particular purpose and noninfringement. In no event shall the - * authors or copyright holders be liable for any claim, damages or other - * liability, whether in an action of contract, tort or otherwise, arising from, - * out of or in connection with the Software or the use or other dealings in the - * Software. - */ - -#pragma once - -#include -#include -#include "QrSegment.hpp" - - -namespace qrcodegen { - -/* - * An appendable sequence of bits. Bits are packed in big endian within a byte. - */ -class BitBuffer final { - - /*---- Fields ----*/ -private: - - std::vector data; - int bitLength; - - - - /*---- Constructor ----*/ -public: - - // Creates an empty bit buffer (length 0). - BitBuffer(); - - - - /*---- Methods ----*/ -public: - - // Returns the number of bits in the buffer, which is a non-negative value. - int getBitLength() const; - - - // Returns a copy of all bytes, padding up to the nearest byte. - std::vector getBytes() const; - - - // Appends the given number of bits of the given value to this sequence. - // If 0 <= len <= 31, then this requires 0 <= val < 2^len. - void appendBits(uint32_t val, int len); - - - // Appends the data of the given segment to this bit buffer. - void appendData(const QrSegment &seg); - -}; - -} diff --git a/qrcode/src/generator/QrCode.cpp b/qrcode/src/generator/QrCode.cpp deleted file mode 100644 index ef37674..0000000 --- a/qrcode/src/generator/QrCode.cpp +++ /dev/null @@ -1,616 +0,0 @@ -/* - * QR Code generator library (C++) - * - * Copyright (c) Project Nayuki - * https://www.nayuki.io/page/qr-code-generator-library - * - * (MIT License) - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - The Software is provided "as is", without warranty of any kind, express or - * implied, including but not limited to the warranties of merchantability, - * fitness for a particular purpose and noninfringement. In no event shall the - * authors or copyright holders be liable for any claim, damages or other - * liability, whether in an action of contract, tort or otherwise, arising from, - * out of or in connection with the Software or the use or other dealings in the - * Software. - */ - -#include -#include -#include -#include -#include -#include "BitBuffer.hpp" -#include "QrCode.hpp" - - -qrcodegen::QrCode::Ecc::Ecc(int ord, int fb) : - ordinal(ord), - formatBits(fb) {} - - -const qrcodegen::QrCode::Ecc qrcodegen::QrCode::Ecc::LOW (0, 1); -const qrcodegen::QrCode::Ecc qrcodegen::QrCode::Ecc::MEDIUM (1, 0); -const qrcodegen::QrCode::Ecc qrcodegen::QrCode::Ecc::QUARTILE(2, 3); -const qrcodegen::QrCode::Ecc qrcodegen::QrCode::Ecc::HIGH (3, 2); - - -qrcodegen::QrCode qrcodegen::QrCode::encodeText(const char *text, const Ecc &ecl) { - std::vector segs(QrSegment::makeSegments(text)); - return encodeSegments(segs, ecl); -} - - -qrcodegen::QrCode qrcodegen::QrCode::encodeBinary(const std::vector &data, const Ecc &ecl) { - std::vector segs; - segs.push_back(QrSegment::makeBytes(data)); - return encodeSegments(segs, ecl); -} - - -qrcodegen::QrCode qrcodegen::QrCode::encodeSegments(const std::vector &segs, const Ecc &ecl, - int minVersion, int maxVersion, int mask, bool boostEcl) { - if (!(1 <= minVersion && minVersion <= maxVersion && maxVersion <= 40) || mask < -1 || mask > 7) - throw "Invalid value"; - - // Find the minimal version number to use - int version, dataUsedBits; - for (version = minVersion; ; version++) { - int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available - dataUsedBits = QrSegment::getTotalBits(segs, version); - if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) - break; // This version number is found to be suitable - if (version >= maxVersion) // All versions in the range could not fit the given data - throw "Data too long"; - } - if (dataUsedBits == -1) - throw "Assertion error"; - - // Increase the error correction level while the data still fits in the current version number - const Ecc *newEcl = &ecl; - if (boostEcl) { - if (dataUsedBits <= getNumDataCodewords(version, Ecc::MEDIUM ) * 8) newEcl = &Ecc::MEDIUM ; - if (dataUsedBits <= getNumDataCodewords(version, Ecc::QUARTILE) * 8) newEcl = &Ecc::QUARTILE; - if (dataUsedBits <= getNumDataCodewords(version, Ecc::HIGH ) * 8) newEcl = &Ecc::HIGH ; - } - - // Create the data bit string by concatenating all segments - int dataCapacityBits = getNumDataCodewords(version, *newEcl) * 8; - BitBuffer bb; - for (size_t i = 0; i < segs.size(); i++) { - const QrSegment &seg(segs.at(i)); - bb.appendBits(seg.mode.modeBits, 4); - bb.appendBits(seg.numChars, seg.mode.numCharCountBits(version)); - bb.appendData(seg); - } - - // Add terminator and pad up to a byte if applicable - bb.appendBits(0, std::min(4, dataCapacityBits - bb.getBitLength())); - bb.appendBits(0, (8 - bb.getBitLength() % 8) % 8); - - // Pad with alternate bytes until data capacity is reached - for (uint8_t padByte = 0xEC; bb.getBitLength() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) - bb.appendBits(padByte, 8); - if (bb.getBitLength() % 8 != 0) - throw "Assertion error"; - - // Create the QR Code symbol - return QrCode(version, *newEcl, bb.getBytes(), mask); -} - - -qrcodegen::QrCode::QrCode(int ver, const Ecc &ecl, const std::vector &dataCodewords, int mask) : - // Initialize scalar fields - version(ver), - size(1 <= ver && ver <= 40 ? ver * 4 + 17 : -1), // Avoid signed overflow undefined behavior - errorCorrectionLevel(ecl) { - - // Check arguments - if (ver < 1 || ver > 40 || mask < -1 || mask > 7) - throw "Value out of range"; - - std::vector row(size); - for (int i = 0; i < size; i++) { - modules.push_back(row); - isFunction.push_back(row); - } - - // Draw function patterns, draw all codewords, do masking - drawFunctionPatterns(); - const std::vector allCodewords(appendErrorCorrection(dataCodewords)); - drawCodewords(allCodewords); - this->mask = handleConstructorMasking(mask); -} - - -qrcodegen::QrCode::QrCode(const QrCode &qr, int mask) : - // Copy scalar fields - version(qr.version), - size(qr.size), - errorCorrectionLevel(qr.errorCorrectionLevel) { - - // Check arguments - if (mask < -1 || mask > 7) - throw "Mask value out of range"; - - // Handle grid fields - modules = qr.modules; - isFunction = qr.isFunction; - - // Handle masking - applyMask(qr.mask); // Undo old mask - this->mask = handleConstructorMasking(mask); -} - - -int qrcodegen::QrCode::getMask() const { - return mask; -} - - -int qrcodegen::QrCode::getModule(int x, int y) const { - if (0 <= x && x < size && 0 <= y && y < size) - return modules.at(y).at(x) ? 1 : 0; - else - return 0; // Infinite white border -} - - -std::string qrcodegen::QrCode::toSvgString(int border) const { - if (border < 0) - throw "Border must be non-negative"; - std::ostringstream sb; - sb << "\n"; - sb << "\n"; - sb << "\n"; - sb << "\t\n"; - sb << "\t\n"; - sb << "\n"; - return sb.str(); -} - - -void qrcodegen::QrCode::drawFunctionPatterns() { - // Draw the horizontal and vertical timing patterns - for (int i = 0; i < size; i++) { - setFunctionModule(6, i, i % 2 == 0); - setFunctionModule(i, 6, i % 2 == 0); - } - - // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) - drawFinderPattern(3, 3); - drawFinderPattern(size - 4, 3); - drawFinderPattern(3, size - 4); - - // Draw the numerous alignment patterns - const std::vector alignPatPos(getAlignmentPatternPositions(version)); - int numAlign = alignPatPos.size(); - for (int i = 0; i < numAlign; i++) { - for (int j = 0; j < numAlign; j++) { - if ((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0)) - continue; // Skip the three finder corners - else - drawAlignmentPattern(alignPatPos.at(i), alignPatPos.at(j)); - } - } - - // Draw configuration data - drawFormatBits(0); // Dummy mask value; overwritten later in the constructor - drawVersion(); -} - - -void qrcodegen::QrCode::drawFormatBits(int mask) { - // Calculate error correction code and pack bits - int data = errorCorrectionLevel.formatBits << 3 | mask; // errCorrLvl is uint2, mask is uint3 - int rem = data; - for (int i = 0; i < 10; i++) - rem = (rem << 1) ^ ((rem >> 9) * 0x537); - data = data << 10 | rem; - data ^= 0x5412; // uint15 - if (data >> 15 != 0) - throw "Assertion error"; - - // Draw first copy - for (int i = 0; i <= 5; i++) - setFunctionModule(8, i, ((data >> i) & 1) != 0); - setFunctionModule(8, 7, ((data >> 6) & 1) != 0); - setFunctionModule(8, 8, ((data >> 7) & 1) != 0); - setFunctionModule(7, 8, ((data >> 8) & 1) != 0); - for (int i = 9; i < 15; i++) - setFunctionModule(14 - i, 8, ((data >> i) & 1) != 0); - - // Draw second copy - for (int i = 0; i <= 7; i++) - setFunctionModule(size - 1 - i, 8, ((data >> i) & 1) != 0); - for (int i = 8; i < 15; i++) - setFunctionModule(8, size - 15 + i, ((data >> i) & 1) != 0); - setFunctionModule(8, size - 8, true); -} - - -void qrcodegen::QrCode::drawVersion() { - if (version < 7) - return; - - // Calculate error correction code and pack bits - int rem = version; // version is uint6, in the range [7, 40] - for (int i = 0; i < 12; i++) - rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); - int data = version << 12 | rem; // uint18 - if (data >> 18 != 0) - throw "Assertion error"; - - // Draw two copies - for (int i = 0; i < 18; i++) { - bool bit = ((data >> i) & 1) != 0; - int a = size - 11 + i % 3, b = i / 3; - setFunctionModule(a, b, bit); - setFunctionModule(b, a, bit); - } -} - - -void qrcodegen::QrCode::drawFinderPattern(int x, int y) { - for (int i = -4; i <= 4; i++) { - for (int j = -4; j <= 4; j++) { - int dist = std::max(std::abs(i), std::abs(j)); // Chebyshev/infinity norm - int xx = x + j, yy = y + i; - if (0 <= xx && xx < size && 0 <= yy && yy < size) - setFunctionModule(xx, yy, dist != 2 && dist != 4); - } - } -} - - -void qrcodegen::QrCode::drawAlignmentPattern(int x, int y) { - for (int i = -2; i <= 2; i++) { - for (int j = -2; j <= 2; j++) - setFunctionModule(x + j, y + i, std::max(std::abs(i), std::abs(j)) != 1); - } -} - - -void qrcodegen::QrCode::setFunctionModule(int x, int y, bool isBlack) { - modules.at(y).at(x) = isBlack; - isFunction.at(y).at(x) = true; -} - - -std::vector qrcodegen::QrCode::appendErrorCorrection(const std::vector &data) const { - if (data.size() != static_cast(getNumDataCodewords(version, errorCorrectionLevel))) - throw "Invalid argument"; - - // Calculate parameter numbers - int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[errorCorrectionLevel.ordinal][version]; - int totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[errorCorrectionLevel.ordinal][version]; - if (totalEcc % numBlocks != 0) - throw "Assertion error"; - int blockEccLen = totalEcc / numBlocks; - int numShortBlocks = numBlocks - getNumRawDataModules(version) / 8 % numBlocks; - int shortBlockLen = getNumRawDataModules(version) / 8 / numBlocks; - - // Split data into blocks and append ECC to each block - std::vector> blocks; - const ReedSolomonGenerator rs(blockEccLen); - for (int i = 0, k = 0; i < numBlocks; i++) { - std::vector dat; - dat.insert(dat.begin(), data.begin() + k, data.begin() + (k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1))); - k += dat.size(); - const std::vector ecc(rs.getRemainder(dat)); - if (i < numShortBlocks) - dat.push_back(0); - dat.insert(dat.end(), ecc.begin(), ecc.end()); - blocks.push_back(dat); - } - - // Interleave (not concatenate) the bytes from every block into a single sequence - std::vector result; - for (int i = 0; static_cast(i) < blocks.at(0).size(); i++) { - for (int j = 0; static_cast(j) < blocks.size(); j++) { - // Skip the padding byte in short blocks - if (i != shortBlockLen - blockEccLen || j >= numShortBlocks) - result.push_back(blocks.at(j).at(i)); - } - } - if (result.size() != static_cast(getNumRawDataModules(version) / 8)) - throw "Assertion error"; - return result; -} - - -void qrcodegen::QrCode::drawCodewords(const std::vector &data) { - if (data.size() != static_cast(getNumRawDataModules(version) / 8)) - throw "Invalid argument"; - - size_t i = 0; // Bit index into the data - // Do the funny zigzag scan - for (int right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair - if (right == 6) - right = 5; - for (int vert = 0; vert < size; vert++) { // Vertical counter - for (int j = 0; j < 2; j++) { - int x = right - j; // Actual x coordinate - bool upwards = ((right & 2) == 0) ^ (x < 6); - int y = upwards ? size - 1 - vert : vert; // Actual y coordinate - if (!isFunction.at(y).at(x) && i < data.size() * 8) { - modules.at(y).at(x) = ((data.at(i >> 3) >> (7 - (i & 7))) & 1) != 0; - i++; - } - // If there are any remainder bits (0 to 7), they are already - // set to 0/false/white when the grid of modules was initialized - } - } - } - if (static_cast(i) != data.size() * 8) - throw "Assertion error"; -} - - -void qrcodegen::QrCode::applyMask(int mask) { - if (mask < 0 || mask > 7) - throw "Mask value out of range"; - for (int y = 0; y < size; y++) { - for (int x = 0; x < size; x++) { - bool invert; - switch (mask) { - case 0: invert = (x + y) % 2 == 0; break; - case 1: invert = y % 2 == 0; break; - case 2: invert = x % 3 == 0; break; - case 3: invert = (x + y) % 3 == 0; break; - case 4: invert = (x / 3 + y / 2) % 2 == 0; break; - case 5: invert = x * y % 2 + x * y % 3 == 0; break; - case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; - case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; - default: throw "Assertion error"; - } - modules.at(y).at(x) = modules.at(y).at(x) ^ (invert & !isFunction.at(y).at(x)); - } - } -} - - -int qrcodegen::QrCode::handleConstructorMasking(int mask) { - if (mask == -1) { // Automatically choose best mask - int32_t minPenalty = INT32_MAX; - for (int i = 0; i < 8; i++) { - drawFormatBits(i); - applyMask(i); - int penalty = getPenaltyScore(); - if (penalty < minPenalty) { - mask = i; - minPenalty = penalty; - } - applyMask(i); // Undoes the mask due to XOR - } - } - if (mask < 0 || mask > 7) - throw "Assertion error"; - drawFormatBits(mask); // Overwrite old format bits - applyMask(mask); // Apply the final choice of mask - return mask; // The caller shall assign this value to the final-declared field -} - - -int qrcodegen::QrCode::getPenaltyScore() const { - int result = 0; - - // Adjacent modules in row having same color - for (int y = 0; y < size; y++) { - bool colorX = modules.at(y).at(0); - for (int x = 1, runX = 1; x < size; x++) { - if (modules.at(y).at(x) != colorX) { - colorX = modules.at(y).at(x); - runX = 1; - } else { - runX++; - if (runX == 5) - result += PENALTY_N1; - else if (runX > 5) - result++; - } - } - } - // Adjacent modules in column having same color - for (int x = 0; x < size; x++) { - bool colorY = modules.at(0).at(x); - for (int y = 1, runY = 1; y < size; y++) { - if (modules.at(y).at(x) != colorY) { - colorY = modules.at(y).at(x); - runY = 1; - } else { - runY++; - if (runY == 5) - result += PENALTY_N1; - else if (runY > 5) - result++; - } - } - } - - // 2*2 blocks of modules having same color - for (int y = 0; y < size - 1; y++) { - for (int x = 0; x < size - 1; x++) { - bool color = modules.at(y).at(x); - if ( color == modules.at(y).at(x + 1) && - color == modules.at(y + 1).at(x) && - color == modules.at(y + 1).at(x + 1)) - result += PENALTY_N2; - } - } - - // Finder-like pattern in rows - for (int y = 0; y < size; y++) { - for (int x = 0, bits = 0; x < size; x++) { - bits = ((bits << 1) & 0x7FF) | (modules.at(y).at(x) ? 1 : 0); - if (x >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated - result += PENALTY_N3; - } - } - // Finder-like pattern in columns - for (int x = 0; x < size; x++) { - for (int y = 0, bits = 0; y < size; y++) { - bits = ((bits << 1) & 0x7FF) | (modules.at(y).at(x) ? 1 : 0); - if (y >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated - result += PENALTY_N3; - } - } - - // Balance of black and white modules - int black = 0; - for (int y = 0; y < size; y++) { - for (int x = 0; x < size; x++) { - if (modules.at(y).at(x)) - black++; - } - } - int total = size * size; - // Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% - for (int k = 0; black*20 < (9-k)*total || black*20 > (11+k)*total; k++) - result += PENALTY_N4; - return result; -} - - -std::vector qrcodegen::QrCode::getAlignmentPatternPositions(int ver) { - if (ver < 1 || ver > 40) - throw "Version number out of range"; - else if (ver == 1) - return std::vector(); - else { - int numAlign = ver / 7 + 2; - int step; - if (ver != 32) - step = (ver * 4 + numAlign * 2 + 1) / (2 * numAlign - 2) * 2; // ceil((size - 13) / (2*numAlign - 2)) * 2 - else // C-C-C-Combo breaker! - step = 26; - - std::vector result; - int size = ver * 4 + 17; - for (int i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step) - result.insert(result.begin(), pos); - result.insert(result.begin(), 6); - return result; - } -} - - -int qrcodegen::QrCode::getNumRawDataModules(int ver) { - if (ver < 1 || ver > 40) - throw "Version number out of range"; - int result = (16 * ver + 128) * ver + 64; - if (ver >= 2) { - int numAlign = ver / 7 + 2; - result -= (25 * numAlign - 10) * numAlign - 55; - if (ver >= 7) - result -= 18 * 2; // Subtract version information - } - return result; -} - - -int qrcodegen::QrCode::getNumDataCodewords(int ver, const Ecc &ecl) { - if (ver < 1 || ver > 40) - throw "Version number out of range"; - return getNumRawDataModules(ver) / 8 - NUM_ERROR_CORRECTION_CODEWORDS[ecl.ordinal][ver]; -} - - -/*---- Tables of constants ----*/ - -const int qrcodegen::QrCode::PENALTY_N1 = 3; -const int qrcodegen::QrCode::PENALTY_N2 = 3; -const int qrcodegen::QrCode::PENALTY_N3 = 40; -const int qrcodegen::QrCode::PENALTY_N4 = 10; - - -const int16_t qrcodegen::QrCode::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 qrcodegen::QrCode::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 -}; - - -qrcodegen::QrCode::ReedSolomonGenerator::ReedSolomonGenerator(int degree) : - coefficients() { - if (degree < 1 || degree > 255) - throw "Degree out of range"; - - // Start with the monomial x^0 - coefficients.resize(degree); - coefficients.at(degree - 1) = 1; - - // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), - // drop the highest term, and store the rest of the coefficients in order of descending powers. - // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). - int root = 1; - for (int i = 0; i < degree; i++) { - // Multiply the current product by (x - r^i) - for (size_t j = 0; j < coefficients.size(); j++) { - coefficients.at(j) = multiply(coefficients.at(j), static_cast(root)); - if (j + 1 < coefficients.size()) - coefficients.at(j) ^= coefficients.at(j + 1); - } - root = (root << 1) ^ ((root >> 7) * 0x11D); // Multiply by 0x02 mod GF(2^8/0x11D) - } -} - - -std::vector qrcodegen::QrCode::ReedSolomonGenerator::getRemainder(const std::vector &data) const { - // Compute the remainder by performing polynomial division - std::vector result(coefficients.size()); - for (size_t i = 0; i < data.size(); i++) { - uint8_t factor = data.at(i) ^ result.at(0); - result.erase(result.begin()); - result.push_back(0); - for (size_t j = 0; j < result.size(); j++) - result.at(j) ^= multiply(coefficients.at(j), factor); - } - return result; -} - - -uint8_t qrcodegen::QrCode::ReedSolomonGenerator::multiply(uint8_t x, uint8_t y) { - // Russian peasant multiplication - int z = 0; - for (int i = 7; i >= 0; i--) { - z = (z << 1) ^ ((z >> 7) * 0x11D); - z ^= ((y >> i) & 1) * x; - } - if (z >> 8 != 0) - throw "Assertion error"; - return static_cast(z); -} diff --git a/qrcode/src/generator/QrCode.hpp b/qrcode/src/generator/QrCode.hpp deleted file mode 100644 index 9ae0c11..0000000 --- a/qrcode/src/generator/QrCode.hpp +++ /dev/null @@ -1,314 +0,0 @@ -/* - * QR Code generator library (C++) - * - * Copyright (c) Project Nayuki - * https://www.nayuki.io/page/qr-code-generator-library - * - * (MIT License) - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - The Software is provided "as is", without warranty of any kind, express or - * implied, including but not limited to the warranties of merchantability, - * fitness for a particular purpose and noninfringement. In no event shall the - * authors or copyright holders be liable for any claim, damages or other - * liability, whether in an action of contract, tort or otherwise, arising from, - * out of or in connection with the Software or the use or other dealings in the - * Software. - */ - -#pragma once - -#include -#include -#include -#include "QrSegment.hpp" - - -namespace qrcodegen { - -/* - * Represents an immutable square grid of black and white cells for a QR Code symbol, and - * provides static functions to create a QR Code from user-supplied textual or binary data. - * This class covers the QR Code model 2 specification, supporting all versions (sizes) - * from 1 to 40, all 4 error correction levels, and only 3 character encoding modes. - */ -class QrCode final { - - /*---- Public helper enumeration ----*/ -public: - - /* - * Represents the error correction level used in a QR Code symbol. - */ - class Ecc final { - // Constants declared in ascending order of error protection. - public: - const static Ecc LOW, MEDIUM, QUARTILE, HIGH; - - // Fields. - public: - const int ordinal; // (Public) In the range 0 to 3 (unsigned 2-bit integer). - const int formatBits; // (Package-private) In the range 0 to 3 (unsigned 2-bit integer). - - // Constructor. - private: - Ecc(int ord, int fb); - }; - - - - /*---- Public static factory functions ----*/ -public: - - /* - * Returns a QR Code symbol representing the given Unicode text string at the given error correction level. - * As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode - * code points (not UTF-16 code units). The smallest possible QR Code version is automatically chosen for the output. - * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. - */ - static QrCode encodeText(const char *text, const Ecc &ecl); - - - /* - * Returns a QR Code symbol representing the given binary data string at the given error correction level. - * This function always encodes using the binary segment mode, not any text mode. The maximum number of - * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. - * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. - */ - static QrCode encodeBinary(const std::vector &data, const Ecc &ecl); - - - /* - * Returns a QR Code symbol representing the given data segments with the given encoding parameters. - * The smallest possible QR Code version within the given range is automatically chosen for the output. - * This function allows the user to create a custom sequence of segments that switches - * between modes (such as alphanumeric and binary) to encode text more efficiently. - * This function is considered to be lower level than simply encoding text or binary data. - */ - static QrCode encodeSegments(const std::vector &segs, const Ecc &ecl, - int minVersion=1, int maxVersion=40, int mask=-1, bool boostEcl=true); // All optional parameters - - - - /*---- Instance fields ----*/ - - // Public immutable scalar parameters -public: - - /* This QR Code symbol's version number, which is always between 1 and 40 (inclusive). */ - const int version; - - /* The width and height of this QR Code symbol, measured in modules. - * Always equal to version × 4 + 17, in the range 21 to 177. */ - const int size; - - /* The error correction level used in this QR Code symbol. */ - const Ecc &errorCorrectionLevel; - - /* The mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer). - * Note that even if a constructor was called with automatic masking requested - * (mask = -1), the resulting object will still have a mask value between 0 and 7. */ -private: - int mask; - - // Private grids of modules/pixels (conceptually immutable) -private: - std::vector> modules; // The modules of this QR Code symbol (false = white, true = black) - std::vector> isFunction; // Indicates function modules that are not subjected to masking - - - - /*---- Constructors ----*/ -public: - - /* - * Creates a new QR Code symbol with the given version number, error correction level, binary data array, - * and mask number. This is a cumbersome low-level constructor that should not be invoked directly by the user. - * To go one level up, see the encodeSegments() function. - */ - QrCode(int ver, const Ecc &ecl, const std::vector &dataCodewords, int mask); - - - /* - * Creates a new QR Code symbol based on the given existing object, but with a potentially - * different mask pattern. The version, error correction level, codewords, etc. of the newly - * created object are all identical to the argument object; only the mask may differ. - */ - QrCode(const QrCode &qr, int mask); - - - - /*---- Public instance methods ----*/ -public: - - int getMask() const; - - - /* - * Returns the color of the module (pixel) at the given coordinates, which is either 0 for white or 1 for black. The top - * left corner has the coordinates (x=0, y=0). If the given coordinates are out of bounds, then 0 (white) is returned. - */ - int getModule(int x, int y) const; - - - /* - * Based on the given number of border modules to add as padding, this returns a - * string whose contents represents an SVG XML file that depicts this QR Code symbol. - * Note that Unix newlines (\n) are always used, regardless of the platform. - */ - std::string toSvgString(int border) const; - - - - /*---- Private helper methods for constructor: Drawing function modules ----*/ -private: - - void drawFunctionPatterns(); - - - // Draws two copies of the format bits (with its own error correction code) - // based on the given mask and this object's error correction level field. - void drawFormatBits(int mask); - - - // Draws two copies of the version bits (with its own error correction code), - // based on this object's version field (which only has an effect for 7 <= version <= 40). - void drawVersion(); - - - // Draws a 9*9 finder pattern including the border separator, with the center module at (x, y). - void drawFinderPattern(int x, int y); - - - // Draws a 5*5 alignment pattern, with the center module at (x, y). - void drawAlignmentPattern(int x, int y); - - - // Sets the color of a module and marks it as a function module. - // Only used by the constructor. Coordinates must be in range. - void setFunctionModule(int x, int y, bool isBlack); - - - /*---- Private helper methods for constructor: Codewords and masking ----*/ -private: - - // Returns a new byte string representing the given data with the appropriate error correction - // codewords appended to it, based on this object's version and error correction level. - std::vector appendErrorCorrection(const std::vector &data) const; - - - // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire - // data area of this QR Code symbol. Function modules need to be marked off before this is called. - void drawCodewords(const std::vector &data); - - - // XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical - // properties, calling applyMask(m) twice with the same value is equivalent to no change at all. - // This means it is possible to apply a mask, undo it, and try another mask. Note that a final - // well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.). - void applyMask(int mask); - - - // A messy helper function for the constructors. This QR Code must be in an unmasked state when this - // method is called. The given argument is the requested mask, which is -1 for auto or 0 to 7 for fixed. - // This method applies and returns the actual mask chosen, from 0 to 7. - int handleConstructorMasking(int mask); - - - // Calculates and returns the penalty score based on state of this QR Code's current modules. - // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. - int getPenaltyScore() const; - - - - /*---- Private static helper functions ----*/ -private: - - // Returns a set of positions of the alignment patterns in ascending order. These positions are - // used on both the x and y axes. Each value in the resulting array is in the range [0, 177). - // This stateless pure function could be implemented as table of 40 variable-length lists of unsigned bytes. - static std::vector getAlignmentPatternPositions(int ver); - - - // Returns the number of raw data modules (bits) available at the given version number. - // These data modules are used for both user data codewords and error correction codewords. - // This stateless pure function could be implemented as a 40-entry lookup table. - static int getNumRawDataModules(int ver); - - - // Returns the number of 8-bit data (i.e. not error correction) codewords contained in any - // QR Code of the given version number and error correction level, with remainder bits discarded. - // This stateless pure function could be implemented as a (40*4)-cell lookup table. - static int getNumDataCodewords(int ver, const Ecc &ecl); - - - /*---- Private tables of constants ----*/ -private: - - // For use in getPenaltyScore(), when evaluating which mask is best. - static const int PENALTY_N1; - static const int PENALTY_N2; - static const int PENALTY_N3; - static const int PENALTY_N4; - - static const int16_t NUM_ERROR_CORRECTION_CODEWORDS[4][41]; - static const int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41]; - - - - /*---- Private helper class ----*/ -private: - - /* - * Computes the Reed-Solomon error correction codewords for a sequence of data codewords - * at a given degree. Objects are immutable, and the state only depends on the degree. - * This class exists because the divisor polynomial does not need to be recalculated for every input. - */ - class ReedSolomonGenerator final { - - /*-- Immutable field --*/ - private: - - // Coefficients of the divisor polynomial, stored from highest to lowest power, excluding the leading term which - // is always 1. For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. - std::vector coefficients; - - - /*-- Constructor --*/ - public: - - /* - * Creates a Reed-Solomon ECC generator for the given degree. This could be implemented - * as a lookup table over all possible parameter values, instead of as an algorithm. - */ - ReedSolomonGenerator(int degree); - - - /*-- Method --*/ - public: - - /* - * Computes and returns the Reed-Solomon error correction codewords for the given sequence of data codewords. - * The returned object is always a new byte array. This method does not alter this object's state (because it is immutable). - */ - std::vector getRemainder(const std::vector &data) const; - - - /*-- Static function --*/ - private: - - // Returns the product of the two given field elements modulo GF(2^8/0x11D). The arguments and result - // are unsigned 8-bit integers. This could be implemented as a lookup table of 256*256 entries of uint8. - static uint8_t multiply(uint8_t x, uint8_t y); - - }; - -}; - -} diff --git a/qrcode/src/generator/QrCodeGeneratorDemo.cpp b/qrcode/src/generator/QrCodeGeneratorDemo.cpp deleted file mode 100644 index e13f43e..0000000 --- a/qrcode/src/generator/QrCodeGeneratorDemo.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/* - * QR Code generator demo (C++) - * - * Run this command-line program with no arguments. The program computes a bunch of demonstration - * QR Codes and prints them to the console. Also, the SVG code for one QR Code is printed as a sample. - * - * Copyright (c) Project Nayuki - * https://www.nayuki.io/page/qr-code-generator-library - * - * (MIT License) - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - The Software is provided "as is", without warranty of any kind, express or - * implied, including but not limited to the warranties of merchantability, - * fitness for a particular purpose and noninfringement. In no event shall the - * authors or copyright holders be liable for any claim, damages or other - * liability, whether in an action of contract, tort or otherwise, arising from, - * out of or in connection with the Software or the use or other dealings in the - * Software. - */ - -#include -#include -#include -#include -#include -#include "QrCode.hpp" - - -// Function prototypes -static void doBasicDemo(); -static void doVarietyDemo(); -static void doSegmentDemo(); -static void printQr(const qrcodegen::QrCode &qr); - - - -// The main application program. -int main() { - doBasicDemo(); - doVarietyDemo(); - doSegmentDemo(); - return EXIT_SUCCESS; -} - - -// Creates a single QR Code, then prints it to the console. -static void doBasicDemo() { - const char *text = "Hello, world!"; // User-supplied text - const qrcodegen::QrCode::Ecc &errCorLvl = qrcodegen::QrCode::Ecc::LOW; // Error correction level - - // Make and print the QR Code symbol - const qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(text, errCorLvl); - std::cout << qr.toSvgString(4) << std::endl; - printQr(qr); -} - - -// Creates a variety of QR Codes that exercise different features of the library, and prints each one to the console. -static void doVarietyDemo() { - // Project Nayuki URL - const qrcodegen::QrCode qr0 = qrcodegen::QrCode::encodeText("https://www.nayuki.io/", qrcodegen::QrCode::Ecc::HIGH); - printQr(qrcodegen::QrCode(qr0, 3)); // Change mask, forcing to mask #3 - - // Numeric mode encoding (3.33 bits per digit) - const qrcodegen::QrCode qr1 = qrcodegen::QrCode::encodeText("314159265358979323846264338327950288419716939937510", qrcodegen::QrCode::Ecc::MEDIUM); - printQr(qr1); - - // Alphanumeric mode encoding (5.5 bits per character) - const qrcodegen::QrCode qr2 = qrcodegen::QrCode::encodeText("DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", qrcodegen::QrCode::Ecc::HIGH); - printQr(qr2); - - // Unicode text as UTF-8, and different masks - const qrcodegen::QrCode qr3 = qrcodegen::QrCode::encodeText("\xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1wa\xE3\x80\x81\xE4\xB8\x96\xE7\x95\x8C\xEF\xBC\x81\x20\xCE\xB1\xCE\xB2\xCE\xB3\xCE\xB4", qrcodegen::QrCode::Ecc::QUARTILE); - printQr(qrcodegen::QrCode(qr3, 0)); - printQr(qrcodegen::QrCode(qr3, 1)); - printQr(qrcodegen::QrCode(qr3, 5)); - printQr(qrcodegen::QrCode(qr3, 7)); - - // Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland) - const qrcodegen::QrCode qr4 = qrcodegen::QrCode::encodeText( - "Alice was beginning to get very tired of sitting by her sister on the bank, " - "and of having nothing to do: once or twice she had peeped into the book her sister was reading, " - "but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice " - "'without pictures or conversations?' So she was considering in her own mind (as well as she could, " - "for the hot day made her feel very sleepy and stupid), whether the pleasure of making a " - "daisy-chain would be worth the trouble of getting up and picking the daisies, when suddenly " - "a White Rabbit with pink eyes ran close by her.", qrcodegen::QrCode::Ecc::HIGH); - printQr(qr4); -} - - -// Creates QR Codes with manually specified segments for better compactness. -static void doSegmentDemo() { - // Illustration "silver" - const char *silver0 = "THE SQUARE ROOT OF 2 IS 1."; - const char *silver1 = "41421356237309504880168872420969807856967187537694807317667973799"; - const qrcodegen::QrCode qr0 = qrcodegen::QrCode::encodeText( - (std::string(silver0) + silver1).c_str(), - qrcodegen::QrCode::Ecc::LOW); - printQr(qr0); - - std::vector segs; - segs.push_back(qrcodegen::QrSegment::makeAlphanumeric(silver0)); - segs.push_back(qrcodegen::QrSegment::makeNumeric(silver1)); - const qrcodegen::QrCode qr1 = qrcodegen::QrCode::encodeSegments(segs, qrcodegen::QrCode::Ecc::LOW); - printQr(qr1); - - // Illustration "golden" - const char *golden0 = "Golden ratio \xCF\x86 = 1."; - const char *golden1 = "6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911374"; - const char *golden2 = "......"; - const qrcodegen::QrCode qr2 = qrcodegen::QrCode::encodeText( - (std::string(golden0) + golden1 + golden2).c_str(), - qrcodegen::QrCode::Ecc::LOW); - printQr(qr2); - - segs.clear(); - std::vector bytes; - for (const char *temp = golden0; *temp != '\0'; temp++) - bytes.push_back(static_cast(*temp)); - segs.push_back(qrcodegen::QrSegment::makeBytes(bytes)); - segs.push_back(qrcodegen::QrSegment::makeNumeric(golden1)); - segs.push_back(qrcodegen::QrSegment::makeAlphanumeric(golden2)); - const qrcodegen::QrCode qr3 = qrcodegen::QrCode::encodeSegments(segs, qrcodegen::QrCode::Ecc::LOW); - printQr(qr3); -} - - -// Prints the given QR Code to the console. -static void printQr(const qrcodegen::QrCode &qr) { - int border = 4; - for (int y = -border; y < qr.size + border; y++) { - for (int x = -border; x < qr.size + border; x++) { - std::cout << (qr.getModule(x, y) == 1 ? "##" : " "); - } - std::cout << std::endl; - } -} diff --git a/qrcode/src/generator/QrSegment.cpp b/qrcode/src/generator/QrSegment.cpp deleted file mode 100644 index 2ae2242..0000000 --- a/qrcode/src/generator/QrSegment.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/* - * QR Code generator library (C++) - * - * Copyright (c) Project Nayuki - * https://www.nayuki.io/page/qr-code-generator-library - * - * (MIT License) - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - The Software is provided "as is", without warranty of any kind, express or - * implied, including but not limited to the warranties of merchantability, - * fitness for a particular purpose and noninfringement. In no event shall the - * authors or copyright holders be liable for any claim, damages or other - * liability, whether in an action of contract, tort or otherwise, arising from, - * out of or in connection with the Software or the use or other dealings in the - * Software. - */ - -#include -#include "BitBuffer.hpp" -#include "QrSegment.hpp" - - -qrcodegen::QrSegment::Mode::Mode(int mode, int cc0, int cc1, int cc2) : - modeBits(mode) { - numBitsCharCount[0] = cc0; - numBitsCharCount[1] = cc1; - numBitsCharCount[2] = cc2; -} - - -int qrcodegen::QrSegment::Mode::numCharCountBits(int ver) const { - if ( 1 <= ver && ver <= 9) return numBitsCharCount[0]; - else if (10 <= ver && ver <= 26) return numBitsCharCount[1]; - else if (27 <= ver && ver <= 40) return numBitsCharCount[2]; - else throw "Version number out of range"; -} - - -const qrcodegen::QrSegment::Mode qrcodegen::QrSegment::Mode::NUMERIC (0x1, 10, 12, 14); -const qrcodegen::QrSegment::Mode qrcodegen::QrSegment::Mode::ALPHANUMERIC(0x2, 9, 11, 13); -const qrcodegen::QrSegment::Mode qrcodegen::QrSegment::Mode::BYTE (0x4, 8, 16, 16); -const qrcodegen::QrSegment::Mode qrcodegen::QrSegment::Mode::KANJI (0x8, 8, 10, 12); - - - -qrcodegen::QrSegment qrcodegen::QrSegment::makeBytes(const std::vector &data) { - return QrSegment(Mode::BYTE, data.size(), data, data.size() * 8); -} - - -qrcodegen::QrSegment qrcodegen::QrSegment::makeNumeric(const char *digits) { - BitBuffer bb; - int accumData = 0; - int accumCount = 0; - int charCount = 0; - for (; *digits != '\0'; digits++, charCount++) { - char c = *digits; - if (c < '0' || c > '9') - throw "String contains non-numeric characters"; - accumData = accumData * 10 + (c - '0'); - accumCount++; - if (accumCount == 3) { - bb.appendBits(accumData, 10); - accumData = 0; - accumCount = 0; - } - } - if (accumCount > 0) // 1 or 2 digits remaining - bb.appendBits(accumData, accumCount * 3 + 1); - return QrSegment(Mode::NUMERIC, charCount, bb.getBytes(), bb.getBitLength()); -} - - -qrcodegen::QrSegment qrcodegen::QrSegment::makeAlphanumeric(const char *text) { - BitBuffer bb; - int accumData = 0; - int accumCount = 0; - int charCount = 0; - for (; *text != '\0'; text++, charCount++) { - char c = *text; - if (c < ' ' || c > 'Z') - throw "String contains unencodable characters in alphanumeric mode"; - accumData = accumData * 45 + ALPHANUMERIC_ENCODING_TABLE[c - ' ']; - accumCount++; - if (accumCount == 2) { - bb.appendBits(accumData, 11); - accumData = 0; - accumCount = 0; - } - } - if (accumCount > 0) // 1 character remaining - bb.appendBits(accumData, 6); - return QrSegment(Mode::ALPHANUMERIC, charCount, bb.getBytes(), bb.getBitLength()); -} - - -std::vector qrcodegen::QrSegment::makeSegments(const char *text) { - // Select the most efficient segment encoding automatically - std::vector result; - if (*text == '\0'); // Leave the vector empty - else if (QrSegment::isNumeric(text)) - result.push_back(QrSegment::makeNumeric(text)); - else if (QrSegment::isAlphanumeric(text)) - result.push_back(QrSegment::makeAlphanumeric(text)); - else { - std::vector bytes; - for (; *text != '\0'; text++) - bytes.push_back(static_cast(*text)); - result.push_back(QrSegment::makeBytes(bytes)); - } - return result; -} - - -qrcodegen::QrSegment::QrSegment(const Mode &md, int numCh, const std::vector &b, int bitLen) : - mode(md), - numChars(numCh), - data(b), - bitLength(bitLen) { - if (numCh < 0 || bitLen < 0 || b.size() != static_cast((bitLen + 7) / 8)) - throw "Invalid value"; -} - - -int qrcodegen::QrSegment::getTotalBits(const std::vector &segs, int version) { - if (version < 1 || version > 40) - throw "Version number out of range"; - int result = 0; - for (size_t i = 0; i < segs.size(); i++) { - const QrSegment &seg(segs.at(i)); - int ccbits = seg.mode.numCharCountBits(version); - // Fail if segment length value doesn't fit in the length field's bit-width - if (seg.numChars >= (1 << ccbits)) - return -1; - result += 4 + ccbits + seg.bitLength; - } - return result; -} - - -bool qrcodegen::QrSegment::isAlphanumeric(const char *text) { - for (; *text != '\0'; text++) { - char c = *text; - if (c < ' ' || c > 'Z' || ALPHANUMERIC_ENCODING_TABLE[c - ' '] == -1) - return false; - } - return true; -} - - -bool qrcodegen::QrSegment::isNumeric(const char *text) { - for (; *text != '\0'; text++) { - char c = *text; - if (c < '0' || c > '9') - return false; - } - return true; -} - - -const int8_t qrcodegen::QrSegment::ALPHANUMERIC_ENCODING_TABLE[59] = { - // SP, !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?, @, // ASCII codes 32 to 64 - 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, -1, // Array indices 0 to 32 - 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, // Array indices 33 to 58 - // A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, // ASCII codes 65 to 90 -}; diff --git a/qrcode/src/generator/QrSegment.hpp b/qrcode/src/generator/QrSegment.hpp deleted file mode 100644 index d46e96d..0000000 --- a/qrcode/src/generator/QrSegment.hpp +++ /dev/null @@ -1,170 +0,0 @@ -/* - * QR Code generator library (C++) - * - * Copyright (c) Project Nayuki - * https://www.nayuki.io/page/qr-code-generator-library - * - * (MIT License) - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - The Software is provided "as is", without warranty of any kind, express or - * implied, including but not limited to the warranties of merchantability, - * fitness for a particular purpose and noninfringement. In no event shall the - * authors or copyright holders be liable for any claim, damages or other - * liability, whether in an action of contract, tort or otherwise, arising from, - * out of or in connection with the Software or the use or other dealings in the - * Software. - */ - -#pragma once - -#include -#include - - -namespace qrcodegen { - -/* - * Represents a character string to be encoded in a QR Code symbol. Each segment has - * a mode, and a sequence of characters that is already encoded as a sequence of bits. - * Instances of this class are immutable. - * This segment class imposes no length restrictions, but QR Codes have restrictions. - * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. - * Any segment longer than this is meaningless for the purpose of generating QR Codes. - */ -class QrSegment final { - - /*---- Public helper enumeration ----*/ - - /* - * The mode field of a segment. Immutable. Provides methods to retrieve closely related values. - */ -public: - class Mode final { - - /*-- Constants --*/ - public: - - static const Mode NUMERIC; - static const Mode ALPHANUMERIC; - static const Mode BYTE; - static const Mode KANJI; - - - /*-- Fields --*/ - - /* (Package-private) An unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object. */ - public: - const int modeBits; - - private: - int numBitsCharCount[3]; - - - /*-- Constructor --*/ - - private: - Mode(int mode, int cc0, int cc1, int cc2); - - - /*-- Method --*/ - - /* - * (Package-private) Returns the bit width of the segment character count field for this mode object at the given version number. - */ - public: - int numCharCountBits(int ver) const; - - }; - - - - /*---- Public static factory functions ----*/ -public: - - /* - * Returns a segment representing the given binary data encoded in byte mode. - */ - static QrSegment makeBytes(const std::vector &data); - - - /* - * Returns a segment representing the given string of decimal digits encoded in numeric mode. - */ - static QrSegment makeNumeric(const char *digits); - - - /* - * Returns a segment representing the given text string encoded in alphanumeric mode. The characters allowed are: - * 0 to 9, A to Z (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. - */ - static QrSegment makeAlphanumeric(const char *text); - - - /* - * Returns a list of zero or more segments to represent the given text string. - * The result may use various segment modes and switch modes to optimize the length of the bit stream. - */ - static std::vector makeSegments(const char *text); - - - /*---- Public static helper functions ----*/ -public: - - /* - * Tests whether the given string can be encoded as a segment in alphanumeric mode. - */ - static bool isAlphanumeric(const char *text); - - - /* - * Tests whether the given string can be encoded as a segment in numeric mode. - */ - static bool isNumeric(const char *text); - - - - /*---- Instance fields ----*/ -public: - - /* The mode indicator for this segment. */ - const Mode mode; - - /* The length of this segment's unencoded data, measured in characters. Always zero or positive. */ - const int numChars; - - /* The bits of this segment packed into a byte array in big endian. */ - const std::vector data; - - /* The length of this segment's encoded data, measured in bits. Satisfies ceil(bitLength / 8) = data.size(). */ - const int bitLength; - - - /*---- Constructor ----*/ -public: - - /* - * Creates a new QR Code data segment with the given parameters and data. - */ - QrSegment(const Mode &md, int numCh, const std::vector &b, int bitLen); - - - // Package-private helper function. - static int getTotalBits(const std::vector &segs, int version); - - - /*---- Private constant ----*/ -private: - - /* Maps shifted ASCII codes to alphanumeric mode character codes. */ - static const int8_t ALPHANUMERIC_ENCODING_TABLE[59]; - -}; - -} diff --git a/qrcode/src/jc_qrencode.h b/qrcode/src/jc_qrencode.h new file mode 100644 index 0000000..42d2ba9 --- /dev/null +++ b/qrcode/src/jc_qrencode.h @@ -0,0 +1,1198 @@ +/* + +ABOUT: + + A C / C++ QRCode encoder + +HISTORY: + + 0.1 2017-04-15 - Initial version + +LICENSE: + + The MIT License (MIT) + + Copyright (c) 2015 Mathias Westerdahl + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + +DISCLAIMER: + + This software is supplied "AS IS" without any warranties and support + +USAGE: + + A simple example usage: + + #define JC_QRENCODE_IMPLEMENTATION + #include "jc_qrencode.h" + + const char* text = "HELLO WORLD"; + JCQRCode* qr = jc_qrencode((const uint8_t*)text, (uint32_t)strlen(text)); + if( !qr ) + { + fprintf(stderr, "Failed to encode text\n"); + return 1; + } + + // Save a png file + stbi_write_png("out.png", qr->size, qr->size, 1, qr->data, 256); // Stride is currently 256 bytes + + free(qr); // free the qr code + +*/ + + +#ifndef JC_QRENCODE_H +#define JC_QRENCODE_H + +#include + +static const uint32_t JC_QRE_MIN_VERSION = 1; +static const uint32_t JC_QRE_MAX_VERSION = 40; + +const static uint32_t JC_QRE_ERROR_CORRECTION_LEVEL_LOW = 0; +const static uint32_t JC_QRE_ERROR_CORRECTION_LEVEL_MEDIUM = 1; +const static uint32_t JC_QRE_ERROR_CORRECTION_LEVEL_QUARTILE = 2; +const static uint32_t JC_QRE_ERROR_CORRECTION_LEVEL_HIGH = 3; + +typedef struct _JCQRCode +{ + uint8_t* data; // The modules of the qrcode + uint32_t size; // Size (in modules) of one side of the qrcode + uint32_t version; // Version [1,40] + uint32_t ecl; // Error correction level + uint32_t _pad; +} JCQRCode; + +/** Creates a QR Code +* Automatically selects the smallest version that fits the data. +* Then it also selects the highest possible error correction level withing that version, that still fits the data. +* +* @input Byte array +* @inputlength Size of input array +* @return 0 if the qr code couldn't be created. The returned qrcode must be deallocated with free() +*/ +JCQRCode* jc_qrencode(const uint8_t* input, uint32_t inputlength); + +/** Creates a QR Code +* If you know which version and error collection level you need +* +* @input Byte array +* @inputlength Size of input array +* @version Version [1,40] +* @ecl Error correction level (JC_QRE_ERROR_CORRECTION_LEVEL_LOW, JC_QRE_ERROR_CORRECTION_LEVEL_MEDIUM, JC_QRE_ERROR_CORRECTION_LEVEL_QUARTILE, JC_QRE_ERROR_CORRECTION_LEVEL_HIGH) +* @return 0 if the qr code couldn't be created. The returned qrcode must be deallocated with free() +*/ +JCQRCode* jc_qrencode_version(const uint8_t* input, uint32_t inputlength, uint32_t version, uint32_t ecl); + + +#if defined(JC_QRENCODE_IMPLEMENTATION) + +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) + #define JC_QRE_IS64BIT 1 + #define JC_QRE_PAD(_X_) uint8_t _pad[_X_] +#else + #define JC_QRE_PAD(_X_) +#endif + + +static void print_bits(const char* tag, uint32_t v, uint32_t count) +{ + printf("%s 0b", tag); + for( uint32_t i = 0; i < count; ++i) + { + printf("%u", (v >> (count - 1 - i)) & 1); + if( i == 31 ) + { + printf(" ..."); + break; + } + } + printf("\n"); +} + + +static const uint8_t JC_QRE_INPUT_TYPE_NUMERIC = 0; +static const uint8_t JC_QRE_INPUT_TYPE_ALPHANUMERIC = 1; +static const uint8_t JC_QRE_INPUT_TYPE_BYTE = 2; +static const uint8_t JC_QRE_INPUT_TYPE_KANJI = 3; + + +static uint32_t JC_QRE_TYPE_BITS[] = {1, 2, 4, 8}; + +static int32_t JC_QRE_ALPHANUMERIC_MAPPINGS[] = { +// ' ', !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?, @, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z + 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, -1, 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 +}; + +// Each array has 4 levels of error correction, and one extra (first) element in each +#define JC_QRE_INDEX(_ECL, _I) ((_ECL)*(JC_QRE_MAX_VERSION+1) + (_I)) + +// 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, +static uint32_t JC_QRE_DATA_CODEWORD_COUNT[] = { + 0, 19, 34, 55, 80, 108, 136, 156, 194, 232, 274, 324, 370, 428, 461, 523, 589, 647, 721, 795, 861, 932, 1006, 1094, 1174, 1276, 1370, 1468, 1531, 1631, 1735, 1843, 1955, 2071, 2191, 2306, 2434, 2566, 2702, 2812, 2956, + 0, 16, 28, 44, 64, 86, 108, 124, 154, 182, 216, 254, 290, 334, 365, 415, 453, 507, 563, 627, 669, 714, 782, 860, 914, 1000, 1062, 1128, 1193, 1267, 1373, 1455, 1541, 1631, 1725, 1812, 1914, 1992, 2102, 2216, 2334, + 0, 13, 22, 34, 48, 62, 76, 88, 110, 132, 154, 180, 206, 244, 261, 295, 325, 367, 397, 445, 485, 512, 568, 614, 664, 718, 754, 808, 871, 911, 985, 1033, 1115, 1171, 1231, 1286, 1354, 1426, 1502, 1582, 1666, + 0, 9, 16, 26, 36, 46, 60, 66, 86, 100, 122, 140, 158, 180, 197, 223, 253, 283, 313, 341, 385, 406, 442, 464, 514, 538, 596, 628, 661, 701, 745, 793, 845, 901, 961, 986, 1054, 1096, 1142, 1222, 1276, +}; +static const uint32_t JC_QRE_ERROR_CORRECTION_CODEWORD_COUNT[] = { + 0, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 0, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 0, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 0, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, +}; +static const uint32_t JC_QRE_CHARACTER_COUNT_BIT_SIZE[] = { + 0, 10, 10, 10, 10, 10, 10, 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +}; +static const uint32_t JC_QRE_BLOCK_COUNT_GROUP1[] = { + 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 4, 2, 4, 3, 5, 5, 1, 5, 3, 3, 4, 2, 4, 6, 8, 10, 8, 3, 7, 5, 13, 17, 17, 13, 12, 6, 17, 4, 20, 19, + 0, 1, 1, 1, 2, 2, 4, 4, 2, 3, 4, 1, 6, 8, 4, 5, 7, 10, 9, 3, 3, 17, 17, 4, 6, 8, 19, 22, 3, 21, 19, 2, 10, 14, 14, 12, 6, 29, 13, 40, 18, + 0, 1, 1, 2, 2, 2, 4, 2, 4, 4, 6, 4, 4, 8, 11, 5, 15, 1, 17, 17, 15, 17, 7, 11, 11, 7, 28, 8, 4, 1, 15, 42, 10, 29, 44, 39, 46, 49, 48, 43, 34, + 0, 1, 1, 2, 4, 2, 4, 4, 4, 4, 6, 3, 7, 12, 11, 11, 3, 2, 2, 9, 15, 19, 34, 16, 30, 22, 33, 12, 11, 19, 23, 23, 19, 11, 59, 22, 2, 24, 42, 10, 20, +}; +static const uint32_t JC_QRE_BLOCK_COUNT_GROUP2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 1, 1, 1, 5, 1, 4, 5, 4, 7, 5, 4, 4, 2, 4, 10, 7, 10, 3, 0, 1, 6, 7, 14, 4, 18, 4, 6, + 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 1, 4, 2, 1, 5, 5, 3, 1, 4, 11, 13, 0, 0, 14, 14, 13, 4, 3, 23, 7, 10, 29, 23, 21, 23, 26, 34, 14, 32, 7, 31, + 0, 0, 0, 0, 0, 2, 0, 4, 2, 4, 2, 4, 6, 4, 5, 7, 2, 15, 1, 4, 5, 6, 16, 14, 16, 22, 6, 26, 31, 37, 25, 1, 35, 19, 7, 14, 10, 10, 14, 22, 34, + 0, 0, 0, 0, 0, 2, 0, 1, 2, 4, 2, 8, 4, 4, 5, 7, 13, 17, 19, 16, 10, 6, 0, 14, 2, 13, 4, 28, 31, 26, 25, 28, 35, 46, 1, 41, 64, 46, 32, 67, 61, +}; +static const uint32_t JC_QRE_CODEWORDS_PER_BLOCK_GROUP1[] = { + 0, 19, 34, 55, 80, 108, 68, 78, 97, 116, 68, 81, 92, 107, 115, 87, 98, 107, 120, 113, 107, 116, 111, 121, 117, 106, 114, 122, 117, 116, 115, 115, 115, 115, 115, 121, 121, 122, 122, 117, 118, + 0, 16, 28, 44, 32, 43, 27, 31, 38, 36, 43, 50, 36, 37, 40, 41, 45, 46, 43, 44, 41, 42, 46, 47, 45, 47, 46, 45, 45, 45, 47, 46, 46, 46, 46, 47, 47, 46, 46, 47, 47, + 0, 13, 22, 17, 24, 15, 19, 14, 18, 16, 19, 22, 20, 20, 16, 24, 19, 22, 22, 21, 24, 22, 24, 24, 24, 24, 22, 23, 24, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 0, 9, 16, 13, 9, 11, 15, 13, 14, 12, 15, 12, 14, 11, 12, 12, 15, 14, 14, 13, 15, 16, 13, 15, 16, 15, 16, 15, 15, 15, 15, 15, 15, 15, 16, 15, 15, 15, 15, 15, 15, +}; +static const uint32_t JC_QRE_CODEWORDS_PER_BLOCK_GROUP2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 0, 93, 0, 116, 88, 99, 108, 121, 114, 108, 117, 112, 122, 118, 107, 115, 123, 118, 117, 116, 116, 0, 116, 116, 122, 122, 123, 123, 118, 119, + 0, 0, 0, 0, 0, 0, 0, 0, 39, 37, 44, 51, 37, 38, 41, 42, 46, 47, 44, 45, 42, 0, 0, 48, 46, 48, 47, 46, 46, 46, 48, 47, 47, 47, 47, 48, 48, 47, 47, 48, 48, + 0, 0, 0, 0, 0, 16, 0, 15, 19, 17, 20, 23, 21, 21, 17, 25, 20, 23, 23, 22, 25, 23, 25, 25, 25, 25, 23, 24, 25, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 0, 0, 0, 0, 0, 12, 0, 14, 15, 13, 16, 13, 15, 12, 13, 13, 16, 15, 15, 14, 16, 17, 0, 16, 17, 16, 17, 16, 16, 16, 16, 16, 16, 16, 17, 16, 16, 16, 16, 16, 16, +}; +// Each error correction level can have 8 format strings depending on what mask was applied +static const uint32_t JC_QRE_FORMAT_BITS[] = { + 30660, 29427, 32170, 30877, 26159, 25368, 27713, 26998, + 21522, 20773, 24188, 23371, 17913, 16590, 20375, 19104, + 13663, 12392, 16177, 14854, 9396, 8579, 11994, 11245, + 5769, 5054, 7399, 6608, 1890, 597, 3340, 2107, +}; + +// Each version has its version string +static const uint32_t JC_QRE_VERSION_BITS[] = { + 0, 0, 0, 0, 0, 0, 0, 31892, 34236, 39577, 42195, 48118, 51042, 55367, 58893, 63784, 68472, 70749, 76311, 79154, 84390, 87683, 92361, 96236, 102084, 102881, 110507, 110734, 117786, 119615, 126325, 127568, 133589, 136944, 141498, 145311, 150283, 152622, 158308, 161089, 167017, +}; + +static const uint32_t JC_QRE_ALIGNMENT_POSITIONS[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 6, 18, 0, 0, 0, 0, 0, 0, + 6, 22, 0, 0, 0, 0, 0, 0, + 6, 26, 0, 0, 0, 0, 0, 0, + 6, 30, 0, 0, 0, 0, 0, 0, + 6, 34, 0, 0, 0, 0, 0, 0, + 6, 22, 38, 0, 0, 0, 0, 0, + 6, 24, 42, 0, 0, 0, 0, 0, + 6, 26, 46, 0, 0, 0, 0, 0, + 6, 28, 50, 0, 0, 0, 0, 0, + 6, 30, 54, 0, 0, 0, 0, 0, + 6, 32, 58, 0, 0, 0, 0, 0, + 6, 34, 62, 0, 0, 0, 0, 0, + 6, 26, 46, 66, 0, 0, 0, 0, + 6, 26, 48, 70, 0, 0, 0, 0, + 6, 26, 50, 74, 0, 0, 0, 0, + 6, 30, 54, 78, 0, 0, 0, 0, + 6, 30, 56, 82, 0, 0, 0, 0, + 6, 30, 58, 86, 0, 0, 0, 0, + 6, 34, 62, 90, 0, 0, 0, 0, + 6, 28, 50, 72, 94, 0, 0, 0, + 6, 26, 50, 74, 98, 0, 0, 0, + 6, 30, 54, 78, 102, 0, 0, 0, + 6, 28, 54, 80, 106, 0, 0, 0, + 6, 32, 58, 84, 110, 0, 0, 0, + 6, 30, 58, 86, 114, 0, 0, 0, + 6, 34, 62, 90, 118, 0, 0, 0, + 6, 26, 50, 74, 98, 122, 0, 0, + 6, 30, 54, 78, 102, 126, 0, 0, + 6, 26, 52, 78, 104, 130, 0, 0, + 6, 30, 56, 82, 108, 134, 0, 0, + 6, 34, 60, 86, 112, 138, 0, 0, + 6, 30, 58, 86, 114, 142, 0, 0, + 6, 34, 62, 90, 118, 146, 0, 0, + 6, 30, 54, 78, 102, 126, 150, 0, + 6, 24, 50, 76, 102, 128, 154, 0, + 6, 28, 54, 80, 106, 132, 158, 0, + 6, 32, 58, 84, 110, 136, 162, 0, + 6, 26, 54, 82, 110, 138, 166, 0, + 6, 30, 58, 86, 114, 142, 170, 0, +}; + +typedef struct _JCQRCodeBitBuffer +{ + uint8_t* bits; // pointer to payload + uint32_t numbits; // current offset (in bits) + JC_QRE_PAD(4); +} JCQRCodeBitBuffer; + +typedef struct _JCQRCodeSegment +{ + JCQRCodeBitBuffer data; + uint32_t offset; + uint32_t elementcount; // character, bytes, or kanji characters + uint8_t type; // numeric, alphanumeric, byte, kanji + JC_QRE_PAD(7); +} JCQRCodeSegment; + +typedef struct _JCQRCodeInternal +{ + JCQRCode qrcode; // the output + + uint8_t bitbuffer[4096]; // storage for all the segments. Each segment starts at a byte boundary + uint8_t databuffer[4096]; // all segments merged into one buffer (appended one after each other) + uint8_t errorcorrection[4096]; // storage for the error correction code words + uint8_t interleaved[4096]; // all interleaved blocks, including error correction + uint8_t image[256*256]; // Enough to store the largest version (177*177) + uint8_t image_fun[256*256]; // Holds info about whether a module is a function module or not + + JCQRCodeSegment segments[8]; + uint32_t num_segments; + uint32_t datasize; // number of bytes used in databuffer + uint32_t interleavedsize; // number of bytes used in interleaved +} JCQRCodeInternal; + + +// Store the integer with the most significant bit first +static inline uint32_t _jc_qre_bitbuffer_write(uint8_t* buffer, uint32_t buffersize, uint32_t* cursor, uint32_t input, uint32_t numbits) +{ + uint32_t num_bytes_to_traverse = (numbits + 7) / 8; + if( *cursor / 8 + num_bytes_to_traverse >= buffersize ) + { + return 0; + } + if( numbits > 32 ) + { + return 0; + } + + uint32_t pos = *cursor; // in bits + +//print_bits("encode:", input, numbits); + + for( uint32_t i = 0; i < numbits; ++i, ++pos) + { + uint32_t targetindex = 7 - pos & 0x7; + + uint32_t ii = numbits - 1 - i; + uint8_t inputbit = (input >> ii) & 1; + + buffer[pos/8] |= (uint8_t)(inputbit << targetindex); +//printf(" %u %u %u: %u\n", pos, pos/8, i, (inputbit << targetindex)); + +//print_bits("byte:", buffer[pos/8], 8); + + } + *cursor += numbits; + +//printf("\n"); + + return 1; +} + +// Appends a previously encoded segment into another +static inline uint32_t _jc_qre_bitbuffer_append(uint8_t* buffer, uint32_t buffersize, uint32_t* cursor, uint8_t* input, uint32_t numbits) +{ + uint32_t num_bytes_to_traverse = (numbits + 7) / 8; + if( *cursor + num_bytes_to_traverse >= buffersize ) + return 0; + + uint32_t pos = *cursor; // in bits + for( uint32_t i = 0; i < numbits; ++i, ++pos ) + { + uint32_t targetindex = 7 - pos & 0x7; + + uint8_t inputbit = input[i / 8] >> (7 - (i & 7)) & 1; + + // clear the target bit and OR in the input bit + buffer[pos/8] |= (uint8_t)(inputbit << targetindex); + } + + *cursor += numbits; + return 1; +} + +static inline uint32_t _jc_qre_bitbuffer_read(uint8_t* buffer, uint32_t buffersize, uint32_t* cursor, uint32_t numbits) +{ + uint32_t pos = *cursor; // in bits + uint32_t value = 0; + for( uint32_t i = 0; i < numbits; ++i, ++pos) + { + if( pos / 8 >= buffersize ) + break; + + uint8_t currentvalue = buffer[pos / 8]; + + uint32_t srcindex = 7 - pos & 0x7; + + uint32_t srcbit = (currentvalue & (1 << srcindex)) ? 1 : 0; + + value = (value & ~(1 << i)) | (srcbit << i); + } + + *cursor = pos; + return value; +} + +static inline uint32_t _jc_qre_guess_type_numeric(const uint8_t* input, uint32_t inputlength) +{ + for( uint32_t i = 0; i < inputlength; ++i ) + { + if( input[i] < '0' || input[i] > '9' ) + { + return 0; + } + } + return 1; +} + + +static inline uint32_t _jc_qre_guess_type_alphanumeric(const uint8_t* input, uint32_t inputlength) +{ + for( uint32_t i = 0; i < inputlength; ++i ) + { + uint8_t c = input[i]; + if( c < ' ' || c > 'Z' || JC_QRE_ALPHANUMERIC_MAPPINGS[c - ' '] == -1 ) + { + return 0; + } + } + return 1; +} + +static inline uint8_t _jc_qre_guess_type(const uint8_t* input, uint32_t inputlength) +{ + if( _jc_qre_guess_type_numeric(input, inputlength) ) + return JC_QRE_INPUT_TYPE_NUMERIC; + else if( _jc_qre_guess_type_alphanumeric(input, inputlength) ) + return JC_QRE_INPUT_TYPE_ALPHANUMERIC; + + return JC_QRE_INPUT_TYPE_BYTE; +} + +static void _jc_qre_encode_numeric(JCQRCodeSegment* seg, uint32_t maxsize, const uint8_t* input, uint32_t inputlength) +{ + uint32_t accum = 0; + for( uint32_t i = 0; i < inputlength; ++i, ++input) + { + char c = *(const char*)input; + accum = accum * 10 + (uint32_t)(c - '0'); + if( i % 3 == 2 ) + { + _jc_qre_bitbuffer_write(seg->data.bits, maxsize, &seg->data.numbits, accum, 10); + accum = 0; + } + } + if( accum ) + { + uint32_t num = inputlength % 3; + _jc_qre_bitbuffer_write(seg->data.bits, maxsize, &seg->data.numbits, accum, num * 3 + 1); + } + seg->elementcount = inputlength; + seg->type = JC_QRE_INPUT_TYPE_NUMERIC; +} + +static void _jc_qre_encode_alphanumeric(JCQRCodeSegment* seg, uint32_t maxsize, const uint8_t* input, uint32_t inputlength) +{ + const char* characters = (const char*)input; + for( uint32_t i = 0; i < inputlength/2; ++i) + { + uint32_t v1 = (uint32_t)JC_QRE_ALPHANUMERIC_MAPPINGS[characters[i*2+0] - ' ']; + uint32_t v2 = (uint32_t)JC_QRE_ALPHANUMERIC_MAPPINGS[characters[i*2+1] - ' ']; + uint32_t value = (v1 * 45) + v2; + + _jc_qre_bitbuffer_write(seg->data.bits, maxsize, &seg->data.numbits, value, 11); + + //printf("hello: %c %c - > %u %u %u bits\n", characters[i*2+0], characters[i*2+1], v1, v2, seg->data.numbits ); + } + if( inputlength & 1 ) + { + uint32_t v = (uint32_t)JC_QRE_ALPHANUMERIC_MAPPINGS[characters[inputlength-1] - ' ']; + _jc_qre_bitbuffer_write(seg->data.bits, maxsize, &seg->data.numbits, v, 6); + } + seg->elementcount = inputlength; + seg->type = JC_QRE_INPUT_TYPE_ALPHANUMERIC; + + //printf("_jc_qre_encode_alphanumeric: %u chars %u bits\n", inputlength, seg->data.numbits); +} + +static void _jc_qre_encode_bytes(JCQRCodeSegment* seg, uint32_t maxsize, const uint8_t* input, uint32_t inputlength) +{ + (void)maxsize; + memcpy(seg->data.bits, input, inputlength); + seg->data.numbits = inputlength * 8; + seg->elementcount = inputlength; + seg->type = JC_QRE_INPUT_TYPE_BYTE; +} + +static uint32_t _jc_qre_add_segment(JCQRCodeInternal* qr, const uint8_t* input, uint32_t inputlength) +{ + if( qr->num_segments >= sizeof(qr->segments)/sizeof(JCQRCodeSegment) ) + return 0xFFFFFFFF; + + JCQRCodeSegment* seg = &qr->segments[qr->num_segments]; + uint32_t offset = 0; + for( uint32_t i = 0; i < qr->num_segments; ++i ) + { + offset += qr->segments[i].offset; + } + seg->offset = offset; + seg->data.bits = &qr->bitbuffer[offset]; + qr->num_segments++; + + seg->type = _jc_qre_guess_type(input, inputlength); + + uint32_t max_size = sizeof(qr->bitbuffer) - offset; + if( inputlength >= max_size ) + return 0xFFFFFFFF; + + if( seg->type == JC_QRE_INPUT_TYPE_NUMERIC ) { + _jc_qre_encode_numeric(seg, max_size, input, inputlength); + } else if( seg->type == JC_QRE_INPUT_TYPE_ALPHANUMERIC ) { + _jc_qre_encode_alphanumeric(seg, max_size, input, inputlength); + } else if( seg->type == JC_QRE_INPUT_TYPE_BYTE ) { + _jc_qre_encode_bytes(seg, max_size, input, inputlength); + } + + return 0; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Reed-Solomon functions + +static uint8_t _jc_qre_rs_multiply(uint8_t x, uint8_t y) +{ + int32_t v = 0; + for( int32_t i = 7; i >= 0; --i ) + { + v = (v << 1) ^ ( (v >> 7) * 0x11D ); // Do modulo 0x11D if it overflows + v ^= ((y >> i) & 1) * x; // Only if y is odd + } + return (uint8_t)v; +} + +// the generator [out] must have the size 'length' +static void _jc_qre_rs_create_generator(uint32_t length, uint8_t* coefficients) +{ + for( uint32_t i = 0; i < length; ++i ) + { + coefficients[i] = 0; + } + coefficients[length-1] = 1; + + uint32_t power = 1; + for( uint32_t i = 0; i < length; ++i ) + { + for( uint32_t j = 0; j < length; ++j ) + { + coefficients[j] = _jc_qre_rs_multiply(coefficients[j], (uint8_t)power); + if( (j + 1) < length ) + coefficients[j] ^= coefficients[j+1]; + } + uint32_t overflow = (power >> 7); // If it became > 255, this is 1... + power = (power << 1) ^ (overflow * 0x11D); // ...in which case, we should do the modulo + } +} + +// length: length of coefficients +// coefficients: the rs generator coefficients +// datasize: the number of data code words +// data: the data code words +// out: the remainder coefficients +static void _jc_qre_rs_encode(uint32_t length, const uint8_t* coefficients, uint32_t datasize, const uint8_t* data, uint8_t* out) +{ + for( uint32_t i = 0; i < length; ++i ) + { + out[i] = 0; + } + + for( uint32_t i = 0; i < datasize; ++i ) + { + uint32_t value = data[i] ^ out[0]; + // shift all elements one step + for( uint32_t j = 1; j < length; ++j ) + { + out[j-1] = out[j]; + } + out[length-1] = 0; + for( uint32_t j = 0; j < length; ++j ) + { + out[j] ^= _jc_qre_rs_multiply(coefficients[j], (uint8_t)value); + } + } +} + +static void _jc_qre_calc_error_correction(JCQRCodeInternal* qr) +{ + uint32_t index = JC_QRE_INDEX(qr->qrcode.ecl, qr->qrcode.version); + uint32_t num_ec_codewords_per_block = JC_QRE_ERROR_CORRECTION_CODEWORD_COUNT[index]; + + uint8_t coefficients[64]; // generator + _jc_qre_rs_create_generator(num_ec_codewords_per_block, coefficients); + + uint32_t num_ec_codewords_total = 0; + uint32_t data_offset = 0; + + uint32_t num_data_codewords_per_block = JC_QRE_CODEWORDS_PER_BLOCK_GROUP1[index]; + for( uint32_t i = 0; i < JC_QRE_BLOCK_COUNT_GROUP1[index]; ++i ) + { + _jc_qre_rs_encode(num_ec_codewords_per_block, coefficients, num_data_codewords_per_block, &qr->databuffer[data_offset], &qr->errorcorrection[num_ec_codewords_total]); + + data_offset += num_data_codewords_per_block; + num_ec_codewords_total += num_ec_codewords_per_block; + } + + num_data_codewords_per_block = JC_QRE_CODEWORDS_PER_BLOCK_GROUP2[index]; + for( uint32_t i = 0; i < JC_QRE_BLOCK_COUNT_GROUP2[index]; ++i ) + { + _jc_qre_rs_encode(num_ec_codewords_per_block, coefficients, num_data_codewords_per_block, &qr->databuffer[data_offset], &qr->errorcorrection[num_ec_codewords_total]); + + data_offset += num_data_codewords_per_block; + num_ec_codewords_total += num_ec_codewords_per_block; + } +} + +static void _jc_qre_interleave_codewords(JCQRCodeInternal* qr) +{ + uint32_t index = JC_QRE_INDEX(qr->qrcode.ecl, qr->qrcode.version); + uint32_t num_blocks = JC_QRE_BLOCK_COUNT_GROUP1[index] + JC_QRE_BLOCK_COUNT_GROUP2[index]; + + uint32_t num_data_cw_block1 = JC_QRE_CODEWORDS_PER_BLOCK_GROUP1[index]; + uint32_t num_data_cw_block2 = JC_QRE_CODEWORDS_PER_BLOCK_GROUP2[index]; + uint32_t num_data_cw_block = num_data_cw_block1 > num_data_cw_block2 ? num_data_cw_block1 : num_data_cw_block2; + + uint32_t block_count_group1 = JC_QRE_BLOCK_COUNT_GROUP1[index]; + + // where each group starts + uint32_t groupoffset1 = 0; + uint32_t groupoffset2 = num_data_cw_block1 * block_count_group1; + + // We have data code words in consecutive order, block after block + // need to interleave those into final buffer + // The blocks in group 1 have smaller size than those in group 2 + uint32_t total = 0; + for( uint32_t cw = 0; cw < num_data_cw_block; ++cw) + { + for( uint32_t i = 0; i < num_blocks; ++i) + { + // are we in group 1 or 2? + uint32_t group1 = i < block_count_group1; + // number of data codewords in group + uint32_t data_cw_count = group1 ? num_data_cw_block1 : num_data_cw_block2; + if( cw >= data_cw_count ) + { + // since the blocks in group 1 are shorter than those in block 2 + continue; + } + + uint32_t offset = group1 ? groupoffset1 : groupoffset2; + uint32_t blocknum = group1 ? i : i - block_count_group1; + + qr->interleaved[total++] = qr->databuffer[offset + blocknum * data_cw_count + cw]; + } + } + + // Interleave the error correction codewords + uint32_t num_ec_codewords_per_block = JC_QRE_ERROR_CORRECTION_CODEWORD_COUNT[index]; + for( uint32_t cw = 0; cw < num_ec_codewords_per_block; ++cw) + { + for( uint32_t i = 0; i < num_blocks; ++i) + { + qr->interleaved[total++] = qr->errorcorrection[i * num_ec_codewords_per_block + cw]; + } + } + qr->interleavedsize = total; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +static inline void _jc_qre_draw_module(JCQRCodeInternal* qr, int32_t x, int32_t y, uint8_t black) +{ + qr->image[y * 256 + x] = black ? 0 : 255; +} +static inline uint8_t _jc_qre_get_module(JCQRCodeInternal* qr, int32_t x, int32_t y) +{ + return qr->image[y * 256 + x]; +} +static inline void _jc_qre_draw_function_module(JCQRCodeInternal* qr, int32_t x, int32_t y, uint8_t black) +{ + qr->image[y * 256 + x] = black ? 0 : 255; + qr->image_fun[y * 256 + x] = 1; +} +static inline uint32_t _jc_qre_is_function_module(JCQRCodeInternal* qr, int32_t x, int32_t y) +{ + return qr->image_fun[y * 256 + x] != 0 ? 1 : 0; +} + +static void _jc_qre_draw_finder_pattern(JCQRCodeInternal* qr, int32_t x, int32_t y) +{ + int size = (int)qr->qrcode.size; + + for( int j = -4; j <= 4; ++j ) + { + int yy = y + j; + if( yy < 0 || yy >= size ) + continue; + for( int i = -4; i <= 4; ++i ) + { + int xx = x + i; + if( xx < 0 || xx >= size ) + continue; + int iabs = i < 0 ? -i : i; + int jabs = j < 0 ? -j : j; + int max = iabs > jabs ? iabs : jabs; + + _jc_qre_draw_function_module(qr, xx, yy, max != 2 && max != 4); + } + } +} + +static void _jc_qre_draw_alignment_pattern(JCQRCodeInternal* qr, int32_t x, int32_t y) +{ + if( _jc_qre_is_function_module(qr, x, y) ) + return; + for( int j = -2; j <= 2; ++j ) + { + int yy = y + j; + for( int i = -2; i <= 2; ++i ) + { + int xx = x + i; + int iabs = i < 0 ? -i : i; + int jabs = j < 0 ? -j : j; + int max = iabs > jabs ? iabs : jabs; + + _jc_qre_draw_function_module(qr, xx, yy, max == 0 || max == 2); + } + } +} + + +static void _jc_qre_draw_data(JCQRCodeInternal* qr) +{ + uint32_t size = qr->qrcode.size; + + uint32_t bitindex = 0; + + uint32_t upwards = 1; + uint32_t x = size - 1; + +/* + printf("qr->interleavedsize * 8: %u\n", qr->interleavedsize * 8); + + print_bits("byte 0: ", qr->interleaved[0], 8); + print_bits("byte 1: ", qr->interleaved[1], 8); + print_bits("byte 2: ", qr->interleaved[2], 8); + print_bits("byte 3: ", qr->interleaved[3], 8); +*/ + + while( bitindex < qr->interleavedsize * 8 ) + { + for( uint32_t i = 0; i < size * 2; ++i ) + { + uint32_t xx = x - (i & 1); + uint32_t y = upwards ? size - 1 - (i >> 1) : (i >> 1); + + if(!_jc_qre_is_function_module(qr, xx, y)) + { + uint32_t bit = _jc_qre_bitbuffer_read(qr->interleaved, qr->interleavedsize, &bitindex, 1); + _jc_qre_draw_module(qr, xx, y, bit != 0); + } + } + upwards ^= 1; + x -= 2; + if( x == 6 ) + x = 5; + } +} + + +static void _jc_qre_draw_finder_patterns(JCQRCodeInternal* qr) +{ + uint32_t size = qr->qrcode.size; + + _jc_qre_draw_finder_pattern(qr, 3, 3); + _jc_qre_draw_finder_pattern(qr, size-4, 3); + _jc_qre_draw_finder_pattern(qr, 3, size-4); + + // alignment patterns + if( qr->qrcode.version > 1 ) + { + for( uint32_t iy = 0; iy < 8; ++iy ) + { + uint32_t y = JC_QRE_ALIGNMENT_POSITIONS[qr->qrcode.version * 8 + iy]; + if( y == 0 ) + continue; + for( uint32_t ix = 0; ix < 8; ++ix ) + { + uint32_t x = JC_QRE_ALIGNMENT_POSITIONS[qr->qrcode.version * 8 + ix]; + if( x == 0 ) + continue; + _jc_qre_draw_alignment_pattern(qr, x, y); + } + } + } + + // timing modules + for( uint32_t i = 0; i < (uint32_t)size; ++i ) + { + if( !_jc_qre_is_function_module(qr, i, 6)) + _jc_qre_draw_function_module(qr, i, 6, i % 2 == 0); + if( !_jc_qre_is_function_module(qr, 6, i)) + _jc_qre_draw_function_module(qr, 6, i, i % 2 == 0); + } + + // the dark module + _jc_qre_draw_function_module(qr, 8, (4 * qr->qrcode.version) + 9, 1); +} + +static void _jc_qre_draw_format(JCQRCodeInternal* qr, uint32_t pattern_mask) +{ + uint32_t size = qr->qrcode.size; + uint32_t format = JC_QRE_FORMAT_BITS[qr->qrcode.ecl * 8 + pattern_mask]; + +/* +printf("version: %u ecl: %d\n", qr->qrcode.version, qr->qrcode.ecl); +printf("pattern_mask: %u\n", pattern_mask); + printf("format: %u, 0x%08x\n", format, format); + print_bits("format:", format, 15); +*/ + // top right + for( uint32_t i = 0; i < 8; ++i ) + { + _jc_qre_draw_function_module(qr, size - 1 - i, 8, (format >> i) & 1); + } + // bottom left + for( uint32_t i = 0; i < 7; ++i ) + { + _jc_qre_draw_function_module(qr, 8, size - 7 + i, (format >> (8+i)) & 1); + } + + // top left -- right side + for( uint32_t i = 0; i < 6; ++i ) + { + _jc_qre_draw_function_module(qr, 8, i, (format >> i) & 1); + } + + _jc_qre_draw_function_module(qr, 8, 7, (format >> 6) & 1); + _jc_qre_draw_function_module(qr, 8, 8, (format >> 7) & 1); + _jc_qre_draw_function_module(qr, 7, 8, (format >> 8) & 1); + + for( uint32_t i = 0; i < 6; ++i ) + { + _jc_qre_draw_function_module(qr, 5 - i, 8, (format >> (9 + i)) & 1); + } +} + +static inline uint32_t _jc_qre_is_masked(uint32_t x, uint32_t y, uint32_t pattern_mask) +{ + switch(pattern_mask) + { + case 0: return ((x + y) & 1) == 0; + case 1: return (y & 1) == 0; + case 2: return (x % 3) == 0; + case 3: return ((x + y) % 3) == 0; + case 4: return ((x / 3 + y / 2) & 1) == 0; + case 5: return ((x * y) & 1 + (x * y) % 3) == 0; + case 6: return (((x * y) & 1 + (x * y) % 3) & 1) == 0; + case 7: return (((x + y) & 1 + (x * y) % 3) & 1) == 0; + default: return 0; + } +} + +static void _jc_qre_draw_mask(JCQRCodeInternal* qr, uint32_t pattern_mask) +{ + uint32_t size = qr->qrcode.size; + for( uint32_t y = 0; y < size; ++y ) + { + for( uint32_t x = 0; x < size; ++x ) + { + if(_jc_qre_is_function_module(qr, x, y)) + { + continue; + } + if(_jc_qre_is_masked(x, y, pattern_mask)) + qr->image[y * 256 + x] ^= 255; + } + } +} + +static inline uint32_t _jc_qre_calc_penalty(JCQRCodeInternal* qr) +{ + uint32_t size = qr->qrcode.size; + uint32_t penalty = 0; + + // 5 or more in a row + for( uint32_t y = 0; y < size; ++y ) + { + uint8_t color = _jc_qre_get_module(qr, 0, y); + uint32_t consecutive = 1; + for( uint32_t x = 1; x < size; ++x ) + { + uint8_t nextcolor = _jc_qre_get_module(qr, x, y); + if( color == nextcolor ) + { + consecutive++; + } + else + { + color = nextcolor; + penalty += consecutive >= 5 ? 3 + (consecutive-5) : 0; + consecutive = 1; + } + } + penalty += consecutive >= 5 ? 3 + (consecutive-5) : 0; + } + // 5 or more in a column + for( uint32_t x = 0; x < size; ++x ) + { + uint8_t color = _jc_qre_get_module(qr, x, 0); + uint32_t consecutive = 1; + for( uint32_t y = 1; y < size; ++y ) + { + uint8_t nextcolor = _jc_qre_get_module(qr, x, y); + if( color == nextcolor ) + { + consecutive++; + } + else + { + color = nextcolor; + penalty += consecutive >= 5 ? 3 + (consecutive-5) : 0; + consecutive = 1; + } + } + penalty += consecutive >= 5 ? 3 + (consecutive-5) : 0; + } + +/* + // find 2x2 blocks of same color + for( uint32_t y = 0; y < size-1; ++y ) + { + for( uint32_t x = 0; x < size-1; ++x ) + { + uint8_t color1 = _jc_qre_get_module(qr, x, y); + uint8_t color2 = _jc_qre_get_module(qr, x+1, y); + uint8_t color3 = _jc_qre_get_module(qr, x, y+1); + uint8_t color4 = _jc_qre_get_module(qr, x+1, y+1); + penalty += (color1 & color2 & color3 & color4 & 1) * 3; + } + } + + // find special patterns + uint32_t mask = (1 << 12) - 1; + uint32_t pattern1 = 0x5d0; // 11 bits long + uint32_t pattern2 = 0x5d; + for( uint32_t y = 0; y < size; ++y ) + { + uint32_t bits = 0; + for( uint32_t x = 0; x < 11; ++x ) + { + bits = bits << 1 | (_jc_qre_get_module(qr, x, y) & 1); + } + for( uint32_t x = 11; x < size; ++x ) + { + bits = ((bits << 1) & mask) | (_jc_qre_get_module(qr, x, y) & 1); + penalty += (bits == pattern1 || bits == pattern2) ? 40 : 0; + } + } + + for( uint32_t x = 0; x < size; ++x ) + { + uint32_t bits = 0; + for( uint32_t y = 0; y < 11; ++y ) + { + bits = bits << 1 | (_jc_qre_get_module(qr, x, y) & 1); + } + for( uint32_t y = 11; y < size; ++y ) + { + bits = ((bits << 1) & mask) | (_jc_qre_get_module(qr, x, y) & 1); + penalty += (bits == pattern1 || bits == pattern2) ? 40 : 0; + } + } + + int32_t num_black = 0; + for( uint32_t y = 0; y < size; ++y ) + { + for( uint32_t x = 0; x < size; ++x ) + { + num_black += _jc_qre_get_module(qr, x, y) & 1; + } + } + int32_t ratio = (num_black * 100) / (size*size); + int32_t multiples_of_5 = ratio / 5; + int32_t value1 = (multiples_of_5 * 5) - 50; + int32_t value2 = value1 + 5; + value1 = value1 < 0 ? -value1 : value1; + value2 = value2 < 0 ? -value2 : value2; + value1 = value1 / 5; + value2 = value2 / 5; + penalty += ((value1 < value2) ? value1 : value2) * 10; +*/ + + return penalty; +} + +static void _jc_qre_draw_version(JCQRCodeInternal* qr) +{ + if( qr->qrcode.version < 7 ) + return; + + uint32_t size = qr->qrcode.size; + uint32_t format = JC_QRE_VERSION_BITS[qr->qrcode.version]; + + for( uint32_t x = 0; x < 6; ++x ) + { + for( uint32_t y = 0; y < 3; ++y ) + { + uint32_t bit = (format >> (x*3 + y)) & 1; + uint32_t yy = size - 11 + y; + // Bottom left + _jc_qre_draw_function_module(qr, x, yy, bit != 0); + // Top right + _jc_qre_draw_function_module(qr, yy, x, bit != 0); + } + } +} + +static void _jc_qre_draw_image(JCQRCodeInternal* qr) +{ + memset(qr->image, 255, sizeof(qr->image)); + memset(qr->image_fun, 0, sizeof(qr->image_fun)); + qr->qrcode.data = qr->image; + qr->qrcode.size = (qr->qrcode.version-1)*4 + 21; + + _jc_qre_draw_finder_patterns(qr); + _jc_qre_draw_format(qr, 0); // reserve area + _jc_qre_draw_version(qr); // reserve area + + _jc_qre_draw_data(qr); + + // apply masks and find the best one + uint32_t best_mask = 0; + + uint32_t lowest_score = 0xFFFFFFFF; + for( uint32_t i = 0; i < 8; ++i ) + { + _jc_qre_draw_format(qr, i); + _jc_qre_draw_mask(qr, i); + uint32_t score = _jc_qre_calc_penalty(qr); + + if( score < lowest_score ) + { + best_mask = i; + lowest_score = score; + } + _jc_qre_draw_mask(qr, i); // undo the mask (using xor) + } + +// best_mask = 0; +// printf("best mask: %u score: %u\n", best_mask, lowest_score); + + _jc_qre_draw_format(qr, best_mask); + _jc_qre_draw_mask(qr, best_mask); + _jc_qre_draw_version(qr); +} + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static inline uint32_t _jc_qre_get_total_bits(JCQRCodeInternal* qr, uint32_t version) +{ + uint32_t numbits = 0; + for( uint32_t i = 0; i < qr->num_segments; ++i ) + { + JCQRCodeSegment* seg = &qr->segments[i]; + uint32_t character_bit_count = JC_QRE_CHARACTER_COUNT_BIT_SIZE[JC_QRE_INDEX(seg->type, version)]; + numbits += 4 + character_bit_count + seg->data.numbits; + } + return numbits; +} + +static JCQRCode* _jc_qrencode_internal(JCQRCodeInternal* qr) +{ + + uint32_t capacity_bits = JC_QRE_DATA_CODEWORD_COUNT[ JC_QRE_INDEX(qr->qrcode.ecl, qr->qrcode.version) ] * 8; + +//printf("Writing segments\n"); + uint32_t datasize = 0; + for( uint32_t i = 0; i < qr->num_segments; ++i ) + { + JCQRCodeSegment* seg = &qr->segments[i]; + _jc_qre_bitbuffer_write(qr->databuffer, sizeof(qr->databuffer), &datasize, JC_QRE_TYPE_BITS[seg->type], 4); + +//print_bits("mode:", JC_QRE_TYPE_BITS[seg->type], 4); + +//printf("line: %d datasize %u\n", __LINE__, datasize); + _jc_qre_bitbuffer_write(qr->databuffer, sizeof(qr->databuffer), &datasize, seg->elementcount, JC_QRE_CHARACTER_COUNT_BIT_SIZE[JC_QRE_INDEX(seg->type, qr->qrcode.version)]); + +//print_bits("numchars:", seg->elementcount, JC_QRE_CHARACTER_COUNT_BIT_SIZE[JC_QRE_INDEX(seg->type, version)]); + +//printf("line: %d datasize %u\n", __LINE__, datasize); + _jc_qre_bitbuffer_append(qr->databuffer, sizeof(qr->databuffer), &datasize, seg->data.bits, seg->data.numbits); + + +//printf("num bits: %u\n", datasize); + +//print_bits("byte 0:", qr->databuffer[0], 8); +//print_bits("byte 1:", qr->databuffer[1], 8); +//print_bits("byte 2:", qr->databuffer[2], 8); +//print_bits("byte 3:", qr->databuffer[3], 8); +//print_bits("byte 4:", qr->databuffer[4], 8); + + + } + +//printf("VERSION: %d ECL: %d capacity_bits: %d\n", qr->qrcode.version, qr->qrcode.ecl, capacity_bits); + +//printf("Writing terminator\n"); + + // add terminator (max 4 zeros) + uint32_t terminator_length = capacity_bits - datasize; + if( terminator_length > 4 ) + terminator_length = 4; + uint32_t zeros = 0; + if( terminator_length ) + { + _jc_qre_bitbuffer_write(qr->databuffer, sizeof(qr->databuffer), &datasize, zeros, terminator_length); + } + + // make it 8 bit aligned + uint32_t numpadzeros = (8 - (datasize & 0x7)) & 0x7; + _jc_qre_bitbuffer_write(qr->databuffer, sizeof(qr->databuffer), &datasize, zeros, numpadzeros); + + // pad with bytes + uint8_t padding[2] = { 0xEC, 0x11 }; + uint32_t numpadbytes = (capacity_bits - datasize) / 8; + + for( uint32_t i = 0; i < numpadbytes; ++i ) + { + _jc_qre_bitbuffer_write(qr->databuffer, sizeof(qr->databuffer), &datasize, padding[i&1], 8); + } + + qr->datasize = datasize / 8; + + _jc_qre_calc_error_correction(qr); + _jc_qre_interleave_codewords(qr); + _jc_qre_draw_image(qr); + + return (JCQRCode*)&qr->qrcode; +} + +JCQRCode* jc_qrencode(const uint8_t* input, uint32_t inputlength) +{ + JCQRCodeInternal* qr = (JCQRCodeInternal*)malloc( sizeof(JCQRCodeInternal) ); + memset(qr, 0, sizeof(JCQRCodeInternal) ); + + uint32_t result = _jc_qre_add_segment(qr, input, inputlength); + + if( result == 0xFFFFFFFF ) + { + // todo: error codes + free(qr); + return 0; + } + + // Pick the smallest version that fits the data + uint32_t version = 0; + uint32_t numbits = 0; + for( uint32_t i = JC_QRE_MIN_VERSION; i <= JC_QRE_MAX_VERSION; ++i ) + { + uint32_t _numbits = _jc_qre_get_total_bits(qr, i); + uint32_t capacity_bits = JC_QRE_DATA_CODEWORD_COUNT[ JC_QRE_INDEX(JC_QRE_ERROR_CORRECTION_LEVEL_LOW, i) ] * 8; + if( _numbits < capacity_bits ) + { + version = i; + numbits = _numbits; + break; + } + } + + // The data was too large + if( !version ) + { + free(qr); + return 0; + } + + qr->qrcode.version = version; + qr->qrcode.ecl = JC_QRE_ERROR_CORRECTION_LEVEL_LOW; + + // Pick the highest error correction that fits within the same version + for( uint32_t i = JC_QRE_ERROR_CORRECTION_LEVEL_MEDIUM; i <= JC_QRE_ERROR_CORRECTION_LEVEL_HIGH; ++i ) + { + uint32_t capacity_bits = JC_QRE_DATA_CODEWORD_COUNT[ JC_QRE_INDEX(i, version) ] * 8; + if( numbits < capacity_bits ) + { + qr->qrcode.ecl = i; + } + } + + _jc_qrencode_internal(qr); + return (JCQRCode*)&qr->qrcode; +} + +JCQRCode* jc_qrencode_version(const uint8_t* input, uint32_t inputlength, uint32_t version, uint32_t ecl) +{ + JCQRCodeInternal* qr = (JCQRCodeInternal*)malloc( sizeof(JCQRCodeInternal) ); + memset(qr, 0, sizeof(JCQRCodeInternal) ); + + uint32_t result = _jc_qre_add_segment(qr, input, inputlength); + + if( result == 0xFFFFFFFF ) + { + // todo: error codes + free(qr); + return 0; + } + + qr->qrcode.version = version; + qr->qrcode.ecl = ecl; + + uint32_t numbits = _jc_qre_get_total_bits(qr, version); + uint32_t capacity_bits = JC_QRE_DATA_CODEWORD_COUNT[ JC_QRE_INDEX(ecl, version) ] * 8; + if( numbits > capacity_bits ) + { + free(qr); + return 0; + } + + _jc_qrencode_internal(qr); + return (JCQRCode*)&qr->qrcode; +} + +#undef JC_QRE_INDEX + +#endif // JC_QRENCODE_IMPLEMENTATION +#endif // JC_QRENCODE_H \ No newline at end of file diff --git a/qrcode/src/qrcode.cpp b/qrcode/src/qrcode.cpp index 5e9e132..1276488 100644 --- a/qrcode/src/qrcode.cpp +++ b/qrcode/src/qrcode.cpp @@ -13,11 +13,11 @@ #include "quirc/quirc.h" -// #define STB_IMAGE_IMPLEMENTATION -// #include "stb_image.h" -// #define STB_IMAGE_WRITE_IMPLEMENTATION -// #include "stb_image_write.h" +#define JC_QRENCODE_IMPLEMENTATION +#include "jc_qrencode.h" +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "stb_image_write.h" // GENERATE // https://github.com/nayuki/QR-Code-generator @@ -80,7 +80,7 @@ static int Scan(lua_State* L) image[y * width + (width - x - 1)] = (uint8_t)(value * 255.0f); } } - + quirc_end(qr); int num_codes = quirc_count(qr); @@ -113,14 +113,96 @@ static int Scan(lua_State* L) return 1; } +static int save_image(JCQRCode* qr, const char* path) +{ + int32_t size = qr->size; + + int32_t border = 0; + int32_t scale = 1; + int32_t newsize = scale*(size + 2 * border); + uint8_t* large = (uint8_t*)malloc( newsize * newsize ); + + memset(large, 255, newsize*newsize); + + for( int y = 0; y < size*scale; ++y ) + { + for( int x = 0; x < size*scale; ++x ) + { + uint8_t module = qr->data[(y/scale)*256 + (x/scale)]; + large[(y + scale*border) * newsize + x + scale*border] = module; + } + } + + int result = stbi_write_png(path, newsize, newsize, 1, large, newsize); + free(large); + + if(result) + printf("Wrote to %s\n", path); + else + printf("Failed to write to %s\n", path); + return result; +} + +static dmBuffer::HBuffer GenerateImage(JCQRCode* qr, uint32_t* outsize) +{ + int32_t size = qr->size; + int32_t border = 2; + int32_t scale = 8; + int32_t newsize = scale*(size + 2 * border); + + dmBuffer::StreamDeclaration streams_decl[] = { + {dmHashString64("data"), dmBuffer::VALUE_TYPE_UINT8, 1} + }; + + dmBuffer::HBuffer buffer = 0; + dmBuffer::Result result = dmBuffer::Create(newsize * newsize, streams_decl, 1, &buffer); + if (result != dmBuffer::RESULT_OK ) + { + return 0; + } + + uint8_t* data; + uint32_t datasize; + dmBuffer::GetBytes(buffer, (void**)&data, &datasize); + + memset(data, 255, newsize*newsize); + + for( int y = 0; y < size*scale; ++y ) + { + for( int x = 0; x < size*scale; ++x ) + { + uint8_t module = qr->data[(y/scale)*256 + (x/scale)]; + data[(y + scale*border) * newsize + x + scale*border] = module; + } + } + + if (outsize) + *outsize = newsize; + return buffer; +} + static int Generate(lua_State* L) { - DM_LUA_STACK_CHECK(L, 1); + DM_LUA_STACK_CHECK(L, 2); - dmScript::LuaHBuffer* buffer = dmScript::CheckBuffer(L, 1); - int width = luaL_checkint(L, 2); - int height = luaL_checkint(L, 3); - int flip = luaL_checkint(L, 4); + const char* text = luaL_checkstring(L, 1); + + JCQRCode* qr = jc_qrencode((const uint8_t*)text, (uint32_t)strlen(text)); + if( !qr ) + { + return DM_LUA_ERROR("Failed to encode text: '%s'\n", text); + } + + uint32_t outsize = 0; + dmBuffer::HBuffer buffer = GenerateImage(qr, &outsize); + + free(qr); // free the qr code + + // Transfer ownership to Lua + dmScript::LuaHBuffer luabuffer = {buffer, true}; + dmScript::PushBuffer(L, luabuffer); + lua_pushinteger(L, outsize); + return 2; } static const luaL_reg Module_methods[] =