forked from lug/matterbridge
		
	
		
			
				
	
	
		
			310 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			310 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // go-qrcode
 | |
| // Copyright 2014 Tom Harwood
 | |
| 
 | |
| package qrcode
 | |
| 
 | |
| import (
 | |
| 	bitset "github.com/skip2/go-qrcode/bitset"
 | |
| )
 | |
| 
 | |
| type regularSymbol struct {
 | |
| 	version qrCodeVersion
 | |
| 	mask    int
 | |
| 
 | |
| 	data *bitset.Bitset
 | |
| 
 | |
| 	symbol *symbol
 | |
| 	size   int
 | |
| }
 | |
| 
 | |
| // Abbreviated true/false.
 | |
| const (
 | |
| 	b0 = false
 | |
| 	b1 = true
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	alignmentPatternCenter = [][]int{
 | |
| 		{}, // Version 0 doesn't exist.
 | |
| 		{}, // Version 1 doesn't use alignment patterns.
 | |
| 		{6, 18},
 | |
| 		{6, 22},
 | |
| 		{6, 26},
 | |
| 		{6, 30},
 | |
| 		{6, 34},
 | |
| 		{6, 22, 38},
 | |
| 		{6, 24, 42},
 | |
| 		{6, 26, 46},
 | |
| 		{6, 28, 50},
 | |
| 		{6, 30, 54},
 | |
| 		{6, 32, 58},
 | |
| 		{6, 34, 62},
 | |
| 		{6, 26, 46, 66},
 | |
| 		{6, 26, 48, 70},
 | |
| 		{6, 26, 50, 74},
 | |
| 		{6, 30, 54, 78},
 | |
| 		{6, 30, 56, 82},
 | |
| 		{6, 30, 58, 86},
 | |
| 		{6, 34, 62, 90},
 | |
| 		{6, 28, 50, 72, 94},
 | |
| 		{6, 26, 50, 74, 98},
 | |
| 		{6, 30, 54, 78, 102},
 | |
| 		{6, 28, 54, 80, 106},
 | |
| 		{6, 32, 58, 84, 110},
 | |
| 		{6, 30, 58, 86, 114},
 | |
| 		{6, 34, 62, 90, 118},
 | |
| 		{6, 26, 50, 74, 98, 122},
 | |
| 		{6, 30, 54, 78, 102, 126},
 | |
| 		{6, 26, 52, 78, 104, 130},
 | |
| 		{6, 30, 56, 82, 108, 134},
 | |
| 		{6, 34, 60, 86, 112, 138},
 | |
| 		{6, 30, 58, 86, 114, 142},
 | |
| 		{6, 34, 62, 90, 118, 146},
 | |
| 		{6, 30, 54, 78, 102, 126, 150},
 | |
| 		{6, 24, 50, 76, 102, 128, 154},
 | |
| 		{6, 28, 54, 80, 106, 132, 158},
 | |
| 		{6, 32, 58, 84, 110, 136, 162},
 | |
| 		{6, 26, 54, 82, 110, 138, 166},
 | |
| 		{6, 30, 58, 86, 114, 142, 170},
 | |
| 	}
 | |
| 
 | |
| 	finderPattern = [][]bool{
 | |
| 		{b1, b1, b1, b1, b1, b1, b1},
 | |
| 		{b1, b0, b0, b0, b0, b0, b1},
 | |
| 		{b1, b0, b1, b1, b1, b0, b1},
 | |
| 		{b1, b0, b1, b1, b1, b0, b1},
 | |
| 		{b1, b0, b1, b1, b1, b0, b1},
 | |
| 		{b1, b0, b0, b0, b0, b0, b1},
 | |
| 		{b1, b1, b1, b1, b1, b1, b1},
 | |
| 	}
 | |
| 
 | |
| 	finderPatternSize = 7
 | |
| 
 | |
| 	finderPatternHorizontalBorder = [][]bool{
 | |
| 		{b0, b0, b0, b0, b0, b0, b0, b0},
 | |
| 	}
 | |
| 
 | |
| 	finderPatternVerticalBorder = [][]bool{
 | |
| 		{b0},
 | |
| 		{b0},
 | |
| 		{b0},
 | |
| 		{b0},
 | |
| 		{b0},
 | |
| 		{b0},
 | |
| 		{b0},
 | |
| 		{b0},
 | |
| 	}
 | |
| 
 | |
| 	alignmentPattern = [][]bool{
 | |
| 		{b1, b1, b1, b1, b1},
 | |
| 		{b1, b0, b0, b0, b1},
 | |
| 		{b1, b0, b1, b0, b1},
 | |
| 		{b1, b0, b0, b0, b1},
 | |
| 		{b1, b1, b1, b1, b1},
 | |
| 	}
 | |
| )
 | |
