posts - 504, comments - 195, trackbacks - 0, articles - 0

导航

Golang 绘图基础 -绘制简单图形

Posted on 2013-11-25 15:02  蝈蝈俊  阅读(...)  评论(...编辑  收藏

前一节讲的是 绘图到不同输出源,请看地址: http://www.cnblogs.com/ghj1976/p/3440856.html

上一节的例子效果是通过设置每一个点的的RGBA属性来实现的,这是最基础的方式,通过这种方式我们可以绘制任意形状的图形。

1、设置点的颜色一个简单例子:

效果如下:

123

代码如下,跟最初我们的代码唯一不同的是设置点颜色时,多了一个条件判断语句: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: }

比如下面一个函数就是简单的画水平线的代码函数。

   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 

这个算法画的线简单可以用下面图来标示:

800px-Bresenham.svg

下面演示代码画出来的效果图如下:

注意,为了便于看到效果, 图的左右都画了一条竖线,斜线是按照上面算法画出来的。

250

相关代码如下:
这里的代码借鉴了下面的代码。
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、特殊图形

这次绘制出来的图形效果如下:

1234

 

相关代码如下:
这里的代码借鉴了下面的代码:
https://github.com/xMachinae/pallinda13/blob/master/uppg2.go

   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