【问题标题】:Draw a rectangle in Golang?在 Golang 中画一个矩形?
【发布时间】:2015-03-11 16:40:40
【问题描述】:

我想画一个带有一些矩形、条码的邮件标签,然后最后生成一个PNG/PDF文件。

在 Go 中,除了逐个像素地使用基元来绘制形状之外,还有更好的方法吗?

【问题讨论】:

  • 我没有尝试过这些,但code.google.com/p/rog-go/source/browse/canvascode.google.com/p/draw2d 可能会有所帮助。
  • 感谢查看 draw2d。下载但遇到大量编译问题(我在 appengine 堆栈上)。很多工作才刚刚开始。最坏的情况会尝试解决它们。
  • 大声笑,要求划清问题被否决的界限是多么令人反感!
  • 我不是投反对票的人之一,所以我不确定,但我认为可能是最初的措辞听起来有点生气(“简单矩形”,“是它只是我或......”,“我只是想......”)。通常在 SO 上会更好地尽可能客观地陈述问题,即使您感到沮丧(这种情况发生了!)。
  • 您可能想要使用新的github.com/llgcode/draw2d 存储库。该库运行良好,库的一个新分支表明将进行清理以增强库的可读性和可用性:参见Draw a line 示例代码和Rect 函数

标签: image go draw


【解决方案1】:

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

它提供的是颜色模型(image/color 包)和带有多个实现的Image 接口(@98​​7654323@ 包)。博文The Go Image package 对此进行了很好的介绍。

它还提供了在 image/draw 包中使用不同操作组合图像(例如,将它们相互绘制)的功能。这可以比一开始听起来要多得多。有一篇关于 image/draw 软件包的精彩博客文章展示了它的一些潜力:The Go image/draw package

另一个例子是开源游戏Gopher's Labyrinth披露:我是作者),它有一个图形界面,它只使用标准 Go 库来组装它的视图。

它是开源的,请查看它的源代码是如何完成的。它有一个可滚动的游戏视图,其中包含移动图像/动画。

标准库还支持读写常见的图像格式,如GIFJPEGPNG,并且开箱即可支持其他格式:BMPRIFFTIFF甚至WEBP(只有一个阅读器/解码器)。

虽然标准库不提供支持,但在图像上绘制线条和矩形相当容易。给定一个支持使用方法更改像素的img 图像:Set(x, y int, c color.Color)(例如image.RGBA 非常适合我们)和col 类型的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。还可以查看这个问题,了解在图像上绘制字符串的简单介绍:How to add a simple text label to an image in Go?

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

https://github.com/llgcode/draw2d

https://github.com/fogleman/gg

【讨论】:

  • icza 非常感谢。我确实看到了图像 pkg 结构。只是无法“连接点”并找到内置的东西,比如你在 hline 和 vline 中所做的事情。这是我不想做的事情,因为线算法对于光栅化抗锯齿、bresenham 等并不简单。我现在将尝试您的示例并学习。也感谢您建议 freetype - 在标签上放置文本也是我的要求的一部分,我忘了在问题中提出。
  • icza 整洁的游戏顺便说一句。为什么不在 appengine 上免费托管呢?
  • @Fakeer 因为它还没有为 AppEngine 和生产做好准备。游戏在不到 48 小时内完成(Gopher Gala 规则)。所以这更像是一个演示,而不是如果花更多的时间在它上面会有多好。
  • +1 提供了一个简单的工作示例。这是我发现的第一个向我展示如何逐像素绘制并保存到 PNG 的方法。现在我要开始学习这门学习课程了github.com/ssloy/tinyrenderer/wiki
【解决方案2】:

这里我们使用标准的 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
}

【讨论】:

    【解决方案3】:

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

    draw2d 中的操作包括描边和填充多边形、圆弧、Bézier 曲线、绘制图像和使用 truetype 字体渲染文本。所有绘图操作都可以通过仿射变换(缩放、旋转、平移)进行变换。

    以下代码绘制一个黑色矩形并将其写入.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

    【讨论】:

    • 我不明白为什么每个人都发布这个损坏的例子。包 draw2d 不包含 NewGraphicContext 和 SaveToPngFile。如果您尝试运行此代码,您会得到类似“.\generate.go:13: undefined: draw2d.NewGraphicContext” 这两个函数都在 github.com/llgcode/draw2d/draw2dimg 中。
    • github页面上的例子已经改变。我已经更新了我的代码以使用新版本
    • 我认为 NewGraphicContext 需要 draw.Image 对吗? image.Image 没有“set()”函数。
    • @RobertRoss 正确,image.NewRGBA 是实现这两个接口的结构。 NewGraphicContext 看到 image.RGBA 并知道它实现了 draw.Image(和 image.Image)。我希望这回答了你的问题。
    • 这个例子还是有问题的:./main.go:16: undefined: draw2d in draw2d.Rect。相反,它正在运行的 github 页面中的示例。
    【解决方案4】:

    我的菜鸟想画一个给定线条粗细的矩形。还是很原始的

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

    【讨论】:

      【解决方案5】:

      我来到这里是因为我想在现有图片上绘制一个矩形(只是边框)。它添加到@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()
      }
      

      结果如下:

      【讨论】:

        猜你喜欢
        • 2017-03-21
        • 2020-04-14
        • 2014-03-31
        • 1970-01-01
        • 2014-01-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多