qrcode.go raw

   1  package qr
   2  
   3  import (
   4  	"image"
   5  	"image/color"
   6  	"math"
   7  
   8  	"github.com/boombuler/barcode"
   9  	"github.com/boombuler/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  }
 167