393 lines
8.7 KiB
Go
393 lines
8.7 KiB
Go
package gozxing
|
|
|
|
import (
|
|
"math/bits"
|
|
"strings"
|
|
|
|
errors "golang.org/x/xerrors"
|
|
)
|
|
|
|
type BitMatrix struct {
|
|
width int
|
|
height int
|
|
rowSize int
|
|
bits []uint32
|
|
}
|
|
|
|
func NewSquareBitMatrix(dimension int) (*BitMatrix, error) {
|
|
return NewBitMatrix(dimension, dimension)
|
|
}
|
|
|
|
func NewBitMatrix(width, height int) (*BitMatrix, error) {
|
|
if width < 1 || height < 1 {
|
|
return nil, errors.New("IllegalArgumentException: Both dimensions must be greater than 0")
|
|
}
|
|
rowSize := (width + 31) / 32
|
|
bits := make([]uint32, rowSize*height)
|
|
return &BitMatrix{width, height, rowSize, bits}, nil
|
|
}
|
|
|
|
func ParseBoolMapToBitMatrix(image [][]bool) (*BitMatrix, error) {
|
|
var width, height int
|
|
height = len(image)
|
|
if height > 0 {
|
|
width = len(image[0])
|
|
}
|
|
bits, e := NewBitMatrix(width, height)
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
for i := 0; i < height; i++ {
|
|
imageI := image[i]
|
|
for j := 0; j < width; j++ {
|
|
if imageI[j] {
|
|
bits.Set(j, i)
|
|
}
|
|
}
|
|
}
|
|
return bits, nil
|
|
}
|
|
|
|
func ParseStringToBitMatrix(stringRepresentation, setString, unsetString string) (*BitMatrix, error) {
|
|
if stringRepresentation == "" {
|
|
return nil, errors.New("IllegalArgumentException")
|
|
}
|
|
|
|
bits := make([]bool, len(stringRepresentation))
|
|
bitsPos := 0
|
|
rowStartPos := 0
|
|
rowLength := -1
|
|
nRows := 0
|
|
pos := 0
|
|
for pos < len(stringRepresentation) {
|
|
if c := stringRepresentation[pos]; c == '\n' || c == '\r' {
|
|
if bitsPos > rowStartPos {
|
|
if rowLength == -1 {
|
|
rowLength = bitsPos - rowStartPos
|
|
} else if bitsPos-rowStartPos != rowLength {
|
|
return nil, errors.New("IllegalArgumentException: row length do not match")
|
|
}
|
|
rowStartPos = bitsPos
|
|
nRows++
|
|
}
|
|
pos++
|
|
} else if strings.HasPrefix(stringRepresentation[pos:], setString) {
|
|
pos += len(setString)
|
|
bits[bitsPos] = true
|
|
bitsPos++
|
|
} else if strings.HasPrefix(stringRepresentation[pos:], unsetString) {
|
|
pos += len(unsetString)
|
|
bits[bitsPos] = false
|
|
bitsPos++
|
|
} else {
|
|
return nil, errors.New(
|
|
"IllegalArgumentException: illegal character encountered: " + stringRepresentation[pos:])
|
|
}
|
|
}
|
|
|
|
if bitsPos > rowStartPos {
|
|
if rowLength == -1 {
|
|
rowLength = bitsPos - rowStartPos
|
|
} else if bitsPos-rowStartPos != rowLength {
|
|
return nil, errors.New("IllegalArgumentException: row length do not match")
|
|
}
|
|
nRows++
|
|
}
|
|
matrix, e := NewBitMatrix(rowLength, nRows)
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
for i := 0; i < bitsPos; i++ {
|
|
if bits[i] {
|
|
matrix.Set(i%rowLength, i/rowLength)
|
|
}
|
|
}
|
|
return matrix, nil
|
|
}
|
|
|
|
func (b *BitMatrix) Get(x, y int) bool {
|
|
if x < 0 || x >= b.width || y < 0 || y >= b.height {
|
|
return false
|
|
}
|
|
offset := (y * b.rowSize) + (x / 32)
|
|
return ((b.bits[offset] >> uint(x%32)) & 1) != 0
|
|
}
|
|
|
|
func (b *BitMatrix) Set(x, y int) {
|
|
offset := (y * b.rowSize) + (x / 32)
|
|
b.bits[offset] |= 1 << uint(x%32)
|
|
}
|
|
|
|
func (b *BitMatrix) Unset(x, y int) {
|
|
offset := (y * b.rowSize) + (x / 32)
|
|
b.bits[offset] &= ^(1 << uint(x%32))
|
|
}
|
|
|
|
func (b *BitMatrix) Flip(x, y int) {
|
|
offset := (y * b.rowSize) + (x / 32)
|
|
b.bits[offset] ^= 1 << uint(x%32)
|
|
}
|
|
|
|
func (b *BitMatrix) FlipAll() {
|
|
max := len(b.bits)
|
|
for i := 0; i < max; i++ {
|
|
b.bits[i] = ^b.bits[i]
|
|
}
|
|
}
|
|
|
|
func (b *BitMatrix) Xor(mask *BitMatrix) error {
|
|
if b.width != mask.width || b.height != mask.height || b.rowSize != mask.rowSize {
|
|
return errors.New("IllegalArgumentException: input matrix dimensions do not match")
|
|
}
|
|
for y := 0; y < b.height; y++ {
|
|
bOffset := y * b.rowSize
|
|
mOffset := y * mask.rowSize
|
|
for x := 0; x < b.rowSize; x++ {
|
|
b.bits[bOffset+x] ^= mask.bits[mOffset+x]
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (b *BitMatrix) Clear() {
|
|
max := len(b.bits)
|
|
for i := 0; i < max; i++ {
|
|
b.bits[i] = 0
|
|
}
|
|
}
|
|
|
|
func (b *BitMatrix) SetRegion(left, top, width, height int) error {
|
|
if top < 0 || left < 0 {
|
|
return errors.New("IllegalArgumentException: Left and top must be nonnegative")
|
|
}
|
|
if height < 1 || width < 1 {
|
|
return errors.New("IllegalArgumentException: Height and width must be at least 1")
|
|
}
|
|
right := left + width
|
|
bottom := top + height
|
|
if bottom > b.height || right > b.width {
|
|
return errors.New("IllegalArgumentException: The region must fit inside the matrix")
|
|
}
|
|
for y := top; y < bottom; y++ {
|
|
offset := y * b.rowSize
|
|
for x := left; x < right; x++ {
|
|
b.bits[offset+(x/32)] |= 1 << uint(x%32)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (b *BitMatrix) GetRow(y int, row *BitArray) *BitArray {
|
|
if row == nil || row.GetSize() < b.width {
|
|
row = NewBitArray(b.width)
|
|
} else {
|
|
row.Clear()
|
|
}
|
|
offset := y * b.rowSize
|
|
for x := 0; x < b.rowSize; x++ {
|
|
row.SetBulk(x*32, b.bits[offset+x])
|
|
}
|
|
return row
|
|
}
|
|
|
|
func (b *BitMatrix) SetRow(y int, row *BitArray) {
|
|
offset := y * b.rowSize
|
|
copy(b.bits[offset:offset+b.rowSize], row.bits)
|
|
}
|
|
|
|
func (b *BitMatrix) Rotate180() {
|
|
height := b.height
|
|
rowSize := b.rowSize
|
|
for i := 0; i < height/2; i++ {
|
|
topOffset := i * rowSize
|
|
bottomOffset := (height-i)*rowSize - 1
|
|
for j := 0; j < rowSize; j++ {
|
|
top := topOffset + j
|
|
bottom := bottomOffset - j
|
|
b.bits[top], b.bits[bottom] = b.bits[bottom], b.bits[top]
|
|
}
|
|
}
|
|
if height%2 != 0 {
|
|
offset := rowSize * (height - 1) / 2
|
|
for j := 0; j < rowSize/2; j++ {
|
|
left := offset + j
|
|
right := offset + rowSize - 1 - j
|
|
b.bits[left], b.bits[right] = b.bits[right], b.bits[left]
|
|
}
|
|
}
|
|
|
|
if shift := uint(b.width % 32); shift != 0 {
|
|
for i := 0; i < height; i++ {
|
|
offset := rowSize * i
|
|
b.bits[offset] = bits.Reverse32(b.bits[offset]) >> uint(32-shift)
|
|
for j := 1; j < rowSize; j++ {
|
|
curbits := bits.Reverse32(b.bits[offset+j])
|
|
b.bits[offset+j-1] |= curbits << shift
|
|
b.bits[offset+j] = curbits >> uint(32-shift)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (b *BitMatrix) Rotate90() {
|
|
newWidth := b.height
|
|
newHeight := b.width
|
|
newRowSize := (newWidth + 31) / 32
|
|
newBits := make([]uint32, newRowSize*newHeight)
|
|
|
|
for y := 0; y < b.height; y++ {
|
|
for x := 0; x < b.width; x++ {
|
|
offset := y*b.rowSize + (x / 32)
|
|
if ((b.bits[offset] >> (x & 0x1f)) & 1) != 0 {
|
|
newOffset := (newHeight-1-x)*newRowSize + (y / 32)
|
|
newBits[newOffset] |= 1 << (y & 0x1f)
|
|
}
|
|
}
|
|
}
|
|
b.width = newWidth
|
|
b.height = newHeight
|
|
b.rowSize = newRowSize
|
|
b.bits = newBits
|
|
}
|
|
|
|
func (b *BitMatrix) GetEnclosingRectangle() []int {
|
|
left := b.width
|
|
top := b.height
|
|
right := -1
|
|
bottom := -1
|
|
|
|
for y := 0; y < b.height; y++ {
|
|
for x32 := 0; x32 < b.rowSize; x32++ {
|
|
theBits := b.bits[y*b.rowSize+x32]
|
|
if theBits != 0 {
|
|
if y < top {
|
|
top = y
|
|
}
|
|
if y > bottom {
|
|
bottom = y
|
|
}
|
|
if x32*32 < left {
|
|
bit := 0
|
|
for (theBits << uint(31-bit)) == 0 {
|
|
bit++
|
|
}
|
|
if (x32*32 + bit) < left {
|
|
left = x32*32 + bit
|
|
}
|
|
}
|
|
if x32*32+31 > right {
|
|
bit := 31
|
|
for (theBits >> uint(bit)) == 0 {
|
|
bit--
|
|
}
|
|
if (x32*32 + bit) > right {
|
|
right = x32*32 + bit
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if right < left || bottom < top {
|
|
return nil
|
|
}
|
|
|
|
return []int{left, top, right - left + 1, bottom - top + 1}
|
|
}
|
|
|
|
func (b *BitMatrix) GetTopLeftOnBit() []int {
|
|
bitsOffset := 0
|
|
for bitsOffset < len(b.bits) && b.bits[bitsOffset] == 0 {
|
|
bitsOffset++
|
|
}
|
|
if bitsOffset == len(b.bits) {
|
|
return nil
|
|
}
|
|
y := bitsOffset / b.rowSize
|
|
x := (bitsOffset % b.rowSize) * 32
|
|
|
|
theBits := b.bits[bitsOffset]
|
|
bit := uint(0)
|
|
for (theBits << (31 - bit)) == 0 {
|
|
bit++
|
|
}
|
|
x += int(bit)
|
|
return []int{x, y}
|
|
}
|
|
|
|
func (b *BitMatrix) GetBottomRightOnBit() []int {
|
|
bitsOffset := len(b.bits) - 1
|
|
for bitsOffset >= 0 && b.bits[bitsOffset] == 0 {
|
|
bitsOffset--
|
|
}
|
|
if bitsOffset < 0 {
|
|
return nil
|
|
}
|
|
|
|
y := bitsOffset / b.rowSize
|
|
x := (bitsOffset % b.rowSize) * 32
|
|
|
|
theBits := b.bits[bitsOffset]
|
|
bit := uint(31)
|
|
for (theBits >> bit) == 0 {
|
|
bit--
|
|
}
|
|
x += int(bit)
|
|
|
|
return []int{x, y}
|
|
}
|
|
|
|
func (b *BitMatrix) GetWidth() int {
|
|
return b.width
|
|
}
|
|
|
|
func (b *BitMatrix) GetHeight() int {
|
|
return b.height
|
|
}
|
|
|
|
func (b *BitMatrix) GetRowSize() int {
|
|
return b.rowSize
|
|
}
|
|
|
|
// public boolean equals(Object o)
|
|
// public int hashCode()
|
|
|
|
func (b *BitMatrix) String() string {
|
|
return b.ToString("X ", " ")
|
|
}
|
|
|
|
func (b *BitMatrix) ToString(setString, unsetString string) string {
|
|
return b.ToStringWithLineSeparator(setString, unsetString, "\n")
|
|
}
|
|
|
|
func (b *BitMatrix) ToStringWithLineSeparator(setString, unsetString, lineSeparator string) string {
|
|
setBytes := []byte(setString)
|
|
unsetBytes := []byte(unsetString)
|
|
lineSepBytes := []byte(lineSeparator)
|
|
|
|
lineSize := len(lineSeparator)
|
|
if len(setString) > len(unsetString) {
|
|
lineSize += b.width * len(setString)
|
|
} else {
|
|
lineSize += b.width * len(unsetString)
|
|
}
|
|
result := make([]byte, 0, b.height*lineSize)
|
|
|
|
for y := 0; y < b.height; y++ {
|
|
for x := 0; x < b.width; x++ {
|
|
var s []byte
|
|
if b.Get(x, y) {
|
|
s = setBytes
|
|
} else {
|
|
s = unsetBytes
|
|
}
|
|
result = append(result, s...)
|
|
}
|
|
result = append(result, lineSepBytes...)
|
|
}
|
|
return string(result)
|
|
}
|
|
|
|
// public BitMatrix clone()
|