git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/imaging/utils_test.go (about) 1 package imaging 2 3 import ( 4 "image" 5 "math" 6 "runtime" 7 "sync/atomic" 8 "testing" 9 ) 10 11 var ( 12 testdataBranchesJPG = mustOpen("testdata/branches.jpg") 13 testdataBranchesPNG = mustOpen("testdata/branches.png") 14 testdataFlowersSmallPNG = mustOpen("testdata/flowers_small.png") 15 ) 16 17 func mustOpen(filename string) image.Image { 18 img, err := Open(filename) 19 if err != nil { 20 panic(err) 21 } 22 return img 23 } 24 25 func TestParallel(t *testing.T) { 26 for _, n := range []int{0, 1, 10, 100, 1000} { 27 for _, p := range []int{1, 2, 4, 8, 16, 100} { 28 if !testParallelN(n, p) { 29 t.Fatalf("test [parallel %d %d] failed", n, p) 30 } 31 } 32 } 33 } 34 35 func testParallelN(n, procs int) bool { 36 data := make([]bool, n) 37 before := runtime.GOMAXPROCS(0) 38 runtime.GOMAXPROCS(procs) 39 parallel(0, n, func(is <-chan int) { 40 for i := range is { 41 data[i] = true 42 } 43 }) 44 runtime.GOMAXPROCS(before) 45 for i := 0; i < n; i++ { 46 if !data[i] { 47 return false 48 } 49 } 50 return true 51 } 52 53 func TestParallelMaxProcs(t *testing.T) { 54 for _, n := range []int{0, 1, 10, 100, 1000} { 55 for _, p := range []int{1, 2, 4, 8, 16, 100} { 56 if !testParallelMaxProcsN(n, p) { 57 t.Fatalf("test [parallel max procs %d %d] failed", n, p) 58 } 59 } 60 } 61 } 62 63 func testParallelMaxProcsN(n, procs int) bool { 64 data := make([]bool, n) 65 SetMaxProcs(procs) 66 parallel(0, n, func(is <-chan int) { 67 for i := range is { 68 data[i] = true 69 } 70 }) 71 SetMaxProcs(0) 72 for i := 0; i < n; i++ { 73 if !data[i] { 74 return false 75 } 76 } 77 return true 78 } 79 80 func TestSetMaxProcs(t *testing.T) { 81 for _, p := range []int{-1, 0, 10} { 82 SetMaxProcs(p) 83 if int(atomic.LoadInt64(&maxProcs)) != p { 84 t.Fatalf("test [set max procs %d] failed", p) 85 } 86 } 87 88 SetMaxProcs(0) 89 } 90 91 func TestClamp(t *testing.T) { 92 testCases := []struct { 93 f float64 94 u uint8 95 }{ 96 {0, 0}, 97 {255, 255}, 98 {128, 128}, 99 {0.49, 0}, 100 {0.50, 1}, 101 {254.9, 255}, 102 {254.0, 254}, 103 {256, 255}, 104 {2500, 255}, 105 {-10, 0}, 106 {127.6, 128}, 107 } 108 109 for _, tc := range testCases { 110 if clamp(tc.f) != tc.u { 111 t.Fatalf("test [clamp %v %v] failed: %v", tc.f, tc.u, clamp(tc.f)) 112 } 113 } 114 } 115 116 func TestReverse(t *testing.T) { 117 testCases := []struct { 118 pix []uint8 119 want []uint8 120 }{ 121 { 122 pix: []uint8{}, 123 want: []uint8{}, 124 }, 125 { 126 pix: []uint8{1, 2, 3, 4}, 127 want: []uint8{1, 2, 3, 4}, 128 }, 129 { 130 pix: []uint8{1, 2, 3, 4, 5, 6, 7, 8}, 131 want: []uint8{5, 6, 7, 8, 1, 2, 3, 4}, 132 }, 133 { 134 pix: []uint8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, 135 want: []uint8{9, 10, 11, 12, 5, 6, 7, 8, 1, 2, 3, 4}, 136 }, 137 } 138 139 for _, tc := range testCases { 140 t.Run("", func(t *testing.T) { 141 reverse(tc.pix) 142 if !compareBytes(tc.pix, tc.want, 0) { 143 t.Fatalf("got pix %v want %v", tc.pix, tc.want) 144 } 145 }) 146 } 147 } 148 149 func compareNRGBA(img1, img2 *image.NRGBA, delta int) bool { 150 if !img1.Rect.Eq(img2.Rect) { 151 return false 152 } 153 return compareBytes(img1.Pix, img2.Pix, delta) 154 } 155 156 func compareBytes(a, b []uint8, delta int) bool { 157 if len(a) != len(b) { 158 return false 159 } 160 for i := 0; i < len(a); i++ { 161 if absint(int(a[i])-int(b[i])) > delta { 162 return false 163 } 164 } 165 return true 166 } 167 168 // compareNRGBAGolden is a special version of compareNRGBA used in golden tests. 169 // All the golden images are generated on amd64 architecture. Due to differences 170 // in floating-point rounding on different architectures, we need to add some 171 // level of tolerance when comparing images on architectures other than amd64. 172 // See https://golang.org/ref/spec#Floating_point_operators for information on 173 // fused multiply and add (FMA) instruction. 174 func compareNRGBAGolden(img1, img2 *image.NRGBA) bool { 175 delta := 0 176 if runtime.GOARCH != "amd64" { 177 delta = 1 178 } 179 return compareNRGBA(img1, img2, delta) 180 } 181 182 func compareFloat64(a, b, delta float64) bool { 183 return math.Abs(a-b) <= delta 184 } 185 186 var rgbHSLTestCases = []struct { 187 r, g, b uint8 188 h, s, l float64 189 }{ 190 { 191 r: 255, 192 g: 0, 193 b: 0, 194 h: 0.000, 195 s: 1.000, 196 l: 0.500, 197 }, 198 { 199 r: 191, 200 g: 191, 201 b: 0, 202 h: 0.167, 203 s: 1.000, 204 l: 0.375, 205 }, 206 { 207 r: 0, 208 g: 128, 209 b: 0, 210 h: 0.333, 211 s: 1.000, 212 l: 0.251, 213 }, 214 { 215 r: 128, 216 g: 255, 217 b: 255, 218 h: 0.500, 219 s: 1.000, 220 l: 0.751, 221 }, 222 { 223 r: 128, 224 g: 128, 225 b: 255, 226 h: 0.667, 227 s: 1.000, 228 l: 0.751, 229 }, 230 { 231 r: 191, 232 g: 64, 233 b: 191, 234 h: 0.833, 235 s: 0.498, 236 l: 0.500, 237 }, 238 { 239 r: 160, 240 g: 164, 241 b: 36, 242 h: 0.172, 243 s: 0.640, 244 l: 0.392, 245 }, 246 { 247 r: 65, 248 g: 27, 249 b: 234, 250 h: 0.697, 251 s: 0.831, 252 l: 0.512, 253 }, 254 { 255 r: 30, 256 g: 172, 257 b: 65, 258 h: 0.374, 259 s: 0.703, 260 l: 0.396, 261 }, 262 { 263 r: 240, 264 g: 200, 265 b: 14, 266 h: 0.137, 267 s: 0.890, 268 l: 0.498, 269 }, 270 { 271 r: 180, 272 g: 48, 273 b: 229, 274 h: 0.788, 275 s: 0.777, 276 l: 0.543, 277 }, 278 { 279 r: 237, 280 g: 119, 281 b: 81, 282 h: 0.040, 283 s: 0.813, 284 l: 0.624, 285 }, 286 { 287 r: 254, 288 g: 248, 289 b: 136, 290 h: 0.158, 291 s: 0.983, 292 l: 0.765, 293 }, 294 { 295 r: 25, 296 g: 203, 297 b: 151, 298 h: 0.451, 299 s: 0.781, 300 l: 0.447, 301 }, 302 { 303 r: 54, 304 g: 38, 305 b: 152, 306 h: 0.690, 307 s: 0.600, 308 l: 0.373, 309 }, 310 { 311 r: 126, 312 g: 126, 313 b: 184, 314 h: 0.667, 315 s: 0.290, 316 l: 0.608, 317 }, 318 } 319 320 func TestRGBToHSL(t *testing.T) { 321 for _, tc := range rgbHSLTestCases { 322 t.Run("", func(t *testing.T) { 323 h, s, l := rgbToHSL(tc.r, tc.g, tc.b) 324 if !compareFloat64(h, tc.h, 0.001) || !compareFloat64(s, tc.s, 0.001) || !compareFloat64(l, tc.l, 0.001) { 325 t.Fatalf("(%d, %d, %d): got (%.3f, %.3f, %.3f) want (%.3f, %.3f, %.3f)", tc.r, tc.g, tc.b, h, s, l, tc.h, tc.s, tc.l) 326 } 327 }) 328 } 329 } 330 331 func TestHSLToRGB(t *testing.T) { 332 for _, tc := range rgbHSLTestCases { 333 t.Run("", func(t *testing.T) { 334 r, g, b := hslToRGB(tc.h, tc.s, tc.l) 335 if r != tc.r || g != tc.g || b != tc.b { 336 t.Fatalf("(%.3f, %.3f, %.3f): got (%d, %d, %d) want (%d, %d, %d)", tc.h, tc.s, tc.l, r, g, b, tc.r, tc.g, tc.b) 337 } 338 }) 339 } 340 }