| 
 | |
| func buildRegularSymbol(version qrCodeVersion, mask int,
 | |
| 	data *bitset.Bitset) (*symbol, error) {
 | |
| 	m := ®ularSymbol{
 | |
| 		version: version,
 | |
| 		mask:    mask,
 | |
| 		data:    data,
 | |
| 
 | |
| 		symbol: newSymbol(version.symbolSize(), version.quietZoneSize()),
 | |
| 		size:   version.symbolSize(),
 | |
| 	}
 | |
| 
 | |
| 	m.addFinderPatterns()
 | |
| 	m.addAlignmentPatterns()
 | |
| 	m.addTimingPatterns()
 | |
| 	m.addFormatInfo()
 | |
| 	m.addVersionInfo()
 | |
| 
 | |
| 	ok, err := m.addData()
 | |
| 	if !ok {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return m.symbol, nil
 | |
| }
 | |
| 
 | |
| func (m *regularSymbol) addFinderPatterns() {
 | |
| 	fpSize := finderPatternSize
 | |
| 	fp := finderPattern
 | |
| 	fpHBorder := finderPatternHorizontalBorder
 | |
| 	fpVBorder := finderPatternVerticalBorder
 | |
| 
 | |
| 	// Top left Finder Pattern.
 | |
| 	m.symbol.set2dPattern(0, 0, fp)
 | |
| 	m.symbol.set2dPattern(0, fpSize, fpHBorder)
 | |
| 	m.symbol.set2dPattern(fpSize, 0, fpVBorder)
 | |
| 
 | |
| 	// Top right Finder Pattern.
 | |
| 	m.symbol.set2dPattern(m.size-fpSize, 0, fp)
 | |
| 	m.symbol.set2dPattern(m.size-fpSize-1, fpSize, fpHBorder)
 | |
| 	m.symbol.set2dPattern(m.size-fpSize-1, 0, fpVBorder)
 | |
| 
 | |
| 	// Bottom left Finder Pattern.
 | |
| 	m.symbol.set2dPattern(0, m.size-fpSize, fp)
 | |
| 	m.symbol.set2dPattern(0, m.size-fpSize-1, fpHBorder)
 | |
| 	m.symbol.set2dPattern(fpSize, m.size-fpSize-1, fpVBorder)
 | |
| }
 | |
| 
 | |
| func (m *regularSymbol) addAlignmentPatterns() {
 | |
| 	for _, x := range alignmentPatternCenter[m.version.version] {
 | |
| 		for _, y := range alignmentPatternCenter[m.version.version] {
 | |
| 			if !m.symbol.empty(x, y) {
 | |
| 				continue
 | |
| 			}
 | |
| 
 | |
| 			m.symbol.set2dPattern(x-2, y-2, alignmentPattern)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (m *regularSymbol) addTimingPatterns() {
 | |
| 	value := true
 | |
| 
 | |
| 	for i := finderPatternSize + 1; i < m.size-finderPatternSize; i++ {
 | |
| 		m.symbol.set(i, finderPatternSize-1, value)
 | |
| 		m.symbol.set(finderPatternSize-1, i, value)
 | |
| 
 | |
| 		value = !value
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (m *regularSymbol) addFormatInfo() {
 | |
| 	fpSize := finderPatternSize
 | |
| 	l := formatInfoLengthBits - 1
 | |
| 
 | |
| 	f := m.version.formatInfo(m.mask)
 | |
| 
 | |
| 	// Bits 0-7, under the top right finder pattern.
 | |
| 	for i := 0; i <= 7; i++ {
 | |
| 		m.symbol.set(m.size-i-1, fpSize+1, f.At(l-i))
 | |
| 	}
 | |
| 
 | |
| 	// Bits 0-5, right of the top left finder pattern.
 | |
| 	for i := 0; i <= 5; i++ {
 | |
| 		m.symbol.set(fpSize+1, i, f.At(l-i))
 | |
| 	}
 | |
| 
 | |
| 	// Bits 6-8 on the corner of the top left finder pattern.
 | |
| 	m.symbol.set(fpSize+1, fpSize, f.At(l-6))
 | |
| 	m.symbol.set(fpSize+1, fpSize+1, f.At(l-7))
 | |
| 	m.symbol.set(fpSize, fpSize+1, f.At(l-8))
 | |
| 
 | |
| 	// Bits 9-14 on the underside of the top left finder pattern.
 | |
| 	for i := 9; i <= 14; i++ {
 | |
| 		m.symbol.set(14-i, fpSize+1, f.At(l-i))
 | |
| 	}
 | |
| 
 | |
| 	// Bits 8-14 on the right side of the bottom left finder pattern.
 | |
| 	for i := 8; i <= 14; i++ {
 | |
| 		m.symbol.set(fpSize+1, m.size-fpSize+i-8, f.At(l-i))
 | |
| 	}
 | |
| 
 | |
| 	// Always dark symbol.
 | |
| 	m.symbol.set(fpSize+1, m.size-fpSize-1, true)
 | |
| }
 | |
| 
 | |
| func (m *regularSymbol) addVersionInfo() {
 | |
| 	fpSize := finderPatternSize
 | |
| 
 | |
| 	v := m.version.versionInfo()
 | |
| 	l := versionInfoLengthBits - 1
 | |
| 
 | |
| 	if v == nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	for i := 0; i < v.Len(); i++ {
 | |
| 		// Above the bottom left finder pattern.
 | |
| 		m.symbol.set(i/3, m.size-fpSize-4+i%3, v.At(l-i))
 | |
| 
 | |
| 		// Left of the top right finder pattern.
 | |
| 		m.symbol.set(m.size-fpSize-4+i%3, i/3, v.At(l-i))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type direction uint8
 | |
| 
 | |
| const (
 | |
| 	up direction = iota
 | |
| 	down
 | |
| )
 | |
| 
 | |
| func (m *regularSymbol) addData() (bool, error) {
 | |
| 	xOffset := 1
 | |
| 	dir := up
 | |
| 
 | |
| 	x := m.size - 2
 | |
| 	y := m.size - 1
 | |
| 
 | |
| 	for i := 0; i < m.data.Len(); i++ {
 | |
| 		var mask bool
 | |
| 		switch m.mask {
 | |
| 		case 0:
 | |
| 			mask = (y+x+xOffset)%2 == 0
 | |
| 		case 1:
 | |
| 			mask = y%2 == 0
 | |
| 		case 2:
 | |
| 			mask = (x+xOffset)%3 == 0
 | |
| 		case 3:
 | |
| 			mask = (y+x+xOffset)%3 == 0
 | |
| 		case 4:
 | |
| 			mask = (y/2+(x+xOffset)/3)%2 == 0
 | |
| 		case 5:
 | |
| 			mask = (y*(x+xOffset))%2+(y*(x+xOffset))%3 == 0
 | |
| 		case 6:
 | |
| 			mask = ((y*(x+xOffset))%2+((y*(x+xOffset))%3))%2 == 0
 | |
| 		case 7:
 | |
| 			mask = ((y+x+xOffset)%2+((y*(x+xOffset))%3))%2 == 0
 | |
| 		}
 | |
| 
 | |
| 		// != is equivalent to XOR.
 | |
| 		m.symbol.set(x+xOffset, y, mask != m.data.At(i))
 | |
| 
 | |
| 		if i == m.data.Len()-1 {
 | |
| 			break
 | |
| 		}
 | |
| 
 | |
| 		// Find next free bit in the symbol.
 | |
| 		for {
 | |
| 			if xOffset == 1 {
 | |
| 				xOffset = 0
 | |
| 			} else {
 | |
| 				xOffset = 1
 | |
| 
 | |
| 				if dir == up {
 | |
| 					if y > 0 {
 | |
| 						y--
 | |
| 					} else {
 | |
| 						dir = down
 | |
| 						x -= 2
 | |
| 					}
 | |
| 				} else {
 | |
| 					if y < m.size-1 {
 | |
| 						y++
 | |
| 					} else {
 | |
| 						dir = up
 | |
| 						x -= 2
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			// Skip over the vertical timing pattern entirely.
 | |
| 			if x == 5 {
 | |
| 				x--
 | |
| 			}
 | |
| 
 | |
| 			if m.symbol.empty(x+xOffset, y) {
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return true, nil
 | |
| }
 | 
