├── 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 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/components/qrCode/qrEcc.brs:
--------------------------------------------------------------------------------
1 | function onLevelChanged(nodeEvent)
2 | level = nodeEvent.getData()
3 |
4 | if level = "LOW" then
5 | constructor(0, 1) ' The QR Code can tolerate about 7% erroneous codewords
6 | else if level = "MEDIUM" then
7 | constructor(1, 0) ' The QR Code can tolerate about 15% erroneous codewords
8 | else if level = "QUARTILE" then
9 | constructor(2, 3) ' The QR Code can tolerate about 25% erroneous codewords
10 | else if level = "HIGH" then
11 | constructor(3, 2) ' The QR Code can tolerate about 30% erroneous codewords
12 | else
13 | throw("Unknown Level")
14 | end if
15 | end function
16 |
17 | '-- Constructor and fields --
18 | function constructor(ordinal as integer, formatBits as integer)
19 | ' In the range 0 to 3 (unsigned 2-bit integer).
20 | m.top.ordinal = ordinal
21 | ' (Package-private) In the range 0 to 3 (unsigned 2-bit integer).
22 | m.top.formatBits = formatBits
23 | end function
24 |
--------------------------------------------------------------------------------
/components/qrCode/qrEcc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/components/qrCode/qrMode.brs:
--------------------------------------------------------------------------------
1 | function onModeChanged(nodeEvent)
2 | mode = nodeEvent.getData()
3 |
4 | if mode = "NUMERIC" then
5 | constructor(&h1, [10, 12, 14])
6 | else if mode = "ALPHANUMERIC" then
7 | constructor(&h2, [9, 11, 13])
8 | else if mode = "BYTE" then
9 | constructor(&h4, [8, 16, 16])
10 | else if mode = "KANJI" then
11 | constructor(&h8, [8, 10, 12])
12 | else if mode = "ECI" then
13 | constructor(&h7, [0, 0, 0])
14 | else
15 | throw("Unknown Mode")
16 | end if
17 | end function
18 |
19 | '-- Constructor and fields --
20 | function constructor(modeBits as integer, numBitsCharCount as object)
21 | ' The mode indicator bits, which is a uint4 value (range 0 to 15).
22 | m.top.modeBits = modeBits
23 | ' Number of character count bits for three different version ranges.
24 | m.top.numBitsCharCount = numBitsCharCount
25 | end function
26 |
27 | '-- Method --
28 | ' (Package-private) Returns the bit width of the character count field for a segment in
29 | ' this mode in a QR Code at the given version number. The result is in the range [0, 16].
30 | function numCharCountBits(version as integer) as integer
31 | return m.top.numBitsCharCount[floor((version + 7) / 17)]
32 | end function
33 |
34 | function onVersionChanged(nodeEvent)
35 | version = nodeEvent.getData()
36 | m.top.numCharCountBits = m.top.numBitsCharCount[floor((version + 7) / 17)]
37 | end function
--------------------------------------------------------------------------------
/components/qrCode/qrMode.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/components/qrCode/qrSegment.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 | '---- Data segment class ----
27 |
28 | ' A segment of character/binary/control data in a QR Code symbol.
29 | ' Instances of this class are immutable.
30 | ' The mid-level way to create a segment is to take the payload data
31 | ' and call a static factory function such as QrSegment.makeNumeric().
32 | ' The low-level way to create a segment is to custom-make the bit buffer
33 | ' and call the QrSegment() constructor with appropriate values.
34 | ' This segment class imposes no length restrictions, but QR Codes have restrictions.
35 | ' Even in the most favorable conditions, a QR Code can only hold 7089 characters of data.
36 | ' Any segment longer than this is meaningless for the purpose of generating QR Codes.
37 | function QrSegment() as object
38 | this = {}
39 |
40 | this.makeBytes = _qrSegment_makeBytes
41 | this.makeNumeric = _qrSegment_makeNumeric
42 | this.makeAlphanumeric = _qrSegment_makeAlphanumeric
43 | this.makeSegments = _qrSegment_makeSegments
44 | this.makeEci = _qrSegment_makeEci
45 | this.isNumeric = _qrSegment_isNumeric
46 | this.isAlphanumeric = _qrSegment_isAlphanumeric
47 | this.constructor = _qrSegment_constructor
48 |
49 | '-- Methods --
50 |
51 | ' Returns a new copy of the data bits of this segment.
52 | this.getData = function()
53 | return slice(m.bitData) ' Make defensive copy
54 | end function
55 |
56 |
57 | ' (Package-private) Calculates and returns the number of bits needed to encode the given segments at
58 | ' the given version. The result is infinity if a segment has too many characters to fit its length field.
59 | this.getTotalBits = function(segs as object, version as integer) as integer
60 | result = 0
61 | for each seg in segs
62 | ccbits = seg.mode.callFunc("numCharCountBits", version)
63 | if (seg.numChars >= (1 << ccbits)) then
64 | return infinity() ' The segment's length doesn't fit the field's bit width
65 | end if
66 | result += 4 + ccbits + seg.bitData.count()
67 | next
68 | return result
69 | end function
70 |
71 |
72 | ' Returns a new array of bytes representing the given string encoded in UTF-8.
73 | this.toUtf8ByteArray = function(str as string) as object
74 | str = str.escape()
75 | result = []
76 | for i = 0 to str.len() - 1
77 | if (str.mid(i, 1) <> "%") then
78 | result.push(asc(str.mid(i, 1)))
79 | else
80 | result.push(val(str.mid(i + 1, 2), 16))
81 | i += 2
82 | end if
83 | next
84 | return result
85 | end function
86 |
87 | '-- Constants --'
88 |
89 | ' Describes precisely all strings that are encodable in numeric mode.
90 | this.NUMERIC_REGEX = CreateObject("roRegex", "^[0-9]*$", "")
91 |
92 | ' Describes precisely all strings that are encodable in alphanumeric mode.
93 | this.ALPHANUMERIC_REGEX = CreateObject("roRegex", "^[A-Z0-9 $%*+.\/:-]*$", "")
94 |
95 | ' The set of all legal characters in alphanumeric mode,
96 | ' where each character value maps to the index in the string.
97 | this.ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"
98 |
99 | return this
100 | end function
101 |
102 | '-- Static factory functions (mid level) --
103 |
104 | ' Returns a segment representing the given binary data encoded in
105 | ' byte mode. All input byte arrays are acceptable. Any text string
106 | ' can be converted to UTF-8 bytes and encoded as a byte mode segment.
107 | function _qrSegment_makeBytes(data as object) as object
108 | bb = []
109 | for each b in data
110 | appendBits(b, 8, bb)
111 | next
112 | return m.constructor("BYTE", data.count(), bb)
113 | end function
114 |
115 |
116 | ' Returns a segment representing the given string of decimal digits encoded in numeric mode.
117 | function _qrSegment_makeNumeric(digits as string) as object
118 | if (not m.isNumeric(digits)) then
119 | throw("String contains non-numeric characters")
120 | end if
121 | bb = []
122 | for i = 0 to digits.len() - 1 ' Consume up to 3 digits per iteration
123 | n = min(digits.len() - i, 3)
124 | appendBits(val(digits.mid(i, n), 10), n * 3 + 1, bb)
125 | i += n
126 | next
127 | return m.constructor("NUMERIC", digits.len(), bb)
128 | end function
129 |
130 |
131 | ' Returns a segment representing the given text string encoded in alphanumeric mode.
132 | ' The characters allowed are: 0 to 9, A to Z (uppercase only), space,
133 | ' dollar, percent, asterisk, plus, hyphen, period, slash, colon.
134 | function _qrSegment_makeAlphanumeric(text as string) as object
135 | if (not m.isAlphanumeric(text)) then
136 | throw("String contains unencodable characters in alphanumeric mode")
137 | end if
138 | bb = []
139 | for i = 0 to text.len() - 2 step 2 ' Process groups of 2
140 | temp = m.ALPHANUMERIC_CHARSET.instr(text.mid(i, 1)) * 45
141 | temp += m.ALPHANUMERIC_CHARSET.instr(text.mid(i + 1, 1))
142 | appendBits(temp, 11, bb)
143 | next
144 | if (i < text.len()) then ' 1 character remaining
145 | appendBits(m.ALPHANUMERIC_CHARSET.instr(text.mid(i, 1)), 6, bb)
146 | end if
147 | return m.constructor("ALPHANUMERIC", text.len(), bb)
148 | end function
149 |
150 |
151 | ' Returns a new mutable list of zero or more segments to represent the given Unicode text string.
152 | ' The result may use various segment modes and switch modes to optimize the length of the bit stream.
153 | function _qrSegment_makeSegments(text as string) as object
154 | ' Select the most efficient segment encoding automatically
155 | if (text = "") then
156 | return []
157 | else if (m.isNumeric(text)) then
158 | return [m.makeNumeric(text)]
159 | else if (m.isAlphanumeric(text)) then
160 | return [m.makeAlphanumeric(text)]
161 | else
162 | return [m.makeBytes(m.toUtf8ByteArray(text))]
163 | end if
164 | end function
165 |
166 |
167 | ' Returns a segment representing an Extended Channel Interpretation
168 | ' (ECI) designator with the given assignment value.
169 | function _qrSegment_makeEci(assignVal as integer) as object
170 | bb = []
171 | if (assignVal < 0) then
172 | throw("ECI assignment value out of range")
173 | else if (assignVal < (1 << 7)) then
174 | appendBits(assignVal, 8, bb)
175 | else if (assignVal < (1 << 14)) then
176 | appendBits(2, 2, bb) '0b10
177 | appendBits(assignVal, 14, bb)
178 | else if (assignVal < 1000000) then
179 | appendBits(6, 3, bb) '0b110
180 | appendBits(assignVal, 21, bb)
181 | else
182 | throw("ECI assignment value out of range")
183 | end if
184 | return m.constructor("ECI", 0, bb)
185 | end function
186 |
187 |
188 | ' Tests whether the given string can be encoded as a segment in numeric mode.
189 | ' A string is encodable iff each character is in the range 0 to 9.
190 | function _qrSegment_isNumeric(text as string) as boolean
191 | return m.NUMERIC_REGEX.isMatch(text)
192 | end function
193 |
194 |
195 | ' Tests whether the given string can be encoded as a segment in alphanumeric mode.
196 | ' A string is encodable iff each character is in the following set: 0 to 9, A to Z
197 | ' (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon.
198 | function _qrSegment_isAlphanumeric(text as string) as boolean
199 | return m.ALPHANUMERIC_REGEX.isMatch(text)
200 | end function
201 |
202 |
203 | '-- Constructor (low level) and fields --
204 |
205 | ' Creates a new QR Code segment with the given attributes and data.
206 | ' The character count (numChars) must agree with the mode and the bit buffer length,
207 | ' but the constraint isn't checked. The given bit buffer is cloned and stored.
208 | function _qrSegment_constructor(mode as string, numChars = 0 as integer, bitData = [] as object) as object
209 | ' The mode indicator of this segment.
210 | m.mode = CreateObject("roSGNode", "QRMode")
211 | m.mode.mode = mode
212 |
213 | ' The length of this segment's unencoded data. Measured in characters for
214 | ' numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode.
215 | ' Always zero or positive. Not the same as the data's bit length.
216 | m.numChars = numChars
217 |
218 | ' The data bits of this segment. Accessed through getData().
219 | m.bitData = bitData
220 |
221 | if (numChars < 0)
222 | throw("Invalid argument")
223 | end if
224 | m.bitData = slice(bitData) ' Make defensive copy
225 | return m
226 | end function
227 |
--------------------------------------------------------------------------------
/components/sampleScene/sampleScene.brs:
--------------------------------------------------------------------------------
1 | sub init()
2 | m.top.setFocus(true)
3 | end sub
4 |
--------------------------------------------------------------------------------
/components/sampleScene/sampleScene.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/images/focus_fhd.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paramount-engineering/QR-Code-generator-brightscript/b5a48b96620e6603078053c8fe7500a23f152b5c/images/focus_fhd.jpg
--------------------------------------------------------------------------------
/images/focus_hd.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paramount-engineering/QR-Code-generator-brightscript/b5a48b96620e6603078053c8fe7500a23f152b5c/images/focus_hd.jpg
--------------------------------------------------------------------------------
/images/focus_sd.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paramount-engineering/QR-Code-generator-brightscript/b5a48b96620e6603078053c8fe7500a23f152b5c/images/focus_sd.jpg
--------------------------------------------------------------------------------
/images/splash_fhd.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paramount-engineering/QR-Code-generator-brightscript/b5a48b96620e6603078053c8fe7500a23f152b5c/images/splash_fhd.jpg
--------------------------------------------------------------------------------
/images/splash_hd.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paramount-engineering/QR-Code-generator-brightscript/b5a48b96620e6603078053c8fe7500a23f152b5c/images/splash_hd.jpg
--------------------------------------------------------------------------------
/images/splash_sd.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paramount-engineering/QR-Code-generator-brightscript/b5a48b96620e6603078053c8fe7500a23f152b5c/images/splash_sd.jpg
--------------------------------------------------------------------------------
/manifest:
--------------------------------------------------------------------------------
1 | title=QR Code Generator
2 | major_version=1
3 | minor_version=0
4 | build_version=00001
5 |
6 | mm_icon_focus_fhd=pkg:/images/focus_fhd.jpg
7 | mm_icon_focus_hd=pkg:/images/focus_hd.jpg
8 | mm_icon_focus_sd=pkg:/images/focus_sd.png
9 |
10 | splash_screen_fhd=pkg:/images/splash_fhd.jpg
11 | splash_screen_hd=pkg:/images/splash_hd.jpg
12 | splash_screen_sd=pkg:/images/splash_sd.jpg
13 |
14 | splash_color=#662D91
15 | splash_min_time=1000
16 |
17 | ui_resolutions = "fhd"
18 |
--------------------------------------------------------------------------------
/source/main.brs:
--------------------------------------------------------------------------------
1 | '********** Copyright 2016 Roku Corp. All Rights Reserved. **********
2 |
3 | sub Main()
4 | showChannelSGScreen()
5 | end sub
6 |
7 | sub showChannelSGScreen()
8 | screen = CreateObject("roSGScreen")
9 | m.port = CreateObject("roMessagePort")
10 | screen.setMessagePort(m.port)
11 | screen.CreateScene("SampleScene")
12 | screen.show()
13 |
14 | while(true)
15 | msg = wait(0, m.port)
16 | msgType = type(msg)
17 |
18 | if msgType = "roSGScreenEvent"
19 | if msg.isScreenClosed() then return
20 | end if
21 | end while
22 |
23 | end sub
24 |
--------------------------------------------------------------------------------