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 }