@moralcode/qrcode-brightscript 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,198 @@
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
@@ -0,0 +1,76 @@
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
@@ -0,0 +1,110 @@
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 infinity()
85
+ return 999999
86
+ end function
87
+
88
+ function joinNums(array as object, sep = ", " as string) as string
89
+ ret = ""
90
+ for each item in array
91
+ if ret <> "" then
92
+ ret += sep
93
+ end if
94
+ if type(item) = "Invalid" then
95
+ ret += "invalid"
96
+ else
97
+ ret += item.toStr()
98
+ end if
99
+ next
100
+ return ret
101
+ end function
102
+
103
+ function isNullOrEmpty(obj as object) as boolean
104
+ if type(obj) = "Invalid" or type(obj) = "roInvalid" then
105
+ return true
106
+ else if obj = "" then
107
+ return true
108
+ end if
109
+ return false
110
+ end function
@@ -0,0 +1,25 @@
1
+ <?xml version="1.0" encoding="utf-8" ?>
2
+ <component name="QRCode" extends="Poster">
3
+ <script type="text/brightscript" uri="./qrCode.brs" />
4
+ <script type="text/brightscript" uri="./qrCode.high.brs" />
5
+ <script type="text/brightscript" uri="./qrCode.mid.brs" />
6
+ <script type="text/brightscript" uri="./qrCode.low.brs" />
7
+ <script type="text/brightscript" uri="./qrCode.draw.brs" />
8
+ <script type="text/brightscript" uri="./qrCode.mask.brs" />
9
+ <script type="text/brightscript" uri="./qrCode.help.brs" />
10
+ <script type="text/brightscript" uri="./qrSegment.brs" />
11
+ <script type="text/brightscript" uri="./qrCode.utils.brs" />
12
+ <interface>
13
+ <field id="text" type="string" onChange="onTextChanged" />
14
+ <!-- <field id="binary" type="string" onChange="onBinaryChanged" /> -->
15
+ <field id="ecl" type="string" value="LOW" />
16
+ <field id="border" type="integer" value="4" />
17
+ <field id="pixel" type="integer" value="8" />
18
+ <field id="lightColor" type="color" value="0xFFFFFFFF" />
19
+ <field id="darkColor" type="color" value="0x000000FF" />
20
+ <field id="minVersion" type="integer" value="1" />
21
+ <field id="maxVersion" type="integer" value="40" />
22
+ <field id="mask" type="integer" value="-1" />
23
+ <field id="boost" type="boolean" value="true" />
24
+ </interface>
25
+ </component>
@@ -0,0 +1,23 @@
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
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="utf-8" ?>
2
+ <component name="QREcc" extends="Node">
3
+ <script type="text/brightscript" uri="./qrEcc.brs" />
4
+ <script type="text/brightscript" uri="./qrCode.utils.brs" />
5
+ <interface>
6
+ <field id="level" type="string" onChange="onLevelChanged" />
7
+ <field id="ordinal" type="integer" />
8
+ <field id="formatBits" type="integer" />
9
+ </interface>
10
+ </component>
@@ -0,0 +1,37 @@
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
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="utf-8" ?>
2
+ <component name="QRMode" extends="Node">
3
+ <script type="text/brightscript" uri="./qrMode.brs" />
4
+ <script type="text/brightscript" uri="./qrCode.utils.brs" />
5
+ <interface>
6
+ <field id="mode" type="string" onChange="onModeChanged" />
7
+ <field id="modeBits" type="integer" />
8
+ <field id="numBitsCharCount" type="array" />
9
+ <function name="numCharCountBits" />
10
+ </interface>
11
+ </component>