英文:
Draw a rectangle in Golang?
问题
我想绘制一个包含一些矩形、条形码的邮寄标签,然后生成一个PNG/PDF文件。
在Go语言中,除了逐像素地使用基本图形绘制形状之外,还有更好的方法吗?
英文:
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?
答案1
得分: 54
标准的Go库并不提供原始的绘图或绘画功能。
它提供了颜色模型(image/color
包)和一个带有多个实现的 Image
接口(image
包)。博客文章 Go Image 包 是对此的很好介绍。
它还提供了一个能够合并图像的能力(例如在彼此上绘制图像)的 image/draw
包中的不同操作。这可以用于更多的事情,比起一开始听起来的要多。有一篇很好的博客文章介绍了 image/draw
包,展示了它的一些潜力:Go image/draw 包。
另一个例子是开源游戏 Gopher's Labyrinth(声明:我是作者),它具有图形界面,只使用标准的Go库来组装视图。
它是开源的,可以查看它的源代码来了解它是如何实现的。它具有可滚动的游戏视图,其中包含移动的图像/动画。
标准库还支持读写常见的图像格式,如 GIF、JPEG、PNG,并且支持其他格式:BMP、RIFF、TIFF,甚至 WEBP(仅限读取/解码)。
虽然标准库不提供绘制线条和矩形的支持,但在图像上绘制线条和矩形是相当简单的。给定一个支持使用 Set(x, y int, c color.Color)
方法更改像素的 img
图像(例如 image.RGBA
对我们来说非常完美),以及一个类型为 color.Color
的 col
:
// HLine 绘制水平线
func HLine(x1, y, x2 int) {
for ; x1 <= x2; x1++ {
img.Set(x1, y, col)
}
}
// VLine 绘制垂直线
func VLine(x, y1, y2 int) {
for ; y1 <= y2; y1++ {
img.Set(x, y1, col)
}
}
// Rect 利用 HLine() 和 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} // 红色
HLine(10, 20, 80)
col = color.RGBA{0, 255, 0, 255} // 绿色
Rect(10, 10, 80, 50)
f, err := os.Create("draw.png")
if err != nil {
panic(err)
}
defer f.Close()
png.Encode(f, img)
}
如果你想绘制文本,可以使用 Go 实现的 FreeType。还可以查看这个问题,了解如何在图像上绘制字符串的简单介绍:https://stackoverflow.com/questions/38299930/how-do-add-a-text-label-to-an-image-in-go
如果你想要更高级和更复杂的绘图功能,还有许多外部库可用,例如:
https://github.com/llgcode/draw2d
https://github.com/fogleman/gg
英文:
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.
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: https://stackoverflow.com/questions/38299930/how-do-add-a-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:
答案2
得分: 15
这里我们使用标准的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" // 输出图像保存在这里
myimage := image.NewRGBA(image.Rect(0, 0, 220, 220)) // 背景矩形的 x1,y1, x2,y2
mygreen := color.RGBA{0, 100, 0, 255} // R, G, B, Alpha
// 用颜色 mygreen 填充整个背景
draw.Draw(myimage, myimage.Bounds(), &image.Uniform{mygreen}, image.ZP, draw.Src)
red_rect := image.Rect(60, 80, 120, 160) // 我们在上面的矩形上绘制的第二个矩形的几何形状
myred := color.RGBA{200, 0, 0, 255}
// 在绿色表面上创建一个红色矩形
draw.Draw(myimage, red_rect, &image.Uniform{myred}, image.ZP, draw.Src)
myfile, err := os.Create(new_png_file) // ... 现在让我们保存输出图像
if err != nil {
panic(err)
}
defer myfile.Close()
png.Encode(myfile, myimage) // 输出文件 /tmp/two_rectangles.png
}
以上代码将生成一个名为 /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} // 绿色
colors[1] = color.RGBA{50, 205, 50, 255} // 青柠绿
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 // 从 0 切换到 1 切换到 0 切换到 1 ...
}
loc_x += size_block
index_color = 1 - index_color // 从 0 切换到 1 切换到 0 切换到 1 ...
}
myfile, err := os.Create(new_png_file)
if err != nil {
panic(err.Error())
}
defer myfile.Close()
png.Encode(myfile, myimage) // ... 保存图像
fmt.Println("firefox ", new_png_file) // 查看图像问题:firefox /tmp/chessboard.png
}
英文:
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
}
答案3
得分: 5
你可能正在寻找draw2d包。根据他们在github
上的自述文件:
draw2d中的操作包括绘制多边形、弧线、贝塞尔曲线、绘制图像和使用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)
}
请参考github页面获取最新版本的信息。
英文:
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.
答案4
得分: 3
我来帮你翻译代码部分:
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) {
// 读取文件
f, err := os.Open(filePath)
if err != nil {
return nil, err
}
defer f.Close()
// 转换为image.Image
orig, _, err := image.Decode(f)
// 转换为可用的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() {
// 读取文件并转换
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()
}
这段代码的作用是从现有图片上绘制一个矩形(只有边框)。它使用了image
和image/draw
包来进行图像处理。代码中的drawRectangle
函数用于绘制矩形的边框,addRectangleToFace
函数用于将矩形添加到图像上,getImageFromFilePath
函数用于从文件路径获取图像。在main
函数中,它读取名为"src.png"的文件并将其转换为图像,然后在图像上添加一个矩形,并将结果保存为"dst.png"文件。
你提供的代码中还包含了一个结果图片的链接,但我无法在文本中显示图片。如果你有其他问题,可以继续问我。
英文:
I got here because I wanted to draw a rectangle (just the border) on from an existing picture. It adds to @Fakeer response the reading and writing on disk.
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()
}
答案5
得分: 0
我尝试画一个给定线条粗细的矩形,但还很原始。
func Rect(x1, y1, x2, y2, thickness int, img *image.RGBA) {
col := color.RGBA{0, 0, 0, 255}
for t := 0; t < thickness; t++ {
// 画水平线
for x := x1; x <= x2; x++ {
img.Set(x, y1+t, col)
img.Set(x, y2-t, col)
}
// 画垂直线
for y := y1; y <= y2; y++ {
img.Set(x1+t, y, col)
img.Set(x2-t, y, col)
}
}
}
// 测试处理程序
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)
}
以上是给定线条粗细的矩形绘制的代码。
英文:
My noob shot at drawing a rectangle of given line thickness. Still primitive
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)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论