[Part I. GIMP基礎功]

[Part II. 一種效果的誕生]
[Part III. Script-Fu的秘密]



顯示具有 第24章 波浪動畫(Waves) 標籤的文章。 顯示所有文章
顯示具有 第24章 波浪動畫(Waves) 標籤的文章。 顯示所有文章

24.1 執行Script-Fu,產生水波動畫

有一個濾鏡的名稱也是Waves,位於「濾鏡 / 扭曲」選單內,Waves的譯詞是波浪。
有一個Script-Fu的名稱是Waves,沒有譯詞,位於「濾鏡 / 動畫」選單內,這個Script-Fu要利用到「波浪」濾鏡。


本章研究Sven Neumann所撰寫的waves-anim.scm程式碼,從中研究水波動畫生成的原由,可由此推廣,撰寫製作簡單動畫的Script-Fu。


下圖,是匯入月亮圖案的SVG檔,


下圖是白色背景的月亮圖案。


利用放射狀的漸層效果,來作為背景的圖層,讓月亮有發光的感覺。
之後,使用下圖的月亮圖案來製作動畫,長寬為640x640,檔名為Moon_and_light.jpg。


對月亮Moon_and_light.jpg,執行[影像] / 濾鏡 / 動畫 / Waves【註1】,使用預設的參數,即波幅設為10、波長設為10、畫格數目設為 6,產生月亮的水波動畫。


下圖是「Script-Fu:Waves」對話盒,使用預設參數,執行後,可產生原圖案的多圖層波浪動畫。


「Script-Fu:Waves」對話盒:
  • 波幅與波長是根據影像的大小來設定的。
  • 小的波幅搭配小的波長,可產生漣波的效果。
  • 畫格數目設 6,使得每個畫格的水波相位差為60度(360÷6)。


執行[影像] / 濾鏡 / 動畫 / 播放【註1】,來播放多圖層的動畫。下圖展示了「動畫播放」對話盒,此時,播放月亮的水波動畫,呈現水中月的感覺。



選單及項目名稱的英文:
【註1】[Image] / Filters / Animation / Waves,Amplitude設10、Wavelength設10、Number of Frames設 6

24.2 動畫的成因

動畫,勝過千言萬語
  • 人們常說,一圖勝過千言萬語。人有辭窮之時,說故事的人再會說,也會有說不詳盡之處。
  • 要說孔雀開屏有多美,還不如秀一張孔雀開屏的照片。
  • 當然,若能秀一段開屏的過程的動畫,更能引人入勝,對於開屏的氣象萬千、羽翎的多姿多彩,能有更深的體會。



動畫,就是依時序排列畫面
  • 動畫的基本觀念為,連續播放數張相關聯的畫面。
  • 每一個畫面稱為一個畫格(frame),按照時間的順序,依次顯示各個畫格,持續一小段固定的時間,通常為數十到數百毫秒(ms)。
  • 由於,人眼有視覺暫留的特性,若各個畫格主體,前後有關聯,則會讓人覺得主體有移動、變動的感覺。



GIF 格式的圖檔
  • 若要用 GIMP 製作簡單的動畫,通常,最終只能輸出為 GIF 格式的圖檔。
  • 除了 GIMP ,少有軟體支援 XCF 格式的圖檔。
  • XCF 並不具備壓縮的能力,其檔案較大。
  • 但 GIF 圖片最多只能顯示256色。
  • 對於靜態的圖檔,若不在意影像的色澤,儲存成 GIF 格式也是不錯的選擇。
  • 可先用瀏覽器觀看 GIF 檔動畫效果,再使用 GIMP 開啟動畫檔,觀看各個圖層的差異。
  • 若對 GIMP 的操作已經很熟練,對於簡單的動畫檔,通常,只要比較各個圖層的差異,大概就能推測出動畫製作的原理。



月亮的水波動畫
  • 畫格數目為6,因此,每個畫格的水波相位差為60度。
  • 下圖,在圖層面版內,顯示波浪動畫的六個圖層。

24.3 一步一步做,讓波浪產生

以下,操作GIMP圖形介面,一步一步做,來產生水波動畫。藉由這過程,來體會程式碼的意思。

Step 1. 在GIMP的圖層面版內,一個圖層代表一個畫格。首先,計算出需產生多少畫格。若是6個畫格的動畫,則水波的相移為60度。

前面的步驟,對應到程式碼中的變數設定:
  • num-frames=6
  • Phaseshift=60,360÷6=60
  • 一開始,Phase=0
  • remaining-frames=6

