10 filters.c - implements filters that operate on images
17 i_unsharp_mask(im, 2.0, 1.0);
22 filters.c implements basic filters for Imager. These filters
23 should be accessible from the filter interface as defined in
26 =head1 FUNCTION REFERENCE
28 Some of these functions are internal.
41 Clamps the input value between 0 and 255. (internal)
51 if (in>255) { return 255; }
52 else if (in>0) return in;
59 =item i_contrast(im, intensity)
61 Scales the pixel values by the amount specified.
64 intensity - scalefactor
70 i_contrast(i_img *im, float intensity) {
73 unsigned int new_color;
76 mm_log((1,"i_contrast(im %p, intensity %f)\n", im, intensity));
78 if(intensity < 0) return;
80 for(y = 0; y < im->ysize; y++) for(x = 0; x < im->xsize; x++) {
81 i_gpix(im, x, y, &rcolor);
83 for(ch = 0; ch < im->channels; ch++) {
84 new_color = (unsigned int) rcolor.channel[ch];
85 new_color *= intensity;
90 rcolor.channel[ch] = (unsigned char) new_color;
92 i_ppix(im, x, y, &rcolor);
98 =item i_hardinvert(im)
100 Inverts the pixel values of the input image.
108 i_hardinvert(i_img *im) {
114 mm_log((1,"i_hardinvert(im %p)\n", im));
116 for(y = 0; y < im->ysize; y++) {
117 for(x = 0; x < im->xsize; x++) {
118 i_gpix(im, x, y, &rcolor);
120 for(ch = 0; ch < im->channels; ch++) {
121 rcolor.channel[ch] = 255 - rcolor.channel[ch];
124 i_ppix(im, x, y, &rcolor);
132 =item i_noise(im, amount, type)
134 Inverts the pixel values by the amount specified.
137 amount - deviation in pixel values
138 type - noise individual for each channel if true
144 /* random() is non-ASCII, even if it is better than rand() */
145 #define random() rand()
149 i_noise(i_img *im, float amount, unsigned char type) {
153 float damount = amount * 2;
157 mm_log((1,"i_noise(im %p, intensity %.2f\n", im, amount));
159 if(amount < 0) return;
161 for(y = 0; y < im->ysize; y++) for(x = 0; x < im->xsize; x++) {
162 i_gpix(im, x, y, &rcolor);
165 color_inc = (amount - (damount * ((float)random() / RAND_MAX)));
168 for(ch = 0; ch < im->channels; ch++) {
169 new_color = (int) rcolor.channel[ch];
172 new_color += (amount - (damount * ((float)random() / RAND_MAX)));
174 new_color += color_inc;
180 if(new_color > 255) {
184 rcolor.channel[ch] = (unsigned char) new_color;
187 i_ppix(im, x, y, &rcolor);
193 =item i_noise(im, amount, type)
195 Inverts the pixel values by the amount specified.
198 amount - deviation in pixel values
199 type - noise individual for each channel if true
206 =item i_applyimage(im, add_im, mode)
208 Apply's an image to another image
211 add_im - image that is applied to target
212 mode - what method is used in applying:
232 void i_applyimage(i_img *im, i_img *add_im, unsigned char mode) {
236 mm_log((1, "i_applyimage(im %p, add_im %p, mode %d", im, add_im, mode));
238 mx = (add_im->xsize <= im->xsize) ? add_im->xsize : add_im->xsize;
239 my = (add_im->ysize <= im->ysize) ? add_im->ysize : add_im->ysize;
241 for(x = 0; x < mx; x++) {
242 for(y = 0; y < my; y++) {
249 =item i_bumpmap(im, bump, channel, light_x, light_y, st)
251 Makes a bumpmap on image im using the bump image as the elevation map.
254 bump - image that contains the elevation info
255 channel - to take the elevation information from
256 light_x - x coordinate of light source
257 light_y - y coordinate of light source
258 st - length of shadow
264 i_bumpmap(i_img *im, i_img *bump, int channel, int light_x, int light_y, int st) {
267 i_color x1_color, y1_color, x2_color, y2_color, dst_color;
272 unsigned char px1, px2, py1, py2;
276 mm_log((1, "i_bumpmap(im %p, add_im %p, channel %d, light_x %d, light_y %d, st %d)\n",
277 im, bump, channel, light_x, light_y, st));
280 if(channel >= bump->channels) {
281 mm_log((1, "i_bumpmap: channel = %d while bump image only has %d channels\n", channel, bump->channels));
285 mx = (bump->xsize <= im->xsize) ? bump->xsize : im->xsize;
286 my = (bump->ysize <= im->ysize) ? bump->ysize : im->ysize;
288 i_img_empty_ch(&new_im, im->xsize, im->ysize, im->channels);
290 aX = (light_x > (mx >> 1)) ? light_x : mx - light_x;
291 aY = (light_y > (my >> 1)) ? light_y : my - light_y;
293 aL = sqrt((aX * aX) + (aY * aY));
295 for(y = 1; y < my - 1; y++) {
296 for(x = 1; x < mx - 1; x++) {
297 i_gpix(bump, x + st, y, &x1_color);
298 i_gpix(bump, x, y + st, &y1_color);
299 i_gpix(bump, x - st, y, &x2_color);
300 i_gpix(bump, x, y - st, &y2_color);
302 i_gpix(im, x, y, &dst_color);
304 px1 = x1_color.channel[channel];
305 py1 = y1_color.channel[channel];
306 px2 = x2_color.channel[channel];
307 py2 = y2_color.channel[channel];
315 fZ = (sqrt((nX * nX) + (nY * nY)) / aL);
317 tX = abs(x - light_x) / aL;
318 tY = abs(y - light_y) / aL;
320 tZ = 1 - (sqrt((tX * tX) + (tY * tY)) * fZ);
325 for(ch = 0; ch < im->channels; ch++)
326 dst_color.channel[ch] = (unsigned char) (float)(dst_color.channel[ch] * tZ);
328 i_ppix(&new_im, x, y, &dst_color);
332 i_copyto(im, &new_im, 0, 0, (int)im->xsize, (int)im->ysize, 0, 0);
334 i_img_exorcise(&new_im);
347 dotp(fvec *a, fvec *b) {
348 return a->x*b->x+a->y*b->y+a->z*b->z;
354 double d = sqrt(dotp(a,a));
368 I = Ia + Ip*( cd*Scol(N.L) + cs*(R.V)^n )
370 Here, the variables are:
372 * Ia - ambient colour
373 * Ip - intensity of the point light source
374 * cd - diffuse coefficient
375 * Scol - surface colour
376 * cs - specular coefficient
377 * n - objects shinyness
379 * L - lighting vector
380 * R - reflection vector
383 static void fvec_dump(fvec *x) {
384 printf("(%.2f %.2f %.2f)", x->x, x->y, x->z);
388 /* XXX: Should these return a code for success? */
394 =item i_bumpmap_complex(im, bump, channel, tx, ty, Lx, Ly, Lz, Ip, cd, cs, n, Ia, Il, Is)
396 Makes a bumpmap on image im using the bump image as the elevation map.
399 bump - image that contains the elevation info
400 channel - to take the elevation information from
401 tx - shift in x direction of where to start applying bumpmap
402 ty - shift in y direction of where to start applying bumpmap
403 Lx - x position/direction of light
404 Ly - y position/direction of light
405 Lz - z position/direction of light
407 cd - diffuse coefficient
408 cs - specular coefficient
409 n - surface shinyness
414 if z<0 then the L is taken to be the direction the light is shining in. Otherwise
415 the L is taken to be the position of the Light, Relative to the image.
422 i_bumpmap_complex(i_img *im,
442 float cdc[MAXCHANNELS];
443 float csc[MAXCHANNELS];
445 i_color x1_color, y1_color, x2_color, y2_color;
447 i_color Scol; /* Surface colour */
449 fvec L; /* Light vector */
450 fvec N; /* surface normal */
451 fvec R; /* Reflection vector */
452 fvec V; /* Vision vector */
454 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",
455 im, bump, channel, tx, ty, Lx, Ly, Lz, cd, cs, n, Ia, Il, Is));
457 if (channel >= bump->channels) {
458 mm_log((1, "i_bumpmap_complex: channel = %d while bump image only has %d channels\n", channel, bump->channels));
462 for(ch=0; ch<im->channels; ch++) {
463 cdc[ch] = (float)Il->channel[ch]*cd/255.f;
464 csc[ch] = (float)Is->channel[ch]*cs/255.f;
476 if (Lz < 0) { /* Light specifies a direction vector, reverse it to get the vector from surface to light */
481 } else { /* Light is the position of the light source */
489 i_img_empty_ch(&new_im, im->xsize, im->ysize, im->channels);
491 for(y = 0; y < im->ysize; y++) {
492 for(x = 0; x < im->xsize; x++) {
494 double dx = 0, dy = 0;
496 /* Calculate surface normal */
497 if (mx<x && x<Mx && my<y && y<My) {
498 i_gpix(bump, x + 1, y, &x1_color);
499 i_gpix(bump, x - 1, y, &x2_color);
500 i_gpix(bump, x, y + 1, &y1_color);
501 i_gpix(bump, x, y - 1, &y2_color);
502 dx = x2_color.channel[channel] - x1_color.channel[channel];
503 dy = y2_color.channel[channel] - y1_color.channel[channel];
513 /* Calculate Light vector if needed */
522 R.x = -L.x + 2*dp1*N.x;
523 R.y = -L.y + 2*dp1*N.y;
524 R.z = -L.z + 2*dp1*N.z;
528 dp1 = dp1<0 ?0 : dp1;
529 dp2 = pow(dp2<0 ?0 : dp2,n);
531 i_gpix(im, x, y, &Scol);
533 for(ch = 0; ch < im->channels; ch++)
535 saturate( Ia->channel[ch] + cdc[ch]*Scol.channel[ch]*dp1 + csc[ch]*dp2 );
537 i_ppix(&new_im, x, y, &Scol);
541 i_copyto(im, &new_im, 0, 0, (int)im->xsize, (int)im->ysize, 0, 0);
542 i_img_exorcise(&new_im);
547 =item i_postlevels(im, levels)
549 Quantizes Images to fewer levels.
552 levels - number of levels
558 i_postlevels(i_img *im, int levels) {
566 rv = (int) ((float)(256 / levels));
569 for(y = 0; y < im->ysize; y++) for(x = 0; x < im->xsize; x++) {
570 i_gpix(im, x, y, &rcolor);
572 for(ch = 0; ch < im->channels; ch++) {
573 pv = (((float)rcolor.channel[ch] / 255)) * av;
574 pv = (int) ((int)pv * rv);
577 else if(pv > 255) pv = 255;
579 rcolor.channel[ch] = (unsigned char) pv;
581 i_ppix(im, x, y, &rcolor);
587 =item i_mosaic(im, size)
589 Makes an image looks like a mosaic with tilesize of size
598 i_mosaic(i_img *im, int size) {
606 sqrsize = size * size;
608 for(y = 0; y < im->ysize; y += size) for(x = 0; x < im->xsize; x += size) {
609 for(z = 0; z < 256; z++) col[z] = 0;
611 for(lx = 0; lx < size; lx++) {
612 for(ly = 0; ly < size; ly++) {
613 i_gpix(im, (x + lx), (y + ly), &rcolor);
615 for(ch = 0; ch < im->channels; ch++) {
616 col[ch] += rcolor.channel[ch];
621 for(ch = 0; ch < im->channels; ch++)
622 rcolor.channel[ch] = (int) ((float)col[ch] / sqrsize);
625 for(lx = 0; lx < size; lx++)
626 for(ly = 0; ly < size; ly++)
627 i_ppix(im, (x + lx), (y + ly), &rcolor);
634 =item i_watermark(im, wmark, tx, ty, pixdiff)
636 Applies a watermark to the target image
639 wmark - watermark image
640 tx - x coordinate of where watermark should be applied
641 ty - y coordinate of where watermark should be applied
642 pixdiff - the magnitude of the watermark, controls how visible it is
648 i_watermark(i_img *im, i_img *wmark, int tx, int ty, int pixdiff) {
652 for(vx=0;vx<128;vx++) for(vy=0;vy<110;vy++) {
654 i_gpix(im, tx+vx, ty+vy,&val );
655 i_gpix(wmark, vx, vy, &wval);
657 for(ch=0;ch<im->channels;ch++)
658 val.channel[ch] = saturate( val.channel[ch] + (pixdiff* (wval.channel[0]-128) )/128 );
660 i_ppix(im,tx+vx,ty+vy,&val);
666 =item i_autolevels(im, lsat, usat, skew)
668 Scales and translates each color such that it fills the range completely.
669 Skew is not implemented yet - purpose is to control the color skew that can
670 occur when changing the contrast.
673 lsat - fraction of pixels that will be truncated at the lower end of the spectrum
674 usat - fraction of pixels that will be truncated at the higher end of the spectrum
681 i_autolevels(i_img *im, float lsat, float usat, float skew) {
683 int i, x, y, rhist[256], ghist[256], bhist[256];
684 int rsum, rmin, rmax;
685 int gsum, gmin, gmax;
686 int bsum, bmin, bmax;
687 int rcl, rcu, gcl, gcu, bcl, bcu;
689 mm_log((1,"i_autolevels(im %p, lsat %f,usat %f,skew %f)\n", im, lsat,usat,skew));
692 for(i=0;i<256;i++) rhist[i]=ghist[i]=bhist[i] = 0;
693 /* create histogram for each channel */
694 for(y = 0; y < im->ysize; y++) for(x = 0; x < im->xsize; x++) {
695 i_gpix(im, x, y, &val);
696 rhist[val.channel[0]]++;
697 ghist[val.channel[1]]++;
698 bhist[val.channel[2]]++;
707 rmin = gmin = bmin = 0;
708 rmax = gmax = bmax = 255;
710 rcu = rcl = gcu = gcl = bcu = bcl = 0;
712 for(i=0; i<256; i++) {
713 rcl += rhist[i]; if ( (rcl<rsum*lsat) ) rmin=i;
714 rcu += rhist[255-i]; if ( (rcu<rsum*usat) ) rmax=255-i;
716 gcl += ghist[i]; if ( (gcl<gsum*lsat) ) gmin=i;
717 gcu += ghist[255-i]; if ( (gcu<gsum*usat) ) gmax=255-i;
719 bcl += bhist[i]; if ( (bcl<bsum*lsat) ) bmin=i;
720 bcu += bhist[255-i]; if ( (bcu<bsum*usat) ) bmax=255-i;
723 for(y = 0; y < im->ysize; y++) for(x = 0; x < im->xsize; x++) {
724 i_gpix(im, x, y, &val);
725 val.channel[0]=saturate((val.channel[0]-rmin)*255/(rmax-rmin));
726 val.channel[1]=saturate((val.channel[1]-gmin)*255/(gmax-gmin));
727 val.channel[2]=saturate((val.channel[2]-bmin)*255/(bmax-bmin));
728 i_ppix(im, x, y, &val);
735 Pseudo noise utility function used to generate perlin noise. (internal)
745 Noise(int x, int y) {
748 return ( 1.0 - ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0);
752 =item SmoothedNoise1(x,y)
754 Pseudo noise utility function used to generate perlin noise. (internal)
764 SmoothedNoise1(float x, float y) {
765 float corners = ( Noise(x-1, y-1)+Noise(x+1, y-1)+Noise(x-1, y+1)+Noise(x+1, y+1) ) / 16;
766 float sides = ( Noise(x-1, y) +Noise(x+1, y) +Noise(x, y-1) +Noise(x, y+1) ) / 8;
767 float center = Noise(x, y) / 4;
768 return corners + sides + center;
773 =item G_Interpolate(a, b, x)
775 Utility function used to generate perlin noise. (internal)
781 float C_Interpolate(float a, float b, float x) {
782 /* float ft = x * 3.1415927; */
784 float f = (1 - cos(ft)) * .5;
785 return a*(1-f) + b*f;
790 =item InterpolatedNoise(x, y)
792 Utility function used to generate perlin noise. (internal)
799 InterpolatedNoise(float x, float y) {
802 float fractional_X = x - integer_X;
804 float fractional_Y = y - integer_Y;
806 float v1 = SmoothedNoise1(integer_X, integer_Y);
807 float v2 = SmoothedNoise1(integer_X + 1, integer_Y);
808 float v3 = SmoothedNoise1(integer_X, integer_Y + 1);
809 float v4 = SmoothedNoise1(integer_X + 1, integer_Y + 1);
811 float i1 = C_Interpolate(v1 , v2 , fractional_X);
812 float i2 = C_Interpolate(v3 , v4 , fractional_X);
814 return C_Interpolate(i1 , i2 , fractional_Y);
820 =item PerlinNoise_2D(x, y)
822 Utility function used to generate perlin noise. (internal)
829 PerlinNoise_2D(float x, float y) {
833 int Number_Of_Octaves=6;
834 int n = Number_Of_Octaves - 1;
839 total = total + InterpolatedNoise(x * frequency, y * frequency) * amplitude;
847 =item i_radnoise(im, xo, yo, rscale, ascale)
849 Perlin-like radial noise.
852 xo - x coordinate of center
853 yo - y coordinate of center
854 rscale - radial scale
855 ascale - angular scale
861 i_radnoise(i_img *im, int xo, int yo, float rscale, float ascale) {
868 for(y = 0; y < im->ysize; y++) for(x = 0; x < im->xsize; x++) {
869 xc = (float)x-xo+0.5;
870 yc = (float)y-yo+0.5;
871 r = rscale*sqrt(xc*xc+yc*yc)+1.2;
872 a = (PI+atan2(yc,xc))*ascale;
873 v = saturate(128+100*(PerlinNoise_2D(a,r)));
874 /* v=saturate(120+12*PerlinNoise_2D(xo+(float)x/scale,yo+(float)y/scale)); Good soft marble */
875 for(ch=0; ch<im->channels; ch++) val.channel[ch]=v;
876 i_ppix(im, x, y, &val);
882 =item i_turbnoise(im, xo, yo, scale)
884 Perlin-like 2d noise noise.
887 xo - x coordinate translation
888 yo - y coordinate translation
889 scale - scale of noise
895 i_turbnoise(i_img *im, float xo, float yo, float scale) {
900 for(y = 0; y < im->ysize; y++) for(x = 0; x < im->xsize; x++) {
901 /* v=saturate(125*(1.0+PerlinNoise_2D(xo+(float)x/scale,yo+(float)y/scale))); */
902 v = saturate(120*(1.0+sin(xo+(float)x/scale+PerlinNoise_2D(xo+(float)x/scale,yo+(float)y/scale))));
903 for(ch=0; ch<im->channels; ch++) val.channel[ch] = v;
904 i_ppix(im, x, y, &val);
911 =item i_gradgen(im, num, xo, yo, ival, dmeasure)
913 Gradient generating function.
916 num - number of points given
917 xo - array of x coordinates
918 yo - array of y coordinates
919 ival - array of i_color objects
920 dmeasure - distance measure to be used.
922 1 = Euclidean squared
923 2 = Manhattan distance
930 i_gradgen(i_img *im, int num, int *xo, int *yo, i_color *ival, int dmeasure) {
934 int channels = im->channels;
935 int xsize = im->xsize;
936 int ysize = im->ysize;
940 mm_log((1,"i_gradgen(im %p, num %d, xo %p, yo %p, ival %p, dmeasure %d)\n", im, num, xo, yo, ival, dmeasure));
942 for(p = 0; p<num; p++) {
943 mm_log((1,"i_gradgen: (%d, %d)\n", xo[p], yo[p]));
947 fdist = mymalloc( sizeof(float) * num );
949 for(y = 0; y<ysize; y++) for(x = 0; x<xsize; x++) {
952 for(p = 0; p<num; p++) {
956 case 0: /* euclidean */
957 fdist[p] = sqrt(xd*xd + yd*yd); /* euclidean distance */
959 case 1: /* euclidean squared */
960 fdist[p] = xd*xd + yd*yd; /* euclidean distance */
962 case 2: /* euclidean squared */
963 fdist[p] = i_max(xd*xd, yd*yd); /* manhattan distance */
966 m_fatal(3,"i_gradgen: Unknown distance measure\n");
971 csd = 1/((num-1)*cs);
973 for(p = 0; p<num; p++) fdist[p] = (cs-fdist[p])*csd;
975 for(ch = 0; ch<channels; ch++) {
977 for(p = 0; p<num; p++) tres += ival[p].channel[ch] * fdist[p];
978 val.channel[ch] = saturate(tres);
980 i_ppix(im, x, y, &val);
987 i_nearest_color_foo(i_img *im, int num, int *xo, int *yo, i_color *ival, int dmeasure) {
990 int xsize = im->xsize;
991 int ysize = im->ysize;
993 mm_log((1,"i_gradgen(im %p, num %d, xo %p, yo %p, ival %p, dmeasure %d)\n", im, num, xo, yo, ival, dmeasure));
995 for(p = 0; p<num; p++) {
996 mm_log((1,"i_gradgen: (%d, %d)\n", xo[p], yo[p]));
1000 for(y = 0; y<ysize; y++) for(x = 0; x<xsize; x++) {
1009 case 0: /* euclidean */
1010 mindist = sqrt(xd*xd + yd*yd); /* euclidean distance */
1012 case 1: /* euclidean squared */
1013 mindist = xd*xd + yd*yd; /* euclidean distance */
1015 case 2: /* euclidean squared */
1016 mindist = i_max(xd*xd, yd*yd); /* manhattan distance */
1019 m_fatal(3,"i_nearest_color: Unknown distance measure\n");
1022 for(p = 1; p<num; p++) {
1026 case 0: /* euclidean */
1027 curdist = sqrt(xd*xd + yd*yd); /* euclidean distance */
1029 case 1: /* euclidean squared */
1030 curdist = xd*xd + yd*yd; /* euclidean distance */
1032 case 2: /* euclidean squared */
1033 curdist = i_max(xd*xd, yd*yd); /* manhattan distance */
1036 m_fatal(3,"i_nearest_color: Unknown distance measure\n");
1038 if (curdist < mindist) {
1043 i_ppix(im, x, y, &ival[midx]);
1048 i_nearest_color(i_img *im, int num, int *xo, int *yo, i_color *oval, int dmeasure) {
1054 int xsize = im->xsize;
1055 int ysize = im->ysize;
1058 mm_log((1,"i_nearest_color(im %p, num %d, xo %p, yo %p, ival %p, dmeasure %d)\n", im, num, xo, yo, oval, dmeasure));
1060 tval = mymalloc( sizeof(float)*num*im->channels );
1061 ival = mymalloc( sizeof(i_color)*num );
1062 cmatch = mymalloc( sizeof(int)*num );
1064 for(p = 0; p<num; p++) {
1065 for(ch = 0; ch<im->channels; ch++) tval[ p * im->channels + ch] = 0;
1070 for(y = 0; y<ysize; y++) for(x = 0; x<xsize; x++) {
1079 case 0: /* euclidean */
1080 mindist = sqrt(xd*xd + yd*yd); /* euclidean distance */
1082 case 1: /* euclidean squared */
1083 mindist = xd*xd + yd*yd; /* euclidean distance */
1085 case 2: /* euclidean squared */
1086 mindist = i_max(xd*xd, yd*yd); /* manhattan distance */
1089 m_fatal(3,"i_nearest_color: Unknown distance measure\n");
1092 for(p = 1; p<num; p++) {
1096 case 0: /* euclidean */
1097 curdist = sqrt(xd*xd + yd*yd); /* euclidean distance */
1099 case 1: /* euclidean squared */
1100 curdist = xd*xd + yd*yd; /* euclidean distance */
1102 case 2: /* euclidean squared */
1103 curdist = i_max(xd*xd, yd*yd); /* manhattan distance */
1106 m_fatal(3,"i_nearest_color: Unknown distance measure\n");
1108 if (curdist < mindist) {
1115 i_gpix(im, x, y, &val);
1116 c2 = 1.0/(float)(cmatch[midx]);
1119 for(ch = 0; ch<im->channels; ch++)
1120 tval[midx*im->channels + ch] = c1*tval[midx*im->channels + ch] + c2 * (float) val.channel[ch];
1125 for(p = 0; p<num; p++) for(ch = 0; ch<im->channels; ch++) ival[p].channel[ch] = tval[p*im->channels + ch];
1127 i_nearest_color_foo(im, num, xo, yo, ival, dmeasure);
1131 =item i_unsharp_mask(im, stddev, scale)
1133 Perform an usharp mask, which is defined as subtracting the blurred
1134 image from double the original.
1138 void i_unsharp_mask(i_img *im, double stddev, double scale) {
1144 /* it really shouldn't ever be more than 1.0, but maybe ... */
1149 i_gaussian(©, stddev);
1150 if (im->bits == i_8_bits) {
1151 i_color *blur = mymalloc(im->xsize * sizeof(i_color) * 2);
1152 i_color *out = blur + im->xsize;
1154 for (y = 0; y < im->ysize; ++y) {
1155 i_glin(©, 0, copy.xsize, y, blur);
1156 i_glin(im, 0, im->xsize, y, out);
1157 for (x = 0; x < im->xsize; ++x) {
1158 for (ch = 0; ch < im->channels; ++ch) {
1159 /*int temp = out[x].channel[ch] +
1160 scale * (out[x].channel[ch] - blur[x].channel[ch]);*/
1161 int temp = out[x].channel[ch] * 2 - blur[x].channel[ch];
1164 else if (temp > 255)
1166 out[x].channel[ch] = temp;
1169 i_plin(im, 0, im->xsize, y, out);
1175 i_fcolor *blur = mymalloc(im->xsize * sizeof(i_fcolor) * 2);
1176 i_fcolor *out = blur + im->xsize;
1178 for (y = 0; y < im->ysize; ++y) {
1179 i_glinf(©, 0, copy.xsize, y, blur);
1180 i_glinf(im, 0, im->xsize, y, out);
1181 for (x = 0; x < im->xsize; ++x) {
1182 for (ch = 0; ch < im->channels; ++ch) {
1183 double temp = out[x].channel[ch] +
1184 scale * (out[x].channel[ch] - blur[x].channel[ch]);
1187 else if (temp > 1.0)
1189 out[x].channel[ch] = temp;
1192 i_plinf(im, 0, im->xsize, y, out);
1197 i_img_exorcise(©);
1201 =item i_diff_image(im1, im2, mindiff)
1203 Creates a new image that is transparent, except where the pixel in im2
1204 is different from im1, where it is the pixel from im2.
1206 The samples must differ by at least mindiff to be considered different.
1212 i_diff_image(i_img *im1, i_img *im2, int mindiff) {
1214 int outchans, diffchans;
1219 if (im1->channels != im2->channels) {
1220 i_push_error(0, "different number of channels");
1224 outchans = diffchans = im1->channels;
1225 if (outchans == 1 || outchans == 3)
1228 xsize = i_min(im1->xsize, im2->xsize);
1229 ysize = i_min(im1->ysize, im2->ysize);
1231 out = i_sametype_chans(im1, xsize, ysize, outchans);
1233 if (im1->bits == i_8_bits && im2->bits == i_8_bits) {
1234 i_color *line1 = mymalloc(2 * xsize * sizeof(*line1));
1235 i_color *line2 = line1 + xsize;
1239 for (ch = 0; ch < MAXCHANNELS; ++ch)
1240 empty.channel[ch] = 0;
1242 for (y = 0; y < ysize; ++y) {
1243 i_glin(im1, 0, xsize, y, line1);
1244 i_glin(im2, 0, xsize, y, line2);
1245 for (x = 0; x < xsize; ++x) {
1247 for (ch = 0; ch < diffchans; ++ch) {
1248 if (line1[x].channel[ch] != line2[x].channel[ch]
1249 && abs(line1[x].channel[ch] - line2[x].channel[ch]) > mindiff) {
1257 i_plin(out, 0, xsize, y, line2);
1262 i_fcolor *line1 = mymalloc(2 * xsize * sizeof(*line1));
1263 i_fcolor *line2 = line1 + xsize;
1266 double dist = mindiff / 255;
1268 for (ch = 0; ch < MAXCHANNELS; ++ch)
1269 empty.channel[ch] = 0;
1271 for (y = 0; y < ysize; ++y) {
1272 i_glinf(im1, 0, xsize, y, line1);
1273 i_glinf(im2, 0, xsize, y, line2);
1274 for (x = 0; x < xsize; ++x) {
1276 for (ch = 0; ch < diffchans; ++ch) {
1277 if (line1[x].channel[ch] != line2[x].channel[ch]
1278 && abs(line1[x].channel[ch] - line2[x].channel[ch]) > dist) {
1286 i_plinf(out, 0, xsize, y, line2);
1295 static double linear_fount_f(double x, double y, struct fount_state *state);
1296 static double bilinear_fount_f(double x, double y, struct fount_state *state);
1297 static double radial_fount_f(double x, double y, struct fount_state *state);
1298 static double square_fount_f(double x, double y, struct fount_state *state);
1299 static double revolution_fount_f(double x, double y,
1300 struct fount_state *state);
1301 static double conical_fount_f(double x, double y, struct fount_state *state);
1303 typedef double (*fount_func)(double, double, struct fount_state *);
1304 static fount_func fount_funcs[] =
1314 static double linear_interp(double pos, i_fountain_seg *seg);
1315 static double sine_interp(double pos, i_fountain_seg *seg);
1316 static double sphereup_interp(double pos, i_fountain_seg *seg);
1317 static double spheredown_interp(double pos, i_fountain_seg *seg);
1318 typedef double (*fount_interp)(double pos, i_fountain_seg *seg);
1319 static fount_interp fount_interps[] =
1328 static void direct_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg);
1329 static void hue_up_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg);
1330 static void hue_down_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg);
1331 typedef void (*fount_cinterp)(i_fcolor *out, double pos, i_fountain_seg *seg);
1332 static fount_cinterp fount_cinterps[] =
1339 typedef double (*fount_repeat)(double v);
1340 static double fount_r_none(double v);
1341 static double fount_r_sawtooth(double v);
1342 static double fount_r_triangle(double v);
1343 static double fount_r_saw_both(double v);
1344 static double fount_r_tri_both(double v);
1345 static fount_repeat fount_repeats[] =
1354 static int simple_ssample(i_fcolor *out, double x, double y,
1355 struct fount_state *state);
1356 static int random_ssample(i_fcolor *out, double x, double y,
1357 struct fount_state *state);
1358 static int circle_ssample(i_fcolor *out, double x, double y,
1359 struct fount_state *state);
1360 typedef int (*fount_ssample)(i_fcolor *out, double x, double y,
1361 struct fount_state *state);
1362 static fount_ssample fount_ssamples[] =
1371 fount_getat(i_fcolor *out, double x, double y, struct fount_state *state);
1374 Keep state information used by each type of fountain fill
1376 struct fount_state {
1377 /* precalculated for the equation of the line perpendicular to the line AB */
1388 fount_repeat rpfunc;
1389 fount_ssample ssfunc;
1391 i_fountain_seg *segs;
1396 fount_init_state(struct fount_state *state, double xa, double ya,
1397 double xb, double yb, i_fountain_type type,
1398 i_fountain_repeat repeat, int combine, int super_sample,
1399 double ssample_param, int count, i_fountain_seg *segs);
1402 fount_finish_state(struct fount_state *state);
1404 #define EPSILON (1e-6)
1407 =item i_fountain(im, xa, ya, xb, yb, type, repeat, combine, super_sample, ssample_param, count, segs)
1409 Draws a fountain fill using A(xa, ya) and B(xb, yb) as reference points.
1411 I<type> controls how the reference points are used:
1417 linear, where A is 0 and B is 1.
1421 linear in both directions from A.
1425 circular, where A is the centre of the fill, and B is a point
1428 =item i_ft_radial_square
1430 where A is the centre of the fill and B is the centre of
1431 one side of the square.
1433 =item i_ft_revolution
1435 where A is the centre of the fill and B defines the 0/1.0
1440 similar to i_ft_revolution, except that the revolution goes in both
1445 I<repeat> can be one of:
1451 values < 0 are treated as zero, values > 1 are treated as 1.
1455 negative values are treated as 0, positive values are modulo 1.0
1459 negative values are treated as zero, if (int)value is odd then the value is treated as 1-(value
1460 mod 1.0), otherwise the same as for sawtooth.
1464 like i_fr_sawtooth, except that the sawtooth pattern repeats into
1469 Like i_fr_triangle, except that negative values are handled as their
1474 If combine is non-zero then non-opaque values are combined with the
1477 I<super_sample> controls super sampling, if any. At some point I'll
1478 probably add a adaptive super-sampler. Current possible values are:
1484 No super-sampling is done.
1488 A square grid of points withing the pixel are sampled.
1492 Random points within the pixel are sampled.
1496 Points on the radius of a circle are sampled. This produces fairly
1497 good results, but is fairly slow since sin() and cos() are evaluated
1502 I<ssample_param> is intended to be roughly the number of points
1503 sampled within the pixel.
1505 I<count> and I<segs> define the segments of the fill.
1512 i_fountain(i_img *im, double xa, double ya, double xb, double yb,
1513 i_fountain_type type, i_fountain_repeat repeat,
1514 int combine, int super_sample, double ssample_param,
1515 int count, i_fountain_seg *segs) {
1516 struct fount_state state;
1518 i_fcolor *line = mymalloc(sizeof(i_fcolor) * im->xsize);
1519 i_fcolor *work = NULL;
1521 i_fountain_seg *my_segs;
1522 i_fill_combine_f combine_func = NULL;
1523 i_fill_combinef_f combinef_func = NULL;
1525 i_get_combine(combine, &combine_func, &combinef_func);
1527 work = mymalloc(sizeof(i_fcolor) * im->xsize);
1529 fount_init_state(&state, xa, ya, xb, yb, type, repeat, combine,
1530 super_sample, ssample_param, count, segs);
1531 my_segs = state.segs;
1533 for (y = 0; y < im->ysize; ++y) {
1534 i_glinf(im, 0, im->xsize, y, line);
1535 for (x = 0; x < im->xsize; ++x) {
1538 if (super_sample == i_fts_none)
1539 got_one = fount_getat(&c, x, y, &state);
1541 got_one = state.ssfunc(&c, x, y, &state);
1550 combinef_func(line, work, im->channels, im->xsize);
1551 i_plinf(im, 0, im->xsize, y, line);
1553 fount_finish_state(&state);
1554 if (work) myfree(work);
1560 struct fount_state state;
1561 } i_fill_fountain_t;
1564 fill_fountf(i_fill_t *fill, int x, int y, int width, int channels,
1567 fount_fill_destroy(i_fill_t *fill);
1570 =item i_new_fount(xa, ya, xb, yb, type, repeat, combine, super_sample, ssample_param, count, segs)
1572 Creates a new general fill which fills with a fountain fill.
1578 i_new_fill_fount(double xa, double ya, double xb, double yb,
1579 i_fountain_type type, i_fountain_repeat repeat,
1580 int combine, int super_sample, double ssample_param,
1581 int count, i_fountain_seg *segs) {
1582 i_fill_fountain_t *fill = mymalloc(sizeof(i_fill_fountain_t));
1584 fill->base.fill_with_color = NULL;
1585 fill->base.fill_with_fcolor = fill_fountf;
1586 fill->base.destroy = fount_fill_destroy;
1588 i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
1590 fill->base.combine = NULL;
1591 fill->base.combinef = NULL;
1593 fount_init_state(&fill->state, xa, ya, xb, yb, type, repeat, combine,
1594 super_sample, ssample_param, count, segs);
1602 =head1 INTERNAL FUNCTIONS
1606 =item fount_init_state(...)
1608 Used by both the fountain fill filter and the fountain fill.
1614 fount_init_state(struct fount_state *state, double xa, double ya,
1615 double xb, double yb, i_fountain_type type,
1616 i_fountain_repeat repeat, int combine, int super_sample,
1617 double ssample_param, int count, i_fountain_seg *segs) {
1619 i_fountain_seg *my_segs = mymalloc(sizeof(i_fountain_seg) * count);
1620 /*int have_alpha = im->channels == 2 || im->channels == 4;*/
1622 memset(state, 0, sizeof(*state));
1623 /* we keep a local copy that we can adjust for speed */
1624 for (i = 0; i < count; ++i) {
1625 i_fountain_seg *seg = my_segs + i;
1628 if (seg->type < 0 || seg->type >= i_fst_end)
1629 seg->type = i_fst_linear;
1630 if (seg->color < 0 || seg->color >= i_fc_end)
1631 seg->color = i_fc_direct;
1632 if (seg->color == i_fc_hue_up || seg->color == i_fc_hue_down) {
1633 /* so we don't have to translate to HSV on each request, do it here */
1634 for (j = 0; j < 2; ++j) {
1635 i_rgb_to_hsvf(seg->c+j);
1637 if (seg->color == i_fc_hue_up) {
1638 if (seg->c[1].channel[0] <= seg->c[0].channel[0])
1639 seg->c[1].channel[0] += 1.0;
1642 if (seg->c[0].channel[0] <= seg->c[0].channel[1])
1643 seg->c[0].channel[0] += 1.0;
1646 /*printf("start %g mid %g end %g c0(%g,%g,%g,%g) c1(%g,%g,%g,%g) type %d color %d\n",
1647 seg->start, seg->middle, seg->end, seg->c[0].channel[0],
1648 seg->c[0].channel[1], seg->c[0].channel[2], seg->c[0].channel[3],
1649 seg->c[1].channel[0], seg->c[1].channel[1], seg->c[1].channel[2],
1650 seg->c[1].channel[3], seg->type, seg->color);*/
1654 /* initialize each engine */
1655 /* these are so common ... */
1656 state->lA = xb - xa;
1657 state->lB = yb - ya;
1658 state->AB = sqrt(state->lA * state->lA + state->lB * state->lB);
1663 type = i_ft_linear; /* make the invalid value valid */
1666 state->lC = ya * ya - ya * yb + xa * xa - xa * xb;
1668 state->mult = 1/linear_fount_f(xb, yb, state);
1672 state->mult = 1.0 / sqrt((double)(xb-xa)*(xb-xa)
1673 + (double)(yb-ya)*(yb-ya));
1676 case i_ft_radial_square:
1677 state->cos = state->lA / state->AB;
1678 state->sin = state->lB / state->AB;
1679 state->mult = 1.0 / state->AB;
1682 case i_ft_revolution:
1683 state->theta = atan2(yb-ya, xb-xa);
1684 state->mult = 1.0 / (PI * 2);
1688 state->theta = atan2(yb-ya, xb-xa);
1689 state->mult = 1.0 / PI;
1692 state->ffunc = fount_funcs[type];
1693 if (super_sample < 0
1694 || super_sample >= (int)(sizeof(fount_ssamples)/sizeof(*fount_ssamples))) {
1697 state->ssample_data = NULL;
1698 switch (super_sample) {
1700 ssample_param = floor(0.5 + sqrt(ssample_param));
1701 state->ssample_data = mymalloc(sizeof(i_fcolor) * ssample_param * ssample_param);
1706 ssample_param = floor(0.5+ssample_param);
1707 state->ssample_data = mymalloc(sizeof(i_fcolor) * ssample_param);
1710 state->parm = ssample_param;
1711 state->ssfunc = fount_ssamples[super_sample];
1712 if (repeat < 0 || repeat >= (sizeof(fount_repeats)/sizeof(*fount_repeats)))
1714 state->rpfunc = fount_repeats[repeat];
1715 state->segs = my_segs;
1716 state->count = count;
1720 fount_finish_state(struct fount_state *state) {
1721 if (state->ssample_data)
1722 myfree(state->ssample_data);
1723 myfree(state->segs);
1728 =item fount_getat(out, x, y, ffunc, rpfunc, state, segs, count)
1730 Evaluates the fountain fill at the given point.
1732 This is called by both the non-super-sampling and super-sampling code.
1734 You might think that it would make sense to sample the fill parameter
1735 instead, and combine those, but this breaks badly.
1741 fount_getat(i_fcolor *out, double x, double y, struct fount_state *state) {
1742 double v = (state->rpfunc)((state->ffunc)(x, y, state));
1746 while (i < state->count
1747 && (v < state->segs[i].start || v > state->segs[i].end)) {
1750 if (i < state->count) {
1751 v = (fount_interps[state->segs[i].type])(v, state->segs+i);
1752 (fount_cinterps[state->segs[i].color])(out, v, state->segs+i);
1760 =item linear_fount_f(x, y, state)
1762 Calculate the fill parameter for a linear fountain fill.
1764 Uses the point to line distance function, with some precalculation
1765 done in i_fountain().
1770 linear_fount_f(double x, double y, struct fount_state *state) {
1771 return (state->lA * x + state->lB * y + state->lC) / state->AB * state->mult;
1775 =item bilinear_fount_f(x, y, state)
1777 Calculate the fill parameter for a bi-linear fountain fill.
1782 bilinear_fount_f(double x, double y, struct fount_state *state) {
1783 return fabs((state->lA * x + state->lB * y + state->lC) / state->AB * state->mult);
1787 =item radial_fount_f(x, y, state)
1789 Calculate the fill parameter for a radial fountain fill.
1791 Simply uses the distance function.
1796 radial_fount_f(double x, double y, struct fount_state *state) {
1797 return sqrt((double)(state->xa-x)*(state->xa-x)
1798 + (double)(state->ya-y)*(state->ya-y)) * state->mult;
1802 =item square_fount_f(x, y, state)
1804 Calculate the fill parameter for a square fountain fill.
1806 Works by rotating the reference co-ordinate around the centre of the
1812 square_fount_f(double x, double y, struct fount_state *state) {
1813 int xc, yc; /* centred on A */
1814 double xt, yt; /* rotated by theta */
1817 xt = fabs(xc * state->cos + yc * state->sin);
1818 yt = fabs(-xc * state->sin + yc * state->cos);
1819 return (xt > yt ? xt : yt) * state->mult;
1823 =item revolution_fount_f(x, y, state)
1825 Calculates the fill parameter for the revolution fountain fill.
1830 revolution_fount_f(double x, double y, struct fount_state *state) {
1831 double angle = atan2(y - state->ya, x - state->xa);
1833 angle -= state->theta;
1835 angle = fmod(angle+ PI * 4, PI*2);
1838 return angle * state->mult;
1842 =item conical_fount_f(x, y, state)
1844 Calculates the fill parameter for the conical fountain fill.
1849 conical_fount_f(double x, double y, struct fount_state *state) {
1850 double angle = atan2(y - state->ya, x - state->xa);
1852 angle -= state->theta;
1855 else if (angle > PI)
1858 return fabs(angle) * state->mult;
1862 =item linear_interp(pos, seg)
1864 Calculates linear interpolation on the fill parameter. Breaks the
1865 segment into 2 regions based in the I<middle> value.
1870 linear_interp(double pos, i_fountain_seg *seg) {
1871 if (pos < seg->middle) {
1872 double len = seg->middle - seg->start;
1876 return (pos - seg->start) / len / 2;
1879 double len = seg->end - seg->middle;
1883 return 0.5 + (pos - seg->middle) / len / 2;
1888 =item sine_interp(pos, seg)
1890 Calculates sine function interpolation on the fill parameter.
1895 sine_interp(double pos, i_fountain_seg *seg) {
1896 /* I wonder if there's a simple way to smooth the transition for this */
1897 double work = linear_interp(pos, seg);
1899 return (1-cos(work * PI))/2;
1903 =item sphereup_interp(pos, seg)
1905 Calculates spherical interpolation on the fill parameter, with the cusp
1911 sphereup_interp(double pos, i_fountain_seg *seg) {
1912 double work = linear_interp(pos, seg);
1914 return sqrt(1.0 - (1-work) * (1-work));
1918 =item spheredown_interp(pos, seg)
1920 Calculates spherical interpolation on the fill parameter, with the cusp
1926 spheredown_interp(double pos, i_fountain_seg *seg) {
1927 double work = linear_interp(pos, seg);
1929 return 1-sqrt(1.0 - work * work);
1933 =item direct_cinterp(out, pos, seg)
1935 Calculates the fountain color based on direct scaling of the channels
1936 of the color channels.
1941 direct_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg) {
1943 for (ch = 0; ch < MAXCHANNELS; ++ch) {
1944 out->channel[ch] = seg->c[0].channel[ch] * (1 - pos)
1945 + seg->c[1].channel[ch] * pos;
1950 =item hue_up_cinterp(put, pos, seg)
1952 Calculates the fountain color based on scaling a HSV value. The hue
1953 increases as the fill parameter increases.
1958 hue_up_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg) {
1960 for (ch = 0; ch < MAXCHANNELS; ++ch) {
1961 out->channel[ch] = seg->c[0].channel[ch] * (1 - pos)
1962 + seg->c[1].channel[ch] * pos;
1968 =item hue_down_cinterp(put, pos, seg)
1970 Calculates the fountain color based on scaling a HSV value. The hue
1971 decreases as the fill parameter increases.
1976 hue_down_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg) {
1978 for (ch = 0; ch < MAXCHANNELS; ++ch) {
1979 out->channel[ch] = seg->c[0].channel[ch] * (1 - pos)
1980 + seg->c[1].channel[ch] * pos;
1986 =item simple_ssample(out, parm, x, y, state, ffunc, rpfunc, segs, count)
1988 Simple grid-based super-sampling.
1993 simple_ssample(i_fcolor *out, double x, double y, struct fount_state *state) {
1994 i_fcolor *work = state->ssample_data;
1996 int grid = state->parm;
1997 double base = -0.5 + 0.5 / grid;
1998 double step = 1.0 / grid;
2002 for (dx = 0; dx < grid; ++dx) {
2003 for (dy = 0; dy < grid; ++dy) {
2004 if (fount_getat(work+samp_count, x + base + step * dx,
2005 y + base + step * dy, state)) {
2010 for (ch = 0; ch < MAXCHANNELS; ++ch) {
2011 out->channel[ch] = 0;
2012 for (i = 0; i < samp_count; ++i) {
2013 out->channel[ch] += work[i].channel[ch];
2015 /* we divide by 4 rather than samp_count since if there's only one valid
2016 sample it should be mostly transparent */
2017 out->channel[ch] /= grid * grid;
2023 =item random_ssample(out, parm, x, y, state, ffunc, rpfunc, segs, count)
2025 Random super-sampling.
2030 random_ssample(i_fcolor *out, double x, double y,
2031 struct fount_state *state) {
2032 i_fcolor *work = state->ssample_data;
2034 int maxsamples = state->parm;
2035 double rand_scale = 1.0 / RAND_MAX;
2037 for (i = 0; i < maxsamples; ++i) {
2038 if (fount_getat(work+samp_count, x - 0.5 + rand() * rand_scale,
2039 y - 0.5 + rand() * rand_scale, state)) {
2043 for (ch = 0; ch < MAXCHANNELS; ++ch) {
2044 out->channel[ch] = 0;
2045 for (i = 0; i < samp_count; ++i) {
2046 out->channel[ch] += work[i].channel[ch];
2048 /* we divide by maxsamples rather than samp_count since if there's
2049 only one valid sample it should be mostly transparent */
2050 out->channel[ch] /= maxsamples;
2056 =item circle_ssample(out, parm, x, y, state, ffunc, rpfunc, segs, count)
2058 Super-sampling around the circumference of a circle.
2060 I considered saving the sin()/cos() values and transforming step-size
2061 around the circle, but that's inaccurate, though it may not matter
2067 circle_ssample(i_fcolor *out, double x, double y,
2068 struct fount_state *state) {
2069 i_fcolor *work = state->ssample_data;
2071 int maxsamples = state->parm;
2072 double angle = 2 * PI / maxsamples;
2073 double radius = 0.3; /* semi-random */
2075 for (i = 0; i < maxsamples; ++i) {
2076 if (fount_getat(work+samp_count, x + radius * cos(angle * i),
2077 y + radius * sin(angle * i), state)) {
2081 for (ch = 0; ch < MAXCHANNELS; ++ch) {
2082 out->channel[ch] = 0;
2083 for (i = 0; i < samp_count; ++i) {
2084 out->channel[ch] += work[i].channel[ch];
2086 /* we divide by maxsamples rather than samp_count since if there's
2087 only one valid sample it should be mostly transparent */
2088 out->channel[ch] /= maxsamples;
2094 =item fount_r_none(v)
2096 Implements no repeats. Simply clamps the fill value.
2101 fount_r_none(double v) {
2102 return v < 0 ? 0 : v > 1 ? 1 : v;
2106 =item fount_r_sawtooth(v)
2108 Implements sawtooth repeats. Clamps negative values and uses fmod()
2114 fount_r_sawtooth(double v) {
2115 return v < 0 ? 0 : fmod(v, 1.0);
2119 =item fount_r_triangle(v)
2121 Implements triangle repeats. Clamps negative values, uses fmod to get
2122 a range 0 through 2 and then adjusts values > 1.
2127 fount_r_triangle(double v) {
2132 return v > 1.0 ? 2.0 - v : v;
2137 =item fount_r_saw_both(v)
2139 Implements sawtooth repeats in the both postive and negative directions.
2141 Adjusts the value to be postive and then just uses fmod().
2146 fount_r_saw_both(double v) {
2149 return fmod(v, 1.0);
2153 =item fount_r_tri_both(v)
2155 Implements triangle repeats in the both postive and negative directions.
2157 Uses fmod on the absolute value, and then adjusts values > 1.
2162 fount_r_tri_both(double v) {
2163 v = fmod(fabs(v), 2.0);
2164 return v > 1.0 ? 2.0 - v : v;
2168 =item fill_fountf(fill, x, y, width, channels, data)
2170 The fill function for fountain fills.
2175 fill_fountf(i_fill_t *fill, int x, int y, int width, int channels,
2177 i_fill_fountain_t *f = (i_fill_fountain_t *)fill;
2183 if (f->state.ssfunc)
2184 got_one = f->state.ssfunc(&c, x, y, &f->state);
2186 got_one = fount_getat(&c, x, y, &f->state);
2195 =item fount_fill_destroy(fill)
2200 fount_fill_destroy(i_fill_t *fill) {
2201 i_fill_fountain_t *f = (i_fill_fountain_t *)fill;
2202 fount_finish_state(&f->state);
2210 Arnar M. Hrafnkelsson <addi@umich.edu>
2212 Tony Cook <tony@develop-help.com> (i_fountain())