The Go Blog
The Go image package
Nigel Tao
21 September 2011
Introduction
Theimage andimage/colorpackages define a number of types:color.Color andcolor.Model describe colors,image.Point andimage.Rectangle describe basic 2-D geometry,andimage.Image brings the two concepts together to represent a rectangular grid of colors.Aseparate articlecovers image composition with theimage/draw package.
Colors and Color Models
Color is an interface thatdefines the minimal method set of any type that can be considered a color:one that can be converted to red, green, blue and alpha values.The conversion may be lossy, such as converting from CMYK or YCbCr color spaces.
type Color interface { // RGBA returns the alpha-premultiplied red, green, blue and alpha values // for the color. Each value ranges within [0, 0xFFFF], but is represented // by a uint32 so that multiplying by a blend factor up to 0xFFFF will not // overflow. RGBA() (r, g, b, a uint32)}There are three important subtleties about the return values.First, the red, green and blue are alpha-premultiplied:a fully saturated red that is also 25% transparent is represented by RGBA returning a 75% r.Second, the channels have a 16-bit effective range:100% red is represented by RGBA returning an r of 65535,not 255, so that converting from CMYK or YCbCr is not as lossy.Third, the type returned isuint32, even though the maximum value is 65535,to guarantee that multiplying two values together won’t overflow.Such multiplications occur when blending two colors according to an alphamask from a third color,in the style ofPorter and Duff’s classic algebra:
dstr, dstg, dstb, dsta := dst.RGBA()srcr, srcg, srcb, srca := src.RGBA()_, _, _, m := mask.RGBA()const M = 1<<16 - 1// The resultant red value is a blend of dstr and srcr, and ranges in [0, M].// The calculation for green, blue and alpha is similar.dstr = (dstr*(M-m) + srcr*m) / MThe last line of that code snippet would have been more complicated if weworked with non-alpha-premultiplied colors,which is whyColor uses alpha-premultiplied values.
The image/color package also defines a number of concrete types that implementtheColor interface.For example,RGBA is a structthat represents the classic “8 bits per channel” color.
type RGBA struct { R, G, B, A uint8}Note that theR field of anRGBA is an 8-bit alpha-premultiplied colorin the range [0, 255].RGBA satisfies theColor interface by multiplying that value by 0x101to generate a 16-bit alpha-premultiplied color in the range [0, 65535].Similarly, theNRGBA structtype represents an 8-bit non-alpha-premultiplied color,as used by the PNG image format.When manipulating anNRGBA’s fields directly,the values are non-alpha-premultiplied, but when calling theRGBA method,the return values are alpha-premultiplied.
AModel is simply somethingthat can convertColors to otherColors, possibly lossily.For example, theGrayModel can convert anyColor to a desaturatedGray.APalette can convert anyColor to one from a limited palette.
type Model interface { Convert(c Color) Color}type Palette []ColorPoints and Rectangles
APoint is an (x,y) co-ordinate on the integer grid, with axes increasing right and down.It is neither a pixel nor a grid square. APoint has no intrinsic width,height or color, but the visualizations below use a small colored square.
type Point struct { X, Y int}
p := image.Point{2, 1}ARectangle is an axis-alignedrectangle on the integer grid,defined by its top-left and bottom-rightPoint.ARectangle also has no intrinsic color,but the visualizations below outline rectangles with a thin colored line,and call out theirMin andMaxPoints.
type Rectangle struct { Min, Max Point}For convenience,image.Rect(x0, y0, x1, y1) is equivalent toimage.Rectangle{image.Point{x0, y0}, image.Point{x1, y1}},but is much easier to type.
ARectangle is inclusive at the top-left and exclusive at the bottom-right.For aPoint p and aRectangle r,p.In(r) if and only ifr.Min.X <= p.X && p.X < r.Max.X,and similarly forY.This is analogous to how a slices[i0:i1] is inclusive at the low endand exclusive at the high end.(Unlike arrays and slices, aRectangle often has a non-zero origin.)

r := image.Rect(2, 1, 5, 5)// Dx and Dy return a rectangle's width and height.fmt.Println(r.Dx(), r.Dy(), image.Pt(0, 0).In(r)) // prints 3 4 falseAdding aPoint to aRectangle translates theRectangle.Points and Rectangles are not restricted to be in the bottom-right quadrant.

r := image.Rect(2, 1, 5, 5).Add(image.Pt(-4, -2))fmt.Println(r.Dx(), r.Dy(), image.Pt(0, 0).In(r)) // prints 3 4 trueIntersecting two Rectangles yields another Rectangle, which may be empty.

r := image.Rect(0, 0, 4, 3).Intersect(image.Rect(2, 2, 5, 5))// Size returns a rectangle's width and height, as a Point.fmt.Printf("%#v\n", r.Size()) // prints image.Point{X:2, Y:1}Points and Rectangles are passed and returned by value.A function that takes aRectangle argument will be as efficient as a functionthat takes twoPoint arguments,or fourint arguments.
Images
AnImage maps every grid squarein aRectangle to aColor from aModel.“The pixel at (x, y)” refers to the color of the grid square defined by the points (x,y), (x+1, y), (x+1, y+1) and (x, y+1).
type Image interface { // ColorModel returns the Image's color model. ColorModel() color.Model // Bounds returns the domain for which At can return non-zero color. // The bounds do not necessarily contain the point (0, 0). Bounds() Rectangle // At returns the color of the pixel at (x, y). // At(Bounds().Min.X, Bounds().Min.Y) returns the upper-left pixel of the grid. // At(Bounds().Max.X-1, Bounds().Max.Y-1) returns the lower-right one. At(x, y int) color.Color}A common mistake is assuming that anImage’s bounds start at (0, 0).For example, an animated GIF contains a sequence of Images,and eachImage after the first typically only holds pixel data for the area that changed,and that area doesn’t necessarily start at (0, 0).The correct way to iterate over anImage m’s pixels looks like:
b := m.Bounds()for y := b.Min.Y; y < b.Max.Y; y++ { for x := b.Min.X; x < b.Max.X; x++ { doStuffWith(m.At(x, y)) }}Image implementations do not have to be based on an in-memory slice of pixel data.For example, aUniform is anImage of enormous bounds and uniform color,whose in-memory representation is simply that color.
type Uniform struct { C color.Color}Typically, though, programs will want an image based on a slice.Struct types likeRGBA andGray(which other packages refer to asimage.RGBA andimage.Gray) hold slicesof pixel data and implement theImage interface.
type RGBA struct { // Pix holds the image's pixels, in R, G, B, A order. The pixel at // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*4]. Pix []uint8 // Stride is the Pix stride (in bytes) between vertically adjacent pixels. Stride int // Rect is the image's bounds. Rect Rectangle}These types also provide aSet(x, y int, c color.Color) method that allows modifying the image one pixel at a time.
m := image.NewRGBA(image.Rect(0, 0, 640, 480))m.Set(5, 5, color.RGBA{255, 0, 0, 255})If you’re reading or writing a lot of pixel data,it can be more efficient, but more complicated,to access these struct type’sPix field directly.
The slice-basedImage implementations also provide aSubImage method,which returns anImage backed by the same array.Modifying the pixels of a sub-image will affect the pixels of the original image,analogous to how modifying the contents of a sub-slices[i0:i1] will affectthe contents of the original slices.

m0 := image.NewRGBA(image.Rect(0, 0, 8, 5))m1 := m0.SubImage(image.Rect(1, 2, 5, 5)).(*image.RGBA)fmt.Println(m0.Bounds().Dx(), m1.Bounds().Dx()) // prints 8, 4fmt.Println(m0.Stride == m1.Stride) // prints trueFor low-level code that works on an image’sPix field,be aware that ranging overPix can affect pixels outside an image’s bounds.In the example above, the pixels covered bym1.Pix are shaded in blue.Higher-level code, such as theAt andSet methods or theimage/draw package,will clip their operations to the image’s bounds.
Image Formats
The standard package library supports a number of common image formats,such as GIF, JPEG and PNG.If you know the format of a source image file,you can decode from anio.Reader directly.
import ( "image/jpeg" "image/png" "io")// convertJPEGToPNG converts from JPEG to PNG.func convertJPEGToPNG(w io.Writer, r io.Reader) error { img, err := jpeg.Decode(r) if err != nil { return err } return png.Encode(w, img)}If you have image data of unknown format,theimage.Decode function can detect the format.The set of recognized formats is constructed at run time and is not limitedto those in the standard package library.An image format package typically registers its format in an init function,and the main package will “underscore import” such a package solely forthe side effect of format registration.
import ( "image" "image/png" "io" _ "code.google.com/p/vp8-go/webp" _ "image/jpeg")// convertToPNG converts from any recognized format to PNG.func convertToPNG(w io.Writer, r io.Reader) error { img, _, err := image.Decode(r) if err != nil { return err } return png.Encode(w, img)}Next article:The Go image/draw package
Previous article:The Laws of Reflection
Blog Index
[8]ページ先頭