W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
原文鏈接:https://gopl-zh.github.io/ch1/ch1-04.html
下面的程序會演示Go語言標(biāo)準(zhǔn)庫里的image這個package的用法,我們會用這個包來生成一系列的bit-mapped圖,然后將這些圖片編碼為一個GIF動畫。我們生成的圖形名字叫利薩如圖形(Lissajous figures),這種效果是在1960年代的老電影里出現(xiàn)的一種視覺特效。它們是協(xié)振子在兩個緯度上振動所產(chǎn)生的曲線,比如兩個sin正弦波分別在x軸和y軸輸入會產(chǎn)生的曲線。圖1.1是這樣的一個例子:
譯注:要看這個程序的結(jié)果,需要將標(biāo)準(zhǔn)輸出重定向到一個GIF圖像文件(使用 ./lissajous > output.gif
命令)。下面是GIF圖像動畫效果:
這段代碼里我們用了一些新的結(jié)構(gòu),包括const聲明,struct結(jié)構(gòu)體類型,復(fù)合聲明。和我們舉的其它的例子不太一樣,這一個例子包含了浮點數(shù)運(yùn)算。這些概念我們只在這里簡單地說明一下,之后的章節(jié)會更詳細(xì)地講解。
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"
"time"
)
var palette = []color.Color{color.White, color.Black}
const (
whiteIndex = 0 // first color in palette
blackIndex = 1 // next color in palette
)
func main() {
// The sequence of images is deterministic unless we seed
// the pseudo-random number generator using the current time.
// Thanks to Randall McPherson for pointing out the omission.
rand.Seed(time.Now().UTC().UnixNano())
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
}
當(dāng)我們import了一個包路徑包含有多個單詞的package時,比如image/color(image和color兩個單詞),通常我們只需要用最后那個單詞表示這個包就可以。所以當(dāng)我們寫color.White時,這個變量指向的是image/color包里的變量,同理gif.GIF是屬于image/gif包里的變量。
這個程序里的常量聲明給出了一系列的常量值,常量是指在程序編譯后運(yùn)行時始終都不會變化的值,比如圈數(shù)、幀數(shù)、延遲值。常量聲明和變量聲明一般都會出現(xiàn)在包級別,所以這些常量在整個包中都是可以共享的,或者你也可以把常量聲明定義在函數(shù)體內(nèi)部,那么這種常量就只能在函數(shù)體內(nèi)用。目前常量聲明的值必須是一個數(shù)字值、字符串或者一個固定的boolean值。
[]color.Color{...}和gif.GIF{...}這兩個表達(dá)式就是我們說的復(fù)合聲明(4.2和4.4.1節(jié)有說明)。這是實例化Go語言里的復(fù)合類型的一種寫法。這里的前者生成的是一個slice切片,后者生成的是一個struct結(jié)構(gòu)體。
gif.GIF是一個struct類型(參考4.4節(jié))。struct是一組值或者叫字段的集合,不同的類型集合在一個struct可以讓我們以一個統(tǒng)一的單元進(jìn)行處理。anim是一個gif.GIF類型的struct變量。這種寫法會生成一個struct變量,并且其內(nèi)部變量LoopCount字段會被設(shè)置為nframes;而其它的字段會被設(shè)置為各自類型默認(rèn)的零值。struct內(nèi)部的變量可以以一個點(.)來進(jìn)行訪問,就像在最后兩個賦值語句中顯式地更新了anim這個struct的Delay和Image字段。
lissajous函數(shù)內(nèi)部有兩層嵌套的for循環(huán)。外層循環(huán)會循環(huán)64次,每一次都會生成一個單獨(dú)的動畫幀。它生成了一個包含兩種顏色的201*201大小的圖片,白色和黑色。所有像素點都會被默認(rèn)設(shè)置為其零值(也就是調(diào)色板palette里的第0個值),這里我們設(shè)置的是白色。每次外層循環(huán)都會生成一張新圖片,并將一些像素設(shè)置為黑色。其結(jié)果會append到之前結(jié)果之后。這里我們用到了append(參考4.2.1)內(nèi)置函數(shù),將結(jié)果append到anim中的幀列表末尾,并設(shè)置一個默認(rèn)的80ms的延遲值。循環(huán)結(jié)束后所有的延遲值被編碼進(jìn)了GIF圖片中,并將結(jié)果寫入到輸出流。out這個變量是io.Writer類型,這個類型支持把輸出結(jié)果寫到很多目標(biāo),很快我們就可以看到例子。
內(nèi)層循環(huán)設(shè)置兩個偏振值。x軸偏振使用sin函數(shù)。y軸偏振也是正弦波,但其相對x軸的偏振是一個0-3的隨機(jī)值,初始偏振值是一個零值,隨著動畫的每一幀逐漸增加。循環(huán)會一直跑到x軸完成五次完整的循環(huán)。每一步它都會調(diào)用SetColorIndex來為(x,y)點來染黑色。
main函數(shù)調(diào)用lissajous函數(shù),用它來向標(biāo)準(zhǔn)輸出流打印信息,所以下面這個命令會像圖1.1中產(chǎn)生一個GIF動畫。
$ go build gopl.io/ch1/lissajous
$ ./lissajous >out.gif
練習(xí) 1.5: 修改前面的Lissajous程序里的調(diào)色板,由黑色改為綠色。我們可以用color.RGBA{0xRR, 0xGG, 0xBB, 0xff}
來得到#RRGGBB
這個色值,三個十六進(jìn)制的字符串分別代表紅、綠、藍(lán)像素。
練習(xí) 1.6: 修改Lissajous程序,修改其調(diào)色板來生成更豐富的顏色,然后修改SetColorIndex的第三個參數(shù),看看顯示結(jié)果吧。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: