blob: 771708ef70914a44794344a7448f8ed2977b1578 [file] [log] [blame]
package imaging
import (
"image"
"math"
"runtime"
"sync/atomic"
"testing"
)
var (
testdataBranchesJPG = mustOpen("testdata/branches.jpg")
testdataBranchesPNG = mustOpen("testdata/branches.png")
testdataFlowersSmallPNG = mustOpen("testdata/flowers_small.png")
)
func mustOpen(filename string) image.Image {
img, err := Open(filename)
if err != nil {
panic(err)
}
return img
}
func TestParallel(t *testing.T) {
for _, n := range []int{0, 1, 10, 100, 1000} {
for _, p := range []int{1, 2, 4, 8, 16, 100} {
if !testParallelN(n, p) {
t.Fatalf("test [parallel %d %d] failed", n, p)
}
}
}
}
func testParallelN(n, procs int) bool {
data := make([]bool, n)
before := runtime.GOMAXPROCS(0)
runtime.GOMAXPROCS(procs)
parallel(0, n, func(is <-chan int) {
for i := range is {
data[i] = true
}
})
runtime.GOMAXPROCS(before)
for i := 0; i < n; i++ {
if !data[i] {
return false
}
}
return true
}
func TestParallelMaxProcs(t *testing.T) {
for _, n := range []int{0, 1, 10, 100, 1000} {
for _, p := range []int{1, 2, 4, 8, 16, 100} {
if !testParallelMaxProcsN(n, p) {
t.Fatalf("test [parallel max procs %d %d] failed", n, p)
}
}
}
}
func testParallelMaxProcsN(n, procs int) bool {
data := make([]bool, n)
SetMaxProcs(procs)
parallel(0, n, func(is <-chan int) {
for i := range is {
data[i] = true
}
})
SetMaxProcs(0)
for i := 0; i < n; i++ {
if !data[i] {
return false
}
}
return true
}
func TestSetMaxProcs(t *testing.T) {
for _, p := range []int{-1, 0, 10} {
SetMaxProcs(p)
if int(atomic.LoadInt64(&maxProcs)) != p {
t.Fatalf("test [set max procs %d] failed", p)
}
}
SetMaxProcs(0)
}
func TestClamp(t *testing.T) {
testCases := []struct {
f float64
u uint8
}{
{0, 0},
{255, 255},
{128, 128},
{0.49, 0},
{0.50, 1},
{254.9, 255},
{254.0, 254},
{256, 255},
{2500, 255},
{-10, 0},
{127.6, 128},
}
for _, tc := range testCases {
if clamp(tc.f) != tc.u {
t.Fatalf("test [clamp %v %v] failed: %v", tc.f, tc.u, clamp(tc.f))
}
}
}
func TestReverse(t *testing.T) {
testCases := []struct {
pix []uint8
want []uint8
}{
{
pix: []uint8{},
want: []uint8{},
},
{
pix: []uint8{1, 2, 3, 4},
want: []uint8{1, 2, 3, 4},
},
{
pix: []uint8{1, 2, 3, 4, 5, 6, 7, 8},
want: []uint8{5, 6, 7, 8, 1, 2, 3, 4},
},
{
pix: []uint8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12},
want: []uint8{9, 10, 11, 12, 5, 6, 7, 8, 1, 2, 3, 4},
},
}
for _, tc := range testCases {
t.Run("", func(t *testing.T) {
reverse(tc.pix)
if !compareBytes(tc.pix, tc.want, 0) {
t.Fatalf("got pix %v want %v", tc.pix, tc.want)
}
})
}
}
func compareNRGBA(img1, img2 *image.NRGBA, delta int) bool {
if !img1.Rect.Eq(img2.Rect) {
return false
}
return compareBytes(img1.Pix, img2.Pix, delta)
}
func compareBytes(a, b []uint8, delta int) bool {
if len(a) != len(b) {
return false
}
for i := 0; i < len(a); i++ {
if absint(int(a[i])-int(b[i])) > delta {
return false
}
}
return true
}
// compareNRGBAGolden is a special version of compareNRGBA used in golden tests.
// All the golden images are generated on amd64 architecture. Due to differences
// in floating-point rounding on different architectures, we need to add some
// level of tolerance when comparing images on architectures other than amd64.
// See https://golang.org/ref/spec#Floating_point_operators for information on
// fused multiply and add (FMA) instruction.
func compareNRGBAGolden(img1, img2 *image.NRGBA) bool {
delta := 0
if runtime.GOARCH != "amd64" {
delta = 1
}
return compareNRGBA(img1, img2, delta)
}
func compareFloat64(a, b, delta float64) bool {
return math.Abs(a-b) <= delta
}
var rgbHSLTestCases = []struct {
r, g, b uint8
h, s, l float64
}{
{
r: 255,
g: 0,
b: 0,
h: 0.000,
s: 1.000,
l: 0.500,
},
{
r: 191,
g: 191,
b: 0,
h: 0.167,
s: 1.000,
l: 0.375,
},
{
r: 0,
g: 128,
b: 0,
h: 0.333,
s: 1.000,
l: 0.251,
},
{
r: 128,
g: 255,
b: 255,
h: 0.500,
s: 1.000,
l: 0.751,
},
{
r: 128,
g: 128,
b: 255,
h: 0.667,
s: 1.000,
l: 0.751,
},
{
r: 191,
g: 64,
b: 191,
h: 0.833,
s: 0.498,
l: 0.500,
},
{
r: 160,
g: 164,
b: 36,
h: 0.172,
s: 0.640,
l: 0.392,
},
{
r: 65,
g: 27,
b: 234,
h: 0.697,
s: 0.831,
l: 0.512,
},
{
r: 30,
g: 172,
b: 65,
h: 0.374,
s: 0.703,
l: 0.396,
},
{
r: 240,
g: 200,
b: 14,
h: 0.137,
s: 0.890,
l: 0.498,
},
{
r: 180,
g: 48,
b: 229,
h: 0.788,
s: 0.777,
l: 0.543,
},
{
r: 237,
g: 119,
b: 81,
h: 0.040,
s: 0.813,
l: 0.624,
},
{
r: 254,
g: 248,
b: 136,
h: 0.158,
s: 0.983,
l: 0.765,
},
{
r: 25,
g: 203,
b: 151,
h: 0.451,
s: 0.781,
l: 0.447,
},
{
r: 54,
g: 38,
b: 152,
h: 0.690,
s: 0.600,
l: 0.373,
},
{
r: 126,
g: 126,
b: 184,
h: 0.667,
s: 0.290,
l: 0.608,
},
}
func TestRGBToHSL(t *testing.T) {
for _, tc := range rgbHSLTestCases {
t.Run("", func(t *testing.T) {
h, s, l := rgbToHSL(tc.r, tc.g, tc.b)
if !compareFloat64(h, tc.h, 0.001) || !compareFloat64(s, tc.s, 0.001) || !compareFloat64(l, tc.l, 0.001) {
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)
}
})
}
}
func TestHSLToRGB(t *testing.T) {
for _, tc := range rgbHSLTestCases {
t.Run("", func(t *testing.T) {
r, g, b := hslToRGB(tc.h, tc.s, tc.l)
if r != tc.r || g != tc.g || b != tc.b {
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)
}
})
}
}