disintegration 10 лет назад
Родитель
Сommit
a806ad60de
2 измененных файлов с 415 добавлено и 17 удалено
  1. 60 17
      tools.go
  2. 355 0
      tools_test.go

+ 60 - 17
tools.go

@@ -5,6 +5,55 @@ import (
 	"math"
 	"math"
 )
 )
 
 
+// Anchor is the anchor point for image alignment.
+type Anchor int
+
+const (
+	Center Anchor = iota
+	TopLeft
+	Top
+	TopRight
+	Left
+	Right
+	BottomLeft
+	Bottom
+	BottomRight
+)
+
+func anchorPt(b image.Rectangle, w, h int, anchor Anchor) image.Point {
+	var x, y int
+	switch anchor {
+	case TopLeft:
+		x = b.Min.X
+		y = b.Min.Y
+	case Top:
+		x = b.Min.X + (b.Dx()-w)/2
+		y = b.Min.Y
+	case TopRight:
+		x = b.Max.X - w
+		y = b.Min.Y
+	case Left:
+		x = b.Min.X
+		y = b.Min.Y + (b.Dy()-h)/2
+	case Right:
+		x = b.Max.X - w
+		y = b.Min.Y + (b.Dy()-h)/2
+	case BottomLeft:
+		x = b.Min.X
+		y = b.Max.Y - h
+	case Bottom:
+		x = b.Min.X + (b.Dx()-w)/2
+		y = b.Max.Y - h
+	case BottomRight:
+		x = b.Max.X - w
+		y = b.Max.Y - h
+	default:
+		x = b.Min.X + (b.Dx()-w)/2
+		y = b.Min.Y + (b.Dy()-h)/2
+	}
+	return image.Pt(x, y)
+}
+
 // Crop cuts out a rectangular region with the specified bounds
 // Crop cuts out a rectangular region with the specified bounds
 // from the image and returns the cropped image.
 // from the image and returns the cropped image.
 func Crop(img image.Image, rect image.Rectangle) *image.NRGBA {
 func Crop(img image.Image, rect image.Rectangle) *image.NRGBA {
@@ -14,26 +63,20 @@ func Crop(img image.Image, rect image.Rectangle) *image.NRGBA {
 	return Clone(sub) // New image Bounds().Min point will be (0, 0)
 	return Clone(sub) // New image Bounds().Min point will be (0, 0)
 }
 }
 
 
+// CropAnchor cuts out a rectangular region with the specified size
+// from the image using the specified anchor point and returns the cropped image.
+func CropAnchor(img image.Image, width, height int, anchor Anchor) *image.NRGBA {
+	srcBounds := img.Bounds()
+	pt := anchorPt(srcBounds, width, height, anchor)
+	r := image.Rect(0, 0, width, height).Add(pt)
+	b := srcBounds.Intersect(r)
+	return Crop(img, b)
+}
+
 // CropCenter cuts out a rectangular region with the specified size
 // CropCenter cuts out a rectangular region with the specified size
 // from the center of the image and returns the cropped image.
 // from the center of the image and returns the cropped image.
 func CropCenter(img image.Image, width, height int) *image.NRGBA {
 func CropCenter(img image.Image, width, height int) *image.NRGBA {
-	cropW, cropH := width, height
-
-	srcBounds := img.Bounds()
-	srcW := srcBounds.Dx()
-	srcH := srcBounds.Dy()
-	srcMinX := srcBounds.Min.X
-	srcMinY := srcBounds.Min.Y
-
-	centerX := srcMinX + srcW/2
-	centerY := srcMinY + srcH/2
-
-	x0 := centerX - cropW/2
-	y0 := centerY - cropH/2
-	x1 := x0 + cropW
-	y1 := y0 + cropH
-
-	return Crop(img, image.Rect(x0, y0, x1, y1))
+	return CropAnchor(img, width, height, Center)
 }
 }
 
 
 // Paste pastes the img image to the background image at the specified position and returns the combined image.
 // Paste pastes the img image to the background image at the specified position and returns the combined image.

+ 355 - 0
tools_test.go

@@ -69,6 +69,46 @@ func TestCropCenter(t *testing.T) {
 				},
 				},
 			},
 			},
 		},
 		},
