This post is the first in a series. For a listing of all the posts, as well as instructions on running the code, see here. It is meant as an introduction to Go, by way of a bit of simple graphics processing. If you haven’t come across Go before, I’d encourage you to head over to http://golang.org to check it out, in particular going through the excellent online tutorial. At the very least you should be familiar with how to run a simple Go program, see here for details.
Anyway, here’s a preview of the beauty that we will soon bestow upon the world:
Such artwork is best digested in stages, and as such we’ll be looking at:
Go comes with the image package which makes it easy to write out in a variety of formats. Here’s how to create a blank PNG and write it out to a file:
width, height := 128, 128
m := image.NewRGBA(image.Rect(0, 0, width, height))
out_filename := "blank.png"
out_file, err := os.Create(out_filename)
if err != nil {
log.Fatal(err)
}
defer out_file.Close()
log.Print("Saving image to: ", out_filename)
png.Encode(out_file, m)
Full blank.go source on github
Now the above is hardly interesting, so let’s actually draw something. The image.RGBA
struct we created above is little more than a 2D array of pixels, so to create an image we’ll iterate over them, and set the color depending the position - creating a gradient effect:
func drawGradient(m image.RGBA) {
size := m.Bounds().Size()
for x := 0; x < size.X; x++ {
for y := 0; y < size.Y; y++ {
color := color.RGBA{
uint8(255 * x / size.X),
uint8(255 * y / size.Y),
55,
255}
m.Set(x, y, color)
}
}
}
Resulting in:
Full gradient.go source on github
Wouldn’t it be much nicer if we could just call drawGradient directly on the RGBA instance, rather than passing? Perhaps not, but we’re going to anyway.
Because the RGBA
type comes from an external package, we cannot declare methods on it. Instead, we’ll create a new type Canvas
, and embed the image.RGBA
type inside it. This is done by having the image.RGBA
type as an anonymous field when defining the struct, like so:
type Canvas struct {
image.RGBA
}
What is happening here is pretty neat. By declaring our Canvas
type in this way we will be able to access the embedded RGBA
instance on a Canvas
instance as we’d expect, at canvas.RGBA
. However, all the methods that are declared on the RGBA
type are now callable on our new Canvas
type, and will automatically get passed through to the embedded RGBA
type. This is very important, as we are now free to pass the Canvas
instance around, as if it were of RGBA
type, for example when we pass it to the png.Encode
function.
To initialize our new type, we need to first create an instance of it and then initialize the embedded RGBA
object.
func NewCanvas(r image.Rectangle) *Canvas {
canvas := new(Canvas)
canvas.RGBA = *image.NewRGBA(r)
return canvas
}
At this point the code should still work with the drawGradient method as long as we change the type of the parameter to Canvas
. However, we want to go one step further and make DrawGradient a method on the Canvas
type itself:
func (c Canvas)DrawGradient() {
size := c.Bounds().Size()
// Rest of function...
}
And we’re done! Now we can call canvas.DrawGradient
directly.
Full canvas.go source on github
Be sure to check out Drawing, where we’ll draw lines onto the canvas, as promised…