[Part I. GIMP基礎功]

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



顯示具有 第29章 火山熔岩(Lava) 標籤的文章。 顯示所有文章
顯示具有 第29章 火山熔岩(Lava) 標籤的文章。 顯示所有文章

29.1 熔岩不夠火紅

  • Lava,在2.2版時,譯詞是「溶岩」。應該是「熔岩」比較好。
  • 節標題「熔岩不夠火紅」意思是,舊版的 GIMP 的熔岩,其質感比較像熔岩。


預設參數下的火山熔岩
  • 執行[影像] / 濾鏡 / 描繪 / Lava【註1】,可繪製出熔岩的圖案。
  • 此功能雖是位於「濾鏡」選單,但這是一個 Script-Fu 。
  • 下圖是「Script-Fu:Circuit」對話盒,使用預設的參數設定,執行後,可產生熔岩的圖案。


  • 下圖是,GIMP 1.2版,預設參數下的火山熔岩。


下圖是,GIMP 2.6版,預設參數下的火山熔岩。



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

29.2 六個濾鏡描繪出熔岩

  • lava 是火山熔岩的意思。
  • lava.scm 的作者 Adrian Likins,用了六個濾鏡,製造出熔岩的效果。


lava.scm 程式碼,最重要的就是這六行:
  • (plug-in-solid-noise RUN-NONINTERACTIVE image active-layer FALSE TRUE seed 2 2 2)
  • (plug-in-cubism RUN-NONINTERACTIVE image active-layer tile_size 2.5 0)
  • (plug-in-oilify RUN-NONINTERACTIVE image active-layer mask_size 0)
  • (plug-in-edge RUN-NONINTERACTIVE image active-layer 2 0 0)
  • (plug-in-gauss-rle RUN-NONINTERACTIVE image active-layer 2 TRUE TRUE)
  • (plug-in-gradmap RUN-NONINTERACTIVE image active-layer)


這六行的作用為:
  • 使用 Solid Noise 濾鏡【註1】,產生雲霧狀的效果。
  • 使用立體主義濾鏡【註2】,產生方塊狀的筆觸。
  • 使用油畫化濾鏡【註3】,製造油畫筆觸的效果。
  • 使用邊緣濾鏡【註4】,尋找黑白交替的邊緣。
  • 使用高斯模糊濾鏡【註5】,些微柔化邊緣。
  • 使用漸層換色技巧【註6】,選用 German flag smooth 漸層,產生燃燒的火焰色澤。


視窗介面裡,中文、英文的選單及項目名稱:
【註1】[影像] / 濾鏡 / 描繪 / 雲狀 / Solid Noise
[Image] / Filters / Render / Clouds / Solid Noise
【註2】[影像] / 濾鏡 / 藝術效果 / 立體主義
Image] / Filters / Artistic / Cubism
【註3】[影像] / 濾鏡 / 藝術效果/ 油畫化
[Image] / Filters / Artistic / Oilify
【註4】[影像] / 濾鏡 / 邊緣偵測 / 邊緣
[Image] / Filters / Edge-Detect / Edge
【註5】[影像] / 濾鏡 / 模糊化 / 高斯模糊
[Image] / Filters / Blur / Gaussian Blur
【註6】[影像] / 色彩 / 映射 / Gradient Map
[Image] / Colors / Map / Gradient Map

注意!
在2.6.7版,Clouds的譯詞是「雲朵」,之前的版本是「雲狀」,我個人偏好「雲狀」一詞。 在2.6.7版,Colors的譯詞是「顏色」(作為選單時),之前的版本是「色彩」,我個人偏好「色彩」一詞。

29.3 一步一步做,讓質感顯現

以下,操作GIMP圖形介面,一步一步做,來製作熔岩圖案。藉由這過程,來瞭解程式碼的意思。

Step 1. 執行Solid Noise濾鏡,參數的設定,如下圖的對話盒所示。


Step 2. 執行立體主義濾鏡,參數的設定,如下圖的對話盒所示。


Step 3. 執行油畫化濾鏡,參數的設定,如下圖的對話盒所示。

