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

     1  package barcode
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"image"
     7  	"image/color"
     8  	"math"
     9  )
    10  
    11  type wrapFunc func(x, y int) color.Color
    12  
    13  type scaledBarcode struct {
    14  	wrapped     Barcode
    15  	wrapperFunc wrapFunc
    16  	rect        image.Rectangle
    17  }
    18  
    19  type intCSscaledBC struct {
    20  	scaledBarcode
    21  }
    22  
    23  func (bc *scaledBarcode) Content() string {
    24  	return bc.wrapped.Content()
    25  }
    26  
    27  func (bc *scaledBarcode) Metadata() Metadata {
    28  	return bc.wrapped.Metadata()
    29  }
    30  
    31  func (bc *scaledBarcode) ColorModel() color.Model {
    32  	return bc.wrapped.ColorModel()
    33  }
    34  
    35  func (bc *scaledBarcode) Bounds() image.Rectangle {
    36  	return bc.rect
    37  }
    38  
    39  func (bc *scaledBarcode) At(x, y int) color.Color {
    40  	return bc.wrapperFunc(x, y)
    41  }
    42  
    43  func (bc *intCSscaledBC) CheckSum() int {
    44  	if cs, ok := bc.wrapped.(BarcodeIntCS); ok {
    45  		return cs.CheckSum()
    46  	}
    47  	return 0
    48  }
    49  
    50  // Scale returns a resized barcode with the given width and height.
    51  func Scale(bc Barcode, width, height int) (Barcode, error) {
    52  	switch bc.Metadata().Dimensions {
    53  	case 1:
    54  		return scale1DCode(bc, width, height)
    55  	case 2:
    56  		return scale2DCode(bc, width, height)
    57  	}
    58  
    59  	return nil, errors.New("unsupported barcode format")
    60  }
    61  
    62  func newScaledBC(wrapped Barcode, wrapperFunc wrapFunc, rect image.Rectangle) Barcode {
    63  	result := &scaledBarcode{
    64  		wrapped:     wrapped,
    65  		wrapperFunc: wrapperFunc,
    66  		rect:        rect,
    67  	}
    68  
    69  	if _, ok := wrapped.(BarcodeIntCS); ok {
    70  		return &intCSscaledBC{*result}
    71  	}
    72  	return result
    73  }
    74  
    75  func scale2DCode(bc Barcode, width, height int) (Barcode, error) {
    76  	orgBounds := bc.Bounds()
    77  	orgWidth := orgBounds.Max.X - orgBounds.Min.X
    78  	orgHeight := orgBounds.Max.Y - orgBounds.Min.Y
    79  
    80  	factor := int(math.Min(float64(width)/float64(orgWidth), float64(height)/float64(orgHeight)))
    81  	if factor <= 0 {
    82  		return nil, fmt.Errorf("can not scale barcode to an image smaller than %dx%d", orgWidth, orgHeight)
    83  	}
    84  
    85  	offsetX := (width - (orgWidth * factor)) / 2
    86  	offsetY := (height - (orgHeight * factor)) / 2
    87  
    88  	wrap := func(x, y int) color.Color {
    89  		if x < offsetX || y < offsetY {
    90  			return color.White
    91  		}
    92  		x = (x - offsetX) / factor
    93  		y = (y - offsetY) / factor
    94  		if x >= orgWidth || y >= orgHeight {
    95  			return color.White
    96  		}
    97  		return bc.At(x, y)
    98  	}
    99  
   100  	return newScaledBC(
   101  		bc,
   102  		wrap,
   103  		image.Rect(0, 0, width, height),
   104  	), nil
   105  }
   106  
   107  func scale1DCode(bc Barcode, width, height int) (Barcode, error) {
   108  	orgBounds := bc.Bounds()
   109  	orgWidth := orgBounds.Max.X - orgBounds.Min.X
   110  	factor := int(float64(width) / float64(orgWidth))
   111  
   112  	if factor <= 0 {
   113  		return nil, fmt.Errorf("can not scale barcode to an image smaller than %dx1", orgWidth)
   114  	}
   115  	offsetX := (width - (orgWidth * factor)) / 2
   116  
   117  	wrap := func(x, y int) color.Color {
   118  		if x < offsetX {
   119  			return color.White
   120  		}
   121  		x = (x - offsetX) / factor
   122  
   123  		if x >= orgWidth {
   124  			return color.White
   125  		}
   126  		return bc.At(x, 0)
   127  	}
   128  
   129  	return newScaledBC(
   130  		bc,
   131  		wrap,
   132  		image.Rect(0, 0, width, height),
   133  	), nil
   134  }