gopl-zh.github.com/ch1/ch1-04.md
2015-12-18 10:53:03 +08:00

88 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 1.4. GIF動畫
下面的程序會演示Go語言標準庫裡的image這個package的用法我們會用這個包來生成一繫列的bit-mapped圖然後將這些圖片編碼爲一個GIF動畫。我們生成的圖形名字叫利薩如圖形(Lissajous figures)這種效果是在1960年代的老電影裡齣現的一種視覺特效。他們是協振子在兩個緯度上振動所產生的麴綫比如兩個sin正絃波分別在x軸和y軸輸入會產生的麴綫。圖1.1是這樣的一個例子:
![](../images/ch1-01.png)
這段代碼裡我們用了一些新的結構包括const聲明數據struct類型復合聲明。和我們舉的其它的例子不太一樣這一個例子包含了浮點數運算。這些概唸我們隻在這裡簡單地說明一下之後的章節會更詳細地講解。
```go
gopl.io/ch1/lissajous
// Lissajous generates GIF animations of random Lissajous figures.
package main
import (
"image"
"image/color"
"image/gif"
"io"
"math"
"math/rand"
"os"
)
var palette = []color.Color{color.White, color.Black}
const (
whiteIndex = 0 // first color in palette
blackIndex = 1 // next color in palette
)
func main() {
lissajous(os.Stdout)
}
func lissajous(out io.Writer) {
const (
cycles = 5 // number of complete x oscillator revolutions
res = 0.001 // angular resolution
size = 100 // image canvas covers [-size..+size]
nframes = 64 // number of animation frames
delay = 8 // delay between frames in 10ms units
)
freq := rand.Float64() * 3.0 // relative frequency of y oscillator
anim := gif.GIF{LoopCount: nframes}
phase := 0.0 // phase difference
for i := 0; i < nframes; i++ {
rect := image.Rect(0, 0, 2*size+1, 2*size+1)
img := image.NewPaletted(rect, palette)
for t := 0.0; t < cycles*2*math.Pi; t += res {
x := math.Sin(t)
y := math.Sin(t*freq + phase)
img.SetColorIndex(size+int(x*size+0.5), size+int(y*size+0.5),
blackIndex)
}
phase += 0.1
anim.Delay = append(anim.Delay, delay)
anim.Image = append(anim.Image, img)
}
gif.EncodeAll(out, &anim) // NOTE: ignoring encoding errors
}
```
當我們import了一個包路徑包含有多個單詞的package時比如image/color(image和color兩個單詞)我們隻需要用最後那個單詞表示這個包就可以。所以當我們寫color.White時這個變量指向的是image/color包裡的變量同理gif.GIF是屬於image/gif包裡的變量。
這個程序裡的常量聲明給齣了一繫列的常量值常量是指在程序編譯後運行時始終都不會變化的值比如圈數、幀數、延遲值。常量聲明和變量聲明一般都會齣現在包級別所以這些常量在整個包中都是可以共享的或者你也可以把常量聲明定義在函數體內部那麽這種常量就隻能在函數體內用。常量聲明的值必鬚是一個數字值、字符串或者一個固定的boolean值。
[]color.Color{...}和gif.GIF{...}這兩個表達式就是我們說的復合聲明(4.2和4.4.1節有說明)。這是實例化Go語言裡的復合類型的一種寫法。這裡的前者生成的是一個slice後者生成的是一個struct。
gif.GIF是一個struct類型(參考4.4節)。struct是一組值或者叫字段的集合不同的類型集合在一個struct可以讓我們以一個統一的單元進行處理。anim是一個gif.GIF類型的struct變量。這種寫法會生成一個struct變量並且其內部變量LoopCount字段會被設置爲nframes而其它的字段會被設置爲各自類型默認的零值。struct內部的變量可以以一個點(.)來進行訪問就像在最後兩個賦值語句中顯式地更新了anim這個struct的Delay和Image字段。
lissajous函數內部有兩層嵌太的for循環。外層循環會循環64次每一次都會生成一個單獨的動畫幀。它生成了一個包含兩種顏色的201&201大小的圖片白色和黑色。所有像素點都會被默認設置爲其零值(也就是palette裡的第0個值)這裡我們設置的是白色。每次經過內存循環都會通過設置像素爲黑色生成一張新圖片。其結果會append到之前結果之後。這裡我們用到了append(參考4.2.1)這個內置函數將結果appen到anim中的幀列表末尾並會設置一個默認的80ms的延遲值。最終循環結束所有的延遲值也被編碼進了GIF圖片中並將結果寫入到輸齣流。out這個變量是io.Writer類型這個類型讓我們可以可以讓我們把輸齣結果寫到很多目標很快我們就可以看到了。
內存循環設置了兩個偏振。x軸偏振使用的是一個sin函數。y軸偏振也是一個正絃波但是其其相對x軸的偏振是一個0-3的隨機值並且初始偏振值是一個零值並隨着動畫的每一幀逐漸增加。循環會一直跑到x軸完成五次完整的循環。每一步它都會調用SetColorIndex來爲(x, y)點來染黑色。
main函數調用了lissajous函數並且用它來向標準輸齣中打印信息所以下面這個命令會像圖1.1中產生一個GIF動畫。
```bash
$ go build gopl.io/ch1/lissajous
$ ./lissajous >out.gif
```
```
Exercise 1.5: 脩改前面的Lissajous程序裡的調色闆由緑色改爲黑色。我們可以用color.RGBA{0xRR, 0xGG, 0xBB}來得到#RRGGBB這個色值三個十六進製的字符串分別代表紅、緑、藍像素。
Exercise 1.6: 脩改Lissajous程序脩改其調色闆來生成更豐富的顏色然後脩改SetColorIndex的第三個參數看看顯示結果吧。
```