前一节讲的是 绘图到不同输出源,请看地址:
上一节的例子效果是通过设置每一个点的的RGBA属性来实现的,这是最基础的方式,通过这种方式我们可以绘制任意形状的图形。
1、设置点的颜色一个简单例子:
效果如下:
代码如下,跟最初我们的代码唯一不同的是设置点颜色时,多了一个条件判断语句:if x%8 == 0 ,代码如下,这种情况下,其实我们通过算法简单的实现了画垂直线的效果:
1: package main
2:
3: import (
4: "fmt"
5: "image"
6: "image/color"
7: "image/png"
8: "log"
9: "os"
10: )
11:
12: func main() {
13: const (
14: dx = 300
15: dy = 500
16: )
17:
18: // 需要保存的文件
19: imgcounter := 123
20: imgfile, _ := os.Create(fmt.Sprintf("%03d.png", imgcounter))
21: defer imgfile.Close()
22:
23: // 新建一个 指定大小的 RGBA位图
24: img := image.NewNRGBA(image.Rect(0, 0, dx, dy))
25:
26: for y := 0; y < dy; y++ {
27: for x := 0; x < dx; x++ {
28:
29: if x%8 == 0 {
30: // 设置某个点的颜色,依次是 RGBA
31: img.Set(x, y, color.RGBA{uint8(x % 256), uint8(y % 256), 0, 255})
32: }
33: }
34: }
35:
36: // 以PNG格式保存文件
37: err := png.Encode(imgfile, img)
38: if err != nil {
39: log.Fatal(err)
40: }
41:
42: }
2:
比如下面一个函数就是简单的画水平线的代码函数。
1: // 画 水平线
2: func (img *Image) drawHorizLine(color color.Color, fromX, toX, y int) {
3: // 遍历画每个点
4: for x := fromX; x <= toX; x++ {
5: img.Set(x, y, color)
6: }
7: }
2、划线
Golang 官方库没有提供划线的库,不过既然有了画点的方法,我们就可以根据一套算法画出点,下面的效果和代码是按照 Bresenham's line algorithm 算法画的线。
http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
这个算法画的线简单可以用下面图来标示:
下面演示代码画出来的效果图如下:
注意,为了便于看到效果, 图的左右都画了一条竖线,斜线是按照上面算法画出来的。
相关代码如下:
这里的代码借鉴了下面的代码。
https://github.com/akavel/polyclip-go/blob/9b07bdd6e0a784f7e5d9321bff03425ab3a98beb/polyutil/draw.go
1: package main
2:
3: import (
4: "fmt"
5: "image"
6: "image/color"
7: "image/png"
8: "log"
9: "os"
10: )
11:
12: // Putpixel describes a function expected to draw a point on a bitmap at (x, y) coordinates.
13: type Putpixel func(x, y int)
14:
15: // 求绝对值
16: func abs(x int) int {
17: if x >= 0 {
18: return x
19: }
20: return -x
21: }
22:
23: // Bresenham's algorithm, http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
24: // https://github.com/akavel/polyclip-go/blob/9b07bdd6e0a784f7e5d9321bff03425ab3a98beb/polyutil/draw.go
25: // TODO: handle int overflow etc.
26: func drawline(x0, y0, x1, y1 int, brush Putpixel) {
27: dx := abs(x1 - x0)
28: dy := abs(y1 - y0)
29: sx, sy := 1, 1
30: if x0 >= x1 {
31: sx = -1
32: }
33: if y0 >= y1 {
34: sy = -1
35: }
36: err := dx - dy
37:
38: for {
39: brush(x0, y0)
40: if x0 == x1 && y0 == y1 {
41: return
42: }
43: e2 := err * 2
44: if e2 > -dy {
45: err -= dy
46: x0 += sx
47: }
48: if e2 < dx {
49: err += dx
50: y0 += sy
51: }
52: }
53: }
54:
55: func main() {
56:
57: const (
58: dx = 300
59: dy = 500
60: )
61:
62: // 需要保存的文件
63:
64: // 新建一个 指定大小的 RGBA位图
65: img := image.NewNRGBA(image.Rect(0, 0, dx, dy))
66:
67: drawline(5, 5, dx-8, dy-10, func(x, y int) {
68: img.Set(x, y, color.RGBA{uint8(x), uint8(y), 0, 255})
69: })
70:
71: // 左右都画一条竖线
72: for i := 0; i < dy; i++ {
73: img.Set(0, i, color.Black)
74: img.Set(dx-1, i, color.Black)
75: }
76:
77: imgcounter := 250
78: imgfile, _ := os.Create(fmt.Sprintf("%03d.png", imgcounter))
79: defer imgfile.Close()
80:
81: // 以PNG格式保存文件
82: err := png.Encode(imgfile, img)
83: if err != nil {
84: log.Fatal(err)
85: }
86: }
3、特殊图形
这次绘制出来的图形效果如下:
相关代码如下:
这里的代码借鉴了下面的代码:
https://github.com/xMachinae/pallinda13/blob/master/uppg2.go
1: package main
1: package main
2:
3: import (
4: "fmt"
5: "image"
6: "image/png"
7: "log"
8: "os"
9: )
10:
11: func Pic(dx, dy int) [][]uint8 {
12: pic := make([][]uint8, dx)
13: for i := range pic {
14: pic[i] = make([]uint8, dy)
15: for j := range pic[i] {
16: pic[i][j] = uint8(i * j)
17: }
18: }
19: return pic
20: }
21:
22: func main() {
23: Show(Pic)
24: }
25:
26: func Show(f func(int, int) [][]uint8) {
27: const (
28: dx = 256
29: dy = 256
30: )
31: data := f(dx, dy) // 图片坐标点的颜色二维数组。
32: m := image.NewNRGBA(image.Rect(0, 0, dx, dy))
33: for y := 0; y < dy; y++ {
34: for x := 0; x < dx; x++ {
35: v := data[y][x]
36: i := y*m.Stride + x*4
37: m.Pix[i] = v
38: m.Pix[i+1] = v
39: m.Pix[i+2] = 255
40: m.Pix[i+3] = 255
41: }
42: }
43: ShowImage(m)
44: }
45:
46: func ShowImage(m image.Image) {
47:
48: // 需要保存的文件
49: imgcounter := 1234
50: imgfile, _ := os.Create(fmt.Sprintf("%03d.png", imgcounter))
51: defer imgfile.Close()
52:
53: // 以PNG格式保存文件
54: err := png.Encode(imgfile, m)
55: if err != nil {
56: log.Fatal(err)
57: }
58:
59: }
复杂的算法
比如下面代码实现了图片简单的上下左右翻转的功能。
图片旋转的算法
https://github.com/mpl/goexif/blob/a588a5577cedfda71e3645f8137c38495f308f6c/exif/rotate_test.go