+		{
+			"CropCenter 2x3 0x1",
+			&image.NRGBA{
+				Rect:   image.Rect(-1, -1, 1, 2),
+				Stride: 2 * 4,
+				Pix: []uint8{
+					0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff,
+					0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+					0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff,
+				},
+			},
+			0, 1,
+			&image.NRGBA{
+				Rect:   image.Rect(0, 0, 0, 0),
+				Stride: 0,
+				Pix:    []uint8{},
+			},
+		},
+		{
+			"CropCenter 2x3 5x5",
+			&image.NRGBA{
+				Rect:   image.Rect(-1, -1, 1, 2),
+				Stride: 2 * 4,
+				Pix: []uint8{
+					0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff,
+					0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+					0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff,
+				},
+			},
+			5, 5,
+			&image.NRGBA{
+				Rect:   image.Rect(0, 0, 2, 3),
+				Stride: 2 * 4,
+				Pix: []uint8{
+					0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff,
+					0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+					0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff,
+				},
+			},
+		},
 	}
 	}
 	for _, d := range td {
 	for _, d := range td {
 		got := CropCenter(d.src, d.w, d.h)
 		got := CropCenter(d.src, d.w, d.h)
@@ -79,6 +119,321 @@ func TestCropCenter(t *testing.T) {
 	}
 	}
 }
 }
 
 