29.4 一步一步做,讓火光顯現

接續前一節,繼續以圖形使用者介面來生成火山熔岩的效果,只是步驟從新編號,從1號開始編號。

Step 1. 執行邊緣濾鏡,參數的設定,如下圖的對話盒所示。



Step 2. 執行高斯模糊濾鏡,模糊半徑設為2個像素。


Step 3. 執行Gradient Map,使用German flag smooth漸層,將黑白圖案變成彩色的圖案。

29.5 程式lava.scm分三部份

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

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

程式lava.scm分三部份:
  • 1~25行:註解,功能說明與版權聲明。
  • 26~115行:定義函式
  • 116~136行:註冊函式

第1到第25行:註解說明1: ; GIMP - The GNU Image Manipulation Program
2: ; Copyright (C) 1995 Spencer Kimball and Peter Mattis
3: ;
4: ; Lava effect
5: ; Copyright (c) 1997 Adrian Likins
6: ; aklikins@eos.ncsu.edu
7: ;
8: ; based on a idea by Sven Riedel <lynx@heim8.tu-clausthal.de>
9: ; tweaked a bit by Sven Neumann <neumanns@uni-duesseldorf.de>
10: ;
11: ; This program is free software; you can redistribute it and/or modify
12: ; it under the terms of the GNU General Public License as published by
13: ; the Free Software Foundation; either version 2 of the License, or
14: ; (at your option) any later version.
15: ;
16: ; This program is distributed in the hope that it will be useful,
17: ; but WITHOUT ANY WARRANTY; without even the implied warranty of
18: ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19: ; GNU General Public License for more details.
20: ;
21: ; You should have received a copy of the GNU General Public License
22: ; along with this program; if not, write to the Free Software
23: ; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24:
25:

第26到第115行:定義函式26: (define (script-fu-lava image
27: drawable
28: seed
29: tile_size
30: mask_size
31: gradient
32: keep-selection
33: separate-layer
34: current-grad)
35: (let* (
36: (type (car (gimp-drawable-type-with-alpha drawable)))
37: (image-width (car (gimp-image-width image)))
38: (image-height (car (gimp-image-height image)))
39: (active-selection 0)
40: (selection-bounds 0)
41: (select-offset-x 0)
42: (select-offset-y 0)
43: (select-width 0)
44: (select-height 0)
45: (lava-layer 0)
46: (active-layer 0)
47: )
48:
49: (gimp-context-push)
50: (gimp-image-undo-group-start image)
51:
52: (if (= (car (gimp-drawable-has-alpha drawable)) FALSE)
53: (gimp-layer-add-alpha drawable)
54: )
55:
56: (if (= (car (gimp-selection-is-empty image)) TRUE)
57: (gimp-selection-layer-alpha drawable)
58: )
59:
60: (set! active-selection (car (gimp-selection-save image)))
61: (gimp-image-set-active-layer image drawable)
62:
63: (set! selection-bounds (gimp-selection-bounds image))
64: (set! select-offset-x (cadr selection-bounds))
65: (set! select-offset-y (caddr selection-bounds))
66: (set! select-width (- (cadr (cddr selection-bounds)) select-offset-x))
67: (set! select-height (- (caddr (cddr selection-bounds)) select-offset-y))
68:
69: (if (= separate-layer TRUE)
70: (begin
71: (set! lava-layer (car (gimp-layer-new image
72: select-width
73: select-height
74: type
75: "Lava Layer"
76: 100
77: NORMAL-MODE)))
78:
79: (gimp-image-add-layer image lava-layer -1)
80: (gimp-layer-set-offsets lava-layer select-offset-x select-offset-y)
81: (gimp-selection-none image)
82: (gimp-edit-clear lava-layer)
83:
84: (gimp-selection-load active-selection)
85: (gimp-image-set-active-layer image lava-layer)
86: )
87: )
88:
89: (set! active-layer (car (gimp-image-get-active-layer image)))
90:
91: (if (= current-grad FALSE)
92: (gimp-context-set-gradient gradient)
93: )
94:
95: (plug-in-solid-noise RUN-NONINTERACTIVE image active-layer FALSE TRUE seed 2 2 2)
96: (plug-in-cubism RUN-NONINTERACTIVE image active-layer tile_size 2.5 0)
97: (plug-in-oilify RUN-NONINTERACTIVE image active-layer mask_size 0)
98: (plug-in-edge RUN-NONINTERACTIVE image active-layer 2 0 0)
99: (plug-in-gauss-rle RUN-NONINTERACTIVE image active-layer 2 TRUE TRUE)
100: (plug-in-gradmap RUN-NONINTERACTIVE image active-layer)
101:
102: (if (= keep-selection FALSE)
103: (gimp-selection-none image)
104: )
105:
106: (gimp-image-set-active-layer image drawable)
107: (gimp-image-remove-channel image active-selection)
108:
109: (gimp-image-undo-group-end image)
110: (gimp-context-pop)
111:
112: (gimp-displays-flush)
113: )
114: )
115:

