package transform

import (
	"image"
	"testing"

	"github.com/anthonynsimon/bild/util"
)

func TestResize(t *testing.T) {
	cases := []struct {
		name     string
		width    int
		height   int
		img      *image.RGBA
		expected *image.RGBA
	}{
		{
			name:   "empty",
			width:  2,
			height: 2,
			img: &image.RGBA{
				Stride: 0,
				Rect:   image.Rect(0, 0, 0, 0),
				Pix:    []uint8{},
			},
			expected: &image.RGBA{
				Stride: 0,
				Rect:   image.Rect(0, 0, 0, 0),
				Pix:    []uint8{},
			},
		},
		{
			name:   "x1",
			width:  1,
			height: 1,
			img: &image.RGBA{
				Stride: 1 * 4,
				Rect:   image.Rect(0, 0, 1, 1),
				Pix:    []uint8{0xFF, 0xFF, 0xFF, 0xFF},
			},
			expected: &image.RGBA{
				Stride: 1 * 4,
				Rect:   image.Rect(0, 0, 1, 1),
				Pix:    []uint8{0xFF, 0xFF, 0xFF, 0xFF},
			},
		},
		{
			name:   "x2",
			width:  2,
			height: 2,
			img: &image.RGBA{
				Stride: 1 * 4,
				Rect:   image.Rect(0, 0, 1, 1),
				Pix:    []uint8{0xFF, 0xFF, 0xFF, 0xFF},
			},
			expected: &image.RGBA{
				Stride: 2 * 4,
				Rect:   image.Rect(0, 0, 2, 2),
				Pix: []uint8{
					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
				},
			},
		},
		{
			name:   "only horizontal x2",
			width:  2,
			height: 1,
			img: &image.RGBA{
				Stride: 1 * 4,
				Rect:   image.Rect(0, 0, 1, 1),
				Pix:    []uint8{0xFF, 0xFF, 0xFF, 0xFF},
			},
			expected: &image.RGBA{
				Stride: 2 * 4,
				Rect:   image.Rect(0, 0, 2, 1),
				Pix: []uint8{
					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
				},
			},
		},
		{
			name:   "only vertical x2",
			width:  1,
			height: 2,
			img: &image.RGBA{
				Stride: 1 * 4,
				Rect:   image.Rect(0, 0, 1, 1),
				Pix:    []uint8{0xFF, 0xFF, 0xFF, 0xFF},
			},
			expected: &image.RGBA{
				Stride: 1 * 4,
				Rect:   image.Rect(0, 0, 1, 2),
				Pix: []uint8{
					0xFF, 0xFF, 0xFF, 0xFF,
					0xFF, 0xFF, 0xFF, 0xFF,
				},
			},
		},
		{
			name:   "x0.5",
			width:  1,
			height: 1,
			img: &image.RGBA{
				Stride: 1 * 4,
				Rect:   image.Rect(0, 0, 1, 2),
				Pix: []uint8{
					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
				},
			},
			expected: &image.RGBA{
				Stride: 1 * 4,
				Rect:   image.Rect(0, 0, 1, 1),
				Pix:    []uint8{0xFF, 0xFF, 0xFF, 0xFF},
			},
		},
		{
			name:   "only horizontal x0.5",
			width:  1,
			height: 2,
			img: &image.RGBA{
				Stride: 2 * 4,
				Rect:   image.Rect(0, 0, 2, 2),
				Pix: []uint8{
					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
				},
			},
			expected: &image.RGBA{
				Stride: 1 * 4,
				Rect:   image.Rect(0, 0, 1, 2),
				Pix: []uint8{
					0xFF, 0xFF, 0xFF, 0xFF,
					0xFF, 0xFF, 0xFF, 0xFF,
				},
			},
		},
		{
			name:   "only vertical x0.5",
			width:  2,
			height: 1,
			img: &image.RGBA{
				Stride: 2 * 4,
				Rect:   image.Rect(0, 0, 2, 2),
				Pix: []uint8{
					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
				},
			},
			expected: &image.RGBA{
				Stride: 2 * 4,
				Rect:   image.Rect(0, 0, 2, 1),
				Pix: []uint8{
					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
				},
			},
		},
		{
			name:   "x0",
			width:  0,
			height: 0,
			img: &image.RGBA{
				Stride: 1 * 4,
				Rect:   image.Rect(0, 0, 1, 2),
				Pix: []uint8{
					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
				},
			},
			expected: &image.RGBA{
				Stride: 0,
				Rect:   image.Rect(0, 0, 0, 0),
				Pix:    []uint8{},
			},
		},
	}

	for _, c := range cases {
		result := Resize(c.img, c.width, c.height, NearestNeighbor)
		if !util.RGBAImageEqual(result, c.expected) {
			t.Errorf("%s: expected: %#v, actual: %#v", "Resize no filter "+c.name, c.expected, result)
		}

		result = Resize(c.img, c.width, c.height, Linear)
		if !util.RGBAImageEqual(result, c.expected) {
			t.Errorf("%s: expected: %#v, actual: %#v", "Resize with filter "+c.name, c.expected, result)
		}
	}
}

