github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/blog/content/go-imagedraw-package.article (about)

     1  The Go image/draw package
     2  29 Sep 2011
     3  Tags: draw, image, libraries, technical
     4  
     5  Nigel Tao
     6  
     7  * Introduction
     8  
     9  [[http://golang.org/pkg/image/draw/][Package image/draw]] defines only one operation: drawing a source image onto a destination image, through an optional mask image. This one operation is surprisingly versatile and can perform a number of common image manipulation tasks elegantly and efficiently.
    10  
    11  Composition is performed pixel by pixel in the style of the Plan 9 graphics library and the X Render extension. The model is based on the classic "Compositing Digital Images" paper by Porter and Duff, with an additional mask parameter: `dst`=`(src`IN`mask)`OP`dst`. For a fully opaque mask, this reduces to the original Porter-Duff formula: `dst`=`src`OP`dst`. In Go, a nil mask image is equivalent to an infinitely sized, fully opaque mask image.
    12  
    13  The Porter-Duff paper presented [[http://www.w3.org/TR/SVGCompositing/examples/compop-porterduff-examples.png][12 different composition operators]], but with an explicit mask, only 2 of these are needed in practice: source-over-destination and source. In Go, these operators are represented by the `Over` and `Src` constants. The `Over` operator performs the natural layering of a source image over a destination image: the change to the destination image is smaller where the source (after masking) is more transparent (that is, has lower alpha). The `Src` operator merely copies the source (after masking) with no regard for the destination image's original content. For fully opaque source and mask images, the two operators produce the same output, but the `Src` operator is usually faster.
    14  
    15  * Geometric Alignment
    16  
    17  Composition requires associating destination pixels with source and mask pixels. Obviously, this requires destination, source and mask images, and a composition operator, but it also requires specifying what rectangle of each image to use. Not every drawing should write to the entire destination: when updating an animating image, it is more efficient to only draw the parts of the image that have changed. Not every drawing should read from the entire source: when using a sprite that combines many small images into one large one, only a part of the image is needed. Not every drawing should read from the entire mask: a mask image that collects a font's glyphs is similar to a sprite. Thus, drawing also needs to know three rectangles, one for each image. Since each rectangle has the same width and height, it suffices to pass a destination rectangle `r` and two points `sp` and `mp`: the source rectangle is equal to `r` translated so that `r.Min` in the destination image aligns with `sp` in the source image, and similarly for `mp`. The effective rectangle is also clipped to each image's bounds in their respective co-ordinate space.
    18  
    19  .image go-imagedraw-package_20.png
    20  
    21  The [[http://golang.org/pkg/image/draw/#DrawMask][`DrawMask`]] function takes seven arguments, but an explicit mask and mask-point are usually unnecessary, so the [[http://golang.org/pkg/image/draw/#Draw][`Draw`]] function takes five:
    22  
    23  	// Draw calls DrawMask with a nil mask.
    24  	func Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point, op Op)
    25  	func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point,
    26  	 mask image.Image, mp image.Point, op Op)
    27  
    28  The destination image must be mutable, so the image/draw package defines a [[http://golang.org/pkg/image/draw/#Image][`draw.Image`]] interface which has a `Set` method.
    29  
    30  	type Image interface {
    31  	    image.Image
    32  	    Set(x, y int, c color.Color)
    33  	}
    34  
    35    
    36  * Filling a Rectangle
    37  
    38  To fill a rectangle with a solid color, use an `image.Uniform` source. The `ColorImage` type re-interprets a `Color` as a practically infinite-sized `Image` of that color. For those familiar with the design of Plan 9's draw library, there is no need for an explicit "repeat bit" in Go's slice-based image types; the concept is subsumed by `Uniform`.
    39  
    40  	    // image.ZP is the zero point -- the origin.
    41  	    draw.Draw(dst, r, &image.Uniform{c}, image.ZP, draw.Src)
    42  
    43  To initialize a new image to all-blue:
    44  
    45  	    m := image.NewRGBA(image.Rect(0, 0, 640, 480))
    46  	    blue := color.RGBA{0, 0, 255, 255}
    47  	    draw.Draw(m, m.Bounds(), &image.Uniform{blue}, image.ZP, draw.Src)
    48  
    49  To reset an image to transparent (or black, if the destination image's color model cannot represent transparency), use `image.Transparent`, which is an `image.Uniform`:
    50  
    51  	    draw.Draw(m, m.Bounds(), image.Transparent, image.ZP, draw.Src)
    52  
    53    
    54  .image go-imagedraw-package_2a.png
    55  
    56   
    57  * Copying an Image
    58  
    59  To copy from a rectangle `sr` in the source image to a rectangle starting at a point `dp` in the destination, convert the source rectangle into the destination image's co-ordinate space:
    60  
    61  	    r := image.Rectangle{dp, dp.Add(sr.Size())}
    62  	    draw.Draw(dst, r, src, sr.Min, draw.Src)
    63  
    64    
    65  Alternatively:
    66  
    67  	    r := sr.Sub(sr.Min).Add(dp)
    68  	    draw.Draw(dst, r, src, sr.Min, draw.Src)
    69  
    70    
    71  To copy the entire source image, use `sr`=`src.Bounds()`.
    72  
    73    
    74  .image go-imagedraw-package_2b.png
    75  
    76   
    77  * Scrolling an Image
    78  
    79  Scrolling an image is just copying an image to itself, with different destination and source rectangles. Overlapping destination and source images are perfectly valid, just as Go's built-in copy function can handle overlapping destination and source slices. To scroll an image m by 20 pixels:
    80  
    81  	    b := m.Bounds()
    82  	    p := image.Pt(0, 20)
    83  	    // Note that even though the second argument is b,
    84  	    // the effective rectangle is smaller due to clipping.
    85  	    draw.Draw(m, b, m, b.Min.Add(p), draw.Src)
    86  	    dirtyRect := b.Intersect(image.Rect(b.Min.X, b.Max.Y-20, b.Max.X, b.Max.Y))
    87  
    88    
    89  .image go-imagedraw-package_2c.png
    90  
    91   
    92  * Converting an Image to RGBA
    93  
    94  The result of decoding an image format might not be an `image.RGBA`: decoding a GIF results in an `image.Paletted`, decoding a JPEG results in a `ycbcr.YCbCr`, and the result of decoding a PNG depends on the image data. To convert any image to an `image.RGBA`:
    95  
    96  	    b := src.Bounds()
    97  	    m := image.NewRGBA(image.Rect(0, 0, b.Dx(), b.Dy()))
    98  	    draw.Draw(m, m.Bounds(), src, b.Min, draw.Src)
    99  
   100    
   101  .image go-imagedraw-package_2d.png
   102  
   103  * Drawing Through a Mask
   104  
   105  To draw an image through a circular mask with center `p` and radius `r`:
   106  
   107  	type circle struct {
   108  	    p image.Point
   109  	    r int
   110  	}
   111  
   112  	func (c *circle) ColorModel() color.Model {
   113  	    return color.AlphaModel
   114  	}
   115  
   116  	func (c *circle) Bounds() image.Rectangle {
   117  	    return image.Rect(c.p.X-c.r, c.p.Y-c.r, c.p.X+c.r, c.p.Y+c.r)
   118  	}
   119  
   120  	func (c *circle) At(x, y int) color.Color {
   121  	    xx, yy, rr := float64(x-c.p.X)+0.5, float64(y-c.p.Y)+0.5, float64(c.r)
   122  	    if xx*xx+yy*yy < rr*rr {
   123  	        return color.Alpha{255}
   124  	    }
   125  	    return color.Alpha{0}
   126  	}
   127  
   128  	    draw.DrawMask(dst, dst.Bounds(), src, image.ZP, &circle{p, r}, image.ZP, draw.Over)
   129  
   130    
   131  .image go-imagedraw-package_2e.png
   132  
   133  * Drawing Font Glyphs
   134  
   135  To draw a font glyph in blue starting from a point `p`, draw with an `image.ColorImage` source and an `image.Alpha`mask`. For simplicity, we aren't performing any sub-pixel positioning or rendering, or correcting for a font's height above a baseline.
   136  
   137  	    src := &image.Uniform{color.RGBA{0, 0, 255, 255}}
   138  	    mask := theGlyphImageForAFont()
   139  	    mr := theBoundsFor(glyphIndex)
   140  	    draw.DrawMask(dst, mr.Sub(mr.Min).Add(p), src, image.ZP, mask, mr.Min, draw.Over)
   141  
   142  .image go-imagedraw-package_2f.png
   143  
   144    
   145  * Performance
   146  
   147  The image/draw package implementation demonstrates how to provide an image manipulation function that is both general purpose, yet efficient for common cases. The `DrawMask` function takes arguments of interface types, but immediately makes type assertions that its arguments are of specific struct types, corresponding to common operations like drawing one `image.RGBA` image onto another, or drawing an `image.Alpha` mask (such as a font glyph) onto an `image.RGBA` image. If a type assertion succeeds, that type information is used to run a specialized implementation of the general algorithm. If the assertions fail, the fallback code path uses the generic `At` and `Set` methods. The fast-paths are purely a performance optimization; the resultant destination image is the same either way. In practice, only a small number of special cases are necessary to support typical applications.