X-Git-Url: http://git.imager.perl.org/imager.git/blobdiff_plain/f1ac5027d2bc4e16dc38c54cf5360415bfb4376d..70591a2b5cde8c99f6cb4fcbd228376d0cba74ba:/filters.c diff --git a/filters.c b/filters.c index fc1a5310..05caae8f 100644 --- a/filters.c +++ b/filters.c @@ -1,4 +1,5 @@ #include "image.h" +#include "imagei.h" #include #include @@ -13,6 +14,7 @@ filters.c - implements filters that operate on images i_contrast(im, 0.8); i_hardinvert(im); + i_unsharp_mask(im, 2.0, 1.0); // and more =head1 DESCRIPTION @@ -25,7 +27,7 @@ the pod for Imager. Some of these functions are internal. -=over 4 +=over =cut */ @@ -33,6 +35,23 @@ Some of these functions are internal. +/* +=item saturate(in) + +Clamps the input value between 0 and 255. (internal) + + in - input integer + +=cut +*/ + +static +unsigned char +saturate(int in) { + if (in>255) { return 255; } + else if (in>0) return in; + return 0; +} @@ -262,12 +281,12 @@ i_bumpmap(i_img *im, i_img *bump, int channel, int light_x, int light_y, int st) mm_log((1, "i_bumpmap: channel = %d while bump image only has %d channels\n", channel, bump->channels)); return; } - + mx = (bump->xsize <= im->xsize) ? bump->xsize : im->xsize; my = (bump->ysize <= im->ysize) ? bump->ysize : im->ysize; i_img_empty_ch(&new_im, im->xsize, im->ysize, im->channels); - + aX = (light_x > (mx >> 1)) ? light_x : mx - light_x; aY = (light_y > (my >> 1)) ? light_y : my - light_y; @@ -317,6 +336,213 @@ i_bumpmap(i_img *im, i_img *bump, int channel, int light_x, int light_y, int st) + +typedef struct { + float x,y,z; +} fvec; + + +static +float +dotp(fvec *a, fvec *b) { + return a->x*b->x+a->y*b->y+a->z*b->z; +} + +static +void +normalize(fvec *a) { + double d = sqrt(dotp(a,a)); + a->x /= d; + a->y /= d; + a->z /= d; +} + + +/* + positive directions: + + x - right, + y - down + z - out of the plane + + I = Ia + Ip*( cd*Scol(N.L) + cs*(R.V)^n ) + + Here, the variables are: + + * Ia - ambient colour + * Ip - intensity of the point light source + * cd - diffuse coefficient + * Scol - surface colour + * cs - specular coefficient + * n - objects shinyness + * N - normal vector + * L - lighting vector + * R - reflection vector + * V - vision vector + + static void fvec_dump(fvec *x) { + printf("(%.2f %.2f %.2f)", x->x, x->y, x->z); + } +*/ + +/* XXX: Should these return a code for success? */ + + + + +/* +=item i_bumpmap_complex(im, bump, channel, tx, ty, Lx, Ly, Lz, Ip, cd, cs, n, Ia, Il, Is) + +Makes a bumpmap on image im using the bump image as the elevation map. + + im - target image + bump - image that contains the elevation info + channel - to take the elevation information from + tx - shift in x direction of where to start applying bumpmap + ty - shift in y direction of where to start applying bumpmap + Lx - x position/direction of light + Ly - y position/direction of light + Lz - z position/direction of light + Ip - light intensity + cd - diffuse coefficient + cs - specular coefficient + n - surface shinyness + Ia - ambient colour + Il - light colour + Is - specular colour + +if z<0 then the L is taken to be the direction the light is shining in. Otherwise +the L is taken to be the position of the Light, Relative to the image. + +=cut +*/ + + +void +i_bumpmap_complex(i_img *im, + i_img *bump, + int channel, + int tx, + int ty, + float Lx, + float Ly, + float Lz, + float cd, + float cs, + float n, + i_color *Ia, + i_color *Il, + i_color *Is) { + i_img new_im; + + int inflight; + int x, y, ch; + int mx, Mx, my, My; + + float cdc[MAXCHANNELS]; + float csc[MAXCHANNELS]; + + i_color x1_color, y1_color, x2_color, y2_color; + + i_color Scol; /* Surface colour */ + + fvec L; /* Light vector */ + fvec N; /* surface normal */ + fvec R; /* Reflection vector */ + fvec V; /* Vision vector */ + + mm_log((1, "i_bumpmap_complex(im %p, bump %p, channel %d, tx %d, ty %d, Lx %.2f, Ly %.2f, Lz %.2f, cd %.2f, cs %.2f, n %.2f, Ia %p, Il %p, Is %p)\n", + im, bump, channel, tx, ty, Lx, Ly, Lz, cd, cs, n, Ia, Il, Is)); + + if (channel >= bump->channels) { + mm_log((1, "i_bumpmap_complex: channel = %d while bump image only has %d channels\n", channel, bump->channels)); + return; + } + + for(ch=0; chchannels; ch++) { + cdc[ch] = (float)Il->channel[ch]*cd/255.f; + csc[ch] = (float)Is->channel[ch]*cs/255.f; + } + + mx = 1; + my = 1; + Mx = bump->xsize-1; + My = bump->ysize-1; + + V.x = 0; + V.y = 0; + V.z = 1; + + if (Lz < 0) { /* Light specifies a direction vector, reverse it to get the vector from surface to light */ + L.x = -Lx; + L.y = -Ly; + L.z = -Lz; + normalize(&L); + } else { /* Light is the position of the light source */ + inflight = 0; + L.x = -0.2; + L.y = -0.4; + L.z = 1; + normalize(&L); + } + + i_img_empty_ch(&new_im, im->xsize, im->ysize, im->channels); + + for(y = 0; y < im->ysize; y++) { + for(x = 0; x < im->xsize; x++) { + double dp1, dp2; + double dx = 0, dy = 0; + + /* Calculate surface normal */ + if (mx=0) { + L.x = Lx - x; + L.y = Ly - y; + L.z = Lz; + normalize(&L); + } + + dp1 = dotp(&L,&N); + R.x = -L.x + 2*dp1*N.x; + R.y = -L.y + 2*dp1*N.y; + R.z = -L.z + 2*dp1*N.z; + + dp2 = dotp(&R,&V); + + dp1 = dp1<0 ?0 : dp1; + dp2 = pow(dp2<0 ?0 : dp2,n); + + i_gpix(im, x, y, &Scol); + + for(ch = 0; ch < im->channels; ch++) + Scol.channel[ch] = + saturate( Ia->channel[ch] + cdc[ch]*Scol.channel[ch]*dp1 + csc[ch]*dp2 ); + + i_ppix(&new_im, x, y, &Scol); + } + } + + i_copyto(im, &new_im, 0, 0, (int)im->xsize, (int)im->ysize, 0, 0); + i_img_exorcise(&new_im); +} + + /* =item i_postlevels(im, levels) @@ -403,24 +629,6 @@ i_mosaic(i_img *im, int size) { } } -/* -=item saturate(in) - -Clamps the input value between 0 and 255. (internal) - - in - input integer - -=cut -*/ - -static -unsigned char -saturate(int in) { - if (in>255) { return 255; } - else if (in>0) return in; - return 0; -} - /* =item i_watermark(im, wmark, tx, ty, pixdiff) @@ -752,7 +960,7 @@ i_gradgen(i_img *im, int num, int *xo, int *yo, i_color *ival, int dmeasure) { fdist[p] = xd*xd + yd*yd; /* euclidean distance */ break; case 2: /* euclidean squared */ - fdist[p] = max(xd*xd, yd*yd); /* manhattan distance */ + fdist[p] = i_max(xd*xd, yd*yd); /* manhattan distance */ break; default: m_fatal(3,"i_gradgen: Unknown distance measure\n"); @@ -771,6 +979,7 @@ i_gradgen(i_img *im, int num, int *xo, int *yo, i_color *ival, int dmeasure) { } i_ppix(im, x, y, &val); } + myfree(fdist); } @@ -804,7 +1013,7 @@ i_nearest_color_foo(i_img *im, int num, int *xo, int *yo, i_color *ival, int dme mindist = xd*xd + yd*yd; /* euclidean distance */ break; case 2: /* euclidean squared */ - mindist = max(xd*xd, yd*yd); /* manhattan distance */ + mindist = i_max(xd*xd, yd*yd); /* manhattan distance */ break; default: m_fatal(3,"i_nearest_color: Unknown distance measure\n"); @@ -821,7 +1030,7 @@ i_nearest_color_foo(i_img *im, int num, int *xo, int *yo, i_color *ival, int dme curdist = xd*xd + yd*yd; /* euclidean distance */ break; case 2: /* euclidean squared */ - curdist = max(xd*xd, yd*yd); /* manhattan distance */ + curdist = i_max(xd*xd, yd*yd); /* manhattan distance */ break; default: m_fatal(3,"i_nearest_color: Unknown distance measure\n"); @@ -874,7 +1083,7 @@ i_nearest_color(i_img *im, int num, int *xo, int *yo, i_color *oval, int dmeasur mindist = xd*xd + yd*yd; /* euclidean distance */ break; case 2: /* euclidean squared */ - mindist = max(xd*xd, yd*yd); /* manhattan distance */ + mindist = i_max(xd*xd, yd*yd); /* manhattan distance */ break; default: m_fatal(3,"i_nearest_color: Unknown distance measure\n"); @@ -891,7 +1100,7 @@ i_nearest_color(i_img *im, int num, int *xo, int *yo, i_color *oval, int dmeasur curdist = xd*xd + yd*yd; /* euclidean distance */ break; case 2: /* euclidean squared */ - curdist = max(xd*xd, yd*yd); /* manhattan distance */ + curdist = i_max(xd*xd, yd*yd); /* manhattan distance */ break; default: m_fatal(3,"i_nearest_color: Unknown distance measure\n"); @@ -918,6 +1127,180 @@ i_nearest_color(i_img *im, int num, int *xo, int *yo, i_color *oval, int dmeasur i_nearest_color_foo(im, num, xo, yo, ival, dmeasure); } +/* +=item i_unsharp_mask(im, stddev, scale) + +Perform an usharp mask, which is defined as subtracting the blurred +image from double the original. + +=cut +*/ +void i_unsharp_mask(i_img *im, double stddev, double scale) { + i_img copy; + int x, y, ch; + + if (scale < 0) + return; + /* it really shouldn't ever be more than 1.0, but maybe ... */ + if (scale > 100) + scale = 100; + + i_copy(©, im); + i_gaussian(©, stddev); + if (im->bits == i_8_bits) { + i_color *blur = mymalloc(im->xsize * sizeof(i_color) * 2); + i_color *out = blur + im->xsize; + + for (y = 0; y < im->ysize; ++y) { + i_glin(©, 0, copy.xsize, y, blur); + i_glin(im, 0, im->xsize, y, out); + for (x = 0; x < im->xsize; ++x) { + for (ch = 0; ch < im->channels; ++ch) { + /*int temp = out[x].channel[ch] + + scale * (out[x].channel[ch] - blur[x].channel[ch]);*/ + int temp = out[x].channel[ch] * 2 - blur[x].channel[ch]; + if (temp < 0) + temp = 0; + else if (temp > 255) + temp = 255; + out[x].channel[ch] = temp; + } + } + i_plin(im, 0, im->xsize, y, out); + } + + myfree(blur); + } + else { + i_fcolor *blur = mymalloc(im->xsize * sizeof(i_fcolor) * 2); + i_fcolor *out = blur + im->xsize; + + for (y = 0; y < im->ysize; ++y) { + i_glinf(©, 0, copy.xsize, y, blur); + i_glinf(im, 0, im->xsize, y, out); + for (x = 0; x < im->xsize; ++x) { + for (ch = 0; ch < im->channels; ++ch) { + double temp = out[x].channel[ch] + + scale * (out[x].channel[ch] - blur[x].channel[ch]); + if (temp < 0) + temp = 0; + else if (temp > 1.0) + temp = 1.0; + out[x].channel[ch] = temp; + } + } + i_plinf(im, 0, im->xsize, y, out); + } + + myfree(blur); + } + i_img_exorcise(©); +} + +/* +=item i_diff_image(im1, im2, mindiff) + +Creates a new image that is transparent, except where the pixel in im2 +is different from im1, where it is the pixel from im2. + +The samples must differ by at least mindiff to be considered different. + +=cut +*/ + +i_img * +i_diff_image(i_img *im1, i_img *im2, int mindiff) { + i_img *out; + int outchans, diffchans; + int xsize, ysize; + i_img temp; + + i_clear_error(); + if (im1->channels != im2->channels) { + i_push_error(0, "different number of channels"); + return NULL; + } + + outchans = diffchans = im1->channels; + if (outchans == 1 || outchans == 3) + ++outchans; + + xsize = i_min(im1->xsize, im2->xsize); + ysize = i_min(im1->ysize, im2->ysize); + + out = i_sametype_chans(im1, xsize, ysize, outchans); + + if (im1->bits == i_8_bits && im2->bits == i_8_bits) { + i_color *line1 = mymalloc(2 * xsize * sizeof(*line1)); + i_color *line2 = line1 + xsize; + i_color empty; + int x, y, ch; + + for (ch = 0; ch < MAXCHANNELS; ++ch) + empty.channel[ch] = 0; + + for (y = 0; y < ysize; ++y) { + i_glin(im1, 0, xsize, y, line1); + i_glin(im2, 0, xsize, y, line2); + if (outchans != diffchans) { + /* give the output an alpha channel since it doesn't have one */ + for (x = 0; x < xsize; ++x) + line2[x].channel[diffchans] = 255; + } + for (x = 0; x < xsize; ++x) { + int diff = 0; + for (ch = 0; ch < diffchans; ++ch) { + if (line1[x].channel[ch] != line2[x].channel[ch] + && abs(line1[x].channel[ch] - line2[x].channel[ch]) > mindiff) { + diff = 1; + break; + } + } + if (!diff) + line2[x] = empty; + } + i_plin(out, 0, xsize, y, line2); + } + myfree(line1); + } + else { + i_fcolor *line1 = mymalloc(2 * xsize * sizeof(*line1)); + i_fcolor *line2 = line1 + xsize; + i_fcolor empty; + int x, y, ch; + double dist = mindiff / 255; + + for (ch = 0; ch < MAXCHANNELS; ++ch) + empty.channel[ch] = 0; + + for (y = 0; y < ysize; ++y) { + i_glinf(im1, 0, xsize, y, line1); + i_glinf(im2, 0, xsize, y, line2); + if (outchans != diffchans) { + /* give the output an alpha channel since it doesn't have one */ + for (x = 0; x < xsize; ++x) + line2[x].channel[diffchans] = 1.0; + } + for (x = 0; x < xsize; ++x) { + int diff = 0; + for (ch = 0; ch < diffchans; ++ch) { + if (line1[x].channel[ch] != line2[x].channel[ch] + && abs(line1[x].channel[ch] - line2[x].channel[ch]) > dist) { + diff = 1; + break; + } + } + if (!diff) + line2[x] = empty; + } + i_plinf(out, 0, xsize, y, line2); + } + myfree(line1); + } + + return out; +} + struct fount_state; static double linear_fount_f(double x, double y, struct fount_state *state); static double bilinear_fount_f(double x, double y, struct fount_state *state); @@ -1143,8 +1526,15 @@ i_fountain(i_img *im, double xa, double ya, double xb, double yb, struct fount_state state; int x, y; i_fcolor *line = mymalloc(sizeof(i_fcolor) * im->xsize); - int ch; + i_fcolor *work = NULL; + i_fountain_seg *my_segs; + i_fill_combine_f combine_func = NULL; + i_fill_combinef_f combinef_func = NULL; + + i_get_combine(combine, &combine_func, &combinef_func); + if (combinef_func) + work = mymalloc(sizeof(i_fcolor) * im->xsize); fount_init_state(&state, xa, ya, xb, yb, type, repeat, combine, super_sample, ssample_param, count, segs); @@ -1155,25 +1545,23 @@ i_fountain(i_img *im, double xa, double ya, double xb, double yb, for (x = 0; x < im->xsize; ++x) { i_fcolor c; int got_one; - double v; if (super_sample == i_fts_none) got_one = fount_getat(&c, x, y, &state); else got_one = state.ssfunc(&c, x, y, &state); if (got_one) { - if (combine) { - for (ch = 0; ch < im->channels; ++ch) { - line[x].channel[ch] = line[x].channel[ch] * (1.0 - c.channel[3]) - + c.channel[ch] * c.channel[3]; - } - } + if (combine) + work[x] = c; else line[x] = c; } } + if (combine) + combinef_func(line, work, im->channels, im->xsize); i_plinf(im, 0, im->xsize, y, line); } fount_finish_state(&state); + if (work) myfree(work); myfree(line); } @@ -1191,6 +1579,8 @@ fount_fill_destroy(i_fill_t *fill); /* =item i_new_fount(xa, ya, xb, yb, type, repeat, combine, super_sample, ssample_param, count, segs) +Creates a new general fill which fills with a fountain fill. + =cut */ @@ -1204,7 +1594,12 @@ i_new_fill_fount(double xa, double ya, double xb, double yb, fill->base.fill_with_color = NULL; fill->base.fill_with_fcolor = fill_fountf; fill->base.destroy = fount_fill_destroy; - fill->base.combines = combine; + if (combine) + i_get_combine(combine, &fill->base.combine, &fill->base.combinef); + else { + fill->base.combine = NULL; + fill->base.combinef = NULL; + } fount_init_state(&fill->state, xa, ya, xb, yb, type, repeat, combine, super_sample, ssample_param, count, segs); @@ -1233,7 +1628,6 @@ fount_init_state(struct fount_state *state, double xa, double ya, int i, j; i_fountain_seg *my_segs = mymalloc(sizeof(i_fountain_seg) * count); /*int have_alpha = im->channels == 2 || im->channels == 4;*/ - int ch; memset(state, 0, sizeof(*state)); /* we keep a local copy that we can adjust for speed */ @@ -1241,8 +1635,8 @@ fount_init_state(struct fount_state *state, double xa, double ya, i_fountain_seg *seg = my_segs + i; *seg = segs[i]; - if (seg->type < 0 || type >= i_ft_end) - seg->type = i_ft_linear; + if (seg->type < 0 || seg->type >= i_fst_end) + seg->type = i_fst_linear; if (seg->color < 0 || seg->color >= i_fc_end) seg->color = i_fc_direct; if (seg->color == i_fc_hue_up || seg->color == i_fc_hue_down) { @@ -1307,7 +1701,7 @@ fount_init_state(struct fount_state *state, double xa, double ya, } state->ffunc = fount_funcs[type]; if (super_sample < 0 - || super_sample >= (sizeof(fount_ssamples)/sizeof(*fount_ssamples))) { + || super_sample >= (int)(sizeof(fount_ssamples)/sizeof(*fount_ssamples))) { super_sample = 0; } state->ssample_data = NULL; @@ -1791,30 +2185,19 @@ static void fill_fountf(i_fill_t *fill, int x, int y, int width, int channels, i_fcolor *data) { i_fill_fountain_t *f = (i_fill_fountain_t *)fill; - int ch; - + while (width--) { i_fcolor c; int got_one; - double v; + if (f->state.ssfunc) got_one = f->state.ssfunc(&c, x, y, &f->state); else got_one = fount_getat(&c, x, y, &f->state); - - if (got_one) { - if (f->base.combines) { - for (ch = 0; ch < channels; ++ch) { - data->channel[ch] = data->channel[ch] * (1.0 - c.channel[3]) - + c.channel[ch] * c.channel[3]; - } - } - else - *data = c; - } - + + *data++ = c; + ++x; - ++data; } }