func TestResizeNearestNeighbor(t *testing.T) {
	cases := []struct {
		name     string
		width    int
		height   int
		img      *image.RGBA
		expected *image.RGBA
	}{
		{
			name:   "x2",
			width:  4,
			height: 4,
			img: &image.RGBA{
				Stride: 2 * 4,
				Rect:   image.Rect(0, 0, 2, 2),
				Pix: []uint8{
					0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
					0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80,
				},
			},
			expected: &image.RGBA{
				Stride: 4 * 4,
				Rect:   image.Rect(0, 0, 4, 4),
				Pix: []uint8{
					0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
					0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
					0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
					0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
				},
			},
		},
		{
			name:   "x0.5",
			width:  2,
			height: 2,
			img: &image.RGBA{
				Stride: 4 * 4,
				Rect:   image.Rect(0, 0, 4, 4),
				Pix: []uint8{
					0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
					0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
					0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
					0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
				},
			},
			expected: &image.RGBA{
				Stride: 2 * 4,
				Rect:   image.Rect(0, 0, 2, 2),
				Pix: []uint8{
					0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
					0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80,
				},
			},
		},
	}

	for _, c := range cases {
		actual := Resize(c.img, c.width, c.height, NearestNeighbor)
		if !util.RGBAImageEqual(actual, c.expected) {
			t.Errorf("%s: expected: %#v, actual: %#v", "ResizeNearestNeighbor "+c.name, util.RGBAToString(c.expected), util.RGBAToString(actual))
		}
	}
}

func TestResizeBox(t *testing.T) {
	cases := []struct {
		name     string
		width    int
		height   int
		img      *image.RGBA
		expected *image.RGBA
	}{
		{
			name:   "x2",
			width:  4,
			height: 4,
			img: &image.RGBA{
				Stride: 2 * 4,
				Rect:   image.Rect(0, 0, 2, 2),
				Pix: []uint8{
					0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
					0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80,
				},
			},
			expected: &image.RGBA{
				Stride: 4 * 4,
				Rect:   image.Rect(0, 0, 4, 4),
				Pix: []uint8{
					0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
					0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
					0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
					0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
				},
			},
		},
		{
			name:   "x0.5",
			width:  2,
			height: 2,
			img: &image.RGBA{
				Stride: 4 * 4,
				Rect:   image.Rect(0, 0, 4, 4),
				Pix: []uint8{
					0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
					0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
					0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
					0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
				},
			},
			expected: &image.RGBA{
				Stride: 2 * 4,
				Rect:   image.Rect(0, 0, 2, 2),
				Pix: []uint8{
					0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
					0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80,
				},
			},
		},
	}

	for _, c := range cases {
		actual := Resize(c.img, c.width, c.height, Box)
		if !util.RGBAImageEqual(actual, c.expected) {
			t.Errorf("%s: expected: %#v, actual: %#v", "ResizeNearestNeighbor "+c.name, util.RGBAToString(c.expected), util.RGBAToString(actual))
		}
	}
}

