#include "regmach.h"
#include <float.h>
#include "imageri.h"
/*#define DEBUG*/
#ifdef DEBUG
#define DBG(x) printf x
#else
#define DBG(x)
#endif
static float MAX_EXP_ARG; /* = log(DBL_MAX); */
/* these functions currently assume RGB images - there seems to be some
support for other color spaces, but I can't tell how you find what
space an image is using.
HSV conversions from pages 401-403 "Procedural Elements for Computer
Graphics", 1985, ISBN 0-07-053534-5. The algorithm presents to produce
an HSV color calculates all components at once - I don't, so I've
simiplified the algorithm to avoid unnecessary calculation (any errors
(of which I had a few ;) are mine).
*/
/* returns the value (brightness) of color from 0 to 1 */
static double hsv_value(i_color color) {
return i_max(i_max(color.rgb.r, color.rgb.g), color.rgb.b) / 255.0;
}
/* returns the hue (color) of color from 0 to 360 */
static double hsv_hue(i_color color) {
int val;
int temp;
temp = i_min(i_min(color.rgb.r, color.rgb.g), color.rgb.b);
val = i_max(color.rgb.r, i_max(color.rgb.g, color.rgb.b));
if (val == 0 || val==temp) {
return 0;
}
else {
double cr = (val - color.rgb.r) / (double)(val - temp);
double cg = (val - color.rgb.g) / (double)(val - temp);
double cb = (val - color.rgb.b) / (double)(val - temp);
double hue;
if (color.rgb.r == val) {
hue = cb-cg;
}
else if (color.rgb.g == val) {
hue = 2.0 + cr-cb;
}
else { /* if (blue == val) */
hue = 4.0 + cg - cr;
}
hue *= 60.0; /* to degrees */
if (hue < 0)
hue += 360;
return hue;
}
}
/* return the saturation of color from 0 to 1 */
static double hsv_sat(i_color color) {
int value = i_max(i_max(color.rgb.r, color.rgb.g), color.rgb.b);
if (value == 0) {
return 0;
}
else {
int temp = i_min(i_min(color.rgb.r, color.rgb.g), color.rgb.b);
return (value - temp) / (double)value;
}
}
static i_color make_hsv(double hue, double sat, double val, int alpha) {
int i;
i_color c;
for( i=0; i< MAXCHANNELS; i++) c.channel[i]=0;
DBG(("hsv=%f %f %f\n", hue, sat, val));
if (sat <= 0) { /* handle -ve in case someone supplies a bad value */
/* should this be * 256? */
c.rgb.r = c.rgb.g = c.rgb.b = 255 * val;
}
else {
int i, m, n, k, v;
double f;
if (val < 0) val = 0;
if (val > 1) val = 1;
if (sat > 1) sat = 1;
/* I want to handle -360 <= hue < 720 so that the caller can
fiddle with colour
*/
if (hue >= 360)
hue -= 360;
else if (hue < 0)
hue += 360;
hue /= 60;
i = hue; /* floor */
f = hue - i;
val *= 255;
m = val * (1.0 - sat);
n = val * (1.0 - sat * f);
k = val * (1.0 - sat * (1 - f));
v = val;
switch (i) {
case 0:
c.rgb.r = v; c.rgb.g = k; c.rgb.b = m;
break;
case 1:
c.rgb.r = n; c.rgb.g = v; c.rgb.b = m;
break;
case 2:
c.rgb.r = m; c.rgb.g = v; c.rgb.b = k;
break;
case 3:
c.rgb.r = m; c.rgb.g = n; c.rgb.b = v;
break;
case 4:
c.rgb.r = k; c.rgb.g = m; c.rgb.b = v;
break;
case 5:
c.rgb.r = v; c.rgb.g = m; c.rgb.b = n;
break;
}
}
c.rgba.a = alpha;
return c;
}
static i_color make_rgb(int r, int g, int b, int a) {
i_color c;
if (r < 0)
r = 0;
if (r > 255)
r = 255;
c.rgb.r = r;
if (g < 0)
g = 0;
if (g > 255)
g = 255;
c.rgb.g = g;
if (b < 0)
b = 0;
if (b > 255)
b = 255;
c.rgb.b = b;
c.rgba.a = a;
return c;
}
/* greatly simplifies the code */
#define nout n_regs[codes->rout]
#define na n_regs[codes->ra]
#define nb n_regs[codes->rb]
#define nc n_regs[codes->rc]
#define nd n_regs[codes->rd]
#define cout c_regs[codes->rout]
#define ca c_regs[codes->ra]
#define cb c_regs[codes->rb]
#define cc c_regs[codes->rc]
#define cd c_regs[codes->rd]
/* this is a pretty poor epsilon used for loosening up equality comparisons
It isn't currently used for inequalities
*/
#define n_epsilon(x, y) (fabs(x)+fabs(y))*0.001
static i_color bcol = {{ 0 }};
i_color i_rm_run(struct rm_op codes[], size_t code_count,
double n_regs[], size_t n_regs_count,
i_color c_regs[], size_t c_regs_count,
i_img *images[], size_t image_count) {
double dx, dy;
struct rm_op *codes_base = codes;
size_t count_base = code_count;
DBG(("rm_run(%p, %d)\n", codes, code_count));
while (code_count) {
DBG((" rm_code %d\n", codes->code));
switch (codes->code) {
case rbc_add:
nout = na + nb;
break;
case rbc_subtract:
nout = na - nb;
break;
case rbc_mult:
nout = na * nb;
break;
case rbc_div:
if (fabs(nb) < 1e-10)
nout = 1e10;
else
nout = na / nb;
break;
case rbc_mod:
if (fabs(nb) > 1e-10) {
nout = fmod(na, nb);
}
else {
nout = 0; /* close enough ;) */
}
break;
case rbc_pow:
nout = pow(na, nb);
break;
case rbc_uminus:
nout = -na;
break;
case rbc_multp:
cout = make_rgb(ca.rgb.r * nb, ca.rgb.g * nb, ca.rgb.b * nb, 255);
break;
case rbc_addp:
cout = make_rgb(ca.rgb.r + cb.rgb.r, ca.rgb.g + cb.rgb.g,
ca.rgb.b + cb.rgb.b, 255);
break;
case rbc_subtractp:
cout = make_rgb(ca.rgb.r - cb.rgb.r, ca.rgb.g - cb.rgb.g,
ca.rgb.b - cb.rgb.b, 255);
break;
case rbc_sin:
nout = sin(na);
break;
case rbc_cos:
nout = cos(na);
break;
case rbc_atan2:
nout = atan2(na, nb);
break;
case rbc_sqrt:
nout = sqrt(na);
break;
case rbc_distance:
dx = na-nc;
dy = nb-nd;
nout = sqrt(dx*dx+dy*dy);
break;
case rbc_getp1:
i_gpix(images[0], na, nb, c_regs+codes->rout);
if (images[0]->channels < 4) cout.rgba.a = 255;
break;
case rbc_getp2:
i_gpix(images[1], na, nb, c_regs+codes->rout);
if (images[1]->channels < 4) cout.rgba.a = 255;
break;
case rbc_getp3:
i_gpix(images[2], na, nb, c_regs+codes->rout);
if (images[2]->channels < 4) cout.rgba.a = 255;
break;
case rbc_value:
nout = hsv_value(ca);
break;
case rbc_hue:
nout = hsv_hue(ca);
break;
case rbc_sat:
nout = hsv_sat(ca);
break;
case rbc_hsv:
cout = make_hsv(na, nb, nc, 255);
break;
case rbc_hsva:
cout = make_hsv(na, nb, nc, nd);
break;
case rbc_red:
nout = ca.rgb.r;
break;
case rbc_green:
nout = ca.rgb.g;
break;
case rbc_blue:
nout = ca.rgb.b;
break;
case rbc_alpha:
nout = ca.rgba.a;
break;
case rbc_rgb:
cout = make_rgb(na, nb, nc, 255);
break;
case rbc_rgba:
cout = make_rgb(na, nb, nc, nd);
break;
case rbc_int:
nout = (int)(na);
break;
case rbc_if:
nout = na ? nb : nc;
break;
case rbc_ifp:
cout = na ? cb : cc;
break;
case rbc_le:
nout = na <= nb + n_epsilon(na,nb);
break;
case rbc_lt:
nout = na < nb;
break;
case rbc_ge:
nout = na >= nb - n_epsilon(na,nb);
break;
case rbc_gt:
nout = na > nb;
break;
case rbc_eq:
nout = fabs(na-nb) <= n_epsilon(na,nb);
break;
case rbc_ne:
nout = fabs(na-nb) > n_epsilon(na,nb);
break;
case rbc_and:
nout = na && nb;
break;
case rbc_or:
nout = na || nb;
break;
case rbc_not:
nout = !na;
break;
case rbc_abs:
nout = fabs(na);
break;
case rbc_ret:
return ca;
break;
case rbc_jump:
/* yes, order is important here */
code_count = count_base - codes->ra;
codes = codes_base + codes->ra;
continue;
case rbc_jumpz:
if (!na) {
/* yes, order is important here */
code_count = count_base - codes->rb;
codes = codes_base + codes->rb;
continue;
}
break;
case rbc_jumpnz:
if (na) {
/* yes, order is important here */
code_count = count_base - codes->rb;
codes = codes_base + codes->rb;
continue;
}
break;
case rbc_set:
nout = na;
break;
case rbc_setp:
cout = ca;
break;
case rbc_log:
if (na > 0) {
nout = log(na);
}
else {
nout = DBL_MAX;
}
break;
case rbc_exp:
if (!MAX_EXP_ARG) MAX_EXP_ARG = log(DBL_MAX);
if (na <= MAX_EXP_ARG) {
nout = exp(na);
}
else {
nout = DBL_MAX;
}
break;
case rbc_print:
nout = na;
printf("r%d is %g\n", codes->ra, na);
break;
case rbc_det:
nout = na*nd-nb*nc;
break;
default:
/*croak("bad opcode"); */
printf("bad op %d\n", codes->code);
return bcol;
}
--code_count;
++codes;
}
return bcol;
/* croak("no return opcode"); */
}