Commit | Line | Data |
---|---|---|
02d1d628 | 1 | #include "regmach.h" |
aed9d070 | 2 | #include <float.h> |
50c75381 | 3 | #include "imageri.h" |
02d1d628 AMH |
4 | |
5 | /*#define DEBUG*/ | |
6 | #ifdef DEBUG | |
7 | #define DBG(x) printf x | |
8 | #else | |
9 | #define DBG(x) | |
10 | #endif | |
11 | ||
aed9d070 TC |
12 | static float MAX_EXP_ARG; /* = log(DBL_MAX); */ |
13 | ||
14 | ||
02d1d628 AMH |
15 | /* these functions currently assume RGB images - there seems to be some |
16 | support for other color spaces, but I can't tell how you find what | |
17 | space an image is using. | |
18 | ||
19 | HSV conversions from pages 401-403 "Procedural Elements for Computer | |
20 | Graphics", 1985, ISBN 0-07-053534-5. The algorithm presents to produce | |
21 | an HSV color calculates all components at once - I don't, so I've | |
22 | simiplified the algorithm to avoid unnecessary calculation (any errors | |
23 | (of which I had a few ;) are mine). | |
24 | */ | |
25 | ||
26 | /* returns the value (brightness) of color from 0 to 1 */ | |
b33c08f8 TC |
27 | static double hsv_value(i_color color) { |
28 | return i_max(i_max(color.rgb.r, color.rgb.g), color.rgb.b) / 255.0; | |
02d1d628 AMH |
29 | } |
30 | ||
31 | /* returns the hue (color) of color from 0 to 360 */ | |
b33c08f8 | 32 | static double hsv_hue(i_color color) { |
02d1d628 AMH |
33 | int val; |
34 | int temp; | |
b33c08f8 TC |
35 | temp = i_min(i_min(color.rgb.r, color.rgb.g), color.rgb.b); |
36 | val = i_max(color.rgb.r, i_max(color.rgb.g, color.rgb.b)); | |
02d1d628 AMH |
37 | if (val == 0 || val==temp) { |
38 | return 0; | |
39 | } | |
40 | else { | |
41 | double cr = (val - color.rgb.r) / (double)(val - temp); | |
42 | double cg = (val - color.rgb.g) / (double)(val - temp); | |
43 | double cb = (val - color.rgb.b) / (double)(val - temp); | |
44 | double hue; | |
45 | if (color.rgb.r == val) { | |
46 | hue = cb-cg; | |
47 | } | |
48 | else if (color.rgb.g == val) { | |
49 | hue = 2.0 + cr-cb; | |
50 | } | |
51 | else { /* if (blue == val) */ | |
52 | hue = 4.0 + cg - cr; | |
53 | } | |
54 | hue *= 60.0; /* to degrees */ | |
55 | if (hue < 0) | |
56 | hue += 360; | |
57 | ||
58 | return hue; | |
59 | } | |
60 | } | |
61 | ||
62 | /* return the saturation of color from 0 to 1 */ | |
b33c08f8 TC |
63 | static double hsv_sat(i_color color) { |
64 | int value = i_max(i_max(color.rgb.r, color.rgb.g), color.rgb.b); | |
02d1d628 AMH |
65 | if (value == 0) { |
66 | return 0; | |
67 | } | |
68 | else { | |
52b0d318 | 69 | int temp = i_min(i_min(color.rgb.r, color.rgb.g), color.rgb.b); |
02d1d628 AMH |
70 | return (value - temp) / (double)value; |
71 | } | |
72 | } | |
73 | ||
e5744e01 | 74 | static i_color make_hsv(double hue, double sat, double val, int alpha) { |
02d1d628 AMH |
75 | int i; |
76 | i_color c; | |
77 | for( i=0; i< MAXCHANNELS; i++) c.channel[i]=0; | |
78 | DBG(("hsv=%f %f %f\n", hue, sat, val)); | |
79 | if (sat <= 0) { /* handle -ve in case someone supplies a bad value */ | |
80 | /* should this be * 256? */ | |
81 | c.rgb.r = c.rgb.g = c.rgb.b = 255 * val; | |
82 | } | |
83 | else { | |
84 | int i, m, n, k, v; | |
85 | double f; | |
86 | ||
87 | if (val < 0) val = 0; | |
88 | if (val > 1) val = 1; | |
89 | if (sat > 1) sat = 1; | |
90 | ||
91 | /* I want to handle -360 <= hue < 720 so that the caller can | |
92 | fiddle with colour | |
93 | */ | |
94 | if (hue >= 360) | |
95 | hue -= 360; | |
96 | else if (hue < 0) | |
97 | hue += 360; | |
98 | hue /= 60; | |
99 | i = hue; /* floor */ | |
100 | f = hue - i; | |
101 | val *= 255; | |
102 | m = val * (1.0 - sat); | |
103 | n = val * (1.0 - sat * f); | |
104 | k = val * (1.0 - sat * (1 - f)); | |
105 | v = val; | |
106 | switch (i) { | |
107 | case 0: | |
108 | c.rgb.r = v; c.rgb.g = k; c.rgb.b = m; | |
109 | break; | |
110 | case 1: | |
111 | c.rgb.r = n; c.rgb.g = v; c.rgb.b = m; | |
112 | break; | |
113 | case 2: | |
114 | c.rgb.r = m; c.rgb.g = v; c.rgb.b = k; | |
115 | break; | |
116 | case 3: | |
117 | c.rgb.r = m; c.rgb.g = n; c.rgb.b = v; | |
118 | break; | |
119 | case 4: | |
120 | c.rgb.r = k; c.rgb.g = m; c.rgb.b = v; | |
121 | break; | |
122 | case 5: | |
123 | c.rgb.r = v; c.rgb.g = m; c.rgb.b = n; | |
124 | break; | |
125 | } | |
126 | } | |
e5744e01 | 127 | c.rgba.a = alpha; |
02d1d628 AMH |
128 | |
129 | return c; | |
130 | } | |
131 | ||
e5744e01 | 132 | static i_color make_rgb(int r, int g, int b, int a) { |
02d1d628 AMH |
133 | i_color c; |
134 | if (r < 0) | |
135 | r = 0; | |
136 | if (r > 255) | |
137 | r = 255; | |
138 | c.rgb.r = r; | |
139 | if (g < 0) | |
140 | g = 0; | |
141 | if (g > 255) | |
142 | g = 255; | |
143 | c.rgb.g = g; | |
144 | if (b < 0) | |
145 | b = 0; | |
146 | if (b > 255) | |
147 | b = 255; | |
148 | c.rgb.b = b; | |
149 | ||
e5744e01 TC |
150 | c.rgba.a = a; |
151 | ||
02d1d628 AMH |
152 | return c; |
153 | } | |
154 | ||
155 | /* greatly simplifies the code */ | |
156 | #define nout n_regs[codes->rout] | |
157 | #define na n_regs[codes->ra] | |
158 | #define nb n_regs[codes->rb] | |
159 | #define nc n_regs[codes->rc] | |
160 | #define nd n_regs[codes->rd] | |
161 | #define cout c_regs[codes->rout] | |
162 | #define ca c_regs[codes->ra] | |
163 | #define cb c_regs[codes->rb] | |
164 | #define cc c_regs[codes->rc] | |
165 | #define cd c_regs[codes->rd] | |
166 | ||
167 | /* this is a pretty poor epsilon used for loosening up equality comparisons | |
168 | It isn't currently used for inequalities | |
169 | */ | |
170 | ||
171 | #define n_epsilon(x, y) (abs(x)+abs(y))*0.001 | |
172 | static i_color bcol = {{ 0 }}; | |
173 | ||
b33c08f8 | 174 | i_color i_rm_run(struct rm_op codes[], size_t code_count, |
02d1d628 AMH |
175 | double n_regs[], size_t n_regs_count, |
176 | i_color c_regs[], size_t c_regs_count, | |
177 | i_img *images[], size_t image_count) { | |
178 | double dx, dy; | |
179 | struct rm_op *codes_base = codes; | |
180 | size_t count_base = code_count; | |
aed9d070 | 181 | |
02d1d628 AMH |
182 | DBG(("rm_run(%p, %d)\n", codes, code_count)); |
183 | while (code_count) { | |
184 | DBG((" rm_code %d\n", codes->code)); | |
185 | switch (codes->code) { | |
186 | case rbc_add: | |
187 | nout = na + nb; | |
188 | break; | |
189 | ||
190 | case rbc_subtract: | |
191 | nout = na - nb; | |
192 | break; | |
193 | ||
194 | case rbc_mult: | |
195 | nout = na * nb; | |
196 | break; | |
197 | ||
198 | case rbc_div: | |
199 | if (abs(nb) < 1e-10) | |
200 | nout = 1e10; | |
201 | else | |
202 | nout = na / nb; | |
203 | break; | |
204 | ||
205 | case rbc_mod: | |
206 | if (abs(nb) > 1e-10) { | |
207 | nout = fmod(na, nb); | |
208 | } | |
209 | else { | |
210 | nout = 0; /* close enough ;) */ | |
211 | } | |
212 | break; | |
213 | ||
214 | case rbc_pow: | |
215 | nout = pow(na, nb); | |
216 | break; | |
217 | ||
218 | case rbc_uminus: | |
219 | nout = -na; | |
3f29de50 | 220 | break; |
02d1d628 AMH |
221 | |
222 | case rbc_multp: | |
e5744e01 | 223 | cout = make_rgb(ca.rgb.r * nb, ca.rgb.g * nb, ca.rgb.b * nb, 255); |
02d1d628 AMH |
224 | break; |
225 | ||
226 | case rbc_addp: | |
227 | cout = make_rgb(ca.rgb.r + cb.rgb.r, ca.rgb.g + cb.rgb.g, | |
e5744e01 | 228 | ca.rgb.b + cb.rgb.b, 255); |
02d1d628 AMH |
229 | break; |
230 | ||
231 | case rbc_subtractp: | |
232 | cout = make_rgb(ca.rgb.r - cb.rgb.r, ca.rgb.g - cb.rgb.g, | |
e5744e01 | 233 | ca.rgb.b - cb.rgb.b, 255); |
02d1d628 AMH |
234 | break; |
235 | ||
236 | case rbc_sin: | |
237 | nout = sin(na); | |
238 | break; | |
239 | ||
240 | case rbc_cos: | |
241 | nout = cos(na); | |
242 | break; | |
243 | ||
244 | case rbc_atan2: | |
245 | nout = atan2(na, nb); | |
246 | break; | |
247 | ||
248 | case rbc_sqrt: | |
249 | nout = sqrt(na); | |
250 | break; | |
251 | ||
252 | case rbc_distance: | |
253 | dx = na-nc; | |
254 | dy = nb-nd; | |
255 | nout = sqrt(dx*dx+dy*dy); | |
256 | break; | |
257 | ||
258 | case rbc_getp1: | |
259 | i_gpix(images[0], na, nb, c_regs+codes->rout); | |
aed9d070 | 260 | if (images[0]->channels < 4) cout.rgba.a = 255; |
02d1d628 AMH |
261 | break; |
262 | ||
263 | case rbc_getp2: | |
264 | i_gpix(images[1], na, nb, c_regs+codes->rout); | |
aed9d070 | 265 | if (images[1]->channels < 4) cout.rgba.a = 255; |
02d1d628 AMH |
266 | break; |
267 | ||
268 | case rbc_getp3: | |
269 | i_gpix(images[2], na, nb, c_regs+codes->rout); | |
aed9d070 | 270 | if (images[2]->channels < 4) cout.rgba.a = 255; |
02d1d628 AMH |
271 | break; |
272 | ||
273 | case rbc_value: | |
274 | nout = hsv_value(ca); | |
275 | break; | |
276 | ||
277 | case rbc_hue: | |
278 | nout = hsv_hue(ca); | |
279 | break; | |
280 | ||
281 | case rbc_sat: | |
282 | nout = hsv_sat(ca); | |
283 | break; | |
284 | ||
285 | case rbc_hsv: | |
e5744e01 TC |
286 | cout = make_hsv(na, nb, nc, 255); |
287 | break; | |
288 | ||
289 | case rbc_hsva: | |
290 | cout = make_hsv(na, nb, nc, nd); | |
02d1d628 AMH |
291 | break; |
292 | ||
293 | case rbc_red: | |
294 | nout = ca.rgb.r; | |
295 | break; | |
296 | ||
297 | case rbc_green: | |
298 | nout = ca.rgb.g; | |
299 | break; | |
300 | ||
301 | case rbc_blue: | |
302 | nout = ca.rgb.b; | |
303 | break; | |
304 | ||
e5744e01 TC |
305 | case rbc_alpha: |
306 | nout = ca.rgba.a; | |
307 | break; | |
308 | ||
02d1d628 | 309 | case rbc_rgb: |
e5744e01 TC |
310 | cout = make_rgb(na, nb, nc, 255); |
311 | break; | |
312 | ||
313 | case rbc_rgba: | |
314 | cout = make_rgb(na, nb, nc, nd); | |
02d1d628 AMH |
315 | break; |
316 | ||
317 | case rbc_int: | |
318 | nout = (int)(na); | |
319 | break; | |
320 | ||
321 | case rbc_if: | |
322 | nout = na ? nb : nc; | |
323 | break; | |
324 | ||
325 | case rbc_ifp: | |
326 | cout = na ? cb : cc; | |
327 | break; | |
328 | ||
329 | case rbc_le: | |
330 | nout = na <= nb + n_epsilon(na,nb); | |
331 | break; | |
332 | ||
333 | case rbc_lt: | |
334 | nout = na < nb; | |
335 | break; | |
336 | ||
337 | case rbc_ge: | |
338 | nout = na >= nb - n_epsilon(na,nb); | |
339 | break; | |
340 | ||
341 | case rbc_gt: | |
342 | nout = na > nb; | |
343 | break; | |
344 | ||
345 | case rbc_eq: | |
346 | nout = abs(na-nb) <= n_epsilon(na,nb); | |
347 | break; | |
348 | ||
349 | case rbc_ne: | |
350 | nout = abs(na-nb) > n_epsilon(na,nb); | |
351 | break; | |
352 | ||
353 | case rbc_and: | |
354 | nout = na && nb; | |
355 | break; | |
356 | ||
357 | case rbc_or: | |
358 | nout = na || nb; | |
359 | break; | |
360 | ||
361 | case rbc_not: | |
362 | nout = !na; | |
363 | break; | |
364 | ||
365 | case rbc_abs: | |
366 | nout = abs(na); | |
367 | break; | |
368 | ||
369 | case rbc_ret: | |
370 | return ca; | |
371 | break; | |
372 | ||
373 | case rbc_jump: | |
374 | /* yes, order is important here */ | |
375 | code_count = count_base - codes->ra; | |
376 | codes = codes_base + codes->ra; | |
377 | continue; | |
378 | ||
379 | case rbc_jumpz: | |
380 | if (!na) { | |
381 | /* yes, order is important here */ | |
382 | code_count = count_base - codes->rb; | |
383 | codes = codes_base + codes->rb; | |
384 | continue; | |
385 | } | |
386 | break; | |
387 | ||
388 | case rbc_jumpnz: | |
389 | if (na) { | |
390 | /* yes, order is important here */ | |
391 | code_count = count_base - codes->rb; | |
392 | codes = codes_base + codes->rb; | |
393 | continue; | |
394 | } | |
395 | break; | |
396 | ||
397 | case rbc_set: | |
398 | nout = na; | |
399 | break; | |
400 | ||
401 | case rbc_setp: | |
402 | cout = ca; | |
403 | break; | |
404 | ||
aed9d070 TC |
405 | case rbc_log: |
406 | if (na > 0) { | |
407 | nout = log(na); | |
408 | } | |
409 | else { | |
410 | nout = DBL_MAX; | |
411 | } | |
412 | break; | |
413 | ||
414 | case rbc_exp: | |
415 | if (!MAX_EXP_ARG) MAX_EXP_ARG = log(DBL_MAX); | |
416 | if (na <= MAX_EXP_ARG) { | |
417 | nout = exp(na); | |
418 | } | |
419 | else { | |
420 | nout = DBL_MAX; | |
421 | } | |
422 | break; | |
423 | ||
02d1d628 | 424 | case rbc_print: |
3309187a | 425 | nout = na; |
02d1d628 AMH |
426 | printf("r%d is %g\n", codes->ra, na); |
427 | break; | |
428 | ||
3309187a TC |
429 | case rbc_det: |
430 | nout = na*nd-nb*nc; | |
431 | break; | |
432 | ||
02d1d628 AMH |
433 | default: |
434 | /*croak("bad opcode"); */ | |
435 | printf("bad op %d\n", codes->code); | |
436 | return bcol; | |
437 | } | |
438 | --code_count; | |
439 | ++codes; | |
440 | } | |
441 | return bcol; | |
442 | /* croak("no return opcode"); */ | |
443 | } |