func TestResizeLinear(t *testing.T) {
	cases := []struct {
		name     string
		width    int
		height   int
		img      *image.RGBA
		expected *image.RGBA
	}{
		{
			name:   "x2",
			width:  4,
			height: 4,
			img: &image.RGBA{
				Stride: 2 * 4,
				Rect:   image.Rect(0, 0, 2, 2),
				Pix: []uint8{
					0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
					0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80,
				},
			},
			expected: &image.RGBA{
				Stride: 4 * 4,
				Rect:   image.Rect(0, 0, 4, 4),
				Pix: []uint8{
					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
				},
			},
		},
		{
			name:   "x0.5",
			width:  2,
			height: 2,
			img: &image.RGBA{
				Stride: 4 * 4,
				Rect:   image.Rect(0, 0, 4, 4),
				Pix: []uint8{
					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
				},
			},
			expected: &image.RGBA{
				Stride: 2 * 4,
				Rect:   image.Rect(0, 0, 2, 2),
				Pix: []uint8{
					0xA3, 0x31, 0x31, 0xF9, 0x41, 0xB3, 0x21, 0xEA,
					0x40, 0x21, 0xB3, 0xEA, 0x5B, 0x7A, 0x7A, 0xB0,
				},
			},
		},
	}

	for _, c := range cases {
		actual := Resize(c.img, c.width, c.height, Linear)
		if !util.RGBAImageEqual(actual, c.expected) {
			t.Errorf("%s: expected: %#v, actual: %#v", "ResizeLinear "+c.name, util.RGBAToString(c.expected), util.RGBAToString(actual))
		}
	}
}

func TestResizeGaussian(t *testing.T) {
	cases := []struct {
		name     string
		width    int
		height   int
		img      *image.RGBA
		expected *image.RGBA
	}{
		{
			name:   "x2",
			width:  4,
			height: 4,
			img: &image.RGBA{
				Stride: 2 * 4,
				Rect:   image.Rect(0, 0, 2, 2),
				Pix: []uint8{
					0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
					0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80,
				},
			},
			expected: &image.RGBA{
				Stride: 4 * 4,
				Rect:   image.Rect(0, 0, 4, 4),
				Pix: []uint8{
					0xFF, 0x0, 0x0, 0xFF, 0xD6, 0x29, 0x0, 0xFF, 0x29, 0xD6, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
					0xD6, 0x0, 0x29, 0xFF, 0xB7, 0x26, 0x25, 0xFC, 0x34, 0xC5, 0x18, 0xEE, 0x14, 0xEB, 0x14, 0xEB,
					0x29, 0x0, 0xD6, 0xFF, 0x33, 0x17, 0xC6, 0xEE, 0x61, 0x7D, 0x7C, 0xA5, 0x6C, 0x94, 0x6C, 0x94,
					0x0, 0x0, 0xFF, 0xFF, 0x14, 0x14, 0xEB, 0xEB, 0x6C, 0x6C, 0x94, 0x94, 0x80, 0x80, 0x80, 0x80,
				},
			},
		},
		{
			name:   "x0.5",
			width:  2,
			height: 2,
			img: &image.RGBA{
				Stride: 4 * 4,
				Rect:   image.Rect(0, 0, 4, 4),
				Pix: []uint8{
					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
				},
			},
			expected: &image.RGBA{
				Stride: 2 * 4,
				Rect:   image.Rect(0, 0, 2, 2),
				Pix: []uint8{
					0xAF, 0x2A, 0x2A, 0xFB, 0x39, 0xBE, 0x1B, 0xEC,
					0x39, 0x1B, 0xBE, 0xEC, 0x5E, 0x7C, 0x7C, 0xAA,
				},
			},
		},
	}

	for _, c := range cases {
		actual := Resize(c.img, c.width, c.height, Gaussian)
		if !util.RGBAImageEqual(actual, c.expected) {
			t.Errorf("%s: expected: %#v, actual: %#v", "ResizeGaussian "+c.name, util.RGBAToString(c.expected), util.RGBAToString(actual))
		}
	}
}

