forked from lug/matterbridge
		
	
		
			
				
	
	
		
			310 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			310 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // 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))
 | |
| }
 | 
