mautrix-whatsapp/vendor/github.com/skip2/go-qrcode/symbol.go

310 lines
6.8 KiB
Go
Raw Normal View History

2018-08-26 16:29:51 +02:00
// go-qrcode
// Copyright 2014 Tom Harwood
package qrcode
// symbol is a 2D array of bits representing a QR Code symbol.
//
// A symbol consists of size*size modules, with each module normally drawn as a
// black or white square. The symbol also has a border of quietZoneSize modules.
//
// A (fictional) size=2, quietZoneSize=1 QR Code looks like:
//
// +----+
// | |
// | ab |
// | cd |
// | |
// +----+
//
// For ease of implementation, the functions to set/get bits ignore the border,
// so (0,0)=a, (0,1)=b, (1,0)=c, and (1,1)=d. The entire symbol (including the
// border) is returned by bitmap().
//
type symbol struct {
// Value of module at [y][x]. True is set.
module [][]bool
// True if the module at [y][x] is used (to either true or false).
// Used to identify unused modules.
isUsed [][]bool
// Combined width/height of the symbol and quiet zones.
//
// size = symbolSize + 2*quietZoneSize.
size int
// Width/height of the symbol only.
symbolSize int
// Width/height of a single quiet zone.
quietZoneSize int
}
// newSymbol constructs a symbol of size size*size, with a border of
// quietZoneSize.
func newSymbol(size int, quietZoneSize int) *symbol {
var m symbol
m.module = make([][]bool, size+2*quietZoneSize)
m.isUsed = make([][]bool, size+2*quietZoneSize)
for i := range m.module {
m.module[i] = make([]bool, size+2*quietZoneSize)
m.isUsed[i] = make([]bool, size+2*quietZoneSize)
}
m.size = size + 2*quietZoneSize
m.symbolSize = size
m.quietZoneSize = quietZoneSize
return &m
}
// get returns the module value at (x, y).
func (m *symbol) get(x int, y int) (v bool) {
v = m.module[y+m.quietZoneSize][x+m.quietZoneSize]
return
}
// empty returns true if the module at (x, y) has not been set (to either true
// or false).
func (m *symbol) empty(x int, y int) bool {
return !m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize]
}
// numEmptyModules returns the number of empty modules.
//
// Initially numEmptyModules is symbolSize * symbolSize. After every module has
// been set (to either true or false), the number of empty modules is zero.
func (m *symbol) numEmptyModules() int {
var count int
for y := 0; y < m.symbolSize; y++ {
for x := 0; x < m.symbolSize; x++ {
if !m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize] {
count++
}
}
}
return count
}
// set sets the module at (x, y) to v.
func (m *symbol) set(x int, y int, v bool) {
m.module[y+m.quietZoneSize][x+m.quietZoneSize] = v
m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize] = true
}
// set2dPattern sets a 2D array of modules, starting at (x, y).
func (m *symbol) set2dPattern(x int, y int, v [][]bool) {
for j, row := range v {
for i, value := range row {
m.set(x+i, y+j, value)
}
}
}
// bitmap returns the entire symbol, including the quiet zone.
func (m *symbol) bitmap() [][]bool {
module := make([][]bool, len(m.module))
for i := range m.module {
module[i] = m.module[i][:]
}
return module
}
// string returns a pictorial representation of the symbol, suitable for
// printing in a TTY.
func (m *symbol) string() string {
var result string
for _, row := range m.module {
for _, value := range row {
switch value {
case true:
result += " "
case false:
// Unicode 'FULL BLOCK' (U+2588).
result += "██"
}
}
result += "\n"
}
return result
}
// Constants used to weight penalty calculations. Specified by ISO/IEC
// 18004:2006.
const (
penaltyWeight1 = 3
penaltyWeight2 = 3
penaltyWeight3 = 40
penaltyWeight4 = 10
)
// penaltyScore returns the penalty score of the symbol. The penalty score
// consists of the sum of the four individual penalty types.
func (m *symbol) penaltyScore() int {
return m.penalty1() + m.penalty2() + m.penalty3() + m.penalty4()
}
// penalty1 returns the penalty score for "adjacent modules in row/column with
// same colour".
//
// The numbers of adjacent matching modules and scores are:
// 0-5: score = 0
// 6+ : score = penaltyWeight1 + (numAdjacentModules - 5)
func (m *symbol) penalty1() int {
penalty := 0
for x := 0; x < m.symbolSize; x++ {
lastValue := m.get(x, 0)
count := 1
for y := 1; y < m.symbolSize; y++ {
v := m.get(x, y)
if v != lastValue {
count = 1
lastValue = v
} else {
count++
if count == 6 {
penalty += penaltyWeight1 + 1
} else if count > 6 {
penalty++
}
}
}
}
for y := 0; y < m.symbolSize; y++ {
lastValue := m.get(0, y)
count := 1
for x := 1; x < m.symbolSize; x++ {
v := m.get(x, y)
if v != lastValue {
count = 1
lastValue = v
} else {
count++
if count == 6 {
penalty += penaltyWeight1 + 1
} else if count > 6 {
penalty++
}
}
}
}
return penalty
}
// penalty2 returns the penalty score for "block of modules in the same colour".
//
// m*n: score = penaltyWeight2 * (m-1) * (n-1).
func (m *symbol) penalty2() int {
penalty := 0
for y := 1; y < m.symbolSize; y++ {
for x := 1; x < m.symbolSize; x++ {
topLeft := m.get(x-1, y-1)
above := m.get(x, y-1)
left := m.get(x-1, y)
current := m.get(x, y)
if current == left && current == above && current == topLeft {
penalty++
}
}
}
return penalty * penaltyWeight2
}
// penalty3 returns the penalty score for "1:1:3:1:1 ratio
// (dark:light:dark:light:dark) pattern in row/column, preceded or followed by
// light area 4 modules wide".
//
// Existence of the pattern scores penaltyWeight3.
func (m *symbol) penalty3() int {
penalty := 0
for y := 0; y < m.symbolSize; y++ {
var bitBuffer int16 = 0x00
for x := 0; x < m.symbolSize; x++ {
bitBuffer <<= 1
if v := m.get(x, y); v {
bitBuffer |= 1
}
switch bitBuffer & 0x7ff {
// 0b000 0101 1101 or 0b10111010000
// 0x05d or 0x5d0
case 0x05d, 0x5d0:
penalty += penaltyWeight3
bitBuffer = 0xFF
default:
if x == m.symbolSize-1 && (bitBuffer&0x7f) == 0x5d {
penalty += penaltyWeight3
bitBuffer = 0xFF
}
}
}
}
for x := 0; x < m.symbolSize; x++ {
var bitBuffer int16 = 0x00
for y := 0; y < m.symbolSize; y++ {
bitBuffer <<= 1
if v := m.get(x, y); v {
bitBuffer |= 1
}
switch bitBuffer & 0x7ff {
// 0b000 0101 1101 or 0b10111010000
// 0x05d or 0x5d0
case 0x05d, 0x5d0:
penalty += penaltyWeight3
bitBuffer = 0xFF
default:
if y == m.symbolSize-1 && (bitBuffer&0x7f) == 0x5d {
penalty += penaltyWeight3
bitBuffer = 0xFF
}
}
}
}
return penalty
}
// penalty4 returns the penalty score...
func (m *symbol) penalty4() int {
numModules := m.symbolSize * m.symbolSize
numDarkModules := 0
for x := 0; x < m.symbolSize; x++ {
for y := 0; y < m.symbolSize; y++ {
if v := m.get(x, y); v {
numDarkModules++
}
}
}
numDarkModuleDeviation := numModules/2 - numDarkModules
if numDarkModuleDeviation < 0 {
numDarkModuleDeviation *= -1
}
return penaltyWeight4 * (numDarkModuleDeviation / (numModules / 20))
}