func TestResizeCatmullRom(t *testing.T) {
	cases := []struct {
		name     string
		width    int
		height   int
		img      *image.RGBA
		expected *image.RGBA
	}{
		{
			name:   "x2",
			width:  4,
			height: 4,
			img: &image.RGBA{
				Stride: 2 * 4,
				Rect:   image.Rect(0, 0, 2, 2),
				Pix: []uint8{
					0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
					0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80,
				},
			},
			expected: &image.RGBA{
				Stride: 4 * 4,
				Rect:   image.Rect(0, 0, 4, 4),
				Pix: []uint8{
					0xFF, 0x0, 0x0, 0xFF, 0xD9, 0x37, 0x0, 0xFF, 0x31, 0xD3, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
					0xCA, 0x0, 0x35, 0xFF, 0xA6, 0x30, 0x2F, 0xFA, 0x3F, 0xB5, 0x20, 0xEA, 0x1D, 0xE7, 0x18, 0xE2,
					0x35, 0x0, 0xCA, 0xFF, 0x3F, 0x20, 0xB6, 0xEA, 0x5B, 0x7A, 0x7A, 0xAF, 0x6E, 0xA3, 0x5D, 0x92,
					0x0, 0x0, 0xFF, 0xFF, 0xC, 0x19, 0xF9, 0xE3, 0x69, 0x5C, 0xA8, 0x91, 0x97, 0x81, 0x7F, 0x69,
				},
			},
		},
		{
			name:   "x0.5",
			width:  2,
			height: 2,
			img: &image.RGBA{
				Stride: 4 * 4,
				Rect:   image.Rect(0, 0, 4, 4),
				Pix: []uint8{
					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
				},
			},
			expected: &image.RGBA{
				Stride: 2 * 4,
				Rect:   image.Rect(0, 0, 2, 2),
				Pix: []uint8{
					0xB4, 0x27, 0x27, 0xFC, 0x36, 0xC3, 0x19, 0xED,
					0x35, 0x19, 0xC3, 0xED, 0x60, 0x7C, 0x7C, 0xA7,
				},
			},
		},
	}

	for _, c := range cases {
		actual := Resize(c.img, c.width, c.height, CatmullRom)
		if !util.RGBAImageEqual(actual, c.expected) {
			t.Errorf("%s: expected: %#v, actual: %#v", "ResizeCatmullRom "+c.name, util.RGBAToString(c.expected), util.RGBAToString(actual))
		}
	}
}

func TestResizeMitchell(t *testing.T) {
	cases := []struct {
		name     string
		width    int
		height   int
		img      *image.RGBA
		expected *image.RGBA
	}{
		{
			name:   "x2",
			width:  4,
			height: 4,
			img: &image.RGBA{
				Stride: 2 * 4,
				Rect:   image.Rect(0, 0, 2, 2),
				Pix: []uint8{
					0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
					0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80,
				},
			},
			expected: &image.RGBA{
				Stride: 4 * 4,
				Rect:   image.Rect(0, 0, 4, 4),
				Pix: []uint8{
					0xFF, 0x0, 0x0, 0xFF, 0xC5, 0x40, 0x0, 0xFF, 0x3E, 0xC3, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
					0xC0, 0x0, 0x3F, 0xFF, 0x99, 0x37, 0x37, 0xF7, 0x47, 0xA8, 0x27, 0xE7, 0x21, 0xE1, 0x1F, 0xDF,
					0x3F, 0x0, 0xC0, 0xFF, 0x47, 0x28, 0xA9, 0xE8, 0x58, 0x78, 0x78, 0xB7, 0x63, 0xA2, 0x5D, 0x9C,
					0x0, 0x0, 0xFF, 0xFF, 0x1B, 0x1F, 0xE7, 0xDF, 0x61, 0x5D, 0xA4, 0x9C, 0x88, 0x80, 0x80, 0x78,
				},
			},
		},
		{
			name:   "x0.5",
			width:  2,
			height: 2,
			img: &image.RGBA{
				Stride: 4 * 4,
				Rect:   image.Rect(0, 0, 4, 4),
				Pix: []uint8{
					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
				},
			},
			expected: &image.RGBA{
				Stride: 2 * 4,
				Rect:   image.Rect(0, 0, 2, 2),
				Pix: []uint8{
					0xA7, 0x2E, 0x2F, 0xFA, 0x3E, 0xB7, 0x1F, 0xEA,
					0x3E, 0x1F, 0xB7, 0xEB, 0x5C, 0x7B, 0x7B, 0xAE,
				},
			},
		},
	}

	for _, c := range cases {
		actual := Resize(c.img, c.width, c.height, MitchellNetravali)
		if !util.RGBAImageEqual(actual, c.expected) {
			t.Errorf("%s: expected: %#v, actual: %#v", "ResizeMitchell "+c.name, util.RGBAToString(c.expected), util.RGBAToString(actual))
		}
	}
}