+func TestCropAnchor(t *testing.T) {
+	td := []struct {
+		desc   string
+		src    image.Image
+		w, h   int
+		anchor Anchor
+		want   *image.NRGBA
+	}{
+		{
+			"CropAnchor 4x4 2x2 TopLeft",
+			&image.NRGBA{
+				Rect:   image.Rect(-1, -1, 3, 3),
+				Stride: 4 * 4,
+				Pix: []uint8{
+					0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+					0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+					0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+					0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+				},
+			},
+			2, 2,
+			TopLeft,
+			&image.NRGBA{
+				Rect:   image.Rect(0, 0, 2, 2),
+				Stride: 2 * 4,
+				Pix: []uint8{
+					0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+					0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+				},
+			},
+		},
+		{
+			"CropAnchor 4x4 2x2 Top",
+			&image.NRGBA{
+				Rect:   image.Rect(-1, -1, 3, 3),
+				Stride: 4 * 4,
+				Pix: []uint8{
+					0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+					0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+					0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+					0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+				},
+			},
+			2, 2,
+			Top,
+			&image.NRGBA{
+				Rect:   image.Rect(0, 0, 2, 2),
+				Stride: 2 * 4,
+				Pix: []uint8{
+					0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+					0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
+				},
+			},
+		},
+		{
+			"CropAnchor 4x4 2x2 TopRight",
+			&image.NRGBA{
+				Rect:   image.Rect(-1, -1, 3, 3),
+				Stride: 4 * 4,
+				Pix: []uint8{
+					0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+					0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+					0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+					0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+				},
+			},
+			2, 2,
+			TopRight,
+			&image.NRGBA{
+				Rect:   image.Rect(0, 0, 2, 2),
+				Stride: 2 * 4,
+				Pix: []uint8{
+					0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+					0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+				},
+			},
+		},
+		{
+			"CropAnchor 4x4 2x2 Left",
+			&image.NRGBA{
+				Rect:   image.Rect(-1, -1, 3, 3),
+				Stride: 4 * 4,
+				Pix: []uint8{
+					0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+					0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+					0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+					0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+				},
+			},
+			2, 2,
+			Left,
+			&image.NRGBA{
+				Rect:   image.Rect(0, 0, 2, 2),
+				Stride: 2 * 4,
+				Pix: []uint8{
+					0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+					0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+				},
+			},
+		},
+		{
+			"CropAnchor 4x4 2x2 Center",
+			&image.NRGBA{
+				Rect:   image.Rect(-1, -1, 3, 3),
+				Stride: 4 * 4,
+				Pix: []uint8{
+					0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+					0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+					0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+					0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+				},
+			},
+			2, 2,
+			Center,
+			&image.NRGBA{
+				Rect:   image.Rect(0, 0, 2, 2),
+				Stride: 2 * 4,
+				Pix: []uint8{
+					0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
+					0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+				},
+			},
+		},
+		{
+			"CropAnchor 4x4 2x2 Right",
+			&image.NRGBA{
+				Rect:   image.Rect(-1, -1, 3, 3),
+				Stride: 4 * 4,
+				Pix: []uint8{
+					0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+					0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+					0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+					0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+				},
+			},
+			2, 2,
+			Right,
+			&image.NRGBA{
+				Rect:   image.Rect(0, 0, 2, 2),
+				Stride: 2 * 4,
+				Pix: []uint8{
+					0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+					0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+				},
+			},
+		},
+		{
+			"CropAnchor 4x4 2x2 BottomLeft",
+			&image.NRGBA{
+				Rect:   image.Rect(-1, -1, 3, 3),
+				Stride: 4 * 4,
+				Pix: []uint8{
+					0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+					0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+					0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+					0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+				},
+			},
+			2, 2,
+			BottomLeft,
+			&image.NRGBA{
+				Rect:   image.Rect(0, 0, 2, 2),
+				Stride: 2 * 4,
+				Pix: []uint8{
+					0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+					0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+				},
+			},
+		},
+		{
+			"CropAnchor 4x4 2x2 Bottom",
+			&image.NRGBA{
+				Rect:   image.Rect(-1, -1, 3, 3),
+				Stride: 4 * 4,
+				Pix: []uint8{
+					0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+					0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+					0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+					0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+				},
+			},
+			2, 2,
+			Bottom,
+			&image.NRGBA{
+				Rect:   image.Rect(0, 0, 2, 2),
+				Stride: 2 * 4,
+				Pix: []uint8{
+					0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+					0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
+				},
+			},
+		},
+		{
+			"CropAnchor 4x4 2x2 BottomRight",
+			&image.NRGBA{
+				Rect:   image.Rect(-1, -1, 3, 3),
+				Stride: 4 * 4,
+				Pix: []uint8{
+					0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+					0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+					0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+					0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+				},
+			},
+			2, 2,
+			BottomRight,
+			&image.NRGBA{
+				Rect:   image.Rect(0, 0, 2, 2),
+				Stride: 2 * 4,
+				Pix: []uint8{
+					0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+					0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+				},
+			},
+		},
+		{
+			"CropAnchor 4x4 0x0 BottomRight",
+			&image.NRGBA{
+				Rect:   image.Rect(-1, -1, 3, 3),
+				Stride: 4 * 4,
+				Pix: []uint8{
+					0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+					0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+					0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+					0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+				},
+			},
+			0, 0,
+			BottomRight,
+			&image.NRGBA{
+				Rect:   image.Rect(0, 0, 0, 0),
+				Stride: 0,
+				Pix:    []uint8{},
+			},
+		},
+		{
+			"CropAnchor 4x4 100x100 BottomRight",
+			&image.NRGBA{
+				Rect:   image.Rect(-1, -1, 3, 3),
+				Stride: 4 * 4,
+				Pix: []uint8{
+					0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+					0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+					0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+					0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+				},
+			},
+			100, 100,
+			BottomRight,
+			&image.NRGBA{
+				Rect:   image.Rect(0, 0, 4, 4),
+				Stride: 4 * 4,
+				Pix: []uint8{
+					0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+					0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+					0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+					0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+				},
+			},
+		},
+		{
+			"CropAnchor 4x4 1x100 BottomRight",
+			&image.NRGBA{
+				Rect:   image.Rect(-1, -1, 3, 3),
+				Stride: 4 * 4,
+				Pix: []uint8{
+					0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+					0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+					0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+					0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+				},
+			},
+			1, 100,
+			BottomRight,
+			&image.NRGBA{
+				Rect:   image.Rect(0, 0, 1, 4),
+				Stride: 1 * 4,
+				Pix: []uint8{
+					0x0c, 0x0d, 0x0e, 0x0f,
+					0x1c, 0x1d, 0x1e, 0x1f,
+					0x2c, 0x2d, 0x2e, 0x2f,
+					0x3c, 0x3d, 0x3e, 0x3f,
+				},
+			},
+		},
+		{
+			"CropAnchor 4x4 0x100 BottomRight",
+			&image.NRGBA{
+				Rect:   image.Rect(-1, -1, 3, 3),
+				Stride: 4 * 4,
+				Pix: []uint8{
+					0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+					0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+					0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+					0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+				},
+			},
+			0, 100,
+			BottomRight,
+			&image.NRGBA{
+				Rect:   image.Rect(0, 0, 0, 0),
+				Stride: 0,
+				Pix:    []uint8{},
+			},
+		},
+	}
+	for _, d := range td {
+		got := CropAnchor(d.src, d.w, d.h, d.anchor)
+		want := d.want
+		if !compareNRGBA(got, want, 0) {
+			t.Errorf("test [%s] failed: %#v", d.desc, got)
+		}
+	}
+}
+
 func TestPaste(t *testing.T) {
 func TestPaste(t *testing.T) {
 	td := []struct {
 	td := []struct {
 		desc string
 		desc string