Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Draw a rectangle in Golang?

Tags:

image

go

draw

I want to draw a mailing label with some rectangles, barcodes, and then finally generate a PNG/PDF file.

Is there is a better way to draw a shape in Go other than to do it with primitives - pixel by pixel?

like image 778
Fakeer Avatar asked Mar 11 '15 16:03

Fakeer


3 Answers

The standard Go library does not provide primitive drawing or painting capabilities.

What it provides is models for colors (image/color package) and an Image interface with several implementations (image package). The blog post The Go Image package is a good introduction to this.

It also provides a capability to combine images (e.g. draw them on each other) with different operations in the image/draw package. This can be used to a lot more than it sounds at first. There is a nice blog article about the image/draw package which showcases some of its potential: The Go image/draw package

Another example is the open-source game Gopher's Labyrinth (disclosure: I'm the author) which has a graphical interface and it uses nothing else just the standard Go library to assemble its view.

Gopher's Labyrinth Screenshot

It's open source, check out its sources how it is done. It has a scrollable game view with moving images/animations in it.

The standard library also supports reading and writing common image formats like GIF, JPEG, PNG, and support for other formats are available out of the box: BMP, RIFF, TIFF and even WEBP (only a reader/decoder).

Although support is not given by the standard library, it is fairly easy to draw lines and rectangles on an image. Given an img image which supports changing a pixel with a method: Set(x, y int, c color.Color) (for example image.RGBA is perfect for us) and a col of type color.Color:

// HLine draws a horizontal line
func HLine(x1, y, x2 int) {
    for ; x1 <= x2; x1++ {
        img.Set(x1, y, col)
    }
}

// VLine draws a veritcal line
func VLine(x, y1, y2 int) {
    for ; y1 <= y2; y1++ {
        img.Set(x, y1, col)
    }
}

// Rect draws a rectangle utilizing HLine() and VLine()
func Rect(x1, y1, x2, y2 int) {
    HLine(x1, y1, x2)
    HLine(x1, y2, x2)
    VLine(x1, y1, y2)
    VLine(x2, y1, y2)
}

Using these simple functions here is a runnable example program which draws a line and a rectangle and saves the image into a .png file:

import (
    "image"
    "image/color"
    "image/png"
    "os"
)

var img = image.NewRGBA(image.Rect(0, 0, 100, 100))
var col color.Color

func main() {
    col = color.RGBA{255, 0, 0, 255} // Red
    HLine(10, 20, 80)
    col = color.RGBA{0, 255, 0, 255} // Green
    Rect(10, 10, 80, 50)

    f, err := os.Create("draw.png")
    if err != nil {
        panic(err)
    }
    defer f.Close()
    png.Encode(f, img)
}

If you want to draw texts, you can use the Go implementation of FreeType. Also check out this question for a simple introduction to drawing strings on images: How to add a simple text label to an image in Go?

If you want advanced and more complex drawing capabilities, there are also many external libraries available, for example:

https://github.com/llgcode/draw2d

https://github.com/fogleman/gg

like image 153
icza Avatar answered Nov 14 '22 13:11

icza


Here we draw two rectangles using standard golang libraries

// https://blog.golang.org/go-imagedraw-package

package main

import (
    "image"
    "image/color"
    "image/draw"
    "image/png"
    "os"
)

func main() {

    new_png_file := "/tmp/two_rectangles.png" // output image will live here

    myimage := image.NewRGBA(image.Rect(0, 0, 220, 220)) // x1,y1,  x2,y2 of background rectangle
    mygreen := color.RGBA{0, 100, 0, 255}  //  R, G, B, Alpha

    // backfill entire background surface with color mygreen
    draw.Draw(myimage, myimage.Bounds(), &image.Uniform{mygreen}, image.ZP, draw.Src)

    red_rect := image.Rect(60, 80, 120, 160) //  geometry of 2nd rectangle which we draw atop above rectangle
    myred := color.RGBA{200, 0, 0, 255}

    // create a red rectangle atop the green surface
    draw.Draw(myimage, red_rect, &image.Uniform{myred}, image.ZP, draw.Src)

    myfile, err := os.Create(new_png_file)     // ... now lets save output image
    if err != nil {
        panic(err)
    }
    defer myfile.Close()
    png.Encode(myfile, myimage)   // output file /tmp/two_rectangles.png
}

above will generate a png file /tmp/two_rectangles.png with our two rectangles :

following code will create a chessboard image from rectangles

package main

import (
    "fmt"
    "image"
    "image/color"
    "image/draw"
    "image/png"
    "os"
)

func main() {

    new_png_file := "/tmp/chessboard.png" // output image lives here
    board_num_pixels := 240

    myimage := image.NewRGBA(image.Rect(0, 0, board_num_pixels, board_num_pixels))
    colors := make(map[int]color.RGBA, 2)

    colors[0] = color.RGBA{0, 100, 0, 255}   // green
    colors[1] = color.RGBA{50, 205, 50, 255} // limegreen

    index_color := 0
    size_board := 8
    size_block := int(board_num_pixels / size_board)
    loc_x := 0

    for curr_x := 0; curr_x < size_board; curr_x++ {

        loc_y := 0
        for curr_y := 0; curr_y < size_board; curr_y++ {

            draw.Draw(myimage, image.Rect(loc_x, loc_y, loc_x+size_block, loc_y+size_block),
                &image.Uniform{colors[index_color]}, image.ZP, draw.Src)

            loc_y += size_block
            index_color = 1 - index_color // toggle from 0 to 1 to 0 to 1 to ...
        }
        loc_x += size_block
        index_color = 1 - index_color // toggle from 0 to 1 to 0 to 1 to ...
    }
    myfile, err := os.Create(new_png_file) 
    if err != nil {
        panic(err.Error())
    }
    defer myfile.Close()
    png.Encode(myfile, myimage) // ... save image
    fmt.Println("firefox ", new_png_file) // view image issue : firefox  /tmp/chessboard.png
}

like image 35
Scott Stensland Avatar answered Nov 14 '22 11:11

Scott Stensland


You are probably looking for the draw2d package. From their github readme:

Operations in draw2d include stroking and filling polygons, arcs, Bézier curves, drawing images and text rendering with truetype fonts. All drawing operations can be transformed by affine transformations (scale, rotation, translation).

The following code draws a black rectangle and writes it to a .png file. It is using the v1 release (go get -u github.com/llgcode/draw2d).

package main

import (
        "github.com/llgcode/draw2d/draw2dimg"

        "image"
        "image/color"
)

func main() {
        i := image.NewRGBA(image.Rect(0, 0, 200, 200))
        gc := draw2dimg.NewGraphicContext(i)
        gc.Save()
        gc.SetStrokeColor(color.Black)
        gc.SetFillColor(color.Black)
        draw2d.Rect(gc, 10, 10, 100, 100)
        gc.FillStroke()
        gc.Restore()

        draw2dimg.SaveToPngFile("yay-rectangle.png", i)
}

Please consult the github page for the newest version.

like image 5
Rick Smith Avatar answered Nov 14 '22 13:11

Rick Smith