func TestResizeLanczos(t *testing.T) {
	cases := []struct {
		name     string
		width    int
		height   int
		img      *image.RGBA
		expected *image.RGBA
	}{
		{
			name:   "x2",
			width:  4,
			height: 4,
			img: &image.RGBA{
				Stride: 2 * 4,
				Rect:   image.Rect(0, 0, 2, 2),
				Pix: []uint8{
					0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
					0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80,
				},
			},
			expected: &image.RGBA{
				Stride: 4 * 4,
				Rect:   image.Rect(0, 0, 4, 4),
				Pix: []uint8{
					0xFF, 0x0, 0x0, 0xFF, 0xE1, 0x40, 0x0, 0xFF, 0x34, 0xD5, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
					0xC4, 0x0, 0x3B, 0xFF, 0x9D, 0x34, 0x34, 0xF8, 0x44, 0xAD, 0x25, 0xE8, 0x23, 0xE7, 0x19, 0xDC,
					0x3B, 0x0, 0xC4, 0xFF, 0x45, 0x25, 0xAD, 0xE8, 0x59, 0x79, 0x79, 0xB5, 0x73, 0xAE, 0x51, 0x8D,
					0x0, 0x0, 0xFF, 0xFF, 0x1, 0x19, 0xFF, 0xDC, 0x69, 0x51, 0xBA, 0x8D, 0xB0, 0x84, 0x7D, 0x50,
				},
			},
		},
		{
			name:   "x0.5",
			width:  2,
			height: 2,
			img: &image.RGBA{
				Stride: 4 * 4,
				Rect:   image.Rect(0, 0, 4, 4),
				Pix: []uint8{
					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
				},
			},
			expected: &image.RGBA{
				Stride: 2 * 4,
				Rect:   image.Rect(0, 0, 2, 2),
				Pix: []uint8{
					0xBA, 0x24, 0x24, 0xFC, 0x31, 0xC8, 0x17, 0xEF,
					0x32, 0x17, 0xC8, 0xEF, 0x62, 0x7D, 0x7D, 0xA4,
				},
			},
		},
	}

	for _, c := range cases {
		actual := Resize(c.img, c.width, c.height, Lanczos)
		if !util.RGBAImageEqual(actual, c.expected) {
			t.Errorf("%s: expected: %#v, actual: %#v", "ResizeLanczos "+c.name, util.RGBAToString(c.expected), util.RGBAToString(actual))
		}
	}
}

