Skip to content

Commit

Permalink
Merge pull request #50 from RexWzh/rex-dev
Browse files Browse the repository at this point in the history
Fix bug of getecinfo
  • Loading branch information
RexWzh authored Dec 23, 2022
2 parents da56cc9 + dcec858 commit 88f2c68
Show file tree
Hide file tree
Showing 10 changed files with 106 additions and 38 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "QRCoders"
uuid = "f42e9828-16f3-11ed-2883-9126170b272d"
authors = ["Jérémie Gillet <jie.gillet@gmail.com> and contributors"]
version = "1.4.3"
version = "1.4.4"

[deps]
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
Expand Down
1 change: 1 addition & 0 deletions src/QRCoders.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export
getindexes, getsegments, getdarkindex,
getalignmentinds, getfinderinds,
finderpattern, alignmentpattern,
getecinfo, countmsgbyte, getrequiredlen,

# QR code style
unicodeplot, unicodeplotbychar, fitimgwidth,
Expand Down
20 changes: 10 additions & 10 deletions src/encode.jl
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,11 @@ end
Pad the encoded message.
"""
function padencodedmessage(data::BitArray{1}, requiredlength::Int)
length(data) > requiredlength && throw(EncodeError("padencodedmessage: the input data is too long"))
function padencodedmessage(data::BitArray{1}, requiredlen::Int)
length(data) > requiredlen && throw(EncodeError("padencodedmessage: the input data is too long"))

# Add up to 4 zeros to terminate the message
data = vcat(data, falses(min(4, requiredlength - length(data))))
data = vcat(data, falses(min(4, requiredlen - length(data))))

# Add zeros to make the length a multiple of 8
if length(data) & 7 != 0
Expand All @@ -174,8 +174,8 @@ function padencodedmessage(data::BitArray{1}, requiredlength::Int)

# Add the repeated pattern until reaching required length
pattern = BitArray{1}([1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1])
pad = repeat(pattern, ceil(Int, requiredlength - length(data) / 8))
data = vcat(data, @view(pad[1:requiredlength - length(data)]))
pad = repeat(pattern, ceil(Int, requiredlen - length(data) >> 4))
data = vcat(data, @view(pad[1:requiredlen - length(data)]))

return data
end
Expand Down Expand Up @@ -271,20 +271,20 @@ function encodemessage(msg::AbstractString, mode::Mode, eclevel::ErrCorrLevel, v
ccindicator = getcharactercountindicator(msglen, version, mode)

# Encoded data: main part of the encoded message
encodeddata = encodedata(msg, mode)
encodedbits = encodedata(msg, mode)

# Getting parameters for the error correction
# Number of error correction codewords per block, number of blocks in
# group 1/2, number of data codewords per block in group 1/2
necwords, nb1, nc1, nb2, nc2 = ecblockinfo[eclevel][version, :]
requiredbits = 8 * (nb1 * nc1 + nb2 * nc2)
requiredlen = 8 * (nb1 * nc1 + nb2 * nc2)

# Pad encoded message before error correction
encoded = vcat(modeindicator, ccindicator, encodeddata)
encoded = padencodedmessage(encoded, requiredbits)
totalbits = padencodedmessage(
vcat(modeindicator, ccindicator, encodedbits), requiredlen)

# Getting error correction codes
blocks = makeblocks(encoded, nb1, nc1, nb2, nc2)
blocks = makeblocks(totalbits, nb1, nc1, nb2, nc2)
ecblocks = getecblock.(blocks, necwords)

# Interleave code blocks
Expand Down
5 changes: 4 additions & 1 deletion src/export.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Generate and export QR codes

"""supported extensions"""
const supportexts = ["png", "jpg", "jpeg", "gif"]

"""
qrcode( message::AbstractString
; eclevel::ErrCorrLevel = Medium()
Expand Down Expand Up @@ -104,7 +107,7 @@ function exportbitmat( matrix::BitMatrix
; targetsize::Int=0
, pixels::Int=160)
# check whether the image format is supported
supportexts = ["png", "jpg", "jpeg", "gif"]
# supportexts = ["png", "jpg", "jpeg", "gif"] read from the predifined const
path = _checkpath(path, supportexts)

# resize the matrix
Expand Down
12 changes: 6 additions & 6 deletions src/polynomial.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ Ambiguity -- there are two kind of Reed-Solomon codes:
- Reed Solomon's original view: The codeword as a sequence of values
- The BCH view: The codeword as a sequence of coefficients
QR code standard uses the second one.
QR code standard uses the last one.
Notations:
1. `m/necwords` is the number of error correction words.
2. `n/msglen` is the length of the message.
3. `m+n/msglen + necwords` is the length of the codeword.
1. `m/necwords` is the number of error correction code words.
2. `n/msglen` is the length of the message code words.
3. `m+n/msglen + necwords` is the length of the received codeword.
Let f(x) = a_0x_0 +...+ a_mx_m be a message polynomial,
and g(x) = b_0x_0 +...+ b_nx_n be the generator polynomial,
Expand All @@ -24,12 +24,12 @@ For computational reasons, we use an ascending order in `Poly`,
i.e. use `Poly([f_0, ..., f_n])` to represents `f(x)`.
The Generator matrix of a linear code, in particular, of the Reed-Solomon
code, is the matrix G such that G⋅f = c.
code, is the matrix G such that G⋅f(x) = c(x).
G can be computed by
[encode(x^{m-1}); encode(x^{m-2}); ...; encode(x^0)]
In our notations, the generator matrix is rotate by 180 degrees.
In our notations, the generator matrix is rotated by 180 degrees.
=#

module Polynomial
Expand Down
63 changes: 44 additions & 19 deletions src/styles/plotimage.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ function imageinqrcode( code::QRCode
) where T <: Union{Bool, Nothing}
imageinqrcode!(copy(code), img; rate=rate, singlemask=singlemask, leftop=leftop, fillalignment=fillalignment)
end
imageinqrcode(code::QRCode; args...) = img -> imageinqrcode(code, img; args...)

function imageinqrcode!( code::QRCode
, img::AbstractMatrix{T}
; rate::Real=1
Expand Down Expand Up @@ -101,7 +103,7 @@ function imageinqrcode!( code::QRCode
centers = validalignment(version, setimgI)
for c in centers
inds = filter((setimgI), Ref(c) .+ (-rad:rad))
bestmat[inds] = canvas[inds]
bestmat[inds] = @view(canvas[inds])
end
end
return addborder(bestmat, border)
Expand Down Expand Up @@ -135,29 +137,13 @@ function getfreeinfo( msg::AbstractString
, mode::Mode
, eclevel::ErrCorrLevel
, version::Int)
msgbyte = countmsgbyte(msg, mode, eclevel, version)
# number of blocks
_, nb1, nc1, nb2, nc2 = getecinfo(version, eclevel)

# length of non-padbits
modelen = 4 # length of mode indicator
i = (version 1) + (version 10) + (version 27)
cclen = charactercountlength[mode][i] # length of character count bits
datalen = length(encodedata(msg, mode)) # length of data bits
nonpadbits = modelen + cclen + datalen
# pad 0 to make the length of bits a multiple of 8
mod8 = nonpadbits & 7
if mod8 != 0
nonpadbits += 8 - mod8
end
# length of required bits
requiredlen = 8 * (nb1 * nc1 + nb2 * nc2)
nonpadbits requiredlen && return nb1 + nb2 - 1, 0, 0 # no free/partial-free block

# number of bytes of *real* message bits
msgbyte = nonpadbits >> 3
grp1 = nb1 * nc1 # number of bytes of group 1
if msgbyte grp1
# number of blocks of the message
# number of blocks of the message
nmsgblock = ceil(Int, msgbyte / nc1)
nfreeblock = nb2 + nb1 - nmsgblock
nfreebyte = nc1 * nmsgblock - msgbyte
Expand All @@ -173,6 +159,45 @@ function getfreeinfo( msg::AbstractString
end
getfreeinfo(code::QRCode) = getfreeinfo(code.message, code.mode, code.eclevel, code.version)

"""
getrequiredlen(eclevel::ErrCorrLevel, version::Int)
Return the number of required bits.
"""
function getrequiredlen(eclevel::ErrCorrLevel, version::Int)
_, nb1, nc1, nb2, nc2 = getecinfo(version, eclevel)
return 8 * (nb1 * nc1 + nb2 * nc2)
end
getrequiredlen(code::QRCode) = getrequiredlen(code.eclevel, code.version)

"""
countmsgbyte( msg::AbstractString
, mode::Mode
, eclevel::ErrCorrLevel
, version::Int)
Return the number of pure message bytes.
"""
function countmsgbyte( msg::AbstractString
, mode::Mode
, eclevel::ErrCorrLevel
, version::Int)
modelen = 4 # length of mode indicator
i = (version 1) + (version 10) + (version 27)
cclen = charactercountlength[mode][i] # length of character count bits
datalen = length(encodedata(msg, mode)) # length of data bits
numofmsgbits = modelen + cclen + datalen
# Add up to 4 zeros to terminate the message
requiredlength = getrequiredlen(eclevel, version)
numofmsgbits += min(4, requiredlength - numofmsgbits)
# Add zeros to make the length a multiple of 8
if numofmsgbits & 7 != 0
numofmsgbits += 8 - numofmsgbits & 7
end
return numofmsgbits >> 3
end
countmsgbyte(code::QRCode) = countmsgbyte(code.message, code.mode, code.eclevel, code.version)

"""
getimagescore(mat::AbstractMatrix{Bool}, img::AbstractMatrix{<:Bool})
Expand Down
2 changes: 2 additions & 0 deletions src/styles/unicodeplot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Uses UnicodePlots.jl to draw the QR code of `message`.
function unicodeplot(message::AbstractString; border=:none)
unicodeplot(qrcode(message;eclevel=Low(), width=2); border=border)
end
unicodeplot(code::QRCode) = unicodeplot(qrcode(code))

## 1.2 Idea by @notinaboat
const pixelchars = ['', '', '', ' ']
Expand All @@ -47,6 +48,7 @@ function unicodeplotbychar(mat::AbstractMatrix)
isodd(m) || return txt
return @views txt * '\n' * join(pixelchar.(mat[m, :]))
end
unicodeplotbychar(code::QRCode)=unicodeplotbychar(qrcode(code))

"""
unicodeplotbychar(message::AbstractString)
Expand Down
31 changes: 31 additions & 0 deletions test/tst_locate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,37 @@ end
end
end

@testset "count pure message byte" begin
# pure message bits
## encodemessage
msg, mode, eclevel, version = "123", Numeric(), Medium(), 1
modeindicator = modeindicators[mode]
msglen = mode != UTF8() ? length(msg) : utf8len(msg) ## utf-8 has flexialbe length
ccindicator = getcharactercountindicator(msglen, version, mode)
encodedbits = encodedata(msg, mode)
catbits = vcat(modeindicator, ccindicator, encodedbits)
necwords, nb1, nc1, nb2, nc2 = ecblockinfo[eclevel][version, :]
requiredlen = 8 * (nb1 * nc1 + nb2 * nc2)
## padencodedmessage
totalbits = vcat(catbits, falses(min(4, requiredlen - length(catbits))))
if length(totalbits) & 7 != 0
totalbits = vcat(totalbits, falses(8 - length(totalbits) & 7))
end
# pad bits
pattern = BitArray{1}([1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1])
pad = repeat(pattern, ceil(Int, requiredlen - length(totalbits) >> 4))
data = vcat(totalbits, @view(pad[1:requiredlen - length(totalbits)]))
# error correction bits
blocks = makeblocks(data, nb1, nc1, nb2, nc2)
ecblocks = getecblock.(blocks, necwords)
msgbits = interleave(blocks, ecblocks, necwords, nb1, nc1, nb2, nc2, version)
# test results
code = QRCode(msg, mode=mode, eclevel=eclevel, version=version, width=0)
@test getrequiredlen(code) == requiredlen == getrequiredlen(eclevel, version)
@test countmsgbyte(code) == length(totalbits) >> 3 == countmsgbyte(msg, mode, eclevel, version)
@test encodemessage(code) == msgbits
end

@testset "general build test" begin
code = QRCode("hello world!", version=7, width=0)
stdmat = qrcode(code)
Expand Down
3 changes: 2 additions & 1 deletion test/tst_qrimage.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ end
qrlen = qrwidth(code) - 2 * code.border # length of QR matrix
# full inside
bitimg = .!(Bool.(round.(reshapewidth(img, fitimgwidth(code)))))
mat = imageinqrcode(code, bitimg, rate=1, singlemask=false, fillalignment=true)
fembed = imageinqrcode(code, rate=1, singlemask=false, fillalignment=true)
mat = fembed(bitimg)
@test getimagescore(mat, bitimg) 100
exportbitmat(mat, "testimages/cam_fullinside.png")
@test true
Expand Down
5 changes: 5 additions & 0 deletions test/tst_style.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
canvas = unicodeplotbychar(msg)
end
@test true

# test for struct type QRCode
code = QRCode("hello world!")
unicodeplot(code)
unicodeplotbychar(code)
end

@testset "locate msg bits" begin
Expand Down

2 comments on commit 88f2c68

@RexWzh
Copy link
Member Author

@RexWzh RexWzh commented on 88f2c68 Dec 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/74557

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v1.4.4 -m "<description of version>" 88f2c681873a31309d6840fdea737e8b804bd3dc
git push origin v1.4.4

Please sign in to comment.