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
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))
|
|
}
|