在 Golang 中画一个矩形?

Draw a rectangle in Golang?

我想画一个邮寄标签,里面有一些矩形,条形码,最后生成一个PNG/PDF文件。

在 Go 中是否有更好的绘制形状的方法,而不是使用基元 - 逐像素绘制?

标准 Go 库不提供原始绘图或绘画功能。

它提供的是颜色模型(image/color package) and an Image interface with several implementations (image package). The blog post The Go Image package对此有很好的介绍。

它还提供了在 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

中通过不同操作组合图像(例如,将它们相互绘制)的功能

另一个例子是开源游戏 Gopher's Labyrinth披露:我是作者),它有一个图形界面,它只使用标准转到图书馆 assemble 其视图。

它是开源的,查看它的来源是如何完成的。它有一个可滚动的游戏视图,其中有移动 images/animations。

标准库还支持读写常见的图像格式,如GIF, JPEG, PNG, and support for other formats are available out of the box: BMP, RIFF, TIFF and even WEBP(只有一个reader/decoder)。

虽然标准库不提供支持,但在图像上绘制线条和矩形相当容易。给定一个 img 图像,它支持使用以下方法更改像素:Set(x, y int, c color.Color)(例如 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)
}

这里使用这些简单的函数是一个可运行的示例程序,它绘制一条线和一个矩形并将图像保存到 .png 文件中:

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)
}

如果要绘制文字,可以使用Go implementation of FreeType. Also check out this question for a simple introduction to drawing strings on images:

如果您想要高级和更复杂的绘图功能,还有 many external libraries 可用,例如:

https://github.com/llgcode/draw2d

https://github.com/fogleman/gg

我的新手尝试绘制给定线条粗细的矩形。依旧原始

func Rect(x1, y1, x2, y2, thickness int, img *image.RGBA) {
    col := color.RGBA{0, 0, 0, 255}

    for t:=0; t<thickness; t++ {
        // draw horizontal lines
        for x := x1; x<= x2; x++ {
            img.Set(x, y1+t, col)
            img.Set(x, y2-t, col)
        }
        // draw vertical lines
        for y := y1; y <= y2; y++ {
            img.Set(x1+t, y, col)
            img.Set(x2-t, y, col)
        }
    }
}


// handler to test
func draw(w http.ResponseWriter, r *http.Request) {
    img := image.NewRGBA(image.Rect(0, 0, 1200, 1800))
    Rect(5, 5, 1195, 1795, 2, img)
    png.Encode(w, img)
}

您可能正在寻找 draw2d 包。来自他们的 github 自述文件:

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).

以下代码绘制一个黑色矩形并将其写入 .png 文件。它使用的是 v1 版本 (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)
}

最新版本请参考the github page

这里我们使用标准的golang库绘制两个矩形

// 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 lives 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 imag
    if err != nil {
        panic(err)
    }
    defer myfile.Close()
    png.Encode(myfile, myimage)   // output file /tmp/two_rectangles.png
}

上面将生成一个带有我们两个矩形的 png 文件:

以下代码将从矩形创建棋盘图像

package main

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

func main() {

    new_png_file := "/tmp/chessboard.png"
    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
}

我来到这里是因为我想在现有图片上绘制一个矩形(只是边框)。它增加了@Fakeer 对磁盘读写的响应。

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

func drawRectangle(img draw.Image, color color.Color, x1, y1, x2, y2 int) {
    for i:= x1; i<x2; i++ {
        img.Set(i, y1, color)
        img.Set(i, y2, color)
    }

    for i:= y1; i<=y2; i++ {
        img.Set(x1, i, color)
        img.Set(x2, i, color)
    }
}

func addRectangleToFace(img draw.Image, rect image.Rectangle) (draw.Image) {
    myColor := color.RGBA{255, 0, 255, 255}

    min := rect.Min
    max := rect.Max

    drawRectangle(img, myColor, min.X, min.Y, max.X, max.Y)

    return img
}

func getImageFromFilePath(filePath string) (draw.Image, error) {

    // read file
    f, err := os.Open(filePath)
    if err != nil {
        return nil, err
    }
    defer f.Close()

    // convert as image.Image
    orig, _, err := image.Decode(f)

    // convert as usable image
    b := orig.Bounds()
    img := image.NewRGBA(image.Rect(0, 0, b.Dx(), b.Dy()))
    draw.Draw(img, img.Bounds(), orig, b.Min, draw.Src)

    return img, err
}

func main() {
    // read file and convert it
    src, err := getImageFromFilePath("src.png")
    if err != nil {
        panic(err.Error())
    }

    myRectangle := image.Rect(10, 20, 30, 40)

    dst := addRectangleToFace(src, myRectangle)

    outputFile, err := os.Create("dst.png")
    if err != nil {
        panic(err.Error())
    }

    png.Encode(outputFile, dst)

    outputFile.Close()
}

结果如下: