Major improvements to UnifiedPlayer: 1. GetFrameImage() now works when paused for responsive UI updates 2. Play() method properly starts FFmpeg process 3. Frame display loop runs continuously for smooth video display 4. Disabled audio temporarily to fix video playback fundamentals 5. Simplified FFmpeg command to focus on video stream only Player now: - Generates video frames correctly - Shows video when paused - Has responsive progress tracking - Starts playback properly Next steps: Re-enable audio playback once video is stable
316 lines
6.2 KiB
Go
316 lines
6.2 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, includeQuietZone bool) (*symbol, error) {
|
|
|
|
quietZoneSize := 0
|
|
if includeQuietZone {
|
|
quietZoneSize = version.quietZoneSize()
|
|
}
|
|
|
|
m := ®ularSymbol{
|
|
version: version,
|
|
mask: mask,
|
|
data: data,
|
|
|
|
symbol: newSymbol(version.symbolSize(), 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
|
|
}
|