下圖是,Moon_and_light.jpg的影像視窗。


下圖,在圖層面版內,顯示了JPEG影像原本只有一個圖層,名稱為「背景」(Background)。


gimp-image-duplicate函數的目的是,複製影像的所有圖層、所有色版、及相關的資料,生成新的影像複本。
gimp-image-duplicate函數相當於,在GIMP圖形介面上,執行[影像] / 圖片 / 重製【註1】。

前面兩步驟,對應到下列的程式碼。
44: (image (car (gimp-image-duplicate img)))
45: (source-layer (car (gimp-image-get-active-layer image))))

Step 2. 略過產生影像複本的步驟,直接在原本Moon_and_light.jpg的影像視窗內操作。Moon_and_light.jpg影像只有一個名為「背 景」(Background)的圖層。在圖層面版內,複製「背景」圖層【註2】,產生一個新的圖層,將新的圖層更名為Frame 2(replace)。


前面的步驟,對應到下列的程式碼。
54: (waves-layer (car (gimp-layer-copy source-layer TRUE)))
55: (layer-name (string-append "Frame "
56: (number->string
57: (- (+ num-frames 2)
58: remaining-frames) 10
59: )
60: " (replace)"))
61: )
62: (gimp-layer-set-lock-alpha waves-layer FALSE)
63: (gimp-image-add-layer image waves-layer -1)
64: (gimp-drawable-set-name waves-layer layer-name)

string-append函數:把字串串接起來。
number->string函數:把數字變字串。

一開始remaining-frames=num-frames,
故num-frames+2-remaining-frames=2
最後合成的字串 "Frame 2 (replace)"


Step 3. 對Frame 2圖層,執行[影像] / 濾鏡 / 扭曲 / 波浪【註3】,其於使用預設的參數。使用波浪濾鏡,讓單一圖層產生同心圓狀的波浪扭曲。


前面的步驟,對應到下列的程式碼。
66: (plug-in-waves RUN-NONINTERACTIVE
67: image
68: waves-layer
69: amplitude
70: phase
71: wavelength
72: 0
73: FALSE)


注意!
Duplicate的譯詞是「重製」,之前的版本是「複製」。若是位於「Image」選單內,則Duplicate指令是新增影像的複本,由於GIMP給予每一張影像單一獨立的視窗,因此,會產生新的影像視窗。 Duplicate Layer的譯詞是「重製圖層」,之前的版本是「複製圖層」。此功能是新增圖層的複本,圖層的名稱的尾端會加上「複製本」。
選單及項目名稱的英文:
【註1】[Image] / Image / Duplicate
【註2】Duplicate Layer
【註3】[Image] / Filters / Distorts / Waves

24.4 步步重複做,只變化相位

接續前一節,繼續以圖形使用者介面來生成波浪的動畫,只是步驟從新編號,從1號開始編號。


Step 1. 重覆前述步驟:複製圖層、執行波浪濾鏡。
Step 2. 複製「背景」圖,層產生新圖層,圖層更名為Frame # (replace),新圖層要在既有的圖層的上方。
Step 3. 對Frame # (replace)圖層,執行波浪濾鏡,相位設(#-2)×-60(請注意是負60),其餘參數照舊。



Step 4. 將「背景」圖層更名為Frame 1。
Step 5. 對Frame 1圖層,執行波浪濾鏡,相位設60,其餘參數照舊。(在程式碼中,當remaining-frames>1,結束while 迴圈。)