第116到第136行:註冊函式116: (script-fu-register "script-fu-lava"
117: _"_Lava..."
118: _"Fill the current selection with lava"
119: "Adrian Likins <adrian@gimp.org>"
120: "Adrian Likins"
121: "10/12/97"
122: "RGB* GRAY*"
123: SF-IMAGE "Image" 0
124: SF-DRAWABLE "Drawable" 0
125: SF-ADJUSTMENT _"Seed" '(10 1 30000 1 10 0 1)
126: SF-ADJUSTMENT _"Size" '(10 0 100 1 10 0 1)
127: SF-ADJUSTMENT _"Roughness" '(7 3 50 1 10 0 0)
128: SF-GRADIENT _"Gradient" "German flag smooth"
129: SF-TOGGLE _"Keep selection" TRUE
130: SF-TOGGLE _"Separate layer" TRUE
131: SF-TOGGLE _"Use current gradient" FALSE
132: )
133:
134: (script-fu-menu-register "script-fu-lava"
135: "<Image>/Filters/Render")
136:

29.6 熔岩圖,修改成可「單行」執行的 Script-Fu

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


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


捨棄函數 script-fu-lava,直接指定輸入變數值
  • 修改成最多的地方就是下列這部份。
  • (if (condition) (expr1) (expr2) .......),要注意此區塊括號的對齊。
  • 記得要刪除多餘的右括號 ) ,不然會出現錯誤訊息。
  • 修改後的程式碼,不再需要預先定義函數 script-fu-lava ,但是要預先設定變數名稱、變數值 。

