Image
}
+// pixelBufferLength returns the length of the []uint8 typed Pix slice field
+// for the NewXxx functions. Conceptually, this is just (bpp * width * height),
+// but this function panics if at least one of those is negative or if the
+// computation would overflow the int type.
+//
+// This panics instead of returning an error because of backwards
+// compatibility. The NewXxx functions do not return an error.
+func pixelBufferLength(bytesPerPixel int, r Rectangle, imageTypeName string) int {
+ totalLength := mul3NonNeg(bytesPerPixel, r.Dx(), r.Dy())
+ if totalLength < 0 {
+ panic("image: New" + imageTypeName + " Rectangle has huge or negative dimensions")
+ }
+ return totalLength
+}
+
// RGBA is an in-memory image whose At method returns color.RGBA values.
type RGBA struct {
// Pix holds the image's pixels, in R, G, B, A order. The pixel at
// NewRGBA returns a new RGBA image with the given bounds.
func NewRGBA(r Rectangle) *RGBA {
- w, h := r.Dx(), r.Dy()
- buf := make([]uint8, 4*w*h)
- return &RGBA{buf, 4 * w, r}
+ return &RGBA{
+ Pix: make([]uint8, pixelBufferLength(4, r, "RGBA")),
+ Stride: 4 * r.Dx(),
+ Rect: r,
+ }
}
// RGBA64 is an in-memory image whose At method returns color.RGBA64 values.
// NewRGBA64 returns a new RGBA64 image with the given bounds.
func NewRGBA64(r Rectangle) *RGBA64 {
- w, h := r.Dx(), r.Dy()
- pix := make([]uint8, 8*w*h)
- return &RGBA64{pix, 8 * w, r}
+ return &RGBA64{
+ Pix: make([]uint8, pixelBufferLength(8, r, "RGBA64")),
+ Stride: 8 * r.Dx(),
+ Rect: r,
+ }
}
// NRGBA is an in-memory image whose At method returns color.NRGBA values.
// NewNRGBA returns a new NRGBA image with the given bounds.
func NewNRGBA(r Rectangle) *NRGBA {
- w, h := r.Dx(), r.Dy()
- pix := make([]uint8, 4*w*h)
- return &NRGBA{pix, 4 * w, r}
+ return &NRGBA{
+ Pix: make([]uint8, pixelBufferLength(4, r, "NRGBA")),
+ Stride: 4 * r.Dx(),
+ Rect: r,
+ }
}
// NRGBA64 is an in-memory image whose At method returns color.NRGBA64 values.
// NewNRGBA64 returns a new NRGBA64 image with the given bounds.
func NewNRGBA64(r Rectangle) *NRGBA64 {
- w, h := r.Dx(), r.Dy()
- pix := make([]uint8, 8*w*h)
- return &NRGBA64{pix, 8 * w, r}
+ return &NRGBA64{
+ Pix: make([]uint8, pixelBufferLength(8, r, "NRGBA64")),
+ Stride: 8 * r.Dx(),
+ Rect: r,
+ }
}
// Alpha is an in-memory image whose At method returns color.Alpha values.
// NewAlpha returns a new Alpha image with the given bounds.
func NewAlpha(r Rectangle) *Alpha {
- w, h := r.Dx(), r.Dy()
- pix := make([]uint8, 1*w*h)
- return &Alpha{pix, 1 * w, r}
+ return &Alpha{
+ Pix: make([]uint8, pixelBufferLength(1, r, "Alpha")),
+ Stride: 1 * r.Dx(),
+ Rect: r,
+ }
}
// Alpha16 is an in-memory image whose At method returns color.Alpha16 values.
// NewAlpha16 returns a new Alpha16 image with the given bounds.
func NewAlpha16(r Rectangle) *Alpha16 {
- w, h := r.Dx(), r.Dy()
- pix := make([]uint8, 2*w*h)
- return &Alpha16{pix, 2 * w, r}
+ return &Alpha16{
+ Pix: make([]uint8, pixelBufferLength(2, r, "Alpha16")),
+ Stride: 2 * r.Dx(),
+ Rect: r,
+ }
}
// Gray is an in-memory image whose At method returns color.Gray values.
// NewGray returns a new Gray image with the given bounds.
func NewGray(r Rectangle) *Gray {
- w, h := r.Dx(), r.Dy()
- pix := make([]uint8, 1*w*h)
- return &Gray{pix, 1 * w, r}
+ return &Gray{
+ Pix: make([]uint8, pixelBufferLength(1, r, "Gray")),
+ Stride: 1 * r.Dx(),
+ Rect: r,
+ }
}
// Gray16 is an in-memory image whose At method returns color.Gray16 values.
// NewGray16 returns a new Gray16 image with the given bounds.
func NewGray16(r Rectangle) *Gray16 {
- w, h := r.Dx(), r.Dy()
- pix := make([]uint8, 2*w*h)
- return &Gray16{pix, 2 * w, r}
+ return &Gray16{
+ Pix: make([]uint8, pixelBufferLength(2, r, "Gray16")),
+ Stride: 2 * r.Dx(),
+ Rect: r,
+ }
}
// CMYK is an in-memory image whose At method returns color.CMYK values.
// NewCMYK returns a new CMYK image with the given bounds.
func NewCMYK(r Rectangle) *CMYK {
- w, h := r.Dx(), r.Dy()
- buf := make([]uint8, 4*w*h)
- return &CMYK{buf, 4 * w, r}
+ return &CMYK{
+ Pix: make([]uint8, pixelBufferLength(4, r, "CMYK")),
+ Stride: 4 * r.Dx(),
+ Rect: r,
+ }
}
// Paletted is an in-memory image of uint8 indices into a given palette.
// NewPaletted returns a new Paletted image with the given width, height and
// palette.
func NewPaletted(r Rectangle, p color.Palette) *Paletted {
- w, h := r.Dx(), r.Dy()
- pix := make([]uint8, 1*w*h)
- return &Paletted{pix, 1 * w, r, p}
+ return &Paletted{
+ Pix: make([]uint8, pixelBufferLength(1, r, "Paletted")),
+ Stride: 1 * r.Dx(),
+ Rect: r,
+ Palette: p,
+ }
}
}
}
+func TestNewXxxBadRectangle(t *testing.T) {
+ // call calls f(r) and reports whether it ran without panicking.
+ call := func(f func(Rectangle), r Rectangle) (ok bool) {
+ defer func() {
+ if recover() != nil {
+ ok = false
+ }
+ }()
+ f(r)
+ return true
+ }
+
+ testCases := []struct {
+ name string
+ f func(Rectangle)
+ }{
+ {"RGBA", func(r Rectangle) { NewRGBA(r) }},
+ {"RGBA64", func(r Rectangle) { NewRGBA64(r) }},
+ {"NRGBA", func(r Rectangle) { NewNRGBA(r) }},
+ {"NRGBA64", func(r Rectangle) { NewNRGBA64(r) }},
+ {"Alpha", func(r Rectangle) { NewAlpha(r) }},
+ {"Alpha16", func(r Rectangle) { NewAlpha16(r) }},
+ {"Gray", func(r Rectangle) { NewGray(r) }},
+ {"Gray16", func(r Rectangle) { NewGray16(r) }},
+ {"CMYK", func(r Rectangle) { NewCMYK(r) }},
+ {"Paletted", func(r Rectangle) { NewPaletted(r, color.Palette{color.Black, color.White}) }},
+ {"YCbCr", func(r Rectangle) { NewYCbCr(r, YCbCrSubsampleRatio422) }},
+ {"NYCbCrA", func(r Rectangle) { NewNYCbCrA(r, YCbCrSubsampleRatio444) }},
+ }
+
+ for _, tc := range testCases {
+ // Calling NewXxx(r) should fail (panic, since NewXxx doesn't return an
+ // error) unless r's width and height are both non-negative.
+ for _, negDx := range []bool{false, true} {
+ for _, negDy := range []bool{false, true} {
+ r := Rectangle{
+ Min: Point{15, 28},
+ Max: Point{16, 29},
+ }
+ if negDx {
+ r.Max.X = 14
+ }
+ if negDy {
+ r.Max.Y = 27
+ }
+
+ got := call(tc.f, r)
+ want := !negDx && !negDy
+ if got != want {
+ t.Errorf("New%s: negDx=%t, negDy=%t: got %t, want %t",
+ tc.name, negDx, negDy, got, want)
+ }
+ }
+ }
+
+ // Passing a Rectangle whose width and height is MaxInt should also fail
+ // (panic), due to overflow.
+ {
+ zeroAsUint := uint(0)
+ maxUint := zeroAsUint - 1
+ maxInt := int(maxUint / 2)
+ got := call(tc.f, Rectangle{
+ Min: Point{0, 0},
+ Max: Point{maxInt, maxInt},
+ })
+ if got {
+ t.Errorf("New%s: overflow: got ok, want !ok", tc.name)
+ }
+ }
+ }
+}
+
func Test16BitsPerColorChannel(t *testing.T) {
testColorModel := []color.Model{
color.RGBA64Model,