git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/imaging/utils.go (about) 1 package imaging 2 3 import ( 4 "image" 5 "math" 6 "runtime" 7 "sync" 8 "sync/atomic" 9 ) 10 11 var maxProcs int64 12 13 // SetMaxProcs limits the number of concurrent processing goroutines to the given value. 14 // A value <= 0 clears the limit. 15 func SetMaxProcs(value int) { 16 atomic.StoreInt64(&maxProcs, int64(value)) 17 } 18 19 // parallel processes the data in separate goroutines. 20 func parallel(start, stop int, fn func(<-chan int)) { 21 count := stop - start 22 if count < 1 { 23 return 24 } 25 26 procs := runtime.GOMAXPROCS(0) 27 limit := int(atomic.LoadInt64(&maxProcs)) 28 if procs > limit && limit > 0 { 29 procs = limit 30 } 31 if procs > count { 32 procs = count 33 } 34 35 c := make(chan int, count) 36 for i := start; i < stop; i++ { 37 c <- i 38 } 39 close(c) 40 41 var wg sync.WaitGroup 42 for i := 0; i < procs; i++ { 43 wg.Add(1) 44 go func() { 45 defer wg.Done() 46 fn(c) 47 }() 48 } 49 wg.Wait() 50 } 51 52 // absint returns the absolute value of i. 53 func absint(i int) int { 54 if i < 0 { 55 return -i 56 } 57 return i 58 } 59 60 // clamp rounds and clamps float64 value to fit into uint8. 61 func clamp(x float64) uint8 { 62 v := int64(x + 0.5) 63 if v > 255 { 64 return 255 65 } 66 if v > 0 { 67 return uint8(v) 68 } 69 return 0 70 } 71 72 func reverse(pix []uint8) { 73 if len(pix) <= 4 { 74 return 75 } 76 i := 0 77 j := len(pix) - 4 78 for i < j { 79 pi := pix[i : i+4 : i+4] 80 pj := pix[j : j+4 : j+4] 81 pi[0], pj[0] = pj[0], pi[0] 82 pi[1], pj[1] = pj[1], pi[1] 83 pi[2], pj[2] = pj[2], pi[2] 84 pi[3], pj[3] = pj[3], pi[3] 85 i += 4 86 j -= 4 87 } 88 } 89 90 func toNRGBA(img image.Image) *image.NRGBA { 91 if img, ok := img.(*image.NRGBA); ok { 92 return &image.NRGBA{ 93 Pix: img.Pix, 94 Stride: img.Stride, 95 Rect: img.Rect.Sub(img.Rect.Min), 96 } 97 } 98 return Clone(img) 99 } 100 101 // rgbToHSL converts a color from RGB to HSL. 102 func rgbToHSL(r, g, b uint8) (float64, float64, float64) { 103 rr := float64(r) / 255 104 gg := float64(g) / 255 105 bb := float64(b) / 255 106 107 max := math.Max(rr, math.Max(gg, bb)) 108 min := math.Min(rr, math.Min(gg, bb)) 109 110 l := (max + min) / 2 111 112 if max == min { 113 return 0, 0, l 114 } 115 116 var h, s float64 117 d := max - min 118 if l > 0.5 { 119 s = d / (2 - max - min) 120 } else { 121 s = d / (max + min) 122 } 123 124 switch max { 125 case rr: 126 h = (gg - bb) / d 127 if g < b { 128 h += 6 129 } 130 case gg: 131 h = (bb-rr)/d + 2 132 case bb: 133 h = (rr-gg)/d + 4 134 } 135 h /= 6 136 137 return h, s, l 138 } 139 140 // hslToRGB converts a color from HSL to RGB. 141 func hslToRGB(h, s, l float64) (uint8, uint8, uint8) { 142 var r, g, b float64 143 if s == 0 { 144 v := clamp(l * 255) 145 return v, v, v 146 } 147 148 var q float64 149 if l < 0.5 { 150 q = l * (1 + s) 151 } else { 152 q = l + s - l*s 153 } 154 p := 2*l - q 155 156 r = hueToRGB(p, q, h+1/3.0) 157 g = hueToRGB(p, q, h) 158 b = hueToRGB(p, q, h-1/3.0) 159 160 return clamp(r * 255), clamp(g * 255), clamp(b * 255) 161 } 162 163 func hueToRGB(p, q, t float64) float64 { 164 if t < 0 { 165 t++ 166 } 167 if t > 1 { 168 t-- 169 } 170 if t < 1/6.0 { 171 return p + (q-p)*6*t 172 } 173 if t < 1/2.0 { 174 return q 175 } 176 if t < 2/3.0 { 177 return p + (q-p)*(2/3.0-t)*6 178 } 179 return p 180 }