(define (script-fu-lava image
drawable
seed
tile_size
mask_size
gradient
keep-selection
separate-layer
current-grad)
(let* (
(type (car (gimp-drawable-type-with-alpha drawable)))
(image-width (car (gimp-image-width image)))
(image-height (car (gimp-image-height image)))
(active-selection 0)
(selection-bounds 0)
(select-offset-x 0)
(select-offset-y 0)
(select-width 0)
(select-height 0)
(lava-layer 0)
(active-layer 0)
)


從下列的程式碼得知變數值的型態與大小
SF-IMAGE       "Image"          0
SF-DRAWABLE    "Drawable"       0
SF-ADJUSTMENT _"Seed"           '(10 1 30000 1 10 0 1)
SF-ADJUSTMENT _"Size"           '(10 0 100 1 10 0 1)
SF-ADJUSTMENT _"Roughness"      '(7 3 50 1 10 0 0)
SF-GRADIENT   _"Gradient"       "German flag smooth"
SF-TOGGLE     _"Keep selection" TRUE
SF-TOGGLE     _"Separate layer" TRUE
SF-TOGGLE     _"Use current gradient" FALSE


修改後的程式碼,與測試的結果圖
  • 下面是修改後的程式碼。
  • 啟動 GIMP Script-fu Console
  • 將下面的 Scheme 程式碼,全部選取、複製、再貼入 Script-fu Console 文字框,按下 Enter 。
  • 過一會,就會看到結果圖。


(define width 256)
(define height 256)
(define img (car (gimp-image-new width height RGB)))
(define layer-one (car (gimp-layer-new img width height RGBA-IMAGE "layer 1" 100 NORMAL-MODE)))
(gimp-image-add-layer img layer-one 0)
(gimp-display-new img)

; 前面的程式碼是為了生成「一個透明的圖層」
; 作為之後生成熔岩圖案的參考圖層
; 此參考圖層規範了熔岩圖案的長寬

(define image img) ; 指派影像物件
(define drawable layer-one)  ; 指派圖層物件

(define seed 10)
(define tile_size 10)
(define mask_size 7)
(define gradient "German flag smooth")
(define keep-selection TRUE)
(define separate-layer TRUE)
(define current-grad FALSE)

(define type (car (gimp-drawable-type-with-alpha drawable)))
(define image-width (car (gimp-image-width image)))
(define image-height (car (gimp-image-height image)))
(define active-selection 0)
(define selection-bounds 0)
(define select-offset-x 0)
(define select-offset-y 0)
(define select-width 0)
(define select-height 0)
(define lava-layer 0)
(define active-layer 0)

(gimp-context-push)
(gimp-image-undo-group-start image)

(if (= (car (gimp-drawable-has-alpha drawable)) FALSE)
(gimp-layer-add-alpha drawable)
)

(if (= (car (gimp-selection-is-empty image)) TRUE)
(gimp-selection-layer-alpha drawable)
)

(set! active-selection (car (gimp-selection-save image)))
(gimp-image-set-active-layer image drawable)

(set! selection-bounds (gimp-selection-bounds image))
(set! select-offset-x (cadr selection-bounds))
(set! select-offset-y (caddr selection-bounds))
(set! select-width (- (cadr (cddr selection-bounds)) select-offset-x))
(set! select-height (- (caddr (cddr selection-bounds)) select-offset-y))

(if (= separate-layer TRUE)
(begin
(set! lava-layer (car (gimp-layer-new image select-width select-height type "Lava Layer" 100 NORMAL-MODE)))

(gimp-image-add-layer image lava-layer -1)
(gimp-layer-set-offsets lava-layer select-offset-x select-offset-y)
(gimp-selection-none image)
(gimp-edit-clear lava-layer)

(gimp-selection-load active-selection)
(gimp-image-set-active-layer image lava-layer)
)
)

(set! active-layer (car (gimp-image-get-active-layer image)))

(if (= current-grad FALSE)
(gimp-context-set-gradient gradient)
)

(plug-in-solid-noise RUN-NONINTERACTIVE image active-layer FALSE TRUE seed 2 2 2)
(plug-in-cubism RUN-NONINTERACTIVE image active-layer tile_size 2.5 0)
(plug-in-oilify RUN-NONINTERACTIVE image active-layer mask_size 0)
(plug-in-edge RUN-NONINTERACTIVE image active-layer 2 0 0)
(plug-in-gauss-rle RUN-NONINTERACTIVE image active-layer 2 TRUE TRUE)
(plug-in-gradmap RUN-NONINTERACTIVE image active-layer)

(if (= keep-selection FALSE)
(gimp-selection-none image)
)

(gimp-image-set-active-layer image drawable)
(gimp-image-remove-channel image active-selection)

(gimp-image-undo-group-end image)
(gimp-context-pop)

(gimp-displays-flush)

29.7 熔岩圖,修改成可「單行」執行的 Python-Fu

修改後的程式碼,與測試的結果圖
  • 啟動 GIMP Python Console
  • 將下面的程式碼,全部選取、複製、再貼到 Python Console 提示符號 >>> 之後,按下 Enter 。
  • 過一會,就會看到結果圖。


width = 256
height = 256
img = pdb.gimp_image_new(width, height, RGB)
layer_one = pdb.gimp_layer_new(img, width, height, RGBA_IMAGE, "layer 1", 100, NORMAL_MODE)
pdb.gimp_image_add_layer(img, layer_one, 0)
pdb.gimp_display_new(img)

# 前面的程式碼是為了生成「一個透明的圖層」
# 作為之後生成熔岩圖案的參考圖層
# 此參考圖層規範了熔岩圖案的長寬

image = img # 指派影像物件
drawable = layer_one # 指派圖層物件

# 可以生成一個矩形選取區域。
# 測試有選取區域時的效果。
# pdb.gimp_rect_select(img, 25, 25, 150, 150, 0, TRUE, 4)

seed = 10
tile_size = 10
mask_size = 7
gradient = "German flag smooth"
keep_selection = TRUE
separate_layer = TRUE
current_grad = FALSE

type = pdb.gimp_drawable_type_with_alpha(drawable)
image_width = pdb.gimp_image_width(image)
image_height = pdb.gimp_image_height(image)
active_selection = 0
selection_bounds = 0
select_offset_x = 0
select_offset_y = 0
select_width = 0
select_height = 0
lava_layer = 0
active_layer = 0

pdb.gimp_context_push()
pdb.gimp_image_undo_group_start(image)

if pdb.gimp_drawable_has_alpha(drawable) == FALSE:
  pdb.gimp_layer_add_alpha(drawable)

if pdb.gimp_selection_is_empty(image):
  pdb.gimp_selection_layer_alpha(drawable)

active_selection = pdb.gimp_selection_save(image)
pdb.gimp_image_set_active_layer(image, drawable)

selection_bounds = pdb.gimp_selection_bounds(image)
select_offset_x = selection_bounds[1]
select_offset_y = selection_bounds[2]
select_width = selection_bounds[3] - select_offset_x
select_height = selection_bounds[4] - select_offset_y

if separate_layer:
  lava_layer = pdb.gimp_layer_new(image, select_width, select_height, type, "Lava Layer", 100, NORMAL_MODE)
  pdb.gimp_image_add_layer(image, lava_layer, -1)
  pdb.gimp_layer_set_offsets(lava_layer, select_offset_x, select_offset_y)
  pdb.gimp_selection_none(image)
  pdb.gimp_edit_clear(lava_layer)
  pdb.gimp_selection_load(active_selection)
  pdb.gimp_image_set_active_layer(image, lava_layer)

active_layer = pdb.gimp_image_get_active_layer(image)

if current_grad == FALSE:
  pdb.gimp_context_set_gradient(gradient)

pdb.plug_in_solid_noise(image, active_layer, FALSE, TRUE, seed, 2, 2, 2)
pdb.plug_in_cubism(image, active_layer, tile_size, 2.5, 0)
pdb.plug_in_oilify(image, active_layer, mask_size, 0)
pdb.plug_in_edge(image, active_layer, 2, 0, 0)
pdb.plug_in_gauss_rle(image, active_layer, 2, TRUE, TRUE)
pdb.plug_in_gradmap(image, active_layer)

if keep_selection == FALSE:
  pdb.gimp_selection_none(image)

pdb.gimp_image_set_active_layer(image, drawable)
pdb.gimp_image_remove_channel(image, active_selection)

29.8 熔岩圖,修改過程中,須注意的 Python 語法特點

  • 經過這樣的轉碼練習,發現電路圖 circuit.scm 、熔岩圖 lava.scm ,寫碼的技巧,有許多雷同之處 ,才發現這兩者都是 Adrian Likins 的作品。
  • 因此,修改 lava.scm 須注意的 Python 語法特點可參考「28.8 節」。


電路圖、熔岩圖,都有「計算出選取區域的長寬」

Scheme code
(set! selection-bounds (gimp-selection-bounds image))
(set! select-offset-x (cadr selection-bounds))
(set! select-offset-y (caddr selection-bounds))
(set! select-width (- (cadr (cddr selection-bounds)) select-offset-x))
(set! select-height (- (caddr (cddr selection-bounds)) select-offset-y))



Python code
selection_bounds = pdb.gimp_selection_bounds(image)
select_offset_x = selection_bounds[1]
select_offset_y = selection_bounds[2]
select_width = selection_bounds[3] - select_offset_x
select_height = selection_bounds[4] - select_offset_y