15317bbafSopenharmony_ci/* 25317bbafSopenharmony_ci * QR Code generator library (C++) 35317bbafSopenharmony_ci * 45317bbafSopenharmony_ci * Copyright (c) Project Nayuki. (MIT License) 55317bbafSopenharmony_ci * https://www.nayuki.io/page/qr-code-generator-library 65317bbafSopenharmony_ci * 75317bbafSopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy of 85317bbafSopenharmony_ci * this software and associated documentation files (the "Software"), to deal in 95317bbafSopenharmony_ci * the Software without restriction, including without limitation the rights to 105317bbafSopenharmony_ci * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 115317bbafSopenharmony_ci * the Software, and to permit persons to whom the Software is furnished to do so, 125317bbafSopenharmony_ci * subject to the following conditions: 135317bbafSopenharmony_ci * - The above copyright notice and this permission notice shall be included in 145317bbafSopenharmony_ci * all copies or substantial portions of the Software. 155317bbafSopenharmony_ci * - The Software is provided "as is", without warranty of any kind, express or 165317bbafSopenharmony_ci * implied, including but not limited to the warranties of merchantability, 175317bbafSopenharmony_ci * fitness for a particular purpose and noninfringement. In no event shall the 185317bbafSopenharmony_ci * authors or copyright holders be liable for any claim, damages or other 195317bbafSopenharmony_ci * liability, whether in an action of contract, tort or otherwise, arising from, 205317bbafSopenharmony_ci * out of or in connection with the Software or the use or other dealings in the 215317bbafSopenharmony_ci * Software. 225317bbafSopenharmony_ci */ 235317bbafSopenharmony_ci 245317bbafSopenharmony_ci#include <algorithm> 255317bbafSopenharmony_ci#include <cassert> 265317bbafSopenharmony_ci#include <climits> 275317bbafSopenharmony_ci#include <cstddef> 285317bbafSopenharmony_ci#include <cstdlib> 295317bbafSopenharmony_ci#include <cstring> 305317bbafSopenharmony_ci#include <sstream> 315317bbafSopenharmony_ci#include <utility> 325317bbafSopenharmony_ci#include "qrcodegen.hpp" 335317bbafSopenharmony_ci 345317bbafSopenharmony_ciusing std::int8_t; 355317bbafSopenharmony_ciusing std::uint8_t; 365317bbafSopenharmony_ciusing std::size_t; 375317bbafSopenharmony_ciusing std::vector; 385317bbafSopenharmony_ci 395317bbafSopenharmony_ci 405317bbafSopenharmony_cinamespace qrcodegen { 415317bbafSopenharmony_ci 425317bbafSopenharmony_ci/*---- Class QrSegment ----*/ 435317bbafSopenharmony_ci 445317bbafSopenharmony_ciQrSegment::Mode::Mode(int mode, int cc0, int cc1, int cc2) : 455317bbafSopenharmony_ci modeBits(mode) { 465317bbafSopenharmony_ci numBitsCharCount[0] = cc0; 475317bbafSopenharmony_ci numBitsCharCount[1] = cc1; 485317bbafSopenharmony_ci numBitsCharCount[2] = cc2; 495317bbafSopenharmony_ci} 505317bbafSopenharmony_ci 515317bbafSopenharmony_ci 525317bbafSopenharmony_ciint QrSegment::Mode::getModeBits() const { 535317bbafSopenharmony_ci return modeBits; 545317bbafSopenharmony_ci} 555317bbafSopenharmony_ci 565317bbafSopenharmony_ci 575317bbafSopenharmony_ciint QrSegment::Mode::numCharCountBits(int ver) const { 585317bbafSopenharmony_ci return numBitsCharCount[(ver + 7) / 17]; 595317bbafSopenharmony_ci} 605317bbafSopenharmony_ci 615317bbafSopenharmony_ci 625317bbafSopenharmony_ciconst QrSegment::Mode QrSegment::Mode::NUMERIC (0x1, 10, 12, 14); 635317bbafSopenharmony_ciconst QrSegment::Mode QrSegment::Mode::ALPHANUMERIC(0x2, 9, 11, 13); 645317bbafSopenharmony_ciconst QrSegment::Mode QrSegment::Mode::BYTE (0x4, 8, 16, 16); 655317bbafSopenharmony_ciconst QrSegment::Mode QrSegment::Mode::KANJI (0x8, 8, 10, 12); 665317bbafSopenharmony_ciconst QrSegment::Mode QrSegment::Mode::ECI (0x7, 0, 0, 0); 675317bbafSopenharmony_ci 685317bbafSopenharmony_ci 695317bbafSopenharmony_ciQrSegment QrSegment::makeBytes(const vector<uint8_t> &data) { 705317bbafSopenharmony_ci if (data.size() > static_cast<unsigned int>(INT_MAX)) 715317bbafSopenharmony_ci throw std::length_error("Data too long"); 725317bbafSopenharmony_ci BitBuffer bb; 735317bbafSopenharmony_ci for (uint8_t b : data) 745317bbafSopenharmony_ci bb.appendBits(b, 8); 755317bbafSopenharmony_ci return QrSegment(Mode::BYTE, static_cast<int>(data.size()), std::move(bb)); 765317bbafSopenharmony_ci} 775317bbafSopenharmony_ci 785317bbafSopenharmony_ci 795317bbafSopenharmony_ciQrSegment QrSegment::makeNumeric(const char *digits) { 805317bbafSopenharmony_ci BitBuffer bb; 815317bbafSopenharmony_ci int accumData = 0; 825317bbafSopenharmony_ci int accumCount = 0; 835317bbafSopenharmony_ci int charCount = 0; 845317bbafSopenharmony_ci for (; *digits != '\0'; digits++, charCount++) { 855317bbafSopenharmony_ci char c = *digits; 865317bbafSopenharmony_ci if (c < '0' || c > '9') 875317bbafSopenharmony_ci throw std::domain_error("String contains non-numeric characters"); 885317bbafSopenharmony_ci accumData = accumData * 10 + (c - '0'); 895317bbafSopenharmony_ci accumCount++; 905317bbafSopenharmony_ci if (accumCount == 3) { 915317bbafSopenharmony_ci bb.appendBits(static_cast<uint32_t>(accumData), 10); 925317bbafSopenharmony_ci accumData = 0; 935317bbafSopenharmony_ci accumCount = 0; 945317bbafSopenharmony_ci } 955317bbafSopenharmony_ci } 965317bbafSopenharmony_ci if (accumCount > 0) // 1 or 2 digits remaining 975317bbafSopenharmony_ci bb.appendBits(static_cast<uint32_t>(accumData), accumCount * 3 + 1); 985317bbafSopenharmony_ci return QrSegment(Mode::NUMERIC, charCount, std::move(bb)); 995317bbafSopenharmony_ci} 1005317bbafSopenharmony_ci 1015317bbafSopenharmony_ci 1025317bbafSopenharmony_ciQrSegment QrSegment::makeAlphanumeric(const char *text) { 1035317bbafSopenharmony_ci BitBuffer bb; 1045317bbafSopenharmony_ci int accumData = 0; 1055317bbafSopenharmony_ci int accumCount = 0; 1065317bbafSopenharmony_ci int charCount = 0; 1075317bbafSopenharmony_ci for (; *text != '\0'; text++, charCount++) { 1085317bbafSopenharmony_ci const char *temp = std::strchr(ALPHANUMERIC_CHARSET, *text); 1095317bbafSopenharmony_ci if (temp == nullptr) 1105317bbafSopenharmony_ci throw std::domain_error("String contains unencodable characters in alphanumeric mode"); 1115317bbafSopenharmony_ci accumData = accumData * 45 + static_cast<int>(temp - ALPHANUMERIC_CHARSET); 1125317bbafSopenharmony_ci accumCount++; 1135317bbafSopenharmony_ci if (accumCount == 2) { 1145317bbafSopenharmony_ci bb.appendBits(static_cast<uint32_t>(accumData), 11); 1155317bbafSopenharmony_ci accumData = 0; 1165317bbafSopenharmony_ci accumCount = 0; 1175317bbafSopenharmony_ci } 1185317bbafSopenharmony_ci } 1195317bbafSopenharmony_ci if (accumCount > 0) // 1 character remaining 1205317bbafSopenharmony_ci bb.appendBits(static_cast<uint32_t>(accumData), 6); 1215317bbafSopenharmony_ci return QrSegment(Mode::ALPHANUMERIC, charCount, std::move(bb)); 1225317bbafSopenharmony_ci} 1235317bbafSopenharmony_ci 1245317bbafSopenharmony_ci 1255317bbafSopenharmony_civector<QrSegment> QrSegment::makeSegments(const char *text) { 1265317bbafSopenharmony_ci // Select the most efficient segment encoding automatically 1275317bbafSopenharmony_ci vector<QrSegment> result; 1285317bbafSopenharmony_ci if (*text == '\0'); // Leave result empty 1295317bbafSopenharmony_ci else if (isNumeric(text)) 1305317bbafSopenharmony_ci result.push_back(makeNumeric(text)); 1315317bbafSopenharmony_ci else if (isAlphanumeric(text)) 1325317bbafSopenharmony_ci result.push_back(makeAlphanumeric(text)); 1335317bbafSopenharmony_ci else { 1345317bbafSopenharmony_ci vector<uint8_t> bytes; 1355317bbafSopenharmony_ci for (; *text != '\0'; text++) 1365317bbafSopenharmony_ci bytes.push_back(static_cast<uint8_t>(*text)); 1375317bbafSopenharmony_ci result.push_back(makeBytes(bytes)); 1385317bbafSopenharmony_ci } 1395317bbafSopenharmony_ci return result; 1405317bbafSopenharmony_ci} 1415317bbafSopenharmony_ci 1425317bbafSopenharmony_ci 1435317bbafSopenharmony_ciQrSegment QrSegment::makeEci(long assignVal) { 1445317bbafSopenharmony_ci BitBuffer bb; 1455317bbafSopenharmony_ci if (assignVal < 0) 1465317bbafSopenharmony_ci throw std::domain_error("ECI assignment value out of range"); 1475317bbafSopenharmony_ci else if (assignVal < (1 << 7)) 1485317bbafSopenharmony_ci bb.appendBits(static_cast<uint32_t>(assignVal), 8); 1495317bbafSopenharmony_ci else if (assignVal < (1 << 14)) { 1505317bbafSopenharmony_ci bb.appendBits(2, 2); 1515317bbafSopenharmony_ci bb.appendBits(static_cast<uint32_t>(assignVal), 14); 1525317bbafSopenharmony_ci } else if (assignVal < 1000000L) { 1535317bbafSopenharmony_ci bb.appendBits(6, 3); 1545317bbafSopenharmony_ci bb.appendBits(static_cast<uint32_t>(assignVal), 21); 1555317bbafSopenharmony_ci } else 1565317bbafSopenharmony_ci throw std::domain_error("ECI assignment value out of range"); 1575317bbafSopenharmony_ci return QrSegment(Mode::ECI, 0, std::move(bb)); 1585317bbafSopenharmony_ci} 1595317bbafSopenharmony_ci 1605317bbafSopenharmony_ci 1615317bbafSopenharmony_ciQrSegment::QrSegment(const Mode &md, int numCh, const std::vector<bool> &dt) : 1625317bbafSopenharmony_ci mode(&md), 1635317bbafSopenharmony_ci numChars(numCh), 1645317bbafSopenharmony_ci data(dt) { 1655317bbafSopenharmony_ci if (numCh < 0) 1665317bbafSopenharmony_ci throw std::domain_error("Invalid value"); 1675317bbafSopenharmony_ci} 1685317bbafSopenharmony_ci 1695317bbafSopenharmony_ci 1705317bbafSopenharmony_ciQrSegment::QrSegment(const Mode &md, int numCh, std::vector<bool> &&dt) : 1715317bbafSopenharmony_ci mode(&md), 1725317bbafSopenharmony_ci numChars(numCh), 1735317bbafSopenharmony_ci data(std::move(dt)) { 1745317bbafSopenharmony_ci if (numCh < 0) 1755317bbafSopenharmony_ci throw std::domain_error("Invalid value"); 1765317bbafSopenharmony_ci} 1775317bbafSopenharmony_ci 1785317bbafSopenharmony_ci 1795317bbafSopenharmony_ciint QrSegment::getTotalBits(const vector<QrSegment> &segs, int version) { 1805317bbafSopenharmony_ci int result = 0; 1815317bbafSopenharmony_ci for (const QrSegment &seg : segs) { 1825317bbafSopenharmony_ci int ccbits = seg.mode->numCharCountBits(version); 1835317bbafSopenharmony_ci if (seg.numChars >= (1L << ccbits)) 1845317bbafSopenharmony_ci return -1; // The segment's length doesn't fit the field's bit width 1855317bbafSopenharmony_ci if (4 + ccbits > INT_MAX - result) 1865317bbafSopenharmony_ci return -1; // The sum will overflow an int type 1875317bbafSopenharmony_ci result += 4 + ccbits; 1885317bbafSopenharmony_ci if (seg.data.size() > static_cast<unsigned int>(INT_MAX - result)) 1895317bbafSopenharmony_ci return -1; // The sum will overflow an int type 1905317bbafSopenharmony_ci result += static_cast<int>(seg.data.size()); 1915317bbafSopenharmony_ci } 1925317bbafSopenharmony_ci return result; 1935317bbafSopenharmony_ci} 1945317bbafSopenharmony_ci 1955317bbafSopenharmony_ci 1965317bbafSopenharmony_cibool QrSegment::isNumeric(const char *text) { 1975317bbafSopenharmony_ci for (; *text != '\0'; text++) { 1985317bbafSopenharmony_ci char c = *text; 1995317bbafSopenharmony_ci if (c < '0' || c > '9') 2005317bbafSopenharmony_ci return false; 2015317bbafSopenharmony_ci } 2025317bbafSopenharmony_ci return true; 2035317bbafSopenharmony_ci} 2045317bbafSopenharmony_ci 2055317bbafSopenharmony_ci 2065317bbafSopenharmony_cibool QrSegment::isAlphanumeric(const char *text) { 2075317bbafSopenharmony_ci for (; *text != '\0'; text++) { 2085317bbafSopenharmony_ci if (std::strchr(ALPHANUMERIC_CHARSET, *text) == nullptr) 2095317bbafSopenharmony_ci return false; 2105317bbafSopenharmony_ci } 2115317bbafSopenharmony_ci return true; 2125317bbafSopenharmony_ci} 2135317bbafSopenharmony_ci 2145317bbafSopenharmony_ci 2155317bbafSopenharmony_ciconst QrSegment::Mode &QrSegment::getMode() const { 2165317bbafSopenharmony_ci return *mode; 2175317bbafSopenharmony_ci} 2185317bbafSopenharmony_ci 2195317bbafSopenharmony_ci 2205317bbafSopenharmony_ciint QrSegment::getNumChars() const { 2215317bbafSopenharmony_ci return numChars; 2225317bbafSopenharmony_ci} 2235317bbafSopenharmony_ci 2245317bbafSopenharmony_ci 2255317bbafSopenharmony_ciconst std::vector<bool> &QrSegment::getData() const { 2265317bbafSopenharmony_ci return data; 2275317bbafSopenharmony_ci} 2285317bbafSopenharmony_ci 2295317bbafSopenharmony_ci 2305317bbafSopenharmony_ciconst char *QrSegment::ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; 2315317bbafSopenharmony_ci 2325317bbafSopenharmony_ci 2335317bbafSopenharmony_ci 2345317bbafSopenharmony_ci/*---- Class QrCode ----*/ 2355317bbafSopenharmony_ci 2365317bbafSopenharmony_ciint QrCode::getFormatBits(Ecc ecl) { 2375317bbafSopenharmony_ci switch (ecl) { 2385317bbafSopenharmony_ci case Ecc::LOW : return 1; 2395317bbafSopenharmony_ci case Ecc::MEDIUM : return 0; 2405317bbafSopenharmony_ci case Ecc::QUARTILE: return 3; 2415317bbafSopenharmony_ci case Ecc::HIGH : return 2; 2425317bbafSopenharmony_ci default: throw std::logic_error("Unreachable"); 2435317bbafSopenharmony_ci } 2445317bbafSopenharmony_ci} 2455317bbafSopenharmony_ci 2465317bbafSopenharmony_ci 2475317bbafSopenharmony_ciQrCode QrCode::encodeText(const char *text, Ecc ecl) { 2485317bbafSopenharmony_ci vector<QrSegment> segs = QrSegment::makeSegments(text); 2495317bbafSopenharmony_ci return encodeSegments(segs, ecl); 2505317bbafSopenharmony_ci} 2515317bbafSopenharmony_ci 2525317bbafSopenharmony_ci 2535317bbafSopenharmony_ciQrCode QrCode::encodeBinary(const vector<uint8_t> &data, Ecc ecl) { 2545317bbafSopenharmony_ci vector<QrSegment> segs{QrSegment::makeBytes(data)}; 2555317bbafSopenharmony_ci return encodeSegments(segs, ecl); 2565317bbafSopenharmony_ci} 2575317bbafSopenharmony_ci 2585317bbafSopenharmony_ci 2595317bbafSopenharmony_ciQrCode QrCode::encodeSegments(const vector<QrSegment> &segs, Ecc ecl, 2605317bbafSopenharmony_ci int minVersion, int maxVersion, int mask, bool boostEcl) { 2615317bbafSopenharmony_ci if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION) || mask < -1 || mask > 7) 2625317bbafSopenharmony_ci throw std::invalid_argument("Invalid value"); 2635317bbafSopenharmony_ci 2645317bbafSopenharmony_ci // Find the minimal version number to use 2655317bbafSopenharmony_ci int version, dataUsedBits; 2665317bbafSopenharmony_ci for (version = minVersion; ; version++) { 2675317bbafSopenharmony_ci int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available 2685317bbafSopenharmony_ci dataUsedBits = QrSegment::getTotalBits(segs, version); 2695317bbafSopenharmony_ci if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) 2705317bbafSopenharmony_ci break; // This version number is found to be suitable 2715317bbafSopenharmony_ci if (version >= maxVersion) { // All versions in the range could not fit the given data 2725317bbafSopenharmony_ci std::ostringstream sb; 2735317bbafSopenharmony_ci if (dataUsedBits == -1) 2745317bbafSopenharmony_ci sb << "Segment too long"; 2755317bbafSopenharmony_ci else { 2765317bbafSopenharmony_ci sb << "Data length = " << dataUsedBits << " bits, "; 2775317bbafSopenharmony_ci sb << "Max capacity = " << dataCapacityBits << " bits"; 2785317bbafSopenharmony_ci } 2795317bbafSopenharmony_ci throw data_too_long(sb.str()); 2805317bbafSopenharmony_ci } 2815317bbafSopenharmony_ci } 2825317bbafSopenharmony_ci assert(dataUsedBits != -1); 2835317bbafSopenharmony_ci 2845317bbafSopenharmony_ci // Increase the error correction level while the data still fits in the current version number 2855317bbafSopenharmony_ci for (Ecc newEcl : {Ecc::MEDIUM, Ecc::QUARTILE, Ecc::HIGH}) { // From low to high 2865317bbafSopenharmony_ci if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8) 2875317bbafSopenharmony_ci ecl = newEcl; 2885317bbafSopenharmony_ci } 2895317bbafSopenharmony_ci 2905317bbafSopenharmony_ci // Concatenate all segments to create the data bit string 2915317bbafSopenharmony_ci BitBuffer bb; 2925317bbafSopenharmony_ci for (const QrSegment &seg : segs) { 2935317bbafSopenharmony_ci bb.appendBits(static_cast<uint32_t>(seg.getMode().getModeBits()), 4); 2945317bbafSopenharmony_ci bb.appendBits(static_cast<uint32_t>(seg.getNumChars()), seg.getMode().numCharCountBits(version)); 2955317bbafSopenharmony_ci bb.insert(bb.end(), seg.getData().begin(), seg.getData().end()); 2965317bbafSopenharmony_ci } 2975317bbafSopenharmony_ci assert(bb.size() == static_cast<unsigned int>(dataUsedBits)); 2985317bbafSopenharmony_ci 2995317bbafSopenharmony_ci // Add terminator and pad up to a byte if applicable 3005317bbafSopenharmony_ci size_t dataCapacityBits = static_cast<size_t>(getNumDataCodewords(version, ecl)) * 8; 3015317bbafSopenharmony_ci assert(bb.size() <= dataCapacityBits); 3025317bbafSopenharmony_ci bb.appendBits(0, std::min(4, static_cast<int>(dataCapacityBits - bb.size()))); 3035317bbafSopenharmony_ci bb.appendBits(0, (8 - static_cast<int>(bb.size() % 8)) % 8); 3045317bbafSopenharmony_ci assert(bb.size() % 8 == 0); 3055317bbafSopenharmony_ci 3065317bbafSopenharmony_ci // Pad with alternating bytes until data capacity is reached 3075317bbafSopenharmony_ci for (uint8_t padByte = 0xEC; bb.size() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) 3085317bbafSopenharmony_ci bb.appendBits(padByte, 8); 3095317bbafSopenharmony_ci 3105317bbafSopenharmony_ci // Pack bits into bytes in big endian 3115317bbafSopenharmony_ci vector<uint8_t> dataCodewords(bb.size() / 8); 3125317bbafSopenharmony_ci for (size_t i = 0; i < bb.size(); i++) 3135317bbafSopenharmony_ci dataCodewords.at(i >> 3) |= (bb.at(i) ? 1 : 0) << (7 - (i & 7)); 3145317bbafSopenharmony_ci 3155317bbafSopenharmony_ci // Create the QR Code object 3165317bbafSopenharmony_ci return QrCode(version, ecl, dataCodewords, mask); 3175317bbafSopenharmony_ci} 3185317bbafSopenharmony_ci 3195317bbafSopenharmony_ci 3205317bbafSopenharmony_ciQrCode::QrCode(int ver, Ecc ecl, const vector<uint8_t> &dataCodewords, int msk) : 3215317bbafSopenharmony_ci // Initialize fields and check arguments 3225317bbafSopenharmony_ci version(ver), 3235317bbafSopenharmony_ci errorCorrectionLevel(ecl) { 3245317bbafSopenharmony_ci if (ver < MIN_VERSION || ver > MAX_VERSION) 3255317bbafSopenharmony_ci throw std::domain_error("Version value out of range"); 3265317bbafSopenharmony_ci if (msk < -1 || msk > 7) 3275317bbafSopenharmony_ci throw std::domain_error("Mask value out of range"); 3285317bbafSopenharmony_ci size = ver * 4 + 17; 3295317bbafSopenharmony_ci size_t sz = static_cast<size_t>(size); 3305317bbafSopenharmony_ci modules = vector<vector<bool> >(sz, vector<bool>(sz)); // Initially all light 3315317bbafSopenharmony_ci isFunction = vector<vector<bool> >(sz, vector<bool>(sz)); 3325317bbafSopenharmony_ci 3335317bbafSopenharmony_ci // Compute ECC, draw modules 3345317bbafSopenharmony_ci drawFunctionPatterns(); 3355317bbafSopenharmony_ci const vector<uint8_t> allCodewords = addEccAndInterleave(dataCodewords); 3365317bbafSopenharmony_ci drawCodewords(allCodewords); 3375317bbafSopenharmony_ci 3385317bbafSopenharmony_ci // Do masking 3395317bbafSopenharmony_ci if (msk == -1) { // Automatically choose best mask 3405317bbafSopenharmony_ci long minPenalty = LONG_MAX; 3415317bbafSopenharmony_ci for (int i = 0; i < 8; i++) { 3425317bbafSopenharmony_ci applyMask(i); 3435317bbafSopenharmony_ci drawFormatBits(i); 3445317bbafSopenharmony_ci long penalty = getPenaltyScore(); 3455317bbafSopenharmony_ci if (penalty < minPenalty) { 3465317bbafSopenharmony_ci msk = i; 3475317bbafSopenharmony_ci minPenalty = penalty; 3485317bbafSopenharmony_ci } 3495317bbafSopenharmony_ci applyMask(i); // Undoes the mask due to XOR 3505317bbafSopenharmony_ci } 3515317bbafSopenharmony_ci } 3525317bbafSopenharmony_ci assert(0 <= msk && msk <= 7); 3535317bbafSopenharmony_ci mask = msk; 3545317bbafSopenharmony_ci applyMask(msk); // Apply the final choice of mask 3555317bbafSopenharmony_ci drawFormatBits(msk); // Overwrite old format bits 3565317bbafSopenharmony_ci 3575317bbafSopenharmony_ci isFunction.clear(); 3585317bbafSopenharmony_ci isFunction.shrink_to_fit(); 3595317bbafSopenharmony_ci} 3605317bbafSopenharmony_ci 3615317bbafSopenharmony_ci 3625317bbafSopenharmony_ciint QrCode::getVersion() const { 3635317bbafSopenharmony_ci return version; 3645317bbafSopenharmony_ci} 3655317bbafSopenharmony_ci 3665317bbafSopenharmony_ci 3675317bbafSopenharmony_ciint QrCode::getSize() const { 3685317bbafSopenharmony_ci return size; 3695317bbafSopenharmony_ci} 3705317bbafSopenharmony_ci 3715317bbafSopenharmony_ci 3725317bbafSopenharmony_ciQrCode::Ecc QrCode::getErrorCorrectionLevel() const { 3735317bbafSopenharmony_ci return errorCorrectionLevel; 3745317bbafSopenharmony_ci} 3755317bbafSopenharmony_ci 3765317bbafSopenharmony_ci 3775317bbafSopenharmony_ciint QrCode::getMask() const { 3785317bbafSopenharmony_ci return mask; 3795317bbafSopenharmony_ci} 3805317bbafSopenharmony_ci 3815317bbafSopenharmony_ci 3825317bbafSopenharmony_cibool QrCode::getModule(int x, int y) const { 3835317bbafSopenharmony_ci return 0 <= x && x < size && 0 <= y && y < size && module(x, y); 3845317bbafSopenharmony_ci} 3855317bbafSopenharmony_ci 3865317bbafSopenharmony_ci 3875317bbafSopenharmony_civoid QrCode::drawFunctionPatterns() { 3885317bbafSopenharmony_ci // Draw horizontal and vertical timing patterns 3895317bbafSopenharmony_ci for (int i = 0; i < size; i++) { 3905317bbafSopenharmony_ci setFunctionModule(6, i, i % 2 == 0); 3915317bbafSopenharmony_ci setFunctionModule(i, 6, i % 2 == 0); 3925317bbafSopenharmony_ci } 3935317bbafSopenharmony_ci 3945317bbafSopenharmony_ci // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) 3955317bbafSopenharmony_ci drawFinderPattern(3, 3); 3965317bbafSopenharmony_ci drawFinderPattern(size - 4, 3); 3975317bbafSopenharmony_ci drawFinderPattern(3, size - 4); 3985317bbafSopenharmony_ci 3995317bbafSopenharmony_ci // Draw numerous alignment patterns 4005317bbafSopenharmony_ci const vector<int> alignPatPos = getAlignmentPatternPositions(); 4015317bbafSopenharmony_ci size_t numAlign = alignPatPos.size(); 4025317bbafSopenharmony_ci for (size_t i = 0; i < numAlign; i++) { 4035317bbafSopenharmony_ci for (size_t j = 0; j < numAlign; j++) { 4045317bbafSopenharmony_ci // Don't draw on the three finder corners 4055317bbafSopenharmony_ci if (!((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0))) 4065317bbafSopenharmony_ci drawAlignmentPattern(alignPatPos.at(i), alignPatPos.at(j)); 4075317bbafSopenharmony_ci } 4085317bbafSopenharmony_ci } 4095317bbafSopenharmony_ci 4105317bbafSopenharmony_ci // Draw configuration data 4115317bbafSopenharmony_ci drawFormatBits(0); // Dummy mask value; overwritten later in the constructor 4125317bbafSopenharmony_ci drawVersion(); 4135317bbafSopenharmony_ci} 4145317bbafSopenharmony_ci 4155317bbafSopenharmony_ci 4165317bbafSopenharmony_civoid QrCode::drawFormatBits(int msk) { 4175317bbafSopenharmony_ci // Calculate error correction code and pack bits 4185317bbafSopenharmony_ci int data = getFormatBits(errorCorrectionLevel) << 3 | msk; // errCorrLvl is uint2, msk is uint3 4195317bbafSopenharmony_ci int rem = data; 4205317bbafSopenharmony_ci for (int i = 0; i < 10; i++) 4215317bbafSopenharmony_ci rem = (rem << 1) ^ ((rem >> 9) * 0x537); 4225317bbafSopenharmony_ci int bits = (data << 10 | rem) ^ 0x5412; // uint15 4235317bbafSopenharmony_ci assert(bits >> 15 == 0); 4245317bbafSopenharmony_ci 4255317bbafSopenharmony_ci // Draw first copy 4265317bbafSopenharmony_ci for (int i = 0; i <= 5; i++) 4275317bbafSopenharmony_ci setFunctionModule(8, i, getBit(bits, i)); 4285317bbafSopenharmony_ci setFunctionModule(8, 7, getBit(bits, 6)); 4295317bbafSopenharmony_ci setFunctionModule(8, 8, getBit(bits, 7)); 4305317bbafSopenharmony_ci setFunctionModule(7, 8, getBit(bits, 8)); 4315317bbafSopenharmony_ci for (int i = 9; i < 15; i++) 4325317bbafSopenharmony_ci setFunctionModule(14 - i, 8, getBit(bits, i)); 4335317bbafSopenharmony_ci 4345317bbafSopenharmony_ci // Draw second copy 4355317bbafSopenharmony_ci for (int i = 0; i < 8; i++) 4365317bbafSopenharmony_ci setFunctionModule(size - 1 - i, 8, getBit(bits, i)); 4375317bbafSopenharmony_ci for (int i = 8; i < 15; i++) 4385317bbafSopenharmony_ci setFunctionModule(8, size - 15 + i, getBit(bits, i)); 4395317bbafSopenharmony_ci setFunctionModule(8, size - 8, true); // Always dark 4405317bbafSopenharmony_ci} 4415317bbafSopenharmony_ci 4425317bbafSopenharmony_ci 4435317bbafSopenharmony_civoid QrCode::drawVersion() { 4445317bbafSopenharmony_ci if (version < 7) 4455317bbafSopenharmony_ci return; 4465317bbafSopenharmony_ci 4475317bbafSopenharmony_ci // Calculate error correction code and pack bits 4485317bbafSopenharmony_ci int rem = version; // version is uint6, in the range [7, 40] 4495317bbafSopenharmony_ci for (int i = 0; i < 12; i++) 4505317bbafSopenharmony_ci rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); 4515317bbafSopenharmony_ci long bits = static_cast<long>(version) << 12 | rem; // uint18 4525317bbafSopenharmony_ci assert(bits >> 18 == 0); 4535317bbafSopenharmony_ci 4545317bbafSopenharmony_ci // Draw two copies 4555317bbafSopenharmony_ci for (int i = 0; i < 18; i++) { 4565317bbafSopenharmony_ci bool bit = getBit(bits, i); 4575317bbafSopenharmony_ci int a = size - 11 + i % 3; 4585317bbafSopenharmony_ci int b = i / 3; 4595317bbafSopenharmony_ci setFunctionModule(a, b, bit); 4605317bbafSopenharmony_ci setFunctionModule(b, a, bit); 4615317bbafSopenharmony_ci } 4625317bbafSopenharmony_ci} 4635317bbafSopenharmony_ci 4645317bbafSopenharmony_ci 4655317bbafSopenharmony_civoid QrCode::drawFinderPattern(int x, int y) { 4665317bbafSopenharmony_ci for (int dy = -4; dy <= 4; dy++) { 4675317bbafSopenharmony_ci for (int dx = -4; dx <= 4; dx++) { 4685317bbafSopenharmony_ci int dist = std::max(std::abs(dx), std::abs(dy)); // Chebyshev/infinity norm 4695317bbafSopenharmony_ci int xx = x + dx, yy = y + dy; 4705317bbafSopenharmony_ci if (0 <= xx && xx < size && 0 <= yy && yy < size) 4715317bbafSopenharmony_ci setFunctionModule(xx, yy, dist != 2 && dist != 4); 4725317bbafSopenharmony_ci } 4735317bbafSopenharmony_ci } 4745317bbafSopenharmony_ci} 4755317bbafSopenharmony_ci 4765317bbafSopenharmony_ci 4775317bbafSopenharmony_civoid QrCode::drawAlignmentPattern(int x, int y) { 4785317bbafSopenharmony_ci for (int dy = -2; dy <= 2; dy++) { 4795317bbafSopenharmony_ci for (int dx = -2; dx <= 2; dx++) 4805317bbafSopenharmony_ci setFunctionModule(x + dx, y + dy, std::max(std::abs(dx), std::abs(dy)) != 1); 4815317bbafSopenharmony_ci } 4825317bbafSopenharmony_ci} 4835317bbafSopenharmony_ci 4845317bbafSopenharmony_ci 4855317bbafSopenharmony_civoid QrCode::setFunctionModule(int x, int y, bool isDark) { 4865317bbafSopenharmony_ci size_t ux = static_cast<size_t>(x); 4875317bbafSopenharmony_ci size_t uy = static_cast<size_t>(y); 4885317bbafSopenharmony_ci modules .at(uy).at(ux) = isDark; 4895317bbafSopenharmony_ci isFunction.at(uy).at(ux) = true; 4905317bbafSopenharmony_ci} 4915317bbafSopenharmony_ci 4925317bbafSopenharmony_ci 4935317bbafSopenharmony_cibool QrCode::module(int x, int y) const { 4945317bbafSopenharmony_ci return modules.at(static_cast<size_t>(y)).at(static_cast<size_t>(x)); 4955317bbafSopenharmony_ci} 4965317bbafSopenharmony_ci 4975317bbafSopenharmony_ci 4985317bbafSopenharmony_civector<uint8_t> QrCode::addEccAndInterleave(const vector<uint8_t> &data) const { 4995317bbafSopenharmony_ci if (data.size() != static_cast<unsigned int>(getNumDataCodewords(version, errorCorrectionLevel))) 5005317bbafSopenharmony_ci throw std::invalid_argument("Invalid argument"); 5015317bbafSopenharmony_ci 5025317bbafSopenharmony_ci // Calculate parameter numbers 5035317bbafSopenharmony_ci int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[static_cast<int>(errorCorrectionLevel)][version]; 5045317bbafSopenharmony_ci int blockEccLen = ECC_CODEWORDS_PER_BLOCK [static_cast<int>(errorCorrectionLevel)][version]; 5055317bbafSopenharmony_ci int rawCodewords = getNumRawDataModules(version) / 8; 5065317bbafSopenharmony_ci int numShortBlocks = numBlocks - rawCodewords % numBlocks; 5075317bbafSopenharmony_ci int shortBlockLen = rawCodewords / numBlocks; 5085317bbafSopenharmony_ci 5095317bbafSopenharmony_ci // Split data into blocks and append ECC to each block 5105317bbafSopenharmony_ci vector<vector<uint8_t> > blocks; 5115317bbafSopenharmony_ci const vector<uint8_t> rsDiv = reedSolomonComputeDivisor(blockEccLen); 5125317bbafSopenharmony_ci for (int i = 0, k = 0; i < numBlocks; i++) { 5135317bbafSopenharmony_ci vector<uint8_t> dat(data.cbegin() + k, data.cbegin() + (k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1))); 5145317bbafSopenharmony_ci k += static_cast<int>(dat.size()); 5155317bbafSopenharmony_ci const vector<uint8_t> ecc = reedSolomonComputeRemainder(dat, rsDiv); 5165317bbafSopenharmony_ci if (i < numShortBlocks) 5175317bbafSopenharmony_ci dat.push_back(0); 5185317bbafSopenharmony_ci dat.insert(dat.end(), ecc.cbegin(), ecc.cend()); 5195317bbafSopenharmony_ci blocks.push_back(std::move(dat)); 5205317bbafSopenharmony_ci } 5215317bbafSopenharmony_ci 5225317bbafSopenharmony_ci // Interleave (not concatenate) the bytes from every block into a single sequence 5235317bbafSopenharmony_ci vector<uint8_t> result; 5245317bbafSopenharmony_ci for (size_t i = 0; i < blocks.at(0).size(); i++) { 5255317bbafSopenharmony_ci for (size_t j = 0; j < blocks.size(); j++) { 5265317bbafSopenharmony_ci // Skip the padding byte in short blocks 5275317bbafSopenharmony_ci if (i != static_cast<unsigned int>(shortBlockLen - blockEccLen) || j >= static_cast<unsigned int>(numShortBlocks)) 5285317bbafSopenharmony_ci result.push_back(blocks.at(j).at(i)); 5295317bbafSopenharmony_ci } 5305317bbafSopenharmony_ci } 5315317bbafSopenharmony_ci assert(result.size() == static_cast<unsigned int>(rawCodewords)); 5325317bbafSopenharmony_ci return result; 5335317bbafSopenharmony_ci} 5345317bbafSopenharmony_ci 5355317bbafSopenharmony_ci 5365317bbafSopenharmony_civoid QrCode::drawCodewords(const vector<uint8_t> &data) { 5375317bbafSopenharmony_ci if (data.size() != static_cast<unsigned int>(getNumRawDataModules(version) / 8)) 5385317bbafSopenharmony_ci throw std::invalid_argument("Invalid argument"); 5395317bbafSopenharmony_ci 5405317bbafSopenharmony_ci size_t i = 0; // Bit index into the data 5415317bbafSopenharmony_ci // Do the funny zigzag scan 5425317bbafSopenharmony_ci for (int right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair 5435317bbafSopenharmony_ci if (right == 6) 5445317bbafSopenharmony_ci right = 5; 5455317bbafSopenharmony_ci for (int vert = 0; vert < size; vert++) { // Vertical counter 5465317bbafSopenharmony_ci for (int j = 0; j < 2; j++) { 5475317bbafSopenharmony_ci size_t x = static_cast<size_t>(right - j); // Actual x coordinate 5485317bbafSopenharmony_ci bool upward = ((right + 1) & 2) == 0; 5495317bbafSopenharmony_ci size_t y = static_cast<size_t>(upward ? size - 1 - vert : vert); // Actual y coordinate 5505317bbafSopenharmony_ci if (!isFunction.at(y).at(x) && i < data.size() * 8) { 5515317bbafSopenharmony_ci modules.at(y).at(x) = getBit(data.at(i >> 3), 7 - static_cast<int>(i & 7)); 5525317bbafSopenharmony_ci i++; 5535317bbafSopenharmony_ci } 5545317bbafSopenharmony_ci // If this QR Code has any remainder bits (0 to 7), they were assigned as 5555317bbafSopenharmony_ci // 0/false/light by the constructor and are left unchanged by this method 5565317bbafSopenharmony_ci } 5575317bbafSopenharmony_ci } 5585317bbafSopenharmony_ci } 5595317bbafSopenharmony_ci assert(i == data.size() * 8); 5605317bbafSopenharmony_ci} 5615317bbafSopenharmony_ci 5625317bbafSopenharmony_ci 5635317bbafSopenharmony_civoid QrCode::applyMask(int msk) { 5645317bbafSopenharmony_ci if (msk < 0 || msk > 7) 5655317bbafSopenharmony_ci throw std::domain_error("Mask value out of range"); 5665317bbafSopenharmony_ci size_t sz = static_cast<size_t>(size); 5675317bbafSopenharmony_ci for (size_t y = 0; y < sz; y++) { 5685317bbafSopenharmony_ci for (size_t x = 0; x < sz; x++) { 5695317bbafSopenharmony_ci bool invert; 5705317bbafSopenharmony_ci switch (msk) { 5715317bbafSopenharmony_ci case 0: invert = (x + y) % 2 == 0; break; 5725317bbafSopenharmony_ci case 1: invert = y % 2 == 0; break; 5735317bbafSopenharmony_ci case 2: invert = x % 3 == 0; break; 5745317bbafSopenharmony_ci case 3: invert = (x + y) % 3 == 0; break; 5755317bbafSopenharmony_ci case 4: invert = (x / 3 + y / 2) % 2 == 0; break; 5765317bbafSopenharmony_ci case 5: invert = x * y % 2 + x * y % 3 == 0; break; 5775317bbafSopenharmony_ci case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; 5785317bbafSopenharmony_ci case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; 5795317bbafSopenharmony_ci default: throw std::logic_error("Unreachable"); 5805317bbafSopenharmony_ci } 5815317bbafSopenharmony_ci modules.at(y).at(x) = modules.at(y).at(x) ^ (invert & !isFunction.at(y).at(x)); 5825317bbafSopenharmony_ci } 5835317bbafSopenharmony_ci } 5845317bbafSopenharmony_ci} 5855317bbafSopenharmony_ci 5865317bbafSopenharmony_ci 5875317bbafSopenharmony_cilong QrCode::getPenaltyScore() const { 5885317bbafSopenharmony_ci long result = 0; 5895317bbafSopenharmony_ci 5905317bbafSopenharmony_ci // Adjacent modules in row having same color, and finder-like patterns 5915317bbafSopenharmony_ci for (int y = 0; y < size; y++) { 5925317bbafSopenharmony_ci bool runColor = false; 5935317bbafSopenharmony_ci int runX = 0; 5945317bbafSopenharmony_ci std::array<int,7> runHistory = {}; 5955317bbafSopenharmony_ci for (int x = 0; x < size; x++) { 5965317bbafSopenharmony_ci if (module(x, y) == runColor) { 5975317bbafSopenharmony_ci runX++; 5985317bbafSopenharmony_ci if (runX == 5) 5995317bbafSopenharmony_ci result += PENALTY_N1; 6005317bbafSopenharmony_ci else if (runX > 5) 6015317bbafSopenharmony_ci result++; 6025317bbafSopenharmony_ci } else { 6035317bbafSopenharmony_ci finderPenaltyAddHistory(runX, runHistory); 6045317bbafSopenharmony_ci if (!runColor) 6055317bbafSopenharmony_ci result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; 6065317bbafSopenharmony_ci runColor = module(x, y); 6075317bbafSopenharmony_ci runX = 1; 6085317bbafSopenharmony_ci } 6095317bbafSopenharmony_ci } 6105317bbafSopenharmony_ci result += finderPenaltyTerminateAndCount(runColor, runX, runHistory) * PENALTY_N3; 6115317bbafSopenharmony_ci } 6125317bbafSopenharmony_ci // Adjacent modules in column having same color, and finder-like patterns 6135317bbafSopenharmony_ci for (int x = 0; x < size; x++) { 6145317bbafSopenharmony_ci bool runColor = false; 6155317bbafSopenharmony_ci int runY = 0; 6165317bbafSopenharmony_ci std::array<int,7> runHistory = {}; 6175317bbafSopenharmony_ci for (int y = 0; y < size; y++) { 6185317bbafSopenharmony_ci if (module(x, y) == runColor) { 6195317bbafSopenharmony_ci runY++; 6205317bbafSopenharmony_ci if (runY == 5) 6215317bbafSopenharmony_ci result += PENALTY_N1; 6225317bbafSopenharmony_ci else if (runY > 5) 6235317bbafSopenharmony_ci result++; 6245317bbafSopenharmony_ci } else { 6255317bbafSopenharmony_ci finderPenaltyAddHistory(runY, runHistory); 6265317bbafSopenharmony_ci if (!runColor) 6275317bbafSopenharmony_ci result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; 6285317bbafSopenharmony_ci runColor = module(x, y); 6295317bbafSopenharmony_ci runY = 1; 6305317bbafSopenharmony_ci } 6315317bbafSopenharmony_ci } 6325317bbafSopenharmony_ci result += finderPenaltyTerminateAndCount(runColor, runY, runHistory) * PENALTY_N3; 6335317bbafSopenharmony_ci } 6345317bbafSopenharmony_ci 6355317bbafSopenharmony_ci // 2*2 blocks of modules having same color 6365317bbafSopenharmony_ci for (int y = 0; y < size - 1; y++) { 6375317bbafSopenharmony_ci for (int x = 0; x < size - 1; x++) { 6385317bbafSopenharmony_ci bool color = module(x, y); 6395317bbafSopenharmony_ci if ( color == module(x + 1, y) && 6405317bbafSopenharmony_ci color == module(x, y + 1) && 6415317bbafSopenharmony_ci color == module(x + 1, y + 1)) 6425317bbafSopenharmony_ci result += PENALTY_N2; 6435317bbafSopenharmony_ci } 6445317bbafSopenharmony_ci } 6455317bbafSopenharmony_ci 6465317bbafSopenharmony_ci // Balance of dark and light modules 6475317bbafSopenharmony_ci int dark = 0; 6485317bbafSopenharmony_ci for (const vector<bool> &row : modules) { 6495317bbafSopenharmony_ci for (bool color : row) { 6505317bbafSopenharmony_ci if (color) 6515317bbafSopenharmony_ci dark++; 6525317bbafSopenharmony_ci } 6535317bbafSopenharmony_ci } 6545317bbafSopenharmony_ci int total = size * size; // Note that size is odd, so dark/total != 1/2 6555317bbafSopenharmony_ci // Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)% 6565317bbafSopenharmony_ci int k = static_cast<int>((std::abs(dark * 20L - total * 10L) + total - 1) / total) - 1; 6575317bbafSopenharmony_ci assert(0 <= k && k <= 9); 6585317bbafSopenharmony_ci result += k * PENALTY_N4; 6595317bbafSopenharmony_ci assert(0 <= result && result <= 2568888L); // Non-tight upper bound based on default values of PENALTY_N1, ..., N4 6605317bbafSopenharmony_ci return result; 6615317bbafSopenharmony_ci} 6625317bbafSopenharmony_ci 6635317bbafSopenharmony_ci 6645317bbafSopenharmony_civector<int> QrCode::getAlignmentPatternPositions() const { 6655317bbafSopenharmony_ci if (version == 1) 6665317bbafSopenharmony_ci return vector<int>(); 6675317bbafSopenharmony_ci else { 6685317bbafSopenharmony_ci int numAlign = version / 7 + 2; 6695317bbafSopenharmony_ci int step = (version == 32) ? 26 : 6705317bbafSopenharmony_ci (version * 4 + numAlign * 2 + 1) / (numAlign * 2 - 2) * 2; 6715317bbafSopenharmony_ci vector<int> result; 6725317bbafSopenharmony_ci for (int i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step) 6735317bbafSopenharmony_ci result.insert(result.begin(), pos); 6745317bbafSopenharmony_ci result.insert(result.begin(), 6); 6755317bbafSopenharmony_ci return result; 6765317bbafSopenharmony_ci } 6775317bbafSopenharmony_ci} 6785317bbafSopenharmony_ci 6795317bbafSopenharmony_ci 6805317bbafSopenharmony_ciint QrCode::getNumRawDataModules(int ver) { 6815317bbafSopenharmony_ci if (ver < MIN_VERSION || ver > MAX_VERSION) 6825317bbafSopenharmony_ci throw std::domain_error("Version number out of range"); 6835317bbafSopenharmony_ci int result = (16 * ver + 128) * ver + 64; 6845317bbafSopenharmony_ci if (ver >= 2) { 6855317bbafSopenharmony_ci int numAlign = ver / 7 + 2; 6865317bbafSopenharmony_ci result -= (25 * numAlign - 10) * numAlign - 55; 6875317bbafSopenharmony_ci if (ver >= 7) 6885317bbafSopenharmony_ci result -= 36; 6895317bbafSopenharmony_ci } 6905317bbafSopenharmony_ci assert(208 <= result && result <= 29648); 6915317bbafSopenharmony_ci return result; 6925317bbafSopenharmony_ci} 6935317bbafSopenharmony_ci 6945317bbafSopenharmony_ci 6955317bbafSopenharmony_ciint QrCode::getNumDataCodewords(int ver, Ecc ecl) { 6965317bbafSopenharmony_ci return getNumRawDataModules(ver) / 8 6975317bbafSopenharmony_ci - ECC_CODEWORDS_PER_BLOCK [static_cast<int>(ecl)][ver] 6985317bbafSopenharmony_ci * NUM_ERROR_CORRECTION_BLOCKS[static_cast<int>(ecl)][ver]; 6995317bbafSopenharmony_ci} 7005317bbafSopenharmony_ci 7015317bbafSopenharmony_ci 7025317bbafSopenharmony_civector<uint8_t> QrCode::reedSolomonComputeDivisor(int degree) { 7035317bbafSopenharmony_ci if (degree < 1 || degree > 255) 7045317bbafSopenharmony_ci throw std::domain_error("Degree out of range"); 7055317bbafSopenharmony_ci // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. 7065317bbafSopenharmony_ci // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. 7075317bbafSopenharmony_ci vector<uint8_t> result(static_cast<size_t>(degree)); 7085317bbafSopenharmony_ci result.at(result.size() - 1) = 1; // Start off with the monomial x^0 7095317bbafSopenharmony_ci 7105317bbafSopenharmony_ci // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), 7115317bbafSopenharmony_ci // and drop the highest monomial term which is always 1x^degree. 7125317bbafSopenharmony_ci // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). 7135317bbafSopenharmony_ci uint8_t root = 1; 7145317bbafSopenharmony_ci for (int i = 0; i < degree; i++) { 7155317bbafSopenharmony_ci // Multiply the current product by (x - r^i) 7165317bbafSopenharmony_ci for (size_t j = 0; j < result.size(); j++) { 7175317bbafSopenharmony_ci result.at(j) = reedSolomonMultiply(result.at(j), root); 7185317bbafSopenharmony_ci if (j + 1 < result.size()) 7195317bbafSopenharmony_ci result.at(j) ^= result.at(j + 1); 7205317bbafSopenharmony_ci } 7215317bbafSopenharmony_ci root = reedSolomonMultiply(root, 0x02); 7225317bbafSopenharmony_ci } 7235317bbafSopenharmony_ci return result; 7245317bbafSopenharmony_ci} 7255317bbafSopenharmony_ci 7265317bbafSopenharmony_ci 7275317bbafSopenharmony_civector<uint8_t> QrCode::reedSolomonComputeRemainder(const vector<uint8_t> &data, const vector<uint8_t> &divisor) { 7285317bbafSopenharmony_ci vector<uint8_t> result(divisor.size()); 7295317bbafSopenharmony_ci for (uint8_t b : data) { // Polynomial division 7305317bbafSopenharmony_ci uint8_t factor = b ^ result.at(0); 7315317bbafSopenharmony_ci result.erase(result.begin()); 7325317bbafSopenharmony_ci result.push_back(0); 7335317bbafSopenharmony_ci for (size_t i = 0; i < result.size(); i++) 7345317bbafSopenharmony_ci result.at(i) ^= reedSolomonMultiply(divisor.at(i), factor); 7355317bbafSopenharmony_ci } 7365317bbafSopenharmony_ci return result; 7375317bbafSopenharmony_ci} 7385317bbafSopenharmony_ci 7395317bbafSopenharmony_ci 7405317bbafSopenharmony_ciuint8_t QrCode::reedSolomonMultiply(uint8_t x, uint8_t y) { 7415317bbafSopenharmony_ci // Russian peasant multiplication 7425317bbafSopenharmony_ci int z = 0; 7435317bbafSopenharmony_ci for (int i = 7; i >= 0; i--) { 7445317bbafSopenharmony_ci z = (z << 1) ^ ((z >> 7) * 0x11D); 7455317bbafSopenharmony_ci z ^= ((y >> i) & 1) * x; 7465317bbafSopenharmony_ci } 7475317bbafSopenharmony_ci assert(z >> 8 == 0); 7485317bbafSopenharmony_ci return static_cast<uint8_t>(z); 7495317bbafSopenharmony_ci} 7505317bbafSopenharmony_ci 7515317bbafSopenharmony_ci 7525317bbafSopenharmony_ciint QrCode::finderPenaltyCountPatterns(const std::array<int,7> &runHistory) const { 7535317bbafSopenharmony_ci int n = runHistory.at(1); 7545317bbafSopenharmony_ci assert(n <= size * 3); 7555317bbafSopenharmony_ci bool core = n > 0 && runHistory.at(2) == n && runHistory.at(3) == n * 3 && runHistory.at(4) == n && runHistory.at(5) == n; 7565317bbafSopenharmony_ci return (core && runHistory.at(0) >= n * 4 && runHistory.at(6) >= n ? 1 : 0) 7575317bbafSopenharmony_ci + (core && runHistory.at(6) >= n * 4 && runHistory.at(0) >= n ? 1 : 0); 7585317bbafSopenharmony_ci} 7595317bbafSopenharmony_ci 7605317bbafSopenharmony_ci 7615317bbafSopenharmony_ciint QrCode::finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, std::array<int,7> &runHistory) const { 7625317bbafSopenharmony_ci if (currentRunColor) { // Terminate dark run 7635317bbafSopenharmony_ci finderPenaltyAddHistory(currentRunLength, runHistory); 7645317bbafSopenharmony_ci currentRunLength = 0; 7655317bbafSopenharmony_ci } 7665317bbafSopenharmony_ci currentRunLength += size; // Add light border to final run 7675317bbafSopenharmony_ci finderPenaltyAddHistory(currentRunLength, runHistory); 7685317bbafSopenharmony_ci return finderPenaltyCountPatterns(runHistory); 7695317bbafSopenharmony_ci} 7705317bbafSopenharmony_ci 7715317bbafSopenharmony_ci 7725317bbafSopenharmony_civoid QrCode::finderPenaltyAddHistory(int currentRunLength, std::array<int,7> &runHistory) const { 7735317bbafSopenharmony_ci if (runHistory.at(0) == 0) 7745317bbafSopenharmony_ci currentRunLength += size; // Add light border to initial run 7755317bbafSopenharmony_ci std::copy_backward(runHistory.cbegin(), runHistory.cend() - 1, runHistory.end()); 7765317bbafSopenharmony_ci runHistory.at(0) = currentRunLength; 7775317bbafSopenharmony_ci} 7785317bbafSopenharmony_ci 7795317bbafSopenharmony_ci 7805317bbafSopenharmony_cibool QrCode::getBit(long x, int i) { 7815317bbafSopenharmony_ci return ((x >> i) & 1) != 0; 7825317bbafSopenharmony_ci} 7835317bbafSopenharmony_ci 7845317bbafSopenharmony_ci 7855317bbafSopenharmony_ci/*---- Tables of constants ----*/ 7865317bbafSopenharmony_ci 7875317bbafSopenharmony_ciconst int QrCode::PENALTY_N1 = 3; 7885317bbafSopenharmony_ciconst int QrCode::PENALTY_N2 = 3; 7895317bbafSopenharmony_ciconst int QrCode::PENALTY_N3 = 40; 7905317bbafSopenharmony_ciconst int QrCode::PENALTY_N4 = 10; 7915317bbafSopenharmony_ci 7925317bbafSopenharmony_ci 7935317bbafSopenharmony_ciconst int8_t QrCode::ECC_CODEWORDS_PER_BLOCK[4][41] = { 7945317bbafSopenharmony_ci // Version: (note that index 0 is for padding, and is set to an illegal value) 7955317bbafSopenharmony_ci //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 7965317bbafSopenharmony_ci {-1, 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}, // Low 7975317bbafSopenharmony_ci {-1, 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}, // Medium 7985317bbafSopenharmony_ci {-1, 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}, // Quartile 7995317bbafSopenharmony_ci {-1, 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}, // High 8005317bbafSopenharmony_ci}; 8015317bbafSopenharmony_ci 8025317bbafSopenharmony_ciconst int8_t QrCode::NUM_ERROR_CORRECTION_BLOCKS[4][41] = { 8035317bbafSopenharmony_ci // Version: (note that index 0 is for padding, and is set to an illegal value) 8045317bbafSopenharmony_ci //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 8055317bbafSopenharmony_ci {-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 8065317bbafSopenharmony_ci {-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 8075317bbafSopenharmony_ci {-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 8085317bbafSopenharmony_ci {-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 8095317bbafSopenharmony_ci}; 8105317bbafSopenharmony_ci 8115317bbafSopenharmony_ci 8125317bbafSopenharmony_cidata_too_long::data_too_long(const std::string &msg) : 8135317bbafSopenharmony_ci std::length_error(msg) {} 8145317bbafSopenharmony_ci 8155317bbafSopenharmony_ci 8165317bbafSopenharmony_ci 8175317bbafSopenharmony_ci/*---- Class BitBuffer ----*/ 8185317bbafSopenharmony_ci 8195317bbafSopenharmony_ciBitBuffer::BitBuffer() 8205317bbafSopenharmony_ci : std::vector<bool>() {} 8215317bbafSopenharmony_ci 8225317bbafSopenharmony_ci 8235317bbafSopenharmony_civoid BitBuffer::appendBits(std::uint32_t val, int len) { 8245317bbafSopenharmony_ci if (len < 0 || len > 31 || val >> len != 0) 8255317bbafSopenharmony_ci throw std::domain_error("Value out of range"); 8265317bbafSopenharmony_ci for (int i = len - 1; i >= 0; i--) // Append bit by bit 8275317bbafSopenharmony_ci this->push_back(((val >> i) & 1) != 0); 8285317bbafSopenharmony_ci} 8295317bbafSopenharmony_ci 8305317bbafSopenharmony_ci} 831