git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/barcode/qr/qrcode.go (about)

     1  package qr
     2  
     3  import (
     4  	"image"
     5  	"image/color"
     6  	"math"
     7  
     8  	"git.sr.ht/~pingoo/stdx/barcode"
     9  	"git.sr.ht/~pingoo/stdx/barcode/utils"
    10  )
    11  
    12  type qrcode struct {
    13  	dimension int
    14  	data      *utils.BitList
    15  	content   string
    16  }
    17  
    18  func (qr *qrcode) Content() string {
    19  	return qr.content
    20  }
    21  
    22  func (qr *qrcode) Metadata() barcode.Metadata {
    23  	return barcode.Metadata{barcode.TypeQR, 2}
    24  }
    25  
    26  func (qr *qrcode) ColorModel() color.Model {
    27  	return color.Gray16Model
    28  }
    29  
    30  func (qr *qrcode) Bounds() image.Rectangle {
    31  	return image.Rect(0, 0, qr.dimension, qr.dimension)
    32  }
    33  
    34  func (qr *qrcode) At(x, y int) color.Color {
    35  	if qr.Get(x, y) {
    36  		return color.Black
    37  	}
    38  	return color.White
    39  }
    40  
    41  func (qr *qrcode) Get(x, y int) bool {
    42  	return qr.data.GetBit(x*qr.dimension + y)
    43  }
    44  
    45  func (qr *qrcode) Set(x, y int, val bool) {
    46  	qr.data.SetBit(x*qr.dimension+y, val)
    47  }
    48  
    49  func (qr *qrcode) calcPenalty() uint {
    50  	return qr.calcPenaltyRule1() + qr.calcPenaltyRule2() + qr.calcPenaltyRule3() + qr.calcPenaltyRule4()
    51  }
    52  
    53  func (qr *qrcode) calcPenaltyRule1() uint {
    54  	var result uint
    55  	for x := 0; x < qr.dimension; x++ {
    56  		checkForX := false
    57  		var cntX uint
    58  		checkForY := false
    59  		var cntY uint
    60  
    61  		for y := 0; y < qr.dimension; y++ {
    62  			if qr.Get(x, y) == checkForX {
    63  				cntX++
    64  			} else {
    65  				checkForX = !checkForX
    66  				if cntX >= 5 {
    67  					result += cntX - 2
    68  				}
    69  				cntX = 1
    70  			}
    71  
    72  			if qr.Get(y, x) == checkForY {
    73  				cntY++
    74  			} else {
    75  				checkForY = !checkForY
    76  				if cntY >= 5 {
    77  					result += cntY - 2
    78  				}
    79  				cntY = 1
    80  			}
    81  		}
    82  
    83  		if cntX >= 5 {
    84  			result += cntX - 2
    85  		}
    86  		if cntY >= 5 {
    87  			result += cntY - 2
    88  		}
    89  	}
    90  
    91  	return result
    92  }
    93  
    94  func (qr *qrcode) calcPenaltyRule2() uint {
    95  	var result uint
    96  	for x := 0; x < qr.dimension-1; x++ {
    97  		for y := 0; y < qr.dimension-1; y++ {
    98  			check := qr.Get(x, y)
    99  			if qr.Get(x, y+1) == check && qr.Get(x+1, y) == check && qr.Get(x+1, y+1) == check {
   100  				result += 3
   101  			}
   102  		}
   103  	}
   104  	return result
   105  }
   106  
   107  func (qr *qrcode) calcPenaltyRule3() uint {
   108  	pattern1 := []bool{true, false, true, true, true, false, true, false, false, false, false}
   109  	pattern2 := []bool{false, false, false, false, true, false, true, true, true, false, true}
   110  
   111  	var result uint
   112  	for x := 0; x <= qr.dimension-len(pattern1); x++ {
   113  		for y := 0; y < qr.dimension; y++ {
   114  			pattern1XFound := true
   115  			pattern2XFound := true
   116  			pattern1YFound := true
   117  			pattern2YFound := true
   118  
   119  			for i := 0; i < len(pattern1); i++ {
   120  				iv := qr.Get(x+i, y)
   121  				if iv != pattern1[i] {
   122  					pattern1XFound = false
   123  				}
   124  				if iv != pattern2[i] {
   125  					pattern2XFound = false
   126  				}
   127  				iv = qr.Get(y, x+i)
   128  				if iv != pattern1[i] {
   129  					pattern1YFound = false
   130  				}
   131  				if iv != pattern2[i] {
   132  					pattern2YFound = false
   133  				}
   134  			}
   135  			if pattern1XFound || pattern2XFound {
   136  				result += 40
   137  			}
   138  			if pattern1YFound || pattern2YFound {
   139  				result += 40
   140  			}
   141  		}
   142  	}
   143  
   144  	return result
   145  }
   146  
   147  func (qr *qrcode) calcPenaltyRule4() uint {
   148  	totalNum := qr.data.Len()
   149  	trueCnt := 0
   150  	for i := 0; i < totalNum; i++ {
   151  		if qr.data.GetBit(i) {
   152  			trueCnt++
   153  		}
   154  	}
   155  	percDark := float64(trueCnt) * 100 / float64(totalNum)
   156  	floor := math.Abs(math.Floor(percDark/5) - 10)
   157  	ceil := math.Abs(math.Ceil(percDark/5) - 10)
   158  	return uint(math.Min(floor, ceil) * 10)
   159  }
   160  
   161  func newBarcode(dim int) *qrcode {
   162  	res := new(qrcode)
   163  	res.dimension = dim
   164  	res.data = utils.NewBitList(dim * dim)
   165  	return res
   166  }