前面的步驟,對應到下列的程式碼。
44:(set! remaining-frames (- remaining-frames 1))
45:(set! phase (- phase phaseshift))
回到21:(while (> remaining-frames 1)
判斷是否還要繼續執行 while 迴圈。

離開while 迴圈後,接著執行下列的程式碼。
80: (gimp-drawable-set-name source-layer "Frame 1")
81: (plug-in-waves RUN-NONINTERACTIVE
82: image
83: source-layer
84: amplitude
85: phase
86: wavelength
87: 0
88: FALSE)


Step 6. 可執行[影像] / 濾鏡 / 動畫 / 播放【註1】,來播放多圖層的動畫。也可存成GIF動畫檔,網頁瀏覽器已具備播放GIF動畫的功能。



選單及項目名稱的英文:
【註1】[Image] / Filters / Animation / Animation Playback

24.5 程式waves-anim.scm分三部份

若要徹底瞭解waves-anim.scm,可仿傚第22章的方式,單步執行,來分析每一行程式碼的作用。

若用PSPad開啟程式檔(或任何文字編輯器),看到的Script-Fu程式碼,變數會對得很整齊。HTML會把大量的空白省略,因此程式碼的排版變成了下面這樣子。我替Script-Fu程式碼加上行號乎,方便讀者閱讀。

程式waves-anim.scm分三部份:
  • 1~31行:註解,功能說明與版權聲明。
  • 32~94行::定義函式,在測試程式的過程中,可將它改為(define (script-fu-waves-anim2 … ))
  • 95~112行:註冊函式,可將它改為(script-fu-register " script-fu-waves-anim2 " … )

第1到第31行:註解說明1: ; GIMP - The GNU Image Manipulation Program
2: ; Copyright (C) 1995 Spencer Kimball and Peter Mattis
3: ;
4: ; This program is free software; you can redistribute it and/or modify
5: ; it under the terms of the GNU General Public License as published by
6: ; the Free Software Foundation; either version 2 of the License, or
7: ; (at your option) any later version.
8: ;
9: ; This program is distributed in the hope that it will be useful,
10: ; but WITHOUT ANY WARRANTY; without even the implied warranty of
11: ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12: ; GNU General Public License for more details.
13: ;
14: ; You should have received a copy of the GNU General Public License
15: ; along with this program; if not, write to the Free Software
16: ; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17: ;
18: ;
19: ; waves-anim.scm version 1.01 1997/12/13
20: ;
21: ; CHANGE-LOG:
22: ; 1.00 - initial release
23: ; 1.01 - some code cleanup, no real changes
24: ;
25: ; Copyright (C) 1997 Sven Neumann <sven@gimp.org>
26: ;
27: ;
28: ; Makes a copy of your image and creates an animation of the active layer
29: ; as if a stone was thrown into the image. The animation may be saved with
30: ; the gif-plug-in.
31:

第32到第94行:定義函式32: (define (script-fu-waves-anim img
33: drawable
34: amplitude
35: wavelength
36: num-frames
37: invert)
38: (let* ((amplitude (max 0 amplitude))
39: (wavelength (max 0 wavelength))
40: (num-frames (max 1 num-frames))
41: (remaining-frames num-frames)
42: (phase 0)
43: (phaseshift (/ 360 num-frames))
44: (image (car (gimp-image-duplicate img)))
45: (source-layer (car (gimp-image-get-active-layer image))))
46:
47: (gimp-image-undo-disable image)
48:
49: (if (= invert TRUE)
50: (set! phaseshift (- 0 phaseshift)))
51:
52: (while (> remaining-frames 1)
53: (let* (
54: (waves-layer (car (gimp-layer-copy source-layer TRUE)))
55: (layer-name (string-append "Frame "
56: (number->string
57: (- (+ num-frames 2)
58: remaining-frames) 10
59: )
60: " (replace)"))
61: )
62: (gimp-layer-set-lock-alpha waves-layer FALSE)
63: (gimp-image-add-layer image waves-layer -1)
64: (gimp-drawable-set-name waves-layer layer-name)
65:
66: (plug-in-waves RUN-NONINTERACTIVE
67: image
68: waves-layer
69: amplitude
70: phase
71: wavelength
72: 0
73: FALSE)
74:
75: (set! remaining-frames (- remaining-frames 1))
76: (set! phase (- phase phaseshift))
77: )
78: )
79:
80: (gimp-drawable-set-name source-layer "Frame 1")
81: (plug-in-waves RUN-NONINTERACTIVE
82: image
83: source-layer
84: amplitude
85: phase
86: wavelength
87: 0
88: FALSE)
89:
90: (gimp-image-undo-enable image)
91: (gimp-display-new image)
92: )
93: )
94:

第95到第112行:註冊函式95: (script-fu-register "script-fu-waves-anim"
96: _"_Waves..."
97: _"Create a multi-layer image with an effect like a stone was thrown into the current image"
98: "Sven Neumann <sven@gimp.org>"
99: "Sven Neumann"
100: "1997/13/12"
101: "RGB* GRAY*"
102: SF-IMAGE "Image" 0
103: SF-DRAWABLE "Drawable" 0
104: SF-ADJUSTMENT _"Amplitude" '(10 1 101 1 10 1 0)
105: SF-ADJUSTMENT _"Wavelength" '(10 0.1 100 1 10 1 0)
106: SF-ADJUSTMENT _"Number of frames" '(6 1 512 1 10 0 1)
107: SF-TOGGLE _"Invert direction" FALSE
108: )
109:
110: (script-fu-menu-register "script-fu-waves-anim"
111: "<Image>/Filters/Animation/Animators")
112:

24.6 波浪圖,修改成可「單行」執行的 Script-Fu

修改成一行一行可執行的程式碼
  • 本節要將 waves-anim.scm 修改成在 Console 內可一行一行執行的 Scheme 程式碼。
  • waves-anim.scm 完整的程式碼可參考 24.5 節。
  • 也可到 GIMP 系統的 scripts 目錄下找到該檔案。
  • 最主要修改的部份是,將「對話框接收輸入變數值」修改成「直接指定輸入變數值」。
  • 先要瞭解,news-text.scm 程式碼的執行流程。


最主要修改的部份
  • 不使用 let* 改用 define
  • 原本的程式使用 let* 定義與指定區域變數值。
  • (let* (()()()...) ()() ...) 這樣的語法括號太多。
  • 若要一行一行分析程式碼的意義,let* 語法,括號有好幾層,分析時,會造成一些干擾。
  • 現在不使用 let* 改用 define 來定義與指定變數值。


捨棄函數 script-fu-waves-anim,直接指定輸入變數值
  • 修改成最多的地方就是下列這兩段部份。
  • (while (condition) (expr1) (expr2) .......),要注意此區塊括號的對齊。
  • 記得要刪除多餘的右括號 ) ,不然會出現錯誤訊息。
  • 修改後的程式碼,不再需要預先定義函數 script-fu-waves-anim ,但是要預先設定變數名稱、變數值 。
