15317bbafSopenharmony_ci/* 25317bbafSopenharmony_ci * Fast QR Code generator library 35317bbafSopenharmony_ci * 45317bbafSopenharmony_ci * Copyright (c) Project Nayuki. (MIT License) 55317bbafSopenharmony_ci * https://www.nayuki.io/page/fast-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_cipackage io.nayuki.fastqrcodegen; 255317bbafSopenharmony_ci 265317bbafSopenharmony_ciimport java.util.Arrays; 275317bbafSopenharmony_ciimport java.util.List; 285317bbafSopenharmony_ciimport java.util.Objects; 295317bbafSopenharmony_ci 305317bbafSopenharmony_ci 315317bbafSopenharmony_ci/** 325317bbafSopenharmony_ci * A QR Code symbol, which is a type of two-dimension barcode. 335317bbafSopenharmony_ci * Invented by Denso Wave and described in the ISO/IEC 18004 standard. 345317bbafSopenharmony_ci * <p>Instances of this class represent an immutable square grid of dark and light cells. 355317bbafSopenharmony_ci * The class provides static factory functions to create a QR Code from text or binary data. 365317bbafSopenharmony_ci * The class covers the QR Code Model 2 specification, supporting all versions (sizes) 375317bbafSopenharmony_ci * from 1 to 40, all 4 error correction levels, and 4 character encoding modes.</p> 385317bbafSopenharmony_ci * <p>Ways to create a QR Code object:</p> 395317bbafSopenharmony_ci * <ul> 405317bbafSopenharmony_ci * <li><p>High level: Take the payload data and call {@link QrCode#encodeText(String,Ecc)} 415317bbafSopenharmony_ci * or {@link QrCode#encodeBinary(byte[],Ecc)}.</p></li> 425317bbafSopenharmony_ci * <li><p>Mid level: Custom-make the list of {@link QrSegment segments} 435317bbafSopenharmony_ci * and call {@link QrCode#encodeSegments(List,Ecc)} or 445317bbafSopenharmony_ci * {@link QrCode#encodeSegments(List,Ecc,int,int,int,boolean)}</p></li> 455317bbafSopenharmony_ci * <li><p>Low level: Custom-make the array of data codeword bytes (including segment headers and 465317bbafSopenharmony_ci * final padding, excluding error correction codewords), supply the appropriate version number, 475317bbafSopenharmony_ci * and call the {@link QrCode#QrCode(int,Ecc,byte[],int) constructor}.</p></li> 485317bbafSopenharmony_ci * </ul> 495317bbafSopenharmony_ci * <p>(Note that all ways require supplying the desired error correction level.)</p> 505317bbafSopenharmony_ci * @see QrSegment 515317bbafSopenharmony_ci */ 525317bbafSopenharmony_cipublic final class QrCode { 535317bbafSopenharmony_ci 545317bbafSopenharmony_ci /*---- Static factory functions (high level) ----*/ 555317bbafSopenharmony_ci 565317bbafSopenharmony_ci /** 575317bbafSopenharmony_ci * Returns a QR Code representing the specified Unicode text string at the specified error correction level. 585317bbafSopenharmony_ci * As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer 595317bbafSopenharmony_ci * Unicode code points (not UTF-16 code units) if the low error correction level is used. The smallest possible 605317bbafSopenharmony_ci * QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the 615317bbafSopenharmony_ci * ecl argument if it can be done without increasing the version. 625317bbafSopenharmony_ci * @param text the text to be encoded (not {@code null}), which can be any Unicode string 635317bbafSopenharmony_ci * @param ecl the error correction level to use (not {@code null}) (boostable) 645317bbafSopenharmony_ci * @return a QR Code (not {@code null}) representing the text 655317bbafSopenharmony_ci * @throws NullPointerException if the text or error correction level is {@code null} 665317bbafSopenharmony_ci * @throws DataTooLongException if the text fails to fit in the 675317bbafSopenharmony_ci * largest version QR Code at the ECL, which means it is too long 685317bbafSopenharmony_ci */ 695317bbafSopenharmony_ci public static QrCode encodeText(String text, Ecc ecl) { 705317bbafSopenharmony_ci Objects.requireNonNull(text); 715317bbafSopenharmony_ci Objects.requireNonNull(ecl); 725317bbafSopenharmony_ci List<QrSegment> segs = QrSegment.makeSegments(text); 735317bbafSopenharmony_ci return encodeSegments(segs, ecl); 745317bbafSopenharmony_ci } 755317bbafSopenharmony_ci 765317bbafSopenharmony_ci 775317bbafSopenharmony_ci /** 785317bbafSopenharmony_ci * Returns a QR Code representing the specified binary data at the specified error correction level. 795317bbafSopenharmony_ci * This function always encodes using the binary segment mode, not any text mode. The maximum number of 805317bbafSopenharmony_ci * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. 815317bbafSopenharmony_ci * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. 825317bbafSopenharmony_ci * @param data the binary data to encode (not {@code null}) 835317bbafSopenharmony_ci * @param ecl the error correction level to use (not {@code null}) (boostable) 845317bbafSopenharmony_ci * @return a QR Code (not {@code null}) representing the data 855317bbafSopenharmony_ci * @throws NullPointerException if the data or error correction level is {@code null} 865317bbafSopenharmony_ci * @throws DataTooLongException if the data fails to fit in the 875317bbafSopenharmony_ci * largest version QR Code at the ECL, which means it is too long 885317bbafSopenharmony_ci */ 895317bbafSopenharmony_ci public static QrCode encodeBinary(byte[] data, Ecc ecl) { 905317bbafSopenharmony_ci Objects.requireNonNull(data); 915317bbafSopenharmony_ci Objects.requireNonNull(ecl); 925317bbafSopenharmony_ci QrSegment seg = QrSegment.makeBytes(data); 935317bbafSopenharmony_ci return encodeSegments(Arrays.asList(seg), ecl); 945317bbafSopenharmony_ci } 955317bbafSopenharmony_ci 965317bbafSopenharmony_ci 975317bbafSopenharmony_ci /*---- Static factory functions (mid level) ----*/ 985317bbafSopenharmony_ci 995317bbafSopenharmony_ci /** 1005317bbafSopenharmony_ci * Returns a QR Code representing the specified segments at the specified error correction 1015317bbafSopenharmony_ci * level. The smallest possible QR Code version is automatically chosen for the output. The ECC level 1025317bbafSopenharmony_ci * of the result may be higher than the ecl argument if it can be done without increasing the version. 1035317bbafSopenharmony_ci * <p>This function allows the user to create a custom sequence of segments that switches 1045317bbafSopenharmony_ci * between modes (such as alphanumeric and byte) to encode text in less space. 1055317bbafSopenharmony_ci * This is a mid-level API; the high-level API is {@link #encodeText(String,Ecc)} 1065317bbafSopenharmony_ci * and {@link #encodeBinary(byte[],Ecc)}.</p> 1075317bbafSopenharmony_ci * @param segs the segments to encode 1085317bbafSopenharmony_ci * @param ecl the error correction level to use (not {@code null}) (boostable) 1095317bbafSopenharmony_ci * @return a QR Code (not {@code null}) representing the segments 1105317bbafSopenharmony_ci * @throws NullPointerException if the list of segments, any segment, or the error correction level is {@code null} 1115317bbafSopenharmony_ci * @throws DataTooLongException if the segments fail to fit in the 1125317bbafSopenharmony_ci * largest version QR Code at the ECL, which means they are too long 1135317bbafSopenharmony_ci */ 1145317bbafSopenharmony_ci public static QrCode encodeSegments(List<QrSegment> segs, Ecc ecl) { 1155317bbafSopenharmony_ci return encodeSegments(segs, ecl, MIN_VERSION, MAX_VERSION, -1, true); 1165317bbafSopenharmony_ci } 1175317bbafSopenharmony_ci 1185317bbafSopenharmony_ci 1195317bbafSopenharmony_ci /** 1205317bbafSopenharmony_ci * Returns a QR Code representing the specified segments with the specified encoding parameters. 1215317bbafSopenharmony_ci * The smallest possible QR Code version within the specified range is automatically 1225317bbafSopenharmony_ci * chosen for the output. Iff boostEcl is {@code true}, then the ECC level of the 1235317bbafSopenharmony_ci * result may be higher than the ecl argument if it can be done without increasing 1245317bbafSopenharmony_ci * the version. The mask number is either between 0 to 7 (inclusive) to force that 1255317bbafSopenharmony_ci * mask, or −1 to automatically choose an appropriate mask (which may be slow). 1265317bbafSopenharmony_ci * <p>This function allows the user to create a custom sequence of segments that switches 1275317bbafSopenharmony_ci * between modes (such as alphanumeric and byte) to encode text in less space. 1285317bbafSopenharmony_ci * This is a mid-level API; the high-level API is {@link #encodeText(String,Ecc)} 1295317bbafSopenharmony_ci * and {@link #encodeBinary(byte[],Ecc)}.</p> 1305317bbafSopenharmony_ci * @param segs the segments to encode 1315317bbafSopenharmony_ci * @param ecl the error correction level to use (not {@code null}) (boostable) 1325317bbafSopenharmony_ci * @param minVersion the minimum allowed version of the QR Code (at least 1) 1335317bbafSopenharmony_ci * @param maxVersion the maximum allowed version of the QR Code (at most 40) 1345317bbafSopenharmony_ci * @param mask the mask number to use (between 0 and 7 (inclusive)), or −1 for automatic mask 1355317bbafSopenharmony_ci * @param boostEcl increases the ECC level as long as it doesn't increase the version number 1365317bbafSopenharmony_ci * @return a QR Code (not {@code null}) representing the segments 1375317bbafSopenharmony_ci * @throws NullPointerException if the list of segments, any segment, or the error correction level is {@code null} 1385317bbafSopenharmony_ci * @throws IllegalArgumentException if 1 ≤ minVersion ≤ maxVersion ≤ 40 1395317bbafSopenharmony_ci * or −1 ≤ mask ≤ 7 is violated 1405317bbafSopenharmony_ci * @throws DataTooLongException if the segments fail to fit in 1415317bbafSopenharmony_ci * the maxVersion QR Code at the ECL, which means they are too long 1425317bbafSopenharmony_ci */ 1435317bbafSopenharmony_ci public static QrCode encodeSegments(List<QrSegment> segs, Ecc ecl, int minVersion, int maxVersion, int mask, boolean boostEcl) { 1445317bbafSopenharmony_ci Objects.requireNonNull(segs); 1455317bbafSopenharmony_ci Objects.requireNonNull(ecl); 1465317bbafSopenharmony_ci if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION) || mask < -1 || mask > 7) 1475317bbafSopenharmony_ci throw new IllegalArgumentException("Invalid value"); 1485317bbafSopenharmony_ci 1495317bbafSopenharmony_ci // Find the minimal version number to use 1505317bbafSopenharmony_ci int version, dataUsedBits; 1515317bbafSopenharmony_ci for (version = minVersion; ; version++) { 1525317bbafSopenharmony_ci int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available 1535317bbafSopenharmony_ci dataUsedBits = QrSegment.getTotalBits(segs, version); 1545317bbafSopenharmony_ci if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) 1555317bbafSopenharmony_ci break; // This version number is found to be suitable 1565317bbafSopenharmony_ci if (version >= maxVersion) { // All versions in the range could not fit the given data 1575317bbafSopenharmony_ci String msg = "Segment too long"; 1585317bbafSopenharmony_ci if (dataUsedBits != -1) 1595317bbafSopenharmony_ci msg = String.format("Data length = %d bits, Max capacity = %d bits", dataUsedBits, dataCapacityBits); 1605317bbafSopenharmony_ci throw new DataTooLongException(msg); 1615317bbafSopenharmony_ci } 1625317bbafSopenharmony_ci } 1635317bbafSopenharmony_ci assert dataUsedBits != -1; 1645317bbafSopenharmony_ci 1655317bbafSopenharmony_ci // Increase the error correction level while the data still fits in the current version number 1665317bbafSopenharmony_ci for (Ecc newEcl : Ecc.values()) { // From low to high 1675317bbafSopenharmony_ci if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8) 1685317bbafSopenharmony_ci ecl = newEcl; 1695317bbafSopenharmony_ci } 1705317bbafSopenharmony_ci 1715317bbafSopenharmony_ci // Concatenate all segments to create the data bit string 1725317bbafSopenharmony_ci BitBuffer bb = new BitBuffer(); 1735317bbafSopenharmony_ci for (QrSegment seg : segs) { 1745317bbafSopenharmony_ci bb.appendBits(seg.mode.modeBits, 4); 1755317bbafSopenharmony_ci bb.appendBits(seg.numChars, seg.mode.numCharCountBits(version)); 1765317bbafSopenharmony_ci bb.appendBits(seg.data, seg.bitLength); 1775317bbafSopenharmony_ci } 1785317bbafSopenharmony_ci assert bb.bitLength == dataUsedBits; 1795317bbafSopenharmony_ci 1805317bbafSopenharmony_ci // Add terminator and pad up to a byte if applicable 1815317bbafSopenharmony_ci int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; 1825317bbafSopenharmony_ci assert bb.bitLength <= dataCapacityBits; 1835317bbafSopenharmony_ci bb.appendBits(0, Math.min(4, dataCapacityBits - bb.bitLength)); 1845317bbafSopenharmony_ci bb.appendBits(0, (8 - bb.bitLength % 8) % 8); 1855317bbafSopenharmony_ci assert bb.bitLength % 8 == 0; 1865317bbafSopenharmony_ci 1875317bbafSopenharmony_ci // Pad with alternating bytes until data capacity is reached 1885317bbafSopenharmony_ci for (int padByte = 0xEC; bb.bitLength < dataCapacityBits; padByte ^= 0xEC ^ 0x11) 1895317bbafSopenharmony_ci bb.appendBits(padByte, 8); 1905317bbafSopenharmony_ci 1915317bbafSopenharmony_ci // Create the QR Code object 1925317bbafSopenharmony_ci return new QrCode(version, ecl, bb.getBytes(), mask); 1935317bbafSopenharmony_ci } 1945317bbafSopenharmony_ci 1955317bbafSopenharmony_ci 1965317bbafSopenharmony_ci 1975317bbafSopenharmony_ci /*---- Instance fields ----*/ 1985317bbafSopenharmony_ci 1995317bbafSopenharmony_ci // Public immutable scalar parameters: 2005317bbafSopenharmony_ci 2015317bbafSopenharmony_ci /** The version number of this QR Code, which is between 1 and 40 (inclusive). 2025317bbafSopenharmony_ci * This determines the size of this barcode. */ 2035317bbafSopenharmony_ci public final int version; 2045317bbafSopenharmony_ci 2055317bbafSopenharmony_ci /** The width and height of this QR Code, measured in modules, between 2065317bbafSopenharmony_ci * 21 and 177 (inclusive). This is equal to version × 4 + 17. */ 2075317bbafSopenharmony_ci public final int size; 2085317bbafSopenharmony_ci 2095317bbafSopenharmony_ci /** The error correction level used in this QR Code, which is not {@code null}. */ 2105317bbafSopenharmony_ci public final Ecc errorCorrectionLevel; 2115317bbafSopenharmony_ci 2125317bbafSopenharmony_ci /** The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive). 2135317bbafSopenharmony_ci * <p>Even if a QR Code is created with automatic masking requested (mask = 2145317bbafSopenharmony_ci * −1), the resulting object still has a mask value between 0 and 7. */ 2155317bbafSopenharmony_ci public final int mask; 2165317bbafSopenharmony_ci 2175317bbafSopenharmony_ci // Private grid of modules of this QR Code, packed tightly into bits. 2185317bbafSopenharmony_ci // Immutable after constructor finishes. Accessed through getModule(). 2195317bbafSopenharmony_ci private final int[] modules; 2205317bbafSopenharmony_ci 2215317bbafSopenharmony_ci 2225317bbafSopenharmony_ci 2235317bbafSopenharmony_ci /*---- Constructor (low level) ----*/ 2245317bbafSopenharmony_ci 2255317bbafSopenharmony_ci /** 2265317bbafSopenharmony_ci * Constructs a QR Code with the specified version number, 2275317bbafSopenharmony_ci * error correction level, data codeword bytes, and mask number. 2285317bbafSopenharmony_ci * <p>This is a low-level API that most users should not use directly. A mid-level 2295317bbafSopenharmony_ci * API is the {@link #encodeSegments(List,Ecc,int,int,int,boolean)} function.</p> 2305317bbafSopenharmony_ci * @param ver the version number to use, which must be in the range 1 to 40 (inclusive) 2315317bbafSopenharmony_ci * @param ecl the error correction level to use 2325317bbafSopenharmony_ci * @param dataCodewords the bytes representing segments to encode (without ECC) 2335317bbafSopenharmony_ci * @param msk the mask pattern to use, which is either −1 for automatic choice or from 0 to 7 for fixed choice 2345317bbafSopenharmony_ci * @throws NullPointerException if the byte array or error correction level is {@code null} 2355317bbafSopenharmony_ci * @throws IllegalArgumentException if the version or mask value is out of range, 2365317bbafSopenharmony_ci * or if the data is the wrong length for the specified version and error correction level 2375317bbafSopenharmony_ci */ 2385317bbafSopenharmony_ci public QrCode(int ver, Ecc ecl, byte[] dataCodewords, int msk) { 2395317bbafSopenharmony_ci // Check arguments and initialize fields 2405317bbafSopenharmony_ci if (ver < MIN_VERSION || ver > MAX_VERSION) 2415317bbafSopenharmony_ci throw new IllegalArgumentException("Version value out of range"); 2425317bbafSopenharmony_ci if (msk < -1 || msk > 7) 2435317bbafSopenharmony_ci throw new IllegalArgumentException("Mask value out of range"); 2445317bbafSopenharmony_ci version = ver; 2455317bbafSopenharmony_ci size = ver * 4 + 17; 2465317bbafSopenharmony_ci errorCorrectionLevel = Objects.requireNonNull(ecl); 2475317bbafSopenharmony_ci Objects.requireNonNull(dataCodewords); 2485317bbafSopenharmony_ci 2495317bbafSopenharmony_ci QrTemplate tpl = QrTemplate.MEMOIZER.get(ver); 2505317bbafSopenharmony_ci modules = tpl.template.clone(); 2515317bbafSopenharmony_ci 2525317bbafSopenharmony_ci // Compute ECC, draw modules, do masking 2535317bbafSopenharmony_ci byte[] allCodewords = addEccAndInterleave(dataCodewords); 2545317bbafSopenharmony_ci drawCodewords(tpl.dataOutputBitIndexes, allCodewords); 2555317bbafSopenharmony_ci mask = handleConstructorMasking(tpl.masks, msk); 2565317bbafSopenharmony_ci } 2575317bbafSopenharmony_ci 2585317bbafSopenharmony_ci 2595317bbafSopenharmony_ci 2605317bbafSopenharmony_ci /*---- Public instance methods ----*/ 2615317bbafSopenharmony_ci 2625317bbafSopenharmony_ci /** 2635317bbafSopenharmony_ci * Returns the color of the module (pixel) at the specified coordinates, which is {@code false} 2645317bbafSopenharmony_ci * for light or {@code true} for dark. The top left corner has the coordinates (x=0, y=0). 2655317bbafSopenharmony_ci * If the specified coordinates are out of bounds, then {@code false} (light) is returned. 2665317bbafSopenharmony_ci * @param x the x coordinate, where 0 is the left edge and size−1 is the right edge 2675317bbafSopenharmony_ci * @param y the y coordinate, where 0 is the top edge and size−1 is the bottom edge 2685317bbafSopenharmony_ci * @return {@code true} if the coordinates are in bounds and the module 2695317bbafSopenharmony_ci * at that location is dark, or {@code false} (light) otherwise 2705317bbafSopenharmony_ci */ 2715317bbafSopenharmony_ci public boolean getModule(int x, int y) { 2725317bbafSopenharmony_ci if (0 <= x && x < size && 0 <= y && y < size) { 2735317bbafSopenharmony_ci int i = y * size + x; 2745317bbafSopenharmony_ci return getBit(modules[i >>> 5], i) != 0; 2755317bbafSopenharmony_ci } else 2765317bbafSopenharmony_ci return false; 2775317bbafSopenharmony_ci } 2785317bbafSopenharmony_ci 2795317bbafSopenharmony_ci 2805317bbafSopenharmony_ci 2815317bbafSopenharmony_ci /*---- Private helper methods for constructor: Drawing function modules ----*/ 2825317bbafSopenharmony_ci 2835317bbafSopenharmony_ci // Draws two copies of the format bits (with its own error correction code) 2845317bbafSopenharmony_ci // based on the given mask and this object's error correction level field. 2855317bbafSopenharmony_ci private void drawFormatBits(int msk) { 2865317bbafSopenharmony_ci // Calculate error correction code and pack bits 2875317bbafSopenharmony_ci int data = errorCorrectionLevel.formatBits << 3 | msk; // errCorrLvl is uint2, mask is uint3 2885317bbafSopenharmony_ci int rem = data; 2895317bbafSopenharmony_ci for (int i = 0; i < 10; i++) 2905317bbafSopenharmony_ci rem = (rem << 1) ^ ((rem >>> 9) * 0x537); 2915317bbafSopenharmony_ci int bits = (data << 10 | rem) ^ 0x5412; // uint15 2925317bbafSopenharmony_ci assert bits >>> 15 == 0; 2935317bbafSopenharmony_ci 2945317bbafSopenharmony_ci // Draw first copy 2955317bbafSopenharmony_ci for (int i = 0; i <= 5; i++) 2965317bbafSopenharmony_ci setModule(8, i, getBit(bits, i)); 2975317bbafSopenharmony_ci setModule(8, 7, getBit(bits, 6)); 2985317bbafSopenharmony_ci setModule(8, 8, getBit(bits, 7)); 2995317bbafSopenharmony_ci setModule(7, 8, getBit(bits, 8)); 3005317bbafSopenharmony_ci for (int i = 9; i < 15; i++) 3015317bbafSopenharmony_ci setModule(14 - i, 8, getBit(bits, i)); 3025317bbafSopenharmony_ci 3035317bbafSopenharmony_ci // Draw second copy 3045317bbafSopenharmony_ci for (int i = 0; i < 8; i++) 3055317bbafSopenharmony_ci setModule(size - 1 - i, 8, getBit(bits, i)); 3065317bbafSopenharmony_ci for (int i = 8; i < 15; i++) 3075317bbafSopenharmony_ci setModule(8, size - 15 + i, getBit(bits, i)); 3085317bbafSopenharmony_ci setModule(8, size - 8, 1); // Always dark 3095317bbafSopenharmony_ci } 3105317bbafSopenharmony_ci 3115317bbafSopenharmony_ci 3125317bbafSopenharmony_ci // Sets the module at the given coordinates to the given color. 3135317bbafSopenharmony_ci // Only used by the constructor. Coordinates must be in bounds. 3145317bbafSopenharmony_ci private void setModule(int x, int y, int dark) { 3155317bbafSopenharmony_ci assert 0 <= x && x < size; 3165317bbafSopenharmony_ci assert 0 <= y && y < size; 3175317bbafSopenharmony_ci assert dark == 0 || dark == 1; 3185317bbafSopenharmony_ci int i = y * size + x; 3195317bbafSopenharmony_ci modules[i >>> 5] &= ~(1 << i); 3205317bbafSopenharmony_ci modules[i >>> 5] |= dark << i; 3215317bbafSopenharmony_ci } 3225317bbafSopenharmony_ci 3235317bbafSopenharmony_ci 3245317bbafSopenharmony_ci /*---- Private helper methods for constructor: Codewords and masking ----*/ 3255317bbafSopenharmony_ci 3265317bbafSopenharmony_ci // Returns a new byte string representing the given data with the appropriate error correction 3275317bbafSopenharmony_ci // codewords appended to it, based on this object's version and error correction level. 3285317bbafSopenharmony_ci private byte[] addEccAndInterleave(byte[] data) { 3295317bbafSopenharmony_ci Objects.requireNonNull(data); 3305317bbafSopenharmony_ci if (data.length != getNumDataCodewords(version, errorCorrectionLevel)) 3315317bbafSopenharmony_ci throw new IllegalArgumentException(); 3325317bbafSopenharmony_ci 3335317bbafSopenharmony_ci // Calculate parameter numbers 3345317bbafSopenharmony_ci int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[errorCorrectionLevel.ordinal()][version]; 3355317bbafSopenharmony_ci int blockEccLen = ECC_CODEWORDS_PER_BLOCK [errorCorrectionLevel.ordinal()][version]; 3365317bbafSopenharmony_ci int rawCodewords = QrTemplate.getNumRawDataModules(version) / 8; 3375317bbafSopenharmony_ci int numShortBlocks = numBlocks - rawCodewords % numBlocks; 3385317bbafSopenharmony_ci int shortBlockDataLen = rawCodewords / numBlocks - blockEccLen; 3395317bbafSopenharmony_ci 3405317bbafSopenharmony_ci // Split data into blocks, calculate ECC, and interleave 3415317bbafSopenharmony_ci // (not concatenate) the bytes into a single sequence 3425317bbafSopenharmony_ci byte[] result = new byte[rawCodewords]; 3435317bbafSopenharmony_ci ReedSolomonGenerator rs = ReedSolomonGenerator.MEMOIZER.get(blockEccLen); 3445317bbafSopenharmony_ci byte[] ecc = new byte[blockEccLen]; // Temporary storage per iteration 3455317bbafSopenharmony_ci for (int i = 0, k = 0; i < numBlocks; i++) { 3465317bbafSopenharmony_ci int datLen = shortBlockDataLen + (i < numShortBlocks ? 0 : 1); 3475317bbafSopenharmony_ci rs.getRemainder(data, k, datLen, ecc); 3485317bbafSopenharmony_ci for (int j = 0, l = i; j < datLen; j++, k++, l += numBlocks) { // Copy data 3495317bbafSopenharmony_ci if (j == shortBlockDataLen) 3505317bbafSopenharmony_ci l -= numShortBlocks; 3515317bbafSopenharmony_ci result[l] = data[k]; 3525317bbafSopenharmony_ci } 3535317bbafSopenharmony_ci for (int j = 0, l = data.length + i; j < blockEccLen; j++, l += numBlocks) // Copy ECC 3545317bbafSopenharmony_ci result[l] = ecc[j]; 3555317bbafSopenharmony_ci } 3565317bbafSopenharmony_ci return result; 3575317bbafSopenharmony_ci } 3585317bbafSopenharmony_ci 3595317bbafSopenharmony_ci 3605317bbafSopenharmony_ci // Draws the given sequence of 8-bit codewords (data and error correction) 3615317bbafSopenharmony_ci // onto the entire data area of this QR Code, based on the given bit indexes. 3625317bbafSopenharmony_ci private void drawCodewords(int[] dataOutputBitIndexes, byte[] allCodewords) { 3635317bbafSopenharmony_ci Objects.requireNonNull(dataOutputBitIndexes); 3645317bbafSopenharmony_ci Objects.requireNonNull(allCodewords); 3655317bbafSopenharmony_ci if (allCodewords.length * 8 != dataOutputBitIndexes.length) 3665317bbafSopenharmony_ci throw new IllegalArgumentException(); 3675317bbafSopenharmony_ci for (int i = 0; i < dataOutputBitIndexes.length; i++) { 3685317bbafSopenharmony_ci int j = dataOutputBitIndexes[i]; 3695317bbafSopenharmony_ci int bit = getBit(allCodewords[i >>> 3], ~i & 7); 3705317bbafSopenharmony_ci modules[j >>> 5] |= bit << j; 3715317bbafSopenharmony_ci } 3725317bbafSopenharmony_ci } 3735317bbafSopenharmony_ci 3745317bbafSopenharmony_ci 3755317bbafSopenharmony_ci // XORs the codeword modules in this QR Code with the given mask pattern. 3765317bbafSopenharmony_ci // The function modules must be marked and the codeword bits must be drawn 3775317bbafSopenharmony_ci // before masking. Due to the arithmetic of XOR, calling applyMask() with 3785317bbafSopenharmony_ci // the same mask value a second time will undo the mask. A final well-formed 3795317bbafSopenharmony_ci // QR Code needs exactly one (not zero, two, etc.) mask applied. 3805317bbafSopenharmony_ci private void applyMask(int[] msk) { 3815317bbafSopenharmony_ci if (msk.length != modules.length) 3825317bbafSopenharmony_ci throw new IllegalArgumentException(); 3835317bbafSopenharmony_ci for (int i = 0; i < msk.length; i++) 3845317bbafSopenharmony_ci modules[i] ^= msk[i]; 3855317bbafSopenharmony_ci } 3865317bbafSopenharmony_ci 3875317bbafSopenharmony_ci 3885317bbafSopenharmony_ci // A messy helper function for the constructor. This QR Code must be in an unmasked state when this 3895317bbafSopenharmony_ci // method is called. The 'mask' argument is the requested mask, which is -1 for auto or 0 to 7 for fixed. 3905317bbafSopenharmony_ci // This method applies and returns the actual mask chosen, from 0 to 7. 3915317bbafSopenharmony_ci private int handleConstructorMasking(int[][] masks, int msk) { 3925317bbafSopenharmony_ci if (msk == -1) { // Automatically choose best mask 3935317bbafSopenharmony_ci int minPenalty = Integer.MAX_VALUE; 3945317bbafSopenharmony_ci for (int i = 0; i < 8; i++) { 3955317bbafSopenharmony_ci applyMask(masks[i]); 3965317bbafSopenharmony_ci drawFormatBits(i); 3975317bbafSopenharmony_ci int penalty = getPenaltyScore(); 3985317bbafSopenharmony_ci if (penalty < minPenalty) { 3995317bbafSopenharmony_ci msk = i; 4005317bbafSopenharmony_ci minPenalty = penalty; 4015317bbafSopenharmony_ci } 4025317bbafSopenharmony_ci applyMask(masks[i]); // Undoes the mask due to XOR 4035317bbafSopenharmony_ci } 4045317bbafSopenharmony_ci } 4055317bbafSopenharmony_ci assert 0 <= msk && msk <= 7; 4065317bbafSopenharmony_ci applyMask(masks[msk]); // Apply the final choice of mask 4075317bbafSopenharmony_ci drawFormatBits(msk); // Overwrite old format bits 4085317bbafSopenharmony_ci return msk; // The caller shall assign this value to the final-declared field 4095317bbafSopenharmony_ci } 4105317bbafSopenharmony_ci 4115317bbafSopenharmony_ci 4125317bbafSopenharmony_ci // Calculates and returns the penalty score based on state of this QR Code's current modules. 4135317bbafSopenharmony_ci // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. 4145317bbafSopenharmony_ci private int getPenaltyScore() { 4155317bbafSopenharmony_ci int result = 0; 4165317bbafSopenharmony_ci int dark = 0; 4175317bbafSopenharmony_ci int[] runHistory = new int[7]; 4185317bbafSopenharmony_ci 4195317bbafSopenharmony_ci // Iterate over adjacent pairs of rows 4205317bbafSopenharmony_ci for (int index = 0, downIndex = size, end = size * size; index < end; ) { 4215317bbafSopenharmony_ci int runColor = 0; 4225317bbafSopenharmony_ci int runX = 0; 4235317bbafSopenharmony_ci Arrays.fill(runHistory, 0); 4245317bbafSopenharmony_ci int curRow = 0; 4255317bbafSopenharmony_ci int nextRow = 0; 4265317bbafSopenharmony_ci for (int x = 0; x < size; x++, index++, downIndex++) { 4275317bbafSopenharmony_ci int c = getBit(modules[index >>> 5], index); 4285317bbafSopenharmony_ci if (c == runColor) { 4295317bbafSopenharmony_ci runX++; 4305317bbafSopenharmony_ci if (runX == 5) 4315317bbafSopenharmony_ci result += PENALTY_N1; 4325317bbafSopenharmony_ci else if (runX > 5) 4335317bbafSopenharmony_ci result++; 4345317bbafSopenharmony_ci } else { 4355317bbafSopenharmony_ci finderPenaltyAddHistory(runX, runHistory); 4365317bbafSopenharmony_ci if (runColor == 0) 4375317bbafSopenharmony_ci result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; 4385317bbafSopenharmony_ci runColor = c; 4395317bbafSopenharmony_ci runX = 1; 4405317bbafSopenharmony_ci } 4415317bbafSopenharmony_ci dark += c; 4425317bbafSopenharmony_ci if (downIndex < end) { 4435317bbafSopenharmony_ci curRow = ((curRow << 1) | c) & 3; 4445317bbafSopenharmony_ci nextRow = ((nextRow << 1) | getBit(modules[downIndex >>> 5], downIndex)) & 3; 4455317bbafSopenharmony_ci // 2*2 blocks of modules having same color 4465317bbafSopenharmony_ci if (x >= 1 && (curRow == 0 || curRow == 3) && curRow == nextRow) 4475317bbafSopenharmony_ci result += PENALTY_N2; 4485317bbafSopenharmony_ci } 4495317bbafSopenharmony_ci } 4505317bbafSopenharmony_ci result += finderPenaltyTerminateAndCount(runColor, runX, runHistory) * PENALTY_N3; 4515317bbafSopenharmony_ci } 4525317bbafSopenharmony_ci 4535317bbafSopenharmony_ci // Iterate over single columns 4545317bbafSopenharmony_ci for (int x = 0; x < size; x++) { 4555317bbafSopenharmony_ci int runColor = 0; 4565317bbafSopenharmony_ci int runY = 0; 4575317bbafSopenharmony_ci Arrays.fill(runHistory, 0); 4585317bbafSopenharmony_ci for (int y = 0, index = x; y < size; y++, index += size) { 4595317bbafSopenharmony_ci int c = getBit(modules[index >>> 5], index); 4605317bbafSopenharmony_ci if (c == runColor) { 4615317bbafSopenharmony_ci runY++; 4625317bbafSopenharmony_ci if (runY == 5) 4635317bbafSopenharmony_ci result += PENALTY_N1; 4645317bbafSopenharmony_ci else if (runY > 5) 4655317bbafSopenharmony_ci result++; 4665317bbafSopenharmony_ci } else { 4675317bbafSopenharmony_ci finderPenaltyAddHistory(runY, runHistory); 4685317bbafSopenharmony_ci if (runColor == 0) 4695317bbafSopenharmony_ci result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; 4705317bbafSopenharmony_ci runColor = c; 4715317bbafSopenharmony_ci runY = 1; 4725317bbafSopenharmony_ci } 4735317bbafSopenharmony_ci } 4745317bbafSopenharmony_ci result += finderPenaltyTerminateAndCount(runColor, runY, runHistory) * PENALTY_N3; 4755317bbafSopenharmony_ci } 4765317bbafSopenharmony_ci 4775317bbafSopenharmony_ci // Balance of dark and light modules 4785317bbafSopenharmony_ci int total = size * size; // Note that size is odd, so dark/total != 1/2 4795317bbafSopenharmony_ci // Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)% 4805317bbafSopenharmony_ci int k = (Math.abs(dark * 20 - total * 10) + total - 1) / total - 1; 4815317bbafSopenharmony_ci result += k * PENALTY_N4; 4825317bbafSopenharmony_ci return result; 4835317bbafSopenharmony_ci } 4845317bbafSopenharmony_ci 4855317bbafSopenharmony_ci 4865317bbafSopenharmony_ci 4875317bbafSopenharmony_ci /*---- Private helper functions ----*/ 4885317bbafSopenharmony_ci 4895317bbafSopenharmony_ci // Returns the number of 8-bit data (i.e. not error correction) codewords contained in any 4905317bbafSopenharmony_ci // QR Code of the given version number and error correction level, with remainder bits discarded. 4915317bbafSopenharmony_ci // This stateless pure function could be implemented as a (40*4)-cell lookup table. 4925317bbafSopenharmony_ci static int getNumDataCodewords(int ver, Ecc ecl) { 4935317bbafSopenharmony_ci return QrTemplate.getNumRawDataModules(ver) / 8 4945317bbafSopenharmony_ci - ECC_CODEWORDS_PER_BLOCK [ecl.ordinal()][ver] 4955317bbafSopenharmony_ci * NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal()][ver]; 4965317bbafSopenharmony_ci } 4975317bbafSopenharmony_ci 4985317bbafSopenharmony_ci 4995317bbafSopenharmony_ci // Can only be called immediately after a light run is added, and 5005317bbafSopenharmony_ci // returns either 0, 1, or 2. A helper function for getPenaltyScore(). 5015317bbafSopenharmony_ci private int finderPenaltyCountPatterns(int[] runHistory) { 5025317bbafSopenharmony_ci int n = runHistory[1]; 5035317bbafSopenharmony_ci assert n <= size * 3; 5045317bbafSopenharmony_ci boolean core = n > 0 && runHistory[2] == n && runHistory[3] == n * 3 && runHistory[4] == n && runHistory[5] == n; 5055317bbafSopenharmony_ci return (core && runHistory[0] >= n * 4 && runHistory[6] >= n ? 1 : 0) 5065317bbafSopenharmony_ci + (core && runHistory[6] >= n * 4 && runHistory[0] >= n ? 1 : 0); 5075317bbafSopenharmony_ci } 5085317bbafSopenharmony_ci 5095317bbafSopenharmony_ci 5105317bbafSopenharmony_ci // Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). 5115317bbafSopenharmony_ci private int finderPenaltyTerminateAndCount(int currentRunColor, int currentRunLength, int[] runHistory) { 5125317bbafSopenharmony_ci if (currentRunColor == 1) { // Terminate dark run 5135317bbafSopenharmony_ci finderPenaltyAddHistory(currentRunLength, runHistory); 5145317bbafSopenharmony_ci currentRunLength = 0; 5155317bbafSopenharmony_ci } 5165317bbafSopenharmony_ci currentRunLength += size; // Add light border to final run 5175317bbafSopenharmony_ci finderPenaltyAddHistory(currentRunLength, runHistory); 5185317bbafSopenharmony_ci return finderPenaltyCountPatterns(runHistory); 5195317bbafSopenharmony_ci } 5205317bbafSopenharmony_ci 5215317bbafSopenharmony_ci 5225317bbafSopenharmony_ci // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). 5235317bbafSopenharmony_ci private void finderPenaltyAddHistory(int currentRunLength, int[] runHistory) { 5245317bbafSopenharmony_ci if (runHistory[0] == 0) 5255317bbafSopenharmony_ci currentRunLength += size; // Add light border to initial run 5265317bbafSopenharmony_ci System.arraycopy(runHistory, 0, runHistory, 1, runHistory.length - 1); 5275317bbafSopenharmony_ci runHistory[0] = currentRunLength; 5285317bbafSopenharmony_ci } 5295317bbafSopenharmony_ci 5305317bbafSopenharmony_ci 5315317bbafSopenharmony_ci // Returns 0 or 1 based on the (i mod 32)'th bit of x. 5325317bbafSopenharmony_ci static int getBit(int x, int i) { 5335317bbafSopenharmony_ci return (x >>> i) & 1; 5345317bbafSopenharmony_ci } 5355317bbafSopenharmony_ci 5365317bbafSopenharmony_ci 5375317bbafSopenharmony_ci /*---- Constants and tables ----*/ 5385317bbafSopenharmony_ci 5395317bbafSopenharmony_ci /** The minimum version number (1) supported in the QR Code Model 2 standard. */ 5405317bbafSopenharmony_ci public static final int MIN_VERSION = 1; 5415317bbafSopenharmony_ci 5425317bbafSopenharmony_ci /** The maximum version number (40) supported in the QR Code Model 2 standard. */ 5435317bbafSopenharmony_ci public static final int MAX_VERSION = 40; 5445317bbafSopenharmony_ci 5455317bbafSopenharmony_ci 5465317bbafSopenharmony_ci // For use in getPenaltyScore(), when evaluating which mask is best. 5475317bbafSopenharmony_ci private static final int PENALTY_N1 = 3; 5485317bbafSopenharmony_ci private static final int PENALTY_N2 = 3; 5495317bbafSopenharmony_ci private static final int PENALTY_N3 = 40; 5505317bbafSopenharmony_ci private static final int PENALTY_N4 = 10; 5515317bbafSopenharmony_ci 5525317bbafSopenharmony_ci 5535317bbafSopenharmony_ci private static final byte[][] ECC_CODEWORDS_PER_BLOCK = { 5545317bbafSopenharmony_ci // Version: (note that index 0 is for padding, and is set to an illegal value) 5555317bbafSopenharmony_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 5565317bbafSopenharmony_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 5575317bbafSopenharmony_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 5585317bbafSopenharmony_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 5595317bbafSopenharmony_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 5605317bbafSopenharmony_ci }; 5615317bbafSopenharmony_ci 5625317bbafSopenharmony_ci private static final byte[][] NUM_ERROR_CORRECTION_BLOCKS = { 5635317bbafSopenharmony_ci // Version: (note that index 0 is for padding, and is set to an illegal value) 5645317bbafSopenharmony_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 5655317bbafSopenharmony_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 5665317bbafSopenharmony_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 5675317bbafSopenharmony_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 5685317bbafSopenharmony_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 5695317bbafSopenharmony_ci }; 5705317bbafSopenharmony_ci 5715317bbafSopenharmony_ci 5725317bbafSopenharmony_ci 5735317bbafSopenharmony_ci /*---- Public helper enumeration ----*/ 5745317bbafSopenharmony_ci 5755317bbafSopenharmony_ci /** 5765317bbafSopenharmony_ci * The error correction level in a QR Code symbol. 5775317bbafSopenharmony_ci */ 5785317bbafSopenharmony_ci public enum Ecc { 5795317bbafSopenharmony_ci // Must be declared in ascending order of error protection 5805317bbafSopenharmony_ci // so that the implicit ordinal() and values() work properly 5815317bbafSopenharmony_ci /** The QR Code can tolerate about 7% erroneous codewords. */ LOW(1), 5825317bbafSopenharmony_ci /** The QR Code can tolerate about 15% erroneous codewords. */ MEDIUM(0), 5835317bbafSopenharmony_ci /** The QR Code can tolerate about 25% erroneous codewords. */ QUARTILE(3), 5845317bbafSopenharmony_ci /** The QR Code can tolerate about 30% erroneous codewords. */ HIGH(2); 5855317bbafSopenharmony_ci 5865317bbafSopenharmony_ci // In the range 0 to 3 (unsigned 2-bit integer). 5875317bbafSopenharmony_ci final int formatBits; 5885317bbafSopenharmony_ci 5895317bbafSopenharmony_ci // Constructor. 5905317bbafSopenharmony_ci private Ecc(int fb) { 5915317bbafSopenharmony_ci formatBits = fb; 5925317bbafSopenharmony_ci } 5935317bbafSopenharmony_ci } 5945317bbafSopenharmony_ci 5955317bbafSopenharmony_ci} 596