Added packages for linear grayscale colour varients.
This commit is contained in:
		| @ -6,8 +6,8 @@ import ( | ||||
| 	"slices" | ||||
| ) | ||||
|  | ||||
| func TestDistance[T tester[T], C color.Color](t T, alpha bool, midpoint func(c0, c1 C) C, f func(c0, c1 C) float64, m color.Model) { | ||||
| 	colors := slices.Collect(EnumColor[C](alpha, false, m)) | ||||
| func TestDistance[T tester[T], C color.Color](t T, color, alpha bool, midpoint func(c0, c1 C) C, f func(c0, c1 C) float64, m color.Model) { | ||||
| 	colors := slices.Collect(EnumColor[C](color, alpha, false, m)) | ||||
|  | ||||
| 	for i, c0 := range colors { | ||||
| 		// a colour should have a distance of zero to itself. | ||||
|  | ||||
| @ -20,13 +20,13 @@ func TestTestDistance(t *testing.T) { | ||||
| 	} | ||||
|  | ||||
| 	mt.run("non-zero distance for identical colours", func(t *mockTest) { | ||||
| 		TestDistance(t, true, midpoint, func(c0, c1 color.RGBA) float64 { | ||||
| 		TestDistance(t, true, true, midpoint, func(c0, c1 color.RGBA) float64 { | ||||
| 			return 1 | ||||
| 		}, color.RGBAModel) | ||||
| 	}) | ||||
|  | ||||
| 	mt.run("NaN distance", func(t *mockTest) { | ||||
| 		TestDistance(t, true, midpoint, func(c0, c1 color.RGBA) float64 { | ||||
| 		TestDistance(t, true, true, midpoint, func(c0, c1 color.RGBA) float64 { | ||||
| 			if c0 == c1 { | ||||
| 				return 0 | ||||
| 			} | ||||
| @ -36,7 +36,7 @@ func TestTestDistance(t *testing.T) { | ||||
| 	}) | ||||
|  | ||||
| 	mt.run("negative distance", func(t *mockTest) { | ||||
| 		TestDistance(t, true, midpoint, func(c0, c1 color.RGBA) float64 { | ||||
| 		TestDistance(t, true, true, midpoint, func(c0, c1 color.RGBA) float64 { | ||||
| 			if c0 == c1 { | ||||
| 				return 0 | ||||
| 			} | ||||
| @ -46,7 +46,7 @@ func TestTestDistance(t *testing.T) { | ||||
| 	}) | ||||
|  | ||||
| 	mt.run("asymmetric distance", func(t *mockTest) { | ||||
| 		TestDistance(t, true, midpoint, func(c0, c1 color.RGBA) float64 { | ||||
| 		TestDistance(t, true, true, midpoint, func(c0, c1 color.RGBA) float64 { | ||||
| 			if c0 == c1 { | ||||
| 				return 0 | ||||
| 			} | ||||
| @ -60,7 +60,7 @@ func TestTestDistance(t *testing.T) { | ||||
| 	}) | ||||
|  | ||||
| 	mt.run("triangle inequality", func(t *mockTest) { | ||||
| 		TestDistance(t, true, midpoint, func(c0, c1 color.RGBA) float64 { | ||||
| 		TestDistance(t, true, true, midpoint, func(c0, c1 color.RGBA) float64 { | ||||
| 			dR := int(c0.R) - int(c1.R) | ||||
| 			dG := int(c0.G) - int(c1.G) | ||||
| 			dB := int(c0.B) - int(c1.B) | ||||
| @ -73,7 +73,7 @@ func TestTestDistance(t *testing.T) { | ||||
| 	}) | ||||
|  | ||||
| 	mt.run("euclidean distance", func(t *mockTest) { | ||||
| 		TestDistance(t, true, midpoint, func(c0, c1 color.RGBA) float64 { | ||||
| 		TestDistance(t, true, true, midpoint, func(c0, c1 color.RGBA) float64 { | ||||
| 			dR := int(c0.R) - int(c1.R) | ||||
| 			dG := int(c0.G) - int(c1.G) | ||||
| 			dB := int(c0.B) - int(c1.B) | ||||
|  | ||||
| @ -1,47 +1,73 @@ | ||||
| package helper | ||||
|  | ||||
| import ( | ||||
| 	"image/color" | ||||
| 	_color "image/color" | ||||
| 	"iter" | ||||
| ) | ||||
|  | ||||
| // Enum iterates over a sparse sample of the RGBA colour space. | ||||
| // | ||||
| // If color is true, the colours will have distinct RGB components. | ||||
| // otherwise, the colours will have identical RGB components. | ||||
| // | ||||
| // If alpha is true, the colours will include transparency, | ||||
| // otherwise the returned colours will be fully opaque. | ||||
| // | ||||
| // If slow is false, an even smaller number of samples will be returned | ||||
| // making this suitable for use in a nested loop. | ||||
| // | ||||
| // alpha=true slow=true: 87481 samples. | ||||
| // alpha=false slow=true: 140608 samples. | ||||
| // alpha=true slow=false: 649 samples. | ||||
| // alpha=false slow=false: 216 samples. | ||||
| func Enum(alpha, slow bool) iter.Seq[color.RGBA64] { | ||||
| // color=true alpha=true slow=true: 87481 samples. | ||||
| // color=true alpha=false slow=true: 140608 samples. | ||||
| // color=true alpha=true slow=false: 649 samples. | ||||
| // color=true alpha=false slow=false: 216 samples. | ||||
| func Enum(color, alpha, slow bool) iter.Seq[_color.RGBA64] { | ||||
| 	var aStart, aStep, cDiv uint32 | ||||
|  | ||||
| 	switch { | ||||
| 	case alpha && slow: | ||||
| 	case color && alpha && slow: | ||||
| 		aStart, aStep, cDiv = 0, 0xffff/15, 17 | ||||
| 	case alpha: // alpha && !slow | ||||
| 	case color && alpha: // color && alpha && !slow | ||||
| 		aStart, aStep, cDiv = 0, 0xffff/3, 5 | ||||
| 	case slow: // !alpha && slow | ||||
| 	case color && slow: // color && !alpha && slow | ||||
| 		aStart, aStep, cDiv = 0xffff, 1, 51 | ||||
| 	default: // !alpha && !slow | ||||
| 	case color: // color && !alpha && !slow | ||||
| 		aStart, aStep, cDiv = 0xffff, 1, 5 | ||||
|  | ||||
| 	case alpha && slow: // !color && alpha && slow | ||||
| 		aStart, aStep, cDiv = 0, 0xffff/15, 4369 | ||||
| 	case alpha: // !color && alpha && !slow | ||||
| 		aStart, aStep, cDiv = 0, 0xffff/15, 17 | ||||
| 	case slow: // !color && !alpha && slow | ||||
| 		aStart, aStep, cDiv = 0xffff, 1, 65535 | ||||
| 	default: // !color && !alpha && !slow | ||||
| 		aStart, aStep, cDiv = 0xffff, 1, 257 | ||||
| 	} | ||||
|  | ||||
| 	return func(yield func(color.RGBA64) bool) { | ||||
| 	if color { | ||||
| 		return func(yield func(_color.RGBA64) bool) { | ||||
| 			for a := aStart; a <= 0xffff; a += aStep { | ||||
| 				cStep := max(1, a/cDiv) | ||||
|  | ||||
| 				for b := uint32(0); b <= a; b += cStep { | ||||
| 					for g := uint32(0); g <= a; g += cStep { | ||||
| 						for r := uint32(0); r <= a; r += cStep { | ||||
| 							if !yield(_color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)}) { | ||||
| 								return | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return func(yield func(_color.RGBA64) bool) { | ||||
| 		for a := aStart; a <= 0xffff; a += aStep { | ||||
| 			cStep := max(1, a/cDiv) | ||||
|  | ||||
| 			for b := uint32(0); b <= a; b += cStep { | ||||
| 				for g := uint32(0); g <= a; g += cStep { | ||||
| 					for r := uint32(0); r <= a; r += cStep { | ||||
| 						if !yield(color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)}) { | ||||
| 							return | ||||
| 						} | ||||
| 					} | ||||
| 			for c := uint32(0); c <= a; c += cStep { | ||||
| 				if !yield(_color.RGBA64{uint16(c), uint16(c), uint16(c), uint16(a)}) { | ||||
| 					return | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| @ -49,9 +75,9 @@ func Enum(alpha, slow bool) iter.Seq[color.RGBA64] { | ||||
| } | ||||
|  | ||||
| // EnumColor is identical to [Enum], but invokes a [color.Model] to return a concrete colour type. | ||||
| func EnumColor[C color.Color](alpha, slow bool, m color.Model) iter.Seq[C] { | ||||
| func EnumColor[C _color.Color](color, alpha, slow bool, m _color.Model) iter.Seq[C] { | ||||
| 	return func(yield func(C) bool) { | ||||
| 		for rgba := range Enum(alpha, slow) { | ||||
| 		for rgba := range Enum(color, alpha, slow) { | ||||
| 			if !yield(m.Convert(rgba).(C)) { | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| @ -24,35 +24,39 @@ func eqRGBA64(a, b color.RGBA64) bool { | ||||
|  | ||||
| func TestEnum(t *testing.T) { | ||||
| 	tests := []struct { | ||||
| 		alpha, slow   bool | ||||
| 		expectedCount int | ||||
| 		color, alpha, slow bool | ||||
| 		expectedCount      int | ||||
| 	}{ | ||||
| 		{true, true, 87481}, | ||||
| 		{false, true, 140608}, | ||||
| 		{true, false, 649}, | ||||
| 		{false, false, 216}, | ||||
| 		{true, true, true, 87481}, | ||||
| 		{true, false, true, 140608}, | ||||
| 		{true, true, false, 649}, | ||||
| 		{true, false, false, 216}, | ||||
| 		{false, true, true, 65551}, | ||||
| 		{false, false, true, 65536}, | ||||
| 		{false, true, false, 271}, | ||||
| 		{false, false, false, 258}, | ||||
| 	} | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(fmt.Sprintf("alpha=%v, slow=%v", tt.alpha, tt.slow), func(t *testing.T) { | ||||
| 		t.Run(fmt.Sprintf("color=%v, alpha=%v, slow=%v", tt.color, tt.alpha, tt.slow), func(t *testing.T) { | ||||
| 			t.Run("sequence meets expected criteria", func(t *testing.T) { | ||||
| 				list := slices.Collect(Enum(tt.alpha, tt.slow)) | ||||
| 				list := slices.Collect(Enum(tt.color, tt.alpha, tt.slow)) | ||||
| 				gotCount := len(list) | ||||
| 				if gotCount != tt.expectedCount { | ||||
| 					t.Errorf("Enum(%v, %v) returned %d items, wanted %d", tt.alpha, tt.slow, gotCount, tt.expectedCount) | ||||
| 					t.Errorf("Enum(%v, %v, %v) returned %d items, wanted %d", tt.color, tt.alpha, tt.slow, gotCount, tt.expectedCount) | ||||
| 				} | ||||
|  | ||||
| 				slices.SortFunc(list, cmpRGBA64) | ||||
| 				list = slices.CompactFunc(list, eqRGBA64) | ||||
|  | ||||
| 				if len(list) != gotCount { | ||||
| 					t.Errorf("Enum(%v, %v) returned %d duplicate items", tt.alpha, tt.slow, gotCount-len(list)) | ||||
| 					t.Errorf("Enum(%v, %v, %v) returned %d duplicate items", tt.color, tt.alpha, tt.slow, gotCount-len(list)) | ||||
| 				} | ||||
|  | ||||
| 				listHasAlpha := false | ||||
| 				for _, c := range list { | ||||
| 					if c.A != 0xffff { | ||||
| 						if !tt.alpha { | ||||
| 							t.Errorf("Enum(%v, %v) returned non-opaque color: %v", tt.alpha, tt.slow, c) | ||||
| 							t.Errorf("Enum(%v, %v, %v) returned non-opaque color: %v", tt.color, tt.alpha, tt.slow, c) | ||||
| 						} | ||||
|  | ||||
| 						listHasAlpha = true | ||||
| @ -61,14 +65,14 @@ func TestEnum(t *testing.T) { | ||||
| 				} | ||||
|  | ||||
| 				if !listHasAlpha && tt.alpha { | ||||
| 					t.Errorf("Enum(%v, %v) didn't return non-opaque colors", tt.alpha, tt.slow) | ||||
| 					t.Errorf("Enum(%v, %v, %v) didn't return non-opaque colors", tt.color, tt.alpha, tt.slow) | ||||
| 				} | ||||
| 			}) | ||||
|  | ||||
| 			t.Run("cancel", func(t *testing.T) { | ||||
| 				// make sure cancelling the iteration doesn't panic. | ||||
| 				// But mostly, we want that sweet, sweet test coverage. | ||||
| 				next, stop := iter.Pull(Enum(tt.alpha, tt.slow)) | ||||
| 				next, stop := iter.Pull(Enum(tt.color, tt.alpha, tt.slow)) | ||||
| 				// need to invoke next to actually have the generated be started. | ||||
| 				if _, ok := next(); !ok { | ||||
| 					t.Error("iteration stopped before we could cancel it") | ||||
| @ -82,19 +86,19 @@ func TestEnum(t *testing.T) { | ||||
|  | ||||
| func TestEnumColor(t *testing.T) { | ||||
| 	tests := []struct { | ||||
| 		alpha, slow bool | ||||
| 		color, alpha, slow bool | ||||
| 	}{ | ||||
| 		{true, true}, | ||||
| 		{false, true}, | ||||
| 		{true, false}, | ||||
| 		{false, false}, | ||||
| 		{true, true, true}, | ||||
| 		{true, false, true}, | ||||
| 		{true, true, false}, | ||||
| 		{true, false, false}, | ||||
| 	} | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(fmt.Sprintf("alpha=%v, slow=%v", tt.alpha, tt.slow), func(t *testing.T) { | ||||
| 		t.Run(fmt.Sprintf("color=%v, alpha=%v, slow=%v", tt.color, tt.alpha, tt.slow), func(t *testing.T) { | ||||
| 			t.Run("sequence equivalence", func(t *testing.T) { | ||||
| 				nextRGBA64, stop1 := iter.Pull(Enum(tt.alpha, tt.slow)) | ||||
| 				nextRGBA64, stop1 := iter.Pull(Enum(tt.color, tt.alpha, tt.slow)) | ||||
| 				defer stop1() | ||||
| 				nextNRGBA, stop2 := iter.Pull(EnumColor[color.NRGBA](tt.alpha, tt.slow, color.NRGBAModel)) | ||||
| 				nextNRGBA, stop2 := iter.Pull(EnumColor[color.NRGBA](tt.color, tt.alpha, tt.slow, color.NRGBAModel)) | ||||
| 				defer stop2() | ||||
|  | ||||
| 				for i := 0; ; i++ { | ||||
| @ -119,7 +123,7 @@ func TestEnumColor(t *testing.T) { | ||||
| 			t.Run("cancel", func(t *testing.T) { | ||||
| 				// make sure cancelling the iteration doesn't panic. | ||||
| 				// But mostly, we want that sweet, sweet test coverage. | ||||
| 				next, stop := iter.Pull(EnumColor[color.NRGBA](tt.alpha, tt.slow, color.NRGBAModel)) | ||||
| 				next, stop := iter.Pull(EnumColor[color.NRGBA](tt.color, tt.alpha, tt.slow, color.NRGBAModel)) | ||||
| 				// need to invoke next to actually have the generated be started. | ||||
| 				if _, ok := next(); !ok { | ||||
| 					t.Error("iteration stopped before we could cancel it") | ||||
|  | ||||
| @ -4,8 +4,11 @@ import "math" | ||||
|  | ||||
| // Linearize converts an sRGB component in the range [0, 0xffff] to a linearRGB component in the range [0, 1]. | ||||
| func Linearize(c uint32) float64 { | ||||
| 	l := float64(c) / 0xffff | ||||
| 	return LinearizeF(float64(c) / 0xffff) | ||||
| } | ||||
|  | ||||
| // LinearizeF converts an sRGB component in the range [0, 1] to a linearRGB component in the range [0, 1]. | ||||
| func LinearizeF(l float64) float64 { | ||||
| 	if l <= 0.039285714285714285714285714285714 { | ||||
| 		return l / 12.923210180787861094641554898407 | ||||
| 	} | ||||
| @ -26,3 +29,12 @@ func Delinearize(l float64) uint32 { | ||||
| 		return uint32(69139.425*math.Pow(l, 1/2.4) - 3603.925) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Delinearize converts a linearRGB component in the range [0, 1] to an sRGB component in the range [0, 1]. | ||||
| func DelinearizeF(l float64) float64 { | ||||
| 	if l <= 0.0030399346397784299969770436366690 { | ||||
| 		return l * 12.923210180787861094641554898407 | ||||
| 	} | ||||
|  | ||||
| 	return 1.055*math.Pow(l, 1/2.4) - 0.055 | ||||
| } | ||||
|  | ||||
| @ -46,6 +46,30 @@ func TestLinearize(t *testing.T) { | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func TestLinearizeF(t *testing.T) { | ||||
| 	// the positive finites were indirectly tested by TestLinearize, so we'll just test the | ||||
| 	// negative and non-finite inputs. | ||||
| 	tests := []struct { | ||||
| 		value float64 | ||||
| 		want  float64 | ||||
| 	}{ | ||||
| 		{-1, -1 / 12.923210180787861094641554898407}, | ||||
| 		{0, 0}, | ||||
| 		{1, 1}, | ||||
| 		{2, 4.9538457515920408157613451180477}, | ||||
| 		{math.Inf(-1), math.Inf(-1)}, | ||||
| 		{math.Inf(1), math.Inf(1)}, | ||||
| 		{math.NaN(), math.NaN()}, | ||||
| 	} | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(fmt.Sprintf("%x", tt.value), func(t *testing.T) { | ||||
| 			if got := LinearizeF(tt.value); !EqFloat64Fuzzy(got, tt.want) { | ||||
| 				t.Errorf("LinearizeF(%x) = %x: want %x", tt.value, got, tt.want) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestDelinearize(t *testing.T) { | ||||
| 	tests := []struct { | ||||
| 		value float64 | ||||
| @ -82,3 +106,35 @@ func TestDelinearize(t *testing.T) { | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func TestDelinearizeF(t *testing.T) { | ||||
| 	// values that would have been impossible for Delinearize to represent. | ||||
| 	tests := []struct { | ||||
| 		value float64 | ||||
| 		want  float64 | ||||
| 	}{ | ||||
| 		{-1, -12.923210180787861094641554898407}, | ||||
| 		{math.Inf(-1), math.Inf(-1)}, | ||||
| 		{math.Inf(1), math.Inf(1)}, | ||||
| 		{math.NaN(), math.NaN()}, | ||||
| 		{2, 1.3532560461493862548965276346496}, | ||||
| 	} | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(fmt.Sprintf("%x", tt.value), func(t *testing.T) { | ||||
| 			if got := DelinearizeF(tt.value); !EqFloat64Fuzzy(got, tt.want) { | ||||
| 				t.Errorf("DelinearizeF(%x) = %x: want %x", tt.value, got, tt.want) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
| 	t.Run("roundtrip conversions", func(t *testing.T) { | ||||
| 		for c := -0x1000; c < 0x11000; c++ { | ||||
| 			f := float64(c) / 0x10000 | ||||
| 			got := DelinearizeF(LinearizeF(f)) | ||||
| 			if !EqFloat64Fuzzy(got, f) { | ||||
| 				t.Errorf("DelinearizeF(LinearizeF(%x)) != %x", c, got) | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| @ -1,11 +1,11 @@ | ||||
| package helper | ||||
|  | ||||
| import ( | ||||
| 	"image/color" | ||||
| 	_color "image/color" | ||||
| ) | ||||
|  | ||||
| func Model[C color.Color](fromColor func(color.Color) C) color.Model { | ||||
| 	return color.ModelFunc(func(c color.Color) color.Color { | ||||
| func Model[C _color.Color](fromColor func(_color.Color) C) _color.Model { | ||||
| 	return _color.ModelFunc(func(c _color.Color) _color.Color { | ||||
| 		return fromColor(c) | ||||
| 	}) | ||||
| } | ||||
| @ -13,7 +13,7 @@ func Model[C color.Color](fromColor func(color.Color) C) color.Model { | ||||
| // Interface that the colours used in this package are expected to implement. | ||||
| type Color interface { | ||||
| 	comparable | ||||
| 	color.Color | ||||
| 	_color.Color | ||||
| 	NRGBA() (r, g, b, a uint32) | ||||
| 	NLRGBA() (r, g, b, a float64) | ||||
| 	NXYZA() (x, y, z, a float64) | ||||
| @ -22,13 +22,13 @@ type Color interface { | ||||
|  | ||||
| type ConvertTest[C Color] struct { | ||||
| 	Name string | ||||
| 	In   color.Color | ||||
| 	In   _color.Color | ||||
| 	Out  C | ||||
| } | ||||
|  | ||||
| func TestModel[T tester[T], C Color](t T, alpha bool, m color.Model, eq func(c0, c1 C) bool, extra []ConvertTest[C]) { | ||||
| func TestModel[T tester[T], C Color](t T, color, alpha bool, m _color.Model, eq func(c0, c1 C) bool, extra []ConvertTest[C]) { | ||||
| 	t.Run("legal colours", func(t T) { | ||||
| 		for wantRGBA := range Enum(alpha, true) { | ||||
| 		for wantRGBA := range Enum(color, alpha, true) { | ||||
| 			_gotC := m.Convert(wantRGBA) | ||||
| 			gotC, ok := _gotC.(C) | ||||
|  | ||||
| @ -38,16 +38,16 @@ func TestModel[T tester[T], C Color](t T, alpha bool, m color.Model, eq func(c0, | ||||
| 			} | ||||
|  | ||||
| 			r, g, b, a := gotC.RGBA() | ||||
| 			gotRGBA := color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)} | ||||
| 			gotRGBA := _color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)} | ||||
|  | ||||
| 			if gotRGBA != wantRGBA { | ||||
| 				t.Errorf("%#+v.RGBA() = %v, want %v", gotC, gotRGBA, wantRGBA) | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			wantNRGBA := color.NRGBA64Model.Convert(wantRGBA) | ||||
| 			wantNRGBA := _color.NRGBA64Model.Convert(wantRGBA) | ||||
| 			r, g, b, a = gotC.NRGBA() | ||||
| 			gotNRGBA := color.NRGBA64{uint16(r), uint16(g), uint16(b), uint16(a)} | ||||
| 			gotNRGBA := _color.NRGBA64{uint16(r), uint16(g), uint16(b), uint16(a)} | ||||
| 			if gotNRGBA != wantNRGBA { | ||||
| 				t.Errorf("%#+v.NRGBA() = %v, want %v", gotC, gotNRGBA, wantNRGBA) | ||||
| 				return | ||||
|  | ||||
| @ -90,31 +90,31 @@ func TestTestModel(t *testing.T) { | ||||
| 	mt := mockTester{t: t} | ||||
|  | ||||
| 	mt.run("wrong colour type", func(t *mockTest) { | ||||
| 		TestModel(t, true, color.RGBAModel, eq[nrgba64], nil) | ||||
| 		TestModel(t, true, true, color.RGBAModel, eq[nrgba64], nil) | ||||
| 	}) | ||||
|  | ||||
| 	mt.run("bad RGBA", func(t *mockTest) { | ||||
| 		TestModel(t, false, Model(convert[nrgba64BadRGBA]), eq[nrgba64BadRGBA], nil) | ||||
| 		TestModel(t, true, false, Model(convert[nrgba64BadRGBA]), eq[nrgba64BadRGBA], nil) | ||||
| 	}) | ||||
|  | ||||
| 	mt.run("bad NRGBA", func(t *mockTest) { | ||||
| 		TestModel(t, false, Model(convert[nrgba64BadNRGBA]), eq[nrgba64BadNRGBA], nil) | ||||
| 		TestModel(t, true, false, Model(convert[nrgba64BadNRGBA]), eq[nrgba64BadNRGBA], nil) | ||||
| 	}) | ||||
|  | ||||
| 	mt.run("bad NLRGBA", func(t *mockTest) { | ||||
| 		TestModel(t, false, Model(convert[nrgba64BadNLRGBA]), eq[nrgba64BadNLRGBA], nil) | ||||
| 		TestModel(t, true, false, Model(convert[nrgba64BadNLRGBA]), eq[nrgba64BadNLRGBA], nil) | ||||
| 	}) | ||||
|  | ||||
| 	mt.run("bad NXYZA", func(t *mockTest) { | ||||
| 		TestModel(t, false, Model(convert[nrgba64BadNXYZA]), eq[nrgba64BadNXYZA], nil) | ||||
| 		TestModel(t, true, false, Model(convert[nrgba64BadNXYZA]), eq[nrgba64BadNXYZA], nil) | ||||
| 	}) | ||||
|  | ||||
| 	mt.run("bad NOkLabA", func(t *mockTest) { | ||||
| 		TestModel(t, false, Model(convert[nrgba64BadNOkLabA]), eq[nrgba64BadNOkLabA], nil) | ||||
| 		TestModel(t, true, false, Model(convert[nrgba64BadNOkLabA]), eq[nrgba64BadNOkLabA], nil) | ||||
| 	}) | ||||
|  | ||||
| 	mt.run("working model", func(t *mockTest) { | ||||
| 		TestModel(t, true, Model(convert[nrgba64]), eq[nrgba64], []ConvertTest[nrgba64]{ | ||||
| 		TestModel(t, true, true, Model(convert[nrgba64]), eq[nrgba64], []ConvertTest[nrgba64]{ | ||||
| 			{"good", color.NRGBA64{0x0123, 0x4567, 0x89ab, 0xcdef}, nrgba64{color.NRGBA64{0x0123, 0x4567, 0x89ab, 0xcdef}}}, | ||||
| 			{"bad", color.NRGBA64{0xcafe, 0xf00d, 0x54ac, 0xce55}, nrgba64{color.NRGBA64{0x0123, 0x4567, 0x89ab, 0xcdef}}}, | ||||
| 		}) | ||||
|  | ||||
| @ -8,7 +8,7 @@ import ( | ||||
| ) | ||||
|  | ||||
| func TestLMSToXYZ(t *testing.T) { | ||||
| 	for c := range Enum(false, true) { | ||||
| 	for c := range Enum(true, false, true) { | ||||
| 		want := collect3(LRGBtoXYZ(RGBtoLRGB(uint32(c.R), uint32(c.G), uint32(c.B)))) | ||||
|  | ||||
| 		if got := collect3(LMStoXYZ(XYZtoLMS(want[0], want[1], want[2]))); !EqFloat64SliceFuzzy(want[:], got[:]) { | ||||
| @ -19,7 +19,7 @@ func TestLMSToXYZ(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestLMSToLRGB(t *testing.T) { | ||||
| 	for c := range Enum(false, true) { | ||||
| 	for c := range Enum(true, false, true) { | ||||
| 		want := collect3(RGBtoLRGB(uint32(c.R), uint32(c.G), uint32(c.B))) | ||||
|  | ||||
| 		l, m, s := LRGBtoLMS(want[0], want[1], want[2]) | ||||
| @ -39,7 +39,7 @@ func TestLMSToLRGB(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestOKLabToLMS(t *testing.T) { | ||||
| 	for c := range Enum(false, true) { | ||||
| 	for c := range Enum(true, false, true) { | ||||
| 		want := collect3(LRGBtoLMS(RGBtoLRGB(uint32(c.R), uint32(c.G), uint32(c.B)))) | ||||
| 		if got := collect3(OkLabToLMS(LMStoOkLab(want[0], want[1], want[2]))); !EqFloat64SliceFuzzy(want[:], got[:]) { | ||||
| 			t.Errorf("OkLabToLMS(LMStoOKLab(%v)) = %v, want unchanged", want, got) | ||||
|  | ||||
| @ -61,12 +61,6 @@ func (s *testStatus) setPanic(v any) { | ||||
| 	s.panicValue = v | ||||
| } | ||||
|  | ||||
| func (s *testStatus) getPanic() any { | ||||
| 	s.m.Lock() | ||||
| 	defer s.m.Unlock() | ||||
| 	return s.panicValue | ||||
| } | ||||
|  | ||||
| func (s *testStatus) setHandled() { | ||||
| 	s.m.Lock() | ||||
| 	defer s.m.Unlock() | ||||
| @ -217,22 +211,6 @@ func (m *mockTester) expectFailedChildren(name string) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // panic if the named test doesn't exist or didn't panic (and returns the panic value) | ||||
| func (m *mockTester) expectPanic(name string) any { | ||||
| 	if s := m.get(name); s != nil { | ||||
| 		s.setHandled() | ||||
|  | ||||
| 		if r := s.getPanic(); r != nil { | ||||
| 			return r | ||||
| 		} | ||||
|  | ||||
| 		m.t.Errorf("%s: didn't panic", name) | ||||
| 		s.log(m.t, name) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // panic if the named test doesn't exist or has failed. | ||||
| func (m *mockTester) expectSuccess(name string) { | ||||
| 	if s := m.get(name); s != nil { | ||||
|  | ||||
| @ -5,7 +5,7 @@ import ( | ||||
| ) | ||||
|  | ||||
| func TestXYZtoLRGB(t *testing.T) { | ||||
| 	for c := range Enum(false, true) { | ||||
| 	for c := range Enum(true, false, true) { | ||||
| 		want := collect3(RGBtoLRGB(uint32(c.R), uint32(c.G), uint32(c.B))) | ||||
| 		if got := collect3(XYZtoLRGB(LRGBtoXYZ(want[0], want[1], want[2]))); !EqFloat64SliceFuzzy(want[:], got[:]) { | ||||
| 			t.Errorf("XYZtoLRGB(LRGBtoXYZ(%v)) = %v, want unchanged", want, got) | ||||
|  | ||||
		Reference in New Issue
	
	Block a user