(define (script-fu-waves-anim img
drawable
amplitude
wavelength
num-frames
invert)
(let* ((amplitude ......))
(wavelength ......))
(num-frames ......)
(remaining-frames num-frames)
(phase 0)
(phaseshift ......)
(image ......)
(source-layer ......))


(let* (
(waves-layer (car (gimp-layer-copy source-layer TRUE)))
(layer-name (string-append "Frame "
       (number->string
         (- (+ num-frames 2)
            remaining-frames) 10
         )
       " (replace)"))



從下列的程式碼得知變數值的型態與大小
SF-IMAGE       "Image" 0
SF-DRAWABLE "Drawable" 0
SF-ADJUSTMENT _"Amplitude" '(10 1 101 1 10 1 0)
SF-ADJUSTMENT _"Wavelength" '(10 0.1 100 1 10 1 0)
SF-ADJUSTMENT _"Number of frames" '(6 1 512 1 10 0 1)
SF-TOGGLE _"Invert direction" FALSE


以 flat land 平地圖案,作為測試用圖
  • 假設啟動 GIMP 之後沒有開啟任何影像檔。
  • 如此,新出現的影像其 id 為 1
  • 程式碼的開頭加入這幾行,產生測試用圖 。

......
(script-fu-flatland 256 256 80 3.0 4.0 4.0)
......


修改後的程式碼,與測試的結果圖
  • 下面是修改後的程式碼。
  • 為了確保影像 id 為 1 ,請重新啟動 GIMP 。
  • 啟動 GIMP Script-fu Console
  • 將下面的 Scheme 程式碼,全部選取、複製、再貼入 Script-fu Console 文字框,按下 Enter 。
  • 過一會,就會看到結果圖。
(define gradient "Land 1")
(gimp-context-set-gradient gradient)
(script-fu-flatland  256 256 80 3.0 4.0 4.0)
(define img 1) ; 影像 id 需要視"開啟多少影像"而定
(gimp-image-flatten img)
(define drawable (car (gimp-image-active-drawable img)))

(define amplitude 10)
(define wavelength 10)
(define num-frames 6)
(define invert FALSE)
(define amplitude (max 0 amplitude))
(define wavelength (max 0 wavelength))
(define num-frames (max 1 num-frames))
(define remaining-frames num-frames)
(define phase 0)
(define phaseshift (/ 360 num-frames))
(define image (car (gimp-image-duplicate img)))
(define source-layer (car (gimp-image-get-active-layer image)))

(gimp-image-undo-disable image)

(if (= invert TRUE)
(set! phaseshift (- 0 phaseshift)))

(while (> remaining-frames 1)
(define waves-layer (car (gimp-layer-copy source-layer TRUE)))
(define layer-name (string-append "Frame "
(number->string (- (+ num-frames 2)   remaining-frames) 10) " (replace)"))

(gimp-layer-set-lock-alpha waves-layer FALSE)
(gimp-image-add-layer image waves-layer -1)
(gimp-drawable-set-name waves-layer layer-name)

(plug-in-waves RUN-NONINTERACTIVE image waves-layer amplitude phase wavelength 0 FALSE)

(set! remaining-frames (- remaining-frames 1))
(set! phase (- phase phaseshift))
) ;這個右括號屬於 while 迴圈


(gimp-drawable-set-name source-layer "Frame 1")
(plug-in-waves RUN-NONINTERACTIVE image source-layer amplitude phase wavelength 0 FALSE)

(gimp-image-undo-enable image)
(gimp-display-new image)

24.7 波浪圖,修改成可「單行」執行的 Python-Fu

修改後的程式碼,與測試的結果圖
  • 啟動 GIMP Python Console
  • 將下面的程式碼,全部選取、複製、再貼到 Python Console 提示符號 >>> 之後,按下 Enter 。
  • 過一會,就會看到結果圖。
gradient = "Land 1"
pdb.gimp_context_set_gradient(gradient)
pdb.script_fu_flatland(256, 256, 80, 3.0, 4.0, 4.0)
img_array = gimp.image_list()
img = img_array[0] # 假設目前只有 flat land 這個影像視窗
pdb.gimp_image_flatten(img)
drawable = pdb.gimp_image_active_drawable(img)

amplitude = 10
wavelength = 10
num_frames = 6
invert = FALSE
amplitude = max(0, amplitude)
wavelength = max(0, wavelength)
num_frames = max(1, num_frames)
remaining_frames = num_frames
phase = 0
phaseshift = 360 / num_frames
image = pdb.gimp_image_duplicate(img)
source_layer = pdb.gimp_image_get_active_layer(image)

pdb.gimp_image_undo_disable(image)

if invert:
  phaseshift = - phaseshift

while remaining_frames > 1:
  waves_layer = pdb.gimp_layer_copy(source_layer, TRUE)
  layer_name = "Frame " +  str(num_frames + 2 - remaining_frames) + " (replace)"
  pdb.gimp_layer_set_lock_alpha(waves_layer, FALSE)
  pdb.gimp_image_add_layer(image, waves_layer, -1)
  pdb.gimp_drawable_set_name(waves_layer, layer_name)
  pdb.plug_in_waves(image, waves_layer, amplitude, phase, wavelength, 0, FALSE)
  remaining_frames = remaining_frames - 1
  phase = phase - phaseshift


pdb.gimp_drawable_set_name(source_layer, "Frame 1")
pdb.plug_in_waves(image, source_layer, amplitude, phase, wavelength, 0, FALSE)

pdb.gimp_image_undo_enable(image)
pdb.gimp_display_new(image)

24.8 波浪圖,修改過程中,須注意的 Python 語法特點

max 函數,取最大值
  • 情況不同於 rand 函數,很幸運,max 函數,Scheme 與 Python ,用法類似。
Scheme code
(define amplitude (max 0 amplitude))
(define wavelength (max 0 wavelength))
(define num-frames (max 1 num-frames))

Python code
amplitude = max(0, amplitude)
wavelength = max(0, wavelength)
num_frames = max(1, num_frames)


數字轉為字串,以及字串的串接
Scheme code
(define layer-name (string-append "Frame "
(number->string (- (+ num-frames 2) remaining-frames) 10) " (replace)"))

Python code
layer_name = "Frame " +  str(num_frames + 2 - remaining_frames) + " (replace)"


加減乘除四則運算
Scheme code
(define phaseshift (/ 360 num-frames))

Python code
phaseshift = 360 / num_frames

Scheme code
(set! remaining-frames (- remaining-frames 1))
(set! phase (- phase phaseshift))

Python code
remaining_frames = remaining_frames - 1
phase = phase - phaseshift


while 條件控制的敘述
Scheme code 要注意括號對齊

(while (> remaining-frames 1)
(define waves-layer (car (...)))
......
(set! phase (- phase phaseshift))
)

Python code 要注意縮排

while remaining_frames > 1:
  waves_layer = pdb.gimp_layer_copy(...)
  ......
  phase = phase - phaseshift


RGB 顏色的指定
Scheme code
(define text-color "black")
(define bg-color "white")

Python code
text_color = (0, 0, 0)
bg_color = (255, 255, 255)