func TestCrop(t *testing.T) {
	cases := []struct {
		name     string
		rect     image.Rectangle
		img      *image.RGBA
		expected *image.RGBA
	}{
		{
			name: "center",
			rect: image.Rect(1, 1, 3, 3),
			img: &image.RGBA{
				Stride: 4 * 4,
				Rect:   image.Rect(0, 0, 4, 4),
				Pix: []uint8{
					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
				},
			},
			expected: &image.RGBA{
				Stride: 2 * 4,
				Rect:   image.Rect(1, 1, 3, 3),
				Pix: []uint8{
					0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7,
					0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8,
				},
			},
		},
		{
			name: "top left",
			rect: image.Rect(0, 0, 1, 1),
			img: &image.RGBA{
				Stride: 4 * 4,
				Rect:   image.Rect(0, 0, 4, 4),
				Pix: []uint8{
					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
				},
			},
			expected: &image.RGBA{
				Stride: 1 * 4,
				Rect:   image.Rect(0, 0, 1, 1),
				Pix: []uint8{
					0xFF, 0x0, 0x0, 0xFF,
				},
			},
		},
		{
			name: "no change",
			rect: image.Rect(0, 0, 4, 4),
			img: &image.RGBA{
				Stride: 4 * 4,
				Rect:   image.Rect(0, 0, 4, 4),
				Pix: []uint8{
					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
				},
			},
			expected: &image.RGBA{
				Stride: 4 * 4,
				Rect:   image.Rect(0, 0, 4, 4),
				Pix: []uint8{
					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
				},
			},
		},
		{
			name: "larger intersect",
			rect: image.Rect(-50, -50, 50, 50),
			img: &image.RGBA{
				Stride: 4 * 4,
				Rect:   image.Rect(0, 0, 4, 4),
				Pix: []uint8{
					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
				},
			},
			expected: &image.RGBA{
				Stride: 4 * 4,
				Rect:   image.Rect(0, 0, 4, 4),
				Pix: []uint8{
					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
				},
			},
		},
		{
			name: "horizontal only",
			rect: image.Rect(2, 0, 4, 4),
			img: &image.RGBA{
				Stride: 4 * 4,
				Rect:   image.Rect(0, 0, 4, 4),
				Pix: []uint8{
					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
				},
			},
			expected: &image.RGBA{
				Stride: 2 * 4,
				Rect:   image.Rect(2, 0, 4, 4),
				Pix: []uint8{
					0x40, 0xBF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
					0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
					0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
					0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
				},
			},
		},
		{
			name: "vertical only",
			rect: image.Rect(0, 2, 4, 4),
			img: &image.RGBA{
				Stride: 4 * 4,
				Rect:   image.Rect(0, 0, 4, 4),
				Pix: []uint8{
					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
				},
			},
			expected: &image.RGBA{
				Stride: 4 * 4,
				Rect:   image.Rect(0, 2, 4, 4),
				Pix: []uint8{
					0x40, 0x00, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
					0x00, 0x00, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
				},
			},
		},
	}

	for _, c := range cases {
		actual := Crop(c.img, c.rect)
		if !util.RGBAImageEqual(actual, c.expected) {
			t.Errorf("%s: expected: %#v, actual: %#v", "Crop "+c.name, util.RGBAToString(c.expected), util.RGBAToString(actual))
		}
	}
}

func BenchmarkResizeTenth(b *testing.B) {
	benchResize(b, 4096, 4096, 0.1, Linear)
}

func BenchmarkResizeQuarter(b *testing.B) {
	benchResize(b, 4096, 4096, 0.25, Linear)
}

func BenchmarkResizeHalf(b *testing.B) {
	benchResize(b, 4096, 4096, 0.5, Linear)
}

func BenchmarkResize1x(b *testing.B) {
	benchResize(b, 1024, 1024, 1.0, Linear)
}

func BenchmarkResize2x(b *testing.B) {
	benchResize(b, 1024, 1024, 2.0, Linear)
}

func BenchmarkResize4x(b *testing.B) {
	benchResize(b, 1024, 1024, 4.0, Linear)
}

func BenchmarkResize8x(b *testing.B) {
	benchResize(b, 1024, 1024, 8.0, Linear)
}

func BenchmarkResize16x(b *testing.B) {
	benchResize(b, 1024, 1024, 16.0, Linear)
}

func benchResize(b *testing.B, w, h int, scale float64, f ResampleFilter) {
	newW := int(float64(w) * scale)
	newH := int(float64(h) * scale)
	img := image.NewRGBA(image.Rect(0, 0, w, h))
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		benchResult = Resize(img, newW, newH, f)
	}
}
