aboutsummaryrefslogtreecommitdiff
path: root/content/image-draw.article
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2020-03-15 15:50:36 -0400
committerRuss Cox <rsc@golang.org>2020-03-17 20:58:46 +0000
commit972d42d925e6cae3f8eebd9b21d445e06c2eb386 (patch)
tree737af27f0d49318b612efec874b1d1328c699d1a /content/image-draw.article
parentfaf1e2da2d911edc717993e8edb24fe88f99b2b5 (diff)
content: rename articles to reinforce convention of short URLs
The Go blog started out on Blogger (http://web.archive.org/web/20100325005843/http://blog.golang.org/). Later, we moved to the current self-hosted blog server with extra Go-specific functionality like playground snippets. The old Blogger posts have very long URLs that Blogger chose for us, such as "go-programming-language-turns-two" or "two-go-talks-lexical-scanning-in-go-and", predating the convention of giving posts shorter, more share-friendly, typeable names. The conversion of the old Blogger posts also predated the convention of putting supporting files in a subdirectory. The result is that although we've established new conventions, you wouldn't know by listing the directory - the old Blogger content presents a conflicting picture. This commit renames the posts with very long names to have shorter, more share-friendly names, and it moves all supporting files to subdirectories. It also adds a README documenting the conventions. For example, blog.golang.org/go-programming-language-turns-two is now blog.golang.org/2years, matching our more recent birthday post URLs, and its supporting files are moved to the new 2years/ directory. The old URLs redirect to the new ones. Change-Id: I9f46a790c2c8fab8459aeda73d4e3d2efc86d88f Reviewed-on: https://go-review.googlesource.com/c/blog/+/223599 Run-TryBot: Russ Cox <rsc@golang.org> Reviewed-by: Andrew Bonventre <andybons@golang.org>
Diffstat (limited to 'content/image-draw.article')
-rw-r--r--content/image-draw.article215
1 files changed, 215 insertions, 0 deletions
diff --git a/content/image-draw.article b/content/image-draw.article
new file mode 100644
index 0000000..aa2f77a
--- /dev/null
+++ b/content/image-draw.article
@@ -0,0 +1,215 @@
+# The Go image/draw package
+29 Sep 2011
+Tags: draw, image, libraries, technical
+Summary: An introduction to image compositing in Go using the image/draw package.
+OldURL: /go-imagedraw-package
+
+Nigel Tao
+
+## Introduction
+
+[Package image/draw](https://golang.org/pkg/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.
+
+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.
+
+The Porter-Duff paper presented [12 different composition operators](http://www.w3.org/TR/SVGCompositing/examples/compop-porterduff-examples.png),
+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.
+
+## Geometric Alignment
+
+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.
+
+.image image-draw/20.png
+
+The [`DrawMask`](https://golang.org/pkg/image/draw/#DrawMask) function
+takes seven arguments,
+but an explicit mask and mask-point are usually unnecessary,
+so the [`Draw`](https://golang.org/pkg/image/draw/#Draw) function takes five:
+
+ // Draw calls DrawMask with a nil mask.
+ func Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point, op Op)
+ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point,
+ mask image.Image, mp image.Point, op Op)
+
+The destination image must be mutable, so the image/draw package defines
+a [`draw.Image`](https://golang.org/pkg/image/draw/#Image) interface which has a `Set` method.
+
+ type Image interface {
+ image.Image
+ Set(x, y int, c color.Color)
+ }
+
+## Filling a Rectangle
+
+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`.
+
+ // image.ZP is the zero point -- the origin.
+ draw.Draw(dst, r, &image.Uniform{c}, image.ZP, draw.Src)
+
+To initialize a new image to all-blue:
+
+ m := image.NewRGBA(image.Rect(0, 0, 640, 480))
+ blue := color.RGBA{0, 0, 255, 255}
+ draw.Draw(m, m.Bounds(), &image.Uniform{blue}, image.ZP, draw.Src)
+
+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`:
+
+ draw.Draw(m, m.Bounds(), image.Transparent, image.ZP, draw.Src)
+
+.image image-draw/2a.png
+
+## Copying an Image
+
+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:
+
+ r := image.Rectangle{dp, dp.Add(sr.Size())}
+ draw.Draw(dst, r, src, sr.Min, draw.Src)
+
+Alternatively:
+
+ r := sr.Sub(sr.Min).Add(dp)
+ draw.Draw(dst, r, src, sr.Min, draw.Src)
+
+To copy the entire source image, use `sr = src.Bounds()`.
+
+.image image-draw/2b.png
+
+## Scrolling an Image
+
+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:
+
+ b := m.Bounds()
+ p := image.Pt(0, 20)
+ // Note that even though the second argument is b,
+ // the effective rectangle is smaller due to clipping.
+ draw.Draw(m, b, m, b.Min.Add(p), draw.Src)
+ dirtyRect := b.Intersect(image.Rect(b.Min.X, b.Max.Y-20, b.Max.X, b.Max.Y))
+
+.image image-draw/2c.png
+
+## Converting an Image to RGBA
+
+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`:
+
+ b := src.Bounds()
+ m := image.NewRGBA(image.Rect(0, 0, b.Dx(), b.Dy()))
+ draw.Draw(m, m.Bounds(), src, b.Min, draw.Src)
+
+.image image-draw/2d.png
+
+## Drawing Through a Mask
+
+To draw an image through a circular mask with center `p` and radius `r`:
+
+ type circle struct {
+ p image.Point
+ r int
+ }
+
+ func (c *circle) ColorModel() color.Model {
+ return color.AlphaModel
+ }
+
+ func (c *circle) Bounds() image.Rectangle {
+ return image.Rect(c.p.X-c.r, c.p.Y-c.r, c.p.X+c.r, c.p.Y+c.r)
+ }
+
+ func (c *circle) At(x, y int) color.Color {
+ xx, yy, rr := float64(x-c.p.X)+0.5, float64(y-c.p.Y)+0.5, float64(c.r)
+ if xx*xx+yy*yy < rr*rr {
+ return color.Alpha{255}
+ }
+ return color.Alpha{0}
+ }
+
+ draw.DrawMask(dst, dst.Bounds(), src, image.ZP, &circle{p, r}, image.ZP, draw.Over)
+
+.image image-draw/2e.png
+
+## Drawing Font Glyphs
+
+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.
+
+ src := &image.Uniform{color.RGBA{0, 0, 255, 255}}
+ mask := theGlyphImageForAFont()
+ mr := theBoundsFor(glyphIndex)
+ draw.DrawMask(dst, mr.Sub(mr.Min).Add(p), src, image.ZP, mask, mr.Min, draw.Over)
+
+.image image-draw/2f.png
+
+## Performance
+
+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.