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  }