SHOW:
|
|
- or go back to the newest paste.
1 | ; Implements | |
2 | ; http://learnfobia.com/category-Computers-47/tutorial-Painting-A-Spaceship-Hull-Texture-2126.html | |
3 | (define (script-fu-render-greebles | |
4 | img drw | |
5 | layers size num | |
6 | aspect-limit change-factor smudge-density | |
7 | fuzz exposure) | |
8 | (list | |
9 | (loop 0 layers (lambda (x) | |
10 | (with-selection img (lambda () | |
11 | (with-color (lambda () | |
12 | (let* | |
13 | ( | |
14 | (w (car (gimp-image-width img))) | |
15 | (h (car (gimp-image-height img)))) | |
16 | (list | |
17 | (random-blend) | |
18 | (greeblestep | |
19 | img (list w h) drw | |
20 | (= x 0) | |
21 | (list size num aspect-limit (* (+ x 1) change-factor)) | |
22 | smudge-density fuzz exposure) | |
23 | (gimp-drawable-update drw 0 0 w h) | |
24 | (gimp-displays-flush))))))))))) | |
25 | (script-fu-register | |
26 | "script-fu-render-greebles" | |
27 | "G_reebles" | |
28 | "Renders greebles" | |
29 | "FeepingCreature" | |
30 | "" | |
31 | "" | |
32 | "*" | |
33 | SF-IMAGE "The Image" 0 | |
34 | SF-DRAWABLE "The Layer" 0 | |
35 | SF-VALUE "Layers" "2" | |
36 | SF-VALUE "Base size" "200" | |
37 | SF-VALUE "numGreebles per layer" "35" | |
38 | SF-VALUE "Base aspect limiter" "2" | |
39 | SF-VALUE "Aspect change factor" "3" | |
40 | SF-VALUE "Smudge Density" "7" | |
41 | SF-VALUE "Edge Fuzz Spread" "4" | |
42 | SF-VALUE "Smudge Exposure" "20") | |
43 | (script-fu-menu-register "script-fu-render-greebles" "<Image>/Filters/Render") | |
44 | ||
45 | ; call f with numbers from 'from' to 'to', end-exclusive | |
46 | (define loop (lambda (from to f) | |
47 | (while (< from to) | |
48 | (f from) | |
49 | (set! from (+ from 1))))) | |
50 | ; call f, then restore foreground/background color | |
51 | (define with-color (lambda (f) | |
52 | (let* | |
53 | ( | |
54 | (bg (car (gimp-context-get-background))) | |
55 | (fg (car (gimp-context-get-foreground)))) | |
56 | (list | |
57 | (f) | |
58 | (gimp-context-set-background bg) | |
59 | (gimp-context-set-foreground fg))))) | |
60 | ; set context color to a random mix of foreground and background | |
61 | (define random-blend (lambda () | |
62 | (let* | |
63 | ( | |
64 | (blend (blend (/ (random 1000) 1000))) | |
65 | (fg (car (gimp-context-get-foreground))) | |
66 | (bg (car (gimp-context-get-background)))) | |
67 | (gimp-context-set-foreground (list | |
68 | (blend (car fg) (car bg)) | |
69 | (blend (cadr fg) (cadr bg)) | |
70 | (blend (caddr fg) (caddr bg))))))) | |
71 | ; generate a blender function that blends between two numbers with factor 'f'. | |
72 | (define blend (lambda (f) | |
73 | (lambda (a b) | |
74 | (+ | |
75 | (* a (- 1 f)) | |
76 | (* b f))))) | |
77 | ; adds one layer of greebles | |
78 | (define greeblestep (lambda (img img-size drawable overwrite-background select-args steps fuzz exposure) | |
79 | ((with-undo-group img (lambda () | |
80 | (with-selection img (lambda () | |
81 | (list | |
82 | ; build our random-rectangles selection | |
83 | (apply (curry reselect img img-size) select-args) | |
84 | ; paint the foreground/background | |
85 | (swap-and-do drawable img | |
86 | (combine | |
87 | (curry2 gimp-edit-fill FOREGROUND-FILL) | |
88 | (curry (thing-it drawable (curry2 gimp-dodgeburn exposure BURN MIDTONES)) steps)) | |
89 | (combine | |
90 | (if overwrite-background (curry2 gimp-edit-fill BACKGROUND-FILL) (lambda (d) #t)) | |
91 | (curry (thing-it drawable (curry2 gimp-dodgeburn exposure DODGE MIDTONES)) steps))) | |
92 | ; grab the border of our selection | |
93 | (border-select img fuzz) | |
94 | (with-color (lambda () | |
95 | (list | |
96 | (gimp-context-set-foreground (list 0 0 0)) ; draw border in black | |
97 | (gimp-edit-fill drawable FOREGROUND-FILL)))))))))))) | |
98 | ; treats f as a single undo group | |
99 | (define with-undo-group (lambda (img f) | |
100 | (guard | |
101 | (curry gimp-image-undo-group-start img) | |
102 | (curry gimp-image-undo-group-end img) | |
103 | f))) | |
104 | ; executes 'first', then 'f', then 'last'. TODO: make error safe. | |
105 | (define guard (lambda (first last f) | |
106 | (lambda x | |
107 | (list (first) (apply f x) (last))))) | |
108 | ; partial application | |
109 | (define (curry fun . args) (lambda x | |
110 | (apply fun (append args x)))) | |
111 | ; 2.6 compatibility stub | |
112 | (define rectselect (if | |
113 | (string=? (substring (car (gimp-version)) 0 3) "2.6") | |
114 | - | (lambda (image operation x y width height) (gimp-rect-select img x y width height operation 0 0)) |
114 | + | (lambda (image operation x y width height) (gimp-rect-select image x y width height operation 0 0)) |
115 | gimp-image-select-rectangle)) | |
116 | ; select a bunch of rectangles at random | |
117 | (define reselect (lambda (img img-size size num range ratio) | |
118 | (let* | |
119 | ( | |
120 | (ch (car (gimp-selection-save img)))) | |
121 | (list | |
122 | (gimp-selection-none img) | |
123 | (loop 0 num (lambda (x) | |
124 | (let* | |
125 | ( | |
126 | (x (random (car img-size))) | |
127 | (y (random (cadr img-size))) | |
128 | (pair (random-one ratio))) | |
129 | (my-select-rectangle-helper | |
130 | x | |
131 | y | |
132 | (/ (random2n (/ size range) size) (car pair)) | |
133 | (/ (random2n (/ size range) size) (cadr pair)) | |
134 | (curry rectselect img CHANNEL-OP-ADD))))) | |
135 | (script-fu-channel-grab img ch CHANNEL-OP-INTERSECT))))) | |
136 | ; randomly append or prepend a 1. | |
137 | ; used for vertical or horizontally scaled rectangles. | |
138 | (define random-one (lambda (n) | |
139 | (if (= (random 2) 0) | |
140 | (list n 1) | |
141 | (list 1 n)))) | |
142 | ; a select-rectangle wrapper that handles negative width/height | |
143 | (define my-select-rectangle-helper (lambda (x y w h f) | |
144 | (let* | |
145 | ( | |
146 | (xx (if (< w 0) (+ x w) x)) | |
147 | (yy (if (< h 0) (+ y h) y))) | |
148 | (f xx yy (abs w) (abs h))))) | |
149 | ; random number from [-to..-from], [from..to] | |
150 | (define random2n (lambda (from to) | |
151 | (let* | |
152 | ( | |
153 | (tmp (+ (random (- to from)) from))) | |
154 | (if (= (random 2) 0) | |
155 | tmp | |
156 | (- 0 tmp))))) | |
157 | ; execute f1, invert selection, execute f2, invert selection | |
158 | (define swap-and-do (lambda (d img f1 f2) | |
159 | (let* | |
160 | ( | |
161 | (w (car (gimp-drawable-width d))) | |
162 | (h (car (gimp-drawable-height d)))) | |
163 | (list | |
164 | (f1 d) | |
165 | (gimp-selection-invert img) | |
166 | (f2 d) | |
167 | (gimp-selection-invert img))))) | |
168 | ; takes a bunch of functions and combines them into one that has the | |
169 | ; same arguments as each of them and calls them in turn | |
170 | (define combine (lambda x | |
171 | (lambda y | |
172 | (if (= (length x) 0) | |
173 | () | |
174 | (append | |
175 | (list (apply (car x) y)) | |
176 | (apply (apply combine (cdr x)) y)))))) | |
177 | ; like curry, except it keeps the first argument | |
178 | (define (curry2 fun . args) (lambda x | |
179 | (apply fun (append (list (car x)) args (cdr x))))) | |
180 | ; execute some action 'fn' over the entire image in subdivisions of 'divs'. | |
181 | (define thing-it (lambda (d fn) | |
182 | (lambda (divs) | |
183 | ((with-undo-group | |
184 | (car (gimp-item-get-image d)) | |
185 | (lambda () | |
186 | (let* | |
187 | ( | |
188 | (w (car (gimp-drawable-width d))) | |
189 | (h (car (gimp-drawable-height d)))) | |
190 | (loop 0 (+ divs 1) (lambda (y) | |
191 | (loop 0 (+ divs 1) (lambda (x) | |
192 | (fn | |
193 | d | |
194 | 2 | |
195 | (floatarray (* (/ w divs) y) (* (/ h divs) x)))))))))))))) | |
196 | ; source for floatarray: | |
197 | ; http://www.math.grinnell.edu/~rebelsky/Glimmer/Summer2006/GIMP/sam.scm | |
198 | ; coerce a list into a float-array | |
199 | (define floatarray (lambda floats | |
200 | (let* | |
201 | ( | |
202 | (len (length floats)) | |
203 | (vec (cons-array len 'double))) | |
204 | (letrec | |
205 | ((kernel (lambda (pos rest) | |
206 | (if | |
207 | (= pos len) | |
208 | vec | |
209 | (begin | |
210 | (vector-set! vec pos (car rest)) | |
211 | (kernel (+ pos 1) (cdr rest))))))) | |
212 | (kernel 0 floats))))) | |
213 | ; saves/restores active selection after 'f' is executed | |
214 | (define with-selection (lambda (img f) | |
215 | (let* | |
216 | ( | |
217 | (ch (car (gimp-selection-save img)))) | |
218 | (list | |
219 | (f) | |
220 | (script-fu-channel-grab img ch CHANNEL-OP-REPLACE))))) | |
221 | ; select the border of our selection using feather | |
222 | (define border-select (lambda (img fuzz) | |
223 | (let* | |
224 | ( | |
225 | (ch1 (car (gimp-selection-save img)))) | |
226 | (list | |
227 | (gimp-selection-feather img fuzz) | |
228 | (let* | |
229 | ( | |
230 | (ch2 (car (gimp-selection-save img)))) | |
231 | (list | |
232 | (script-fu-channel-grab img ch1 CHANNEL-OP-REPLACE) | |
233 | (gimp-selection-invert img) | |
234 | (gimp-selection-feather img fuzz) | |
235 | (script-fu-channel-grab img ch2 CHANNEL-OP-INTERSECT))))))) | |
236 | ; remove a (temporary) channel after applying it as a selection | |
237 | (define script-fu-channel-grab (lambda (img ch op) | |
238 | (list | |
239 | (gimp-image-select-item img op ch) | |
240 | (gimp-image-remove-channel img ch)))) |