├── LICENSE.txt ├── README.md ├── components ├── qrCode │ ├── qrCode.brs │ ├── qrCode.draw.brs │ ├── qrCode.help.brs │ ├── qrCode.high.brs │ ├── qrCode.low.brs │ ├── qrCode.mask.brs │ ├── qrCode.mid.brs │ ├── qrCode.utils.brs │ ├── qrCode.xml │ ├── qrEcc.brs │ ├── qrEcc.xml │ ├── qrMode.brs │ ├── qrMode.xml │ └── qrSegment.brs └── sampleScene │ ├── sampleScene.brs │ └── sampleScene.xml ├── images ├── focus_fhd.jpg ├── focus_hd.jpg ├── focus_sd.jpg ├── splash_fhd.jpg ├── splash_hd.jpg └── splash_sd.jpg ├── manifest └── source └── main.brs /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Paramount 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QR-Code-generator-brightscript 2 | 3 | ## Introduction: 4 | 5 | With this Library, adding a QR code to your Roku app is a simple as including this in your xml file: 6 | 7 | \ 8 | 9 | ## License 10 | 11 | Copyright (c) 2022 Paramount (MIT License) 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy 14 | of this software and associated documentation files (the "Software"), to deal 15 | in the Software without restriction, including without limitation the rights 16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | copies of the Software, and to permit persons to whom the Software is 18 | furnished to do so, subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be included in all 21 | copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | SOFTWARE. 30 | 31 | ## Attribution 32 | 33 | This work, ["QR-Code-generator-brightscript"](https://github.com/paramount-engineering/QR-Code-generator-brightscript), is a derivative of ["QR Code generator"](https://github.com/nayuki/QR-Code-generator) by [Project Nayuki](https://www.nayuki.io/page/qr-code-generator-library), used under [MIT](https://choosealicense.com/licenses/mit/). ["QR-Code-generator-brightscript"](https://github.com/paramount-engineering/QR-Code-generator-brightscript) is licensed under [MIT](https://choosealicense.com/licenses/mit/) BY [Paramount](https://www.paramount.com). -------------------------------------------------------------------------------- /components/qrCode/qrCode.brs: -------------------------------------------------------------------------------- 1 | '************************************************************************************** 2 | ' QR Code generator library (Brightscript) 3 | ' Copyright (c) Kevin Hoos. 4 | '************************************************************************************** 5 | ' Ported from: 6 | ' Copyright (c) Project Nayuki. (MIT License) 7 | ' https://www.nayuki.io/page/qr-code-generator-library 8 | ' 9 | ' Permission is hereby granted, free of charge, to any person obtaining a copy of 10 | ' this software and associated documentation files (the "Software"), to deal in 11 | ' the Software without restriction, including without limitation the rights to 12 | ' use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 13 | ' the Software, and to permit persons to whom the Software is furnished to do so, 14 | ' subject to the following conditions: 15 | ' - The above copyright notice and this permission notice shall be included in 16 | ' all copies or substantial portions of the Software. 17 | ' - The Software is provided "as is", without warranty of any kind, express or 18 | ' implied, including but not limited to the warranties of merchantability, 19 | ' fitness for a particular purpose and noninfringement. In no event shall the 20 | ' authors or copyright holders be liable for any claim, damages or other 21 | ' liability, whether in an action of contract, tort or otherwise, arising from, 22 | ' out of or in connection with the Software or the use or other dealings in the 23 | ' Software. 24 | '************************************************************************************** 25 | 26 | 27 | '---- QR Code symbol class ---- 28 | 29 | '************************************************************************************** 30 | ' A QR Code symbol, which is a type of two-dimension barcode. 31 | ' Invented by Denso Wave and described in the ISO/IEC 18004 standard. 32 | ' Instances of this class represent an immutable square grid of dark and light cells. 33 | ' The class provides static factory functions to create a QR Code from text or binary data. 34 | ' The class covers the QR Code Model 2 specification, supporting all versions (sizes) 35 | ' from 1 to 40, all 4 error correction levels, and 4 character encoding modes. 36 | ' 37 | ' Ways to create a QR Code object: 38 | ' - High level: Take the payload data and call QrCode.encodeText() or QrCode.encodeBinary(). 39 | ' - Mid level: Custom-make the list of segments and call QrCode.encodeSegments(). 40 | ' - Low level: Custom-make the array of data codeword bytes (including 41 | ' segment headers and final padding, excluding error correction codewords), 42 | ' supply the appropriate version number, and call the QrCode() constructor. 43 | ' (Note that all ways require supplying the desired error correction level.) 44 | '************************************************************************************** 45 | function QrCode() 46 | this = {} 47 | '-- Static factory functions (high level) -- 48 | this.encodeText = _qrCode_high_encodeText 49 | this.encodeBinary = _qrCode_high_encodeBinary 50 | 51 | 52 | '-- Static factory functions (mid level) -- 53 | this.encodeSegments = _qrCode_mid_encodeSegments 54 | 55 | 56 | '-- Fields -- 57 | 58 | ' The width and height of this QR Code, measured in modules, between 59 | ' 21 and 177 (inclusive). This is equal to version * 4 + 17. 60 | this.size = 0 61 | 62 | ' The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive). 63 | ' Even if a QR Code is created with automatic masking requested (mask = -1), 64 | ' the resulting object still has a mask value between 0 and 7. 65 | this.mask = -1 66 | 67 | ' The modules of this QR Code (false = light, true = dark). 68 | ' Immutable after constructor finishes. Accessed through getModule(). 69 | this.modules = [] 70 | 71 | ' Indicates function modules that are not subjected to masking. Discarded when constructor finishes. 72 | this.isFunction = [] 73 | 74 | 75 | '-- Constructor (low level) and fields -- 76 | this.constructor = _qrCode_low_constructor 77 | 78 | 79 | '-- Accessor methods -- 80 | this.getModule = _qrCode_low_getModule 81 | 82 | 83 | '-- Private helper methods for constructor: Drawing function modules -- 84 | this.drawFunctionPatterns = _qrCode_draw_drawFunctionPatterns 85 | this.drawFormatBits = _qrCode_draw_drawFormatBits 86 | this.drawVersion = _qrCode_draw_drawVersion 87 | this.drawFinderPattern = _qrCode_draw_drawFinderPattern 88 | this.drawAlignmentPattern = _qrCode_draw_drawAlignmentPattern 89 | this.setFunctionModule = _qrCode_draw_setFunctionModule 90 | 91 | 92 | '-- Private helper methods for constructor: Codewords and masking -- 93 | this.addEccAndInterleave = _qrCode_mask_addEccAndInterleave 94 | this.drawCodewords = _qrCode_mask_drawCodewords 95 | this.applyMask = _qrCode_mask_applyMask 96 | this.getPenaltyScore = _qrCode_mask_getPenaltyScore 97 | 98 | 99 | '-- Private helper functions -- 100 | this.getAlignmentPatternPositions = _qrCode_help_getAlignmentPatternPositions 101 | this.getNumRawDataModules = _qrCode_help_getNumRawDataModules 102 | this.getNumDataCodewords = _qrCode_help_getNumDataCodewords 103 | this.reedSolomonComputeDivisor = _qrCode_help_reedSolomonComputeDivisor 104 | this.reedSolomonComputeRemainder = _qrCode_help_reedSolomonComputeRemainder 105 | this.reedSolomonMultiply = _qrCode_help_reedSolomonMultiply 106 | this.finderPenaltyCountPatterns = _qrCode_help_finderPenaltyCountPatterns 107 | this.finderPenaltyTerminateAndCount = _qrCode_help_finderPenaltyTerminateAndCount 108 | this.finderPenaltyAddHistory = _qrCode_help_finderPenaltyAddHistory 109 | 110 | this.QrSegment = QrSegment() 111 | this.Ecc = [] 112 | levels = ["LOW", "MEDIUM", "QUARTILE", "HIGH"] 113 | for each level in levels 114 | ecc = CreateObject("roSGNode", "QREcc") 115 | ecc.level = level 116 | this.Ecc.push(ecc) 117 | next 118 | 119 | 120 | '-- Constants and tables -- 121 | 122 | ' The minimum version number supported in the QR Code Model 2 standard. 123 | this.MIN_VERSION = 1 124 | ' The maximum version number supported in the QR Code Model 2 standard. 125 | this.MAX_VERSION = 40 126 | 127 | ' For use in getPenaltyScore(), when evaluating which mask is best. 128 | this.PENALTY_N1 = 3 129 | this.PENALTY_N2 = 3 130 | this.PENALTY_N3 = 40 131 | this.PENALTY_N4 = 10 132 | 133 | this.ECC_CODEWORDS_PER_BLOCK = [ 134 | ' Version: (note that index 0 is for padding, and is set to an illegal value) 135 | '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 136 | [-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 137 | [-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 138 | [-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 139 | [-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 140 | ] 141 | 142 | this.NUM_ERROR_CORRECTION_BLOCKS = [ 143 | ' Version: (note that index 0 is for padding, and is set to an illegal value) 144 | '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 145 | [-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 146 | [-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 147 | [-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 148 | [-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 149 | ] 150 | 151 | return this 152 | end function 153 | 154 | function onTextChanged(nodeEvent) 155 | time = createObject("roTimespan") 156 | text = nodeEvent.getData() 157 | qr = QrCode() 158 | ecl = getEcl() 159 | qr.encodeText(text, ecl) 160 | ba = CreateObject("roByteArray") 161 | ba.FromAsciiString(text) 162 | digest = CreateObject("roEVPDigest") 163 | digest.Setup("sha1") 164 | sha1 = digest.Process(ba) 165 | renderCode(qr, "tmp:/" + sha1 + ".png") 166 | ?"Time:", time.totalMilliseconds(), text 167 | end function 168 | 169 | function getEcl() 170 | ecl = CreateObject("roSGNode", "QREcc") 171 | ecl.level = "LOW" 172 | if not isNullOrEmpty(m.top.ecl) then 173 | ecl.level = uCase(m.top.ecl) 174 | end if 175 | return ecl 176 | end function 177 | 178 | sub renderCode(qr as object, filename as string) 179 | border = m.top.border 180 | pixel = m.top.pixel 181 | lightColor = m.top.lightColor 182 | darkColor = m.top.darkColor 183 | size = qr.size 184 | imageSide = ((border * 2) + size) * pixel 185 | 186 | bm = CreateObject("roBitmap", { width: imageSide, height: imageSide, AlphaEnable: true }) 187 | if bm <> invalid then 188 | bm.DrawRect(0, 0, imageSide, imageSide, lightColor) 189 | for y = 0 to size - 1 190 | for x = 0 to size - 1 191 | if qr.modules[y][x] = 1 then 192 | bm.DrawRect((border + x) * pixel, (border + y) * pixel, pixel, pixel, darkColor) 193 | end if 194 | next 195 | next 196 | bm.Finish() 197 | ba = bm.GetPng(0, 0, imageSide, imageSide) 198 | ba.WriteFile(filename) 199 | m.top.uri = filename 200 | end if 201 | end sub 202 | -------------------------------------------------------------------------------- /components/qrCode/qrCode.draw.brs: -------------------------------------------------------------------------------- 1 | '-- Private helper methods for constructor: Drawing function modules -- 2 | 3 | ' Reads this object's version field, and draws and marks all function modules. 4 | function _qrCode_draw_drawFunctionPatterns() 5 | ' Draw horizontal and vertical timing patterns 6 | for i = 0 to m.size - 1 7 | m.setFunctionModule(6, i, i MOD 2 = 0) 8 | m.setFunctionModule(i, 6, i MOD 2 = 0) 9 | next 10 | 11 | ' Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) 12 | m.drawFinderPattern(3, 3) 13 | m.drawFinderPattern(m.size - 4, 3) 14 | m.drawFinderPattern(3, m.size - 4) 15 | 16 | ' Draw numerous alignment patterns 17 | alignPatPos = m.getAlignmentPatternPositions() 18 | numAlign = alignPatPos.count() 19 | for i = 0 to numAlign - 1 20 | for j = 0 to numAlign - 1 21 | ' Don't draw on the three finder corners 22 | if (not ((i = 0) and (j = 0) or (i = 0) and (j = numAlign - 1) or (i = numAlign - 1) and (j = 0))) then 23 | m.drawAlignmentPattern(alignPatPos[i], alignPatPos[j]) 24 | end if 25 | next 26 | next 27 | 28 | ' Draw configuration data 29 | m.drawFormatBits(0) ' Dummy mask value; overwritten later in the constructor 30 | m.drawVersion() 31 | end function 32 | 33 | 34 | ' Draws two copies of the format bits (with its own error correction code) 35 | ' based on the given mask and this object's error correction level field. 36 | function _qrCode_draw_drawFormatBits(mask as integer) 37 | ' Calculate error correction code and pack bits 38 | data = m.errorCorrectionLevel.formatBits << 3 or mask ' errCorrLvl is uint2, mask is uint3 39 | remain = data 40 | for i = 0 to 9 41 | remain = xor((remain << 1), (remain >> 9) * &h537) 42 | next 43 | bits = xor((data << 10 or remain), &h5412) ' uint15 44 | assert(bits >> 15 = 0) 45 | 46 | ' Draw first copy 47 | for i = 0 to 5 48 | m.setFunctionModule(8, i, getBit(bits, i)) 49 | next 50 | m.setFunctionModule(8, 7, getBit(bits, 6)) 51 | m.setFunctionModule(8, 8, getBit(bits, 7)) 52 | m.setFunctionModule(7, 8, getBit(bits, 8)) 53 | for i = 9 to 14 54 | m.setFunctionModule(14 - i, 8, getBit(bits, i)) 55 | next 56 | 57 | ' Draw second copy 58 | for i = 0 to 7 59 | m.setFunctionModule(m.size - 1 - i, 8, getBit(bits, i)) 60 | next 61 | for i = 8 to 14 62 | m.setFunctionModule(8, m.size - 15 + i, getBit(bits, i)) 63 | next 64 | m.setFunctionModule(8, m.size - 8, true) ' Always dark 65 | end function 66 | 67 | 68 | ' Draws two copies of the version bits (with its own error correction code), 69 | ' based on this object's version field, iff 7 <= version <= 40. 70 | function _qrCode_draw_drawVersion() 71 | if (m.version < 7) then 72 | return false 73 | end if 74 | 75 | ' Calculate error correction code and pack bits 76 | remain = m.version ' version is uint6, in the range [7, 40] 77 | for i = 0 to 11 78 | remain = xor((remain << 1), (remain >> 11) * &h1F25) 79 | next 80 | bits = m.version << 12 or remain ' uint18 81 | assert(bits >> 18 = 0) 82 | 83 | ' Draw two copies 84 | for i = 0 to 17 85 | color = getBit(bits, i) 86 | a = m.size - 11 + i MOD 3 87 | b = floor(i / 3) 88 | m.setFunctionModule(a, b, color) 89 | m.setFunctionModule(b, a, color) 90 | next 91 | end function 92 | 93 | 94 | ' Draws a 9*9 finder pattern including the border separator, 95 | ' with the center module at (x, y). Modules can be out of bounds. 96 | function _qrCode_draw_drawFinderPattern(x as integer, y as integer) 97 | for dy = -4 to 4 98 | for dx = -4 to 4 99 | dist = max(abs(dx), abs(dy)) ' Chebyshev/infinity norm 100 | xx = x + dx 101 | yy = y + dy 102 | if ((0 <= xx) and (xx < m.size) and (0 <= yy) and (yy < m.size)) then 103 | m.setFunctionModule(xx, yy, (dist <> 2) and (dist <> 4)) 104 | end if 105 | next 106 | next 107 | end function 108 | 109 | 110 | ' Draws a 5*5 alignment pattern, with the center module 111 | ' at (x, y). All modules must be in bounds. 112 | function _qrCode_draw_drawAlignmentPattern(x as integer, y as integer) 113 | for dy = -2 to 2 114 | for dx = -2 to 2 115 | m.setFunctionModule(x + dx, y + dy, max(abs(dx), abs(dy)) <> 1) 116 | next 117 | next 118 | end function 119 | 120 | 121 | ' Sets the color of a module and marks it as a function module. 122 | ' Only used by the constructor. Coordinates must be in bounds. 123 | function _qrCode_draw_setFunctionModule(x as integer, y as integer, isDark as boolean) 124 | m.modules[y][x] = iif(isDark, 1, 0) 125 | m.isFunction[y][x] = true 126 | end function 127 | -------------------------------------------------------------------------------- /components/qrCode/qrCode.help.brs: -------------------------------------------------------------------------------- 1 | '-- Private helper functions -- 2 | 3 | ' Returns an ascending list of positions of alignment patterns for this version number. 4 | ' Each position is in the range [0,177), and are used on both the x and y axes. 5 | ' This could be implemented as lookup table of 40 variable-length lists of integers. 6 | function _qrCode_help_getAlignmentPatternPositions() as object 7 | if (m.version = 1) then 8 | return [] 9 | else 10 | numAlign = floor(m.version / 7) + 2 11 | stepSize = iif(m.version = 32, 26, ceil((m.version * 4 + 4) / (numAlign * 2 - 2)) * 2) 12 | result = [6] 13 | position = m.size - 7 14 | while(result.count() < numAlign) 15 | splice(result, 1, 0, [position]) 16 | position -= stepSize 17 | end while 18 | return result 19 | end if 20 | end function 21 | 22 | 23 | ' Returns the number of data bits that can be stored in a QR Code of the given version number, after 24 | ' all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. 25 | ' The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. 26 | function _qrCode_help_getNumRawDataModules(ver as integer) as integer 27 | if ((ver < m.MIN_VERSION) or (ver > m.MAX_VERSION)) then 28 | throw("Version number out of range") 29 | end if 30 | result = (16 * ver + 128) * ver + 64 31 | if (ver >= 2) then 32 | numAlign = floor(ver / 7) + 2 33 | result -= (25 * numAlign - 10) * numAlign - 55 34 | if (ver >= 7) then 35 | result -= 36 36 | end if 37 | end if 38 | assert((208 <= result) and (result <= 29648)) 39 | return result 40 | end function 41 | 42 | 43 | ' Returns the number of 8-bit data (i.e. not error correction) codewords contained in any 44 | ' QR Code of the given version number and error correction level, with remainder bits discarded. 45 | ' This stateless pure function could be implemented as a (40*4)-cell lookup table. 46 | function _qrCode_help_getNumDataCodewords(ver as integer, ecl as object) as integer 47 | return floor(m.getNumRawDataModules(ver) / 8) - m.ECC_CODEWORDS_PER_BLOCK[ecl.ordinal][ver] * m.NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver] 48 | end function 49 | 50 | 51 | ' Returns a Reed-Solomon ECC generator polynomial for the given degree. This could be 52 | ' implemented as a lookup table over all possible parameter values, instead of as an algorithm. 53 | function _qrCode_help_reedSolomonComputeDivisor(degree as integer) as object 54 | if ((degree < 1) or (degree > 255)) then 55 | throw("Degree out of range") 56 | end if 57 | ' Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. 58 | ' For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array [255, 8, 93]. 59 | result = [] 60 | for i = 0 to degree - 2 61 | result.push(0) 62 | next 63 | result.push(1) ' Start off with the monomial x^0 64 | 65 | ' Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), 66 | ' and drop the highest monomial term which is always 1x^degree. 67 | ' Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). 68 | root = 1 69 | for i = 0 to degree - 1 70 | ' Multiply the current product by (x - r^i) 71 | for j = 0 to result.count() - 1 72 | result[j] = m.reedSolomonMultiply(result[j], root) 73 | if (j + 1 < result.count()) then 74 | result[j] = xor(result[j], result[j + 1]) 75 | end if 76 | next 77 | root = m.reedSolomonMultiply(root, &h02) 78 | next 79 | return result 80 | end function 81 | 82 | 83 | ' Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials. 84 | function _qrCode_help_reedSolomonComputeRemainder(data as object, divisor as object) as object 85 | result = [] 86 | for each _d in divisor ' Prefix d with underscore to avoid unused variable warning 87 | result.push(0) 88 | next 89 | for each b in data ' Polynomial division 90 | factor = xor(b, result.shift()) 91 | result.push(0) 92 | for i = 0 to divisor.count() - 1 93 | coef = divisor[i] 94 | res = result[i] 95 | result[i] = xor(res, m.reedSolomonMultiply(coef, factor)) 96 | next 97 | next 98 | return result 99 | end function 100 | 101 | 102 | ' Returns the product of the two given field elements modulo GF(2^8/0x11D). The arguments and result 103 | ' are unsigned 8-bit integers. This could be implemented as a lookup table of 256*256 entries of uint8. 104 | function _qrCode_help_reedSolomonMultiply(x as integer, y as integer) as integer 105 | if ((x >> 8 <> 0) or (y >> 8 <> 0)) then 106 | throw("Byte out of range") 107 | end if 108 | ' Russian peasant multiplication 109 | z = 0 110 | for i = 7 to 0 step -1 111 | z = xor((z << 1), ((z >> 7) * &h11D)) 112 | z = xor(z, ((y >> i) and 1) * x) 113 | next 114 | assert(z >> 8 = 0) 115 | return z 116 | end function 117 | 118 | 119 | ' Can only be called immediately after a light run is added, and 120 | ' returns either 0, 1, or 2. A helper function for getPenaltyScore(). 121 | function _qrCode_help_finderPenaltyCountPatterns(runHistory as object) as integer 122 | n = runHistory[1] 123 | assert(n <= m.size * 3) 124 | core = iif((n > 0) and (runHistory[2] = n) and (runHistory[3] = n * 3) and (runHistory[4] = n) and (runHistory[5] = n), 1, 0) 125 | return (core and iif(runHistory[0] >= n * 4, 1, 0) and iif(runHistory[6] >= n, 1, 0)) + (core and iif(runHistory[6] >= n * 4, 1, 0) and iif(runHistory[0] >= n, 1, 0)) 126 | end function 127 | 128 | 129 | ' Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). 130 | function _qrCode_help_finderPenaltyTerminateAndCount(currentRunColor as boolean, currentRunLength as integer, runHistory as object) as integer 131 | if (currentRunColor) then ' Terminate dark run 132 | m.finderPenaltyAddHistory(currentRunLength, runHistory) 133 | currentRunLength = 0 134 | end if 135 | currentRunLength += m.size ' Add light border to final run 136 | m.finderPenaltyAddHistory(currentRunLength, runHistory) 137 | return m.finderPenaltyCountPatterns(runHistory) 138 | end function 139 | 140 | 141 | ' Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). 142 | function _qrCode_help_finderPenaltyAddHistory(currentRunLength as integer, runHistory as object) 143 | if (runHistory[0] = 0) 144 | currentRunLength += m.size ' Add light border to initial run 145 | end if 146 | runHistory.pop() 147 | runHistory.unshift(currentRunLength) 148 | end function 149 | -------------------------------------------------------------------------------- /components/qrCode/qrCode.high.brs: -------------------------------------------------------------------------------- 1 | '-- Static factory functions (high level) -- 2 | 3 | ' Returns a QR Code representing the given Unicode text string at the given error correction level. 4 | ' As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer 5 | ' Unicode code points (not UTF-16 code units) if the low error correction level is used. The smallest possible 6 | ' QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the 7 | ' ecl argument if it can be done without increasing the version. 8 | function _qrCode_high_encodeText(text as string, ecl as object) as object 9 | segs = m.QrSegment.makeSegments(text) 10 | return m.encodeSegments(segs, ecl) 11 | end function 12 | 13 | ' Returns a QR Code representing the given binary data at the given error correction level. 14 | ' This function always encodes using the binary segment mode, not any text mode. The maximum number of 15 | ' bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. 16 | ' The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. 17 | function _qrCode_high_encodeBinary(data as object, ecl as object) as object 18 | seg = m.QrSegment.makeBytes(data) 19 | return m.encodeSegments([seg], ecl) 20 | end function -------------------------------------------------------------------------------- /components/qrCode/qrCode.low.brs: -------------------------------------------------------------------------------- 1 | '-- Constructor (low level) and fields -- 2 | 3 | ' Creates a new QR Code with the given version number, 4 | ' error correction level, data codeword bytes, and mask number. 5 | ' This is a low - level API that most users should not use directly. 6 | ' A mid - level API is the encodeSegments() function. 7 | function _qrCode_low_constructor(version as integer, errorCorrectionLevel as object, dataCodewords as object, msk as integer) 8 | ' The version number of this QR Code, which is between 1 and 40 (inclusive). 9 | ' This determines the size of this barcode. 10 | m.version = version 11 | 12 | ' The error correction level used in this QR Code. 13 | m.errorCorrectionLevel = errorCorrectionLevel 14 | 15 | ' Check scalar arguments 16 | if ((version < m.MIN_VERSION) or (version > m.MAX_VERSION)) then 17 | throw("Version value out of range") 18 | end if 19 | if ((msk < -1) or (msk > 7)) then 20 | throw("Mask value out of range") 21 | end if 22 | m.size = version * 4 + 17 23 | 24 | ' Initialize both grids to be size * size arrays of boolean false 25 | for y = 0 to m.size - 1 26 | m.modules[y] = [] 27 | m.isFunction[y] = [] 28 | for x = 0 to m.size - 1 29 | m.modules[y][x] = 0 ' Initially all light 30 | m.isFunction[y][x] = false 31 | next 32 | next 33 | 34 | ' Compute ECC, draw modules 35 | m.drawFunctionPatterns() 36 | allCodewords = m.addEccAndInterleave(dataCodewords) 37 | m.drawCodewords(allCodewords) 38 | 39 | ' Do masking 40 | if (msk = -1) then ' Automatically choose best mask 41 | minPenalty = 1000000000 42 | for i = 0 to 7 43 | m.applyMask(i) 44 | m.drawFormatBits(i) 45 | penalty = m.getPenaltyScore() 46 | if (penalty < minPenalty) then 47 | msk = i 48 | minPenalty = penalty 49 | end if 50 | m.applyMask(i) ' Undoes the mask due to XOR 51 | next 52 | end if 53 | assert((0 <= msk) and (msk <= 7)) 54 | m.mask = msk 55 | m.applyMask(msk) ' Apply the final choice of mask 56 | m.drawFormatBits(msk) ' Overwrite old format bits 57 | 58 | m.isFunction = [] 59 | end function 60 | 61 | '-- Accessor methods -- 62 | 63 | ' Returns the color of the module (pixel) at the given coordinates, which is false 64 | ' for light or true for dark. The top left corner has the coordinates (x = 0, y = 0). 65 | ' if the given coordinates are out of bounds, then false (light) is returned. 66 | function _qrCode_low_getModule(x as integer, y as integer) as boolean 67 | return (0 <= x) and (x < m.size) and (0 <= y) and (y < m.size) and m.modules[y][x] 68 | end function -------------------------------------------------------------------------------- /components/qrCode/qrCode.mask.brs: -------------------------------------------------------------------------------- 1 | '-- Private helper methods for constructor: Codewords and masking -- 2 | 3 | ' Returns a new byte string representing the given data with the appropriate error correction 4 | ' codewords appended to it, based on this object's version and error correction level. 5 | function _qrCode_mask_addEccAndInterleave(data as object) as object 6 | ver = m.version 7 | ecl = m.errorCorrectionLevel 8 | if (data.count() <> m.getNumDataCodewords(ver, ecl)) then 9 | throw("Invalid argument") 10 | end if 11 | 12 | ' Calculate parameter numbers 13 | numBlocks = m.NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver] 14 | blockEccLen = m.ECC_CODEWORDS_PER_BLOCK [ecl.ordinal][ver] 15 | rawCodewords = floor(m.getNumRawDataModules(ver) / 8) 16 | numShortBlocks = numBlocks - rawCodewords MOD numBlocks 17 | shortBlockLen = floor(rawCodewords / numBlocks) 18 | 19 | ' Split data into blocks and append ECC to each block 20 | blocks = [] 21 | rsDiv = m.reedSolomonComputeDivisor(blockEccLen) 22 | k = 0 23 | for i = 0 to numBlocks - 1 24 | dat = slice(data, k, k + shortBlockLen - blockEccLen + iif(i < numShortBlocks, 0, 1)) 25 | k += dat.count() 26 | ecc = m.reedSolomonComputeRemainder(dat, rsDiv) 27 | if (i < numShortBlocks) then 28 | dat.push(0) 29 | end if 30 | blocks.push(concat(dat, ecc)) 31 | next 32 | 33 | ' Interleave (not concatenate) the bytes from every block into a single sequence 34 | result = [] 35 | for i = 0 to blocks[0].count() - 1 36 | j = 0 37 | for each block in blocks 38 | ' Skip the padding byte in short blocks 39 | if ((i <> shortBlockLen - blockEccLen) or (j >= numShortBlocks)) then 40 | result.push(block[i]) 41 | end if 42 | j += 1 43 | next 44 | next 45 | assert(result.count() = rawCodewords) 46 | return result 47 | end function 48 | 49 | 50 | ' Draws the given sequence of 8-bit codewords (data and error correction) onto the entire 51 | ' data area of this QR Code. Function modules need to be marked off before this is called. 52 | function _qrCode_mask_drawCodewords(data as object) 53 | if (data.count() <> floor(m.getNumRawDataModules(m.version) / 8)) then 54 | throw("Invalid argument") 55 | end if 56 | i = 0 ' Bit index into the data 57 | ' Do the funny zigzag scan 58 | for right = m.size - 1 to 1 step -2' Index of right column in each column pair 59 | if (right = 6) then 60 | right = 5 61 | end if 62 | for vert = 0 to m.size - 1 ' Vertical counter 63 | for j = 0 to 1 64 | x = right - j ' Actual x coordinate 65 | upward = ((right + 1) and 2) = 0 66 | y = iif(upward, m.size - 1 - vert, vert) ' Actual y coordinate 67 | if (not m.isFunction[y][x] and (i < data.count() * 8)) then 68 | m.modules[y][x] = iif(getBit(data[i >> 3], 7 - (i and 7)), 1, 0) 69 | i += 1 70 | end if 71 | ' If this QR Code has any remainder bits (0 to 7), they were assigned as 72 | ' 0/false/light by the constructor and are left unchanged by this method 73 | next 74 | next 75 | next 76 | assert(i = data.count() * 8) 77 | end function 78 | 79 | 80 | ' XORs the codeword modules in this QR Code with the given mask pattern. 81 | ' The function modules must be marked and the codeword bits must be drawn 82 | ' before masking. Due to the arithmetic of XOR, calling applyMask() with 83 | ' the same mask value a second time will undo the mask. A final well-formed 84 | ' QR Code needs exactly one (not zero, two, etc.) mask applied. 85 | function _qrCode_mask_applyMask(mask as integer) 86 | if ((mask < 0) or (mask > 7)) then 87 | throw("Mask value out of range") 88 | end if 89 | for y = 0 to m.size - 1 90 | for x = 0 to m.size - 1 91 | if (mask = 0) then 92 | invert = (x + y) MOD 2 = 0 93 | else if (mask = 1) then 94 | invert = y MOD 2 = 0 95 | else if (mask = 2) then 96 | invert = x MOD 3 = 0 97 | else if (mask = 3) then 98 | invert = (x + y) MOD 3 = 0 99 | else if (mask = 4) then 100 | invert = (floor(x / 3) + floor(y / 2)) MOD 2 = 0 101 | else if (mask = 5) then 102 | invert = x * y MOD 2 + x * y MOD 3 = 0 103 | else if (mask = 6) then 104 | invert = (x * y MOD 2 + x * y MOD 3) MOD 2 = 0 105 | else if (mask = 7) then 106 | invert = ((x + y) MOD 2 + x * y MOD 3) MOD 2 = 0 107 | else 108 | throw("Unreachable") 109 | end if 110 | if (not m.isFunction[y][x] and invert) 111 | m.modules[y][x] = abs(m.modules[y][x] - 1) 112 | end if 113 | next 114 | next 115 | end function 116 | 117 | 118 | ' Calculates and returns the penalty score based on state of this QR Code's current modules. 119 | ' This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. 120 | function _qrCode_mask_getPenaltyScore() as integer 121 | result = 0 122 | 123 | ' Adjacent modules in row having same color, and finder-like patterns 124 | for y = 0 to m.size - 1 125 | runColor = 0 126 | runX = 0 127 | runHistory = [0, 0, 0, 0, 0, 0, 0] 128 | for x = 0 to m.size - 1 129 | if (m.modules[y][x] = runColor) then 130 | runX += 1 131 | if (runX = 5) then 132 | result += m.PENALTY_N1 133 | else if (runX > 5) then 134 | result += 1 135 | end if 136 | else 137 | m.finderPenaltyAddHistory(runX, runHistory) 138 | if (runColor = 0) then 139 | result += m.finderPenaltyCountPatterns(runHistory) * m.PENALTY_N3 140 | end if 141 | runColor = m.modules[y][x] 142 | runX = 1 143 | end if 144 | next 145 | result += m.finderPenaltyTerminateAndCount(runColor, runX, runHistory) * m.PENALTY_N3 146 | next 147 | ' Adjacent modules in column having same color, and finder-like patterns 148 | for x = 0 to m.size - 1 149 | runColor = 0 150 | runY = 0 151 | runHistory = [0, 0, 0, 0, 0, 0, 0] 152 | for y = 0 to m.size - 1 153 | if (m.modules[y][x] = runColor) then 154 | runY += 1 155 | if (runY = 5) then 156 | result += m.PENALTY_N1 157 | else if (runY > 5) then 158 | result += 1 159 | end if 160 | else 161 | m.finderPenaltyAddHistory(runY, runHistory) 162 | if (not runColor) then 163 | result += m.finderPenaltyCountPatterns(runHistory) * m.PENALTY_N3 164 | end if 165 | runColor = m.modules[y][x] 166 | runY = 1 167 | end if 168 | next 169 | result += m.finderPenaltyTerminateAndCount(runColor, runY, runHistory) * m.PENALTY_N3 170 | next 171 | 172 | ' 2*2 blocks of modules having same color 173 | for y = 0 to m.size - 2 174 | for x = 0 to m.size - 2 175 | color = m.modules[y][x] 176 | if ((color = m.modules[y][x + 1]) and (color = m.modules[y + 1][x]) and (color = m.modules[y + 1][x + 1])) 177 | result += m.PENALTY_N2 178 | end if 179 | next 180 | next 181 | 182 | ' Balance of dark and light modules 183 | dark = 0 184 | for each row in m.modules 185 | for each item in row 186 | if item then 187 | dark += 1 188 | end if 189 | next 190 | next 191 | total = m.size * m.size ' Note that size is odd, so dark/total != 1/2 192 | ' Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)% 193 | k = ceil(abs(dark * 20 - total * 10) / total) - 1 194 | assert((0 <= k) and (k <= 9)) 195 | result += k * m.PENALTY_N4 196 | assert((0 <= result) and (result <= 2568888)) ' Non-tight upper bound based on default values of PENALTY_N1, ..., N4 197 | return result 198 | end function 199 | -------------------------------------------------------------------------------- /components/qrCode/qrCode.mid.brs: -------------------------------------------------------------------------------- 1 | '-- Static factory functions (mid level) -- 2 | 3 | ' Returns a QR Code representing the given segments with the given encoding parameters. 4 | ' The smallest possible QR Code version within the given range is automatically 5 | ' chosen for the output. Iff boostEcl is true, then the ECC level of the result 6 | ' may be higher than the ecl argument if it can be done without increasing the 7 | ' version. The mask number is either between 0 to 7 (inclusive) to force that 8 | ' mask, or -1 to automatically choose an appropriate mask (which may be slow). 9 | ' This function allows the user to create a custom sequence of segments that switches 10 | ' between modes (such as alphanumeric and byte) to encode text in less space. 11 | ' This is a mid-level API; the high-level API is encodeText() and encodeBinary(). 12 | function _qrCode_mid_encodeSegments(segs as object, ecl as object, minVersion = 1 as integer, maxVersion = 40 as integer, mask = -1 as integer, boostEcl = true as boolean) as object 13 | if (not ((m.MIN_VERSION <= minVersion) and (minVersion <= maxVersion) and (maxVersion <= m.MAX_VERSION)) or (mask < -1) or (mask > 7)) then 14 | throw("Invalid value") 15 | end if 16 | 17 | ' Find the minimal version number to use 18 | for version = minVersion to maxVersion 19 | dataCapacityBits = m.getNumDataCodewords(version, ecl) * 8 ' Number of data bits available 20 | usedBits = m.QrSegment.getTotalBits(segs, version) 21 | if (usedBits <= dataCapacityBits) then 22 | dataUsedBits = usedBits 23 | exit for ' This version number is found to be suitable 24 | end if 25 | if (version = maxVersion) then ' All versions in the range could not fit the given data 26 | throw("Data too long") 27 | end if 28 | next 29 | 30 | ' Increase the error correction level while the data still fits in the current version number 31 | for each newEcl in m.Ecc ' From low to high 32 | if (boostEcl and (dataUsedBits <= m.getNumDataCodewords(version, newEcl) * 8)) then 33 | ecl = newEcl 34 | end if 35 | next 36 | 37 | ' Concatenate all segments to create the data bit string 38 | bb = [] 39 | for each seg in segs 40 | appendBits(seg.mode.modeBits, 4, bb) 41 | appendBits(seg.numChars, seg.mode.callFunc("numCharCountBits", version), bb) 42 | for each b in seg.getData() 43 | bb.push(b) 44 | next 45 | next 46 | assert(bb.count() = dataUsedBits) 47 | 48 | ' Add terminator and pad up to a byte if applicable 49 | dataCapacityBits = m.getNumDataCodewords(version, ecl) * 8 50 | assert(bb.count() <= dataCapacityBits) 51 | appendBits(0, min(4, dataCapacityBits - bb.count()), bb) 52 | appendBits(0, (8 - bb.count() MOD 8) MOD 8, bb) 53 | assert(bb.count() MOD 8 = 0) 54 | 55 | ' Pad with alternating bytes until data capacity is reached 56 | padByte = &hEC 57 | while(bb.count() < dataCapacityBits) 58 | appendBits(padByte, 8, bb) 59 | padByte = xor(padByte, xor(&hEC, &h11)) 60 | end while 61 | 62 | ' Pack bits into bytes in big endian 63 | dataCodewords = [] 64 | while (dataCodewords.count() * 8 < bb.count()) 65 | dataCodewords.push(0) 66 | end while 67 | 68 | for x = 0 to bb.count() - 1 69 | b = bb[x] 70 | i = x 71 | dataCodewords[i >> 3] = dataCodewords[i >> 3] or (b << (7 - (i and 7))) 72 | next 73 | 74 | ' Create the QR Code object 75 | return m.constructor(version, ecl, dataCodewords, mask) 76 | end function 77 | -------------------------------------------------------------------------------- /components/qrCode/qrCode.utils.brs: -------------------------------------------------------------------------------- 1 | ' Appends the given number of low-order bits of the given value 2 | ' to the given buffer. Requires 0 <= len <= 31 and 0 <= val < 2^len. 3 | function appendBits(value as integer, length as integer, bb as object) 4 | if ((length < 0) or (length > 31) or (value >> length <> 0)) then 5 | throw("Value out of range") 6 | end if 7 | for i = length - 1 to 0 step -1 ' Append bit by bit 8 | bb.push((value >> i) and 1) 9 | next 10 | end function 11 | 12 | 13 | ' Returns true iff the i'th bit of x is set to 1. 14 | function getBit(x as integer, i as integer) as boolean 15 | return ((x >> i) and 1) <> 0 16 | end function 17 | 18 | 19 | ' Throws an exception if the given condition is false. 20 | function assert(condition as boolean) 21 | if (not condition) then 22 | throw("Assertion error") 23 | end if 24 | end function 25 | 26 | 27 | 'Since Brightscript doesn't have a XOR operator, we use this function to calculate it. 28 | function xor(x, y) 29 | if type(x) = "roInvalid" then x = 0 30 | if type(y) = "roInvalid" then y = 0 31 | return (x or y) - (x and y) 32 | end function 33 | 34 | 35 | function iif(check as boolean, yes as dynamic, no as dynamic) as dynamic 36 | if check then 37 | return yes 38 | end if 39 | return no 40 | end function 41 | 42 | function floor(f as float) as integer 43 | return int(f) 44 | end function 45 | 46 | function ceil(f as float) as integer 47 | return int(f + 0.999) 48 | end function 49 | 50 | function min(a, b) 51 | if a < b then return a 52 | return b 53 | end function 54 | 55 | function max(a, b) 56 | if a > b then return a 57 | return b 58 | end function 59 | 60 | function slice(array as object, start = 0 as integer, finish = 0 as integer) as object 61 | size = array.count() - 1 62 | if (start < 0) then 63 | start += size 64 | end if 65 | if (finish = 0 or finish >= size) then 66 | finish = size + 1 67 | else if (finish < 0) then 68 | finish += size 69 | end if 70 | new = [] 71 | for x = start to finish - 1 72 | new.push(array[x]) 73 | next 74 | return new 75 | end function 76 | 77 | function concat(array1 as object, array2 as object) as object 78 | new = [] 79 | new.append(array1) 80 | new.append(array2) 81 | return new 82 | end function 83 | 84 | function splice(array as object, start as integer, delete = 999999 as integer, insert = [] as object) 85 | if start > array.count() - 1 then start = array.count() - 1 86 | if start < 0 then start = array.count() - start 87 | if start < 0 then start = 0 88 | if delete < 0 then delete = 0 89 | for d = 1 to delete 90 | array.delete(start) 91 | next 92 | tmp = [] 93 | tmp.append(insert) 94 | while(array.count() - 1 > start) 95 | tmp.push(array[start]) 96 | array.delete(start) 97 | end while 98 | array.append(tmp) 99 | return array 100 | end function 101 | 102 | function infinity() 103 | return 999999 104 | end function 105 | 106 | function joinNums(array as object, sep = ", " as string) as string 107 | ret = "" 108 | for each item in array 109 | if ret <> "" then 110 | ret += sep 111 | end if 112 | if type(item) = "Invalid" then 113 | ret += "invalid" 114 | else 115 | ret += item.toStr() 116 | end if 117 | next 118 | return ret 119 | end function 120 | 121 | function isNullOrEmpty(obj as object) as boolean 122 | if type(obj) = "Invalid" or type(obj) = "roInvalid" then 123 | return true 124 | else if obj = "" then 125 | return true 126 | end if 127 | return false 128 | end function -------------------------------------------------------------------------------- /components/qrCode/qrCode.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 |