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 int mx = wmark->xsize;
653 int my = wmark->ysize;
655 for(vx=0;vx<mx;vx++) for(vy=0;vy<my;vy++) {
657 i_gpix(im, tx+vx, ty+vy,&val );
658 i_gpix(wmark, vx, vy, &wval);
660 for(ch=0;ch<im->channels;ch++)
661 val.channel[ch] = saturate( val.channel[ch] + (pixdiff* (wval.channel[0]-128) )/128 );
663 i_ppix(im,tx+vx,ty+vy,&val);
669 =item i_autolevels(im, lsat, usat, skew)
671 Scales and translates each color such that it fills the range completely.
672 Skew is not implemented yet - purpose is to control the color skew that can
673 occur when changing the contrast.
676 lsat - fraction of pixels that will be truncated at the lower end of the spectrum
677 usat - fraction of pixels that will be truncated at the higher end of the spectrum
684 i_autolevels(i_img *im, float lsat, float usat, float skew) {
686 int i, x, y, rhist[256], ghist[256], bhist[256];
687 int rsum, rmin, rmax;
688 int gsum, gmin, gmax;
689 int bsum, bmin, bmax;
690 int rcl, rcu, gcl, gcu, bcl, bcu;
692 mm_log((1,"i_autolevels(im %p, lsat %f,usat %f,skew %f)\n", im, lsat,usat,skew));
695 for(i=0;i<256;i++) rhist[i]=ghist[i]=bhist[i] = 0;
696 /* create histogram for each channel */
697 for(y = 0; y < im->ysize; y++) for(x = 0; x < im->xsize; x++) {
698 i_gpix(im, x, y, &val);
699 rhist[val.channel[0]]++;
700 ghist[val.channel[1]]++;
701 bhist[val.channel[2]]++;
710 rmin = gmin = bmin = 0;
711 rmax = gmax = bmax = 255;
713 rcu = rcl = gcu = gcl = bcu = bcl = 0;
715 for(i=0; i<256; i++) {
716 rcl += rhist[i]; if ( (rcl<rsum*lsat) ) rmin=i;
717 rcu += rhist[255-i]; if ( (rcu<rsum*usat) ) rmax=255-i;
719 gcl += ghist[i]; if ( (gcl<gsum*lsat) ) gmin=i;
720 gcu += ghist[255-i]; if ( (gcu<gsum*usat) ) gmax=255-i;
722 bcl += bhist[i]; if ( (bcl<bsum*lsat) ) bmin=i;
723 bcu += bhist[255-i]; if ( (bcu<bsum*usat) ) bmax=255-i;
726 for(y = 0; y < im->ysize; y++) for(x = 0; x < im->xsize; x++) {
727 i_gpix(im, x, y, &val);
728 val.channel[0]=saturate((val.channel[0]-rmin)*255/(rmax-rmin));
729 val.channel[1]=saturate((val.channel[1]-gmin)*255/(gmax-gmin));
730 val.channel[2]=saturate((val.channel[2]-bmin)*255/(bmax-bmin));
731 i_ppix(im, x, y, &val);
738 Pseudo noise utility function used to generate perlin noise. (internal)
748 Noise(int x, int y) {
751 return ( 1.0 - ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0);
755 =item SmoothedNoise1(x,y)
757 Pseudo noise utility function used to generate perlin noise. (internal)
767 SmoothedNoise1(float x, float y) {
768 float corners = ( Noise(x-1, y-1)+Noise(x+1, y-1)+Noise(x-1, y+1)+Noise(x+1, y+1) ) / 16;
769 float sides = ( Noise(x-1, y) +Noise(x+1, y) +Noise(x, y-1) +Noise(x, y+1) ) / 8;
770 float center = Noise(x, y) / 4;
771 return corners + sides + center;
776 =item G_Interpolate(a, b, x)
778 Utility function used to generate perlin noise. (internal)
784 float C_Interpolate(float a, float b, float x) {
785 /* float ft = x * 3.1415927; */
787 float f = (1 - cos(ft)) * .5;
788 return a*(1-f) + b*f;
793 =item InterpolatedNoise(x, y)
795 Utility function used to generate perlin noise. (internal)
802 InterpolatedNoise(float x, float y) {
805 float fractional_X = x - integer_X;
807 float fractional_Y = y - integer_Y;
809 float v1 = SmoothedNoise1(integer_X, integer_Y);
810 float v2 = SmoothedNoise1(integer_X + 1, integer_Y);
811 float v3 = SmoothedNoise1(integer_X, integer_Y + 1);
812 float v4 = SmoothedNoise1(integer_X + 1, integer_Y + 1);
814 float i1 = C_Interpolate(v1 , v2 , fractional_X);
815 float i2 = C_Interpolate(v3 , v4 , fractional_X);
817 return C_Interpolate(i1 , i2 , fractional_Y);
823 =item PerlinNoise_2D(x, y)
825 Utility function used to generate perlin noise. (internal)
832 PerlinNoise_2D(float x, float y) {
836 int Number_Of_Octaves=6;
837 int n = Number_Of_Octaves - 1;
842 total = total + InterpolatedNoise(x * frequency, y * frequency) * amplitude;
850 =item i_radnoise(im, xo, yo, rscale, ascale)
852 Perlin-like radial noise.
855 xo - x coordinate of center
856 yo - y coordinate of center
857 rscale - radial scale
858 ascale - angular scale
864 i_radnoise(i_img *im, int xo, int yo, float rscale, float ascale) {
871 for(y = 0; y < im->ysize; y++) for(x = 0; x < im->xsize; x++) {
872 xc = (float)x-xo+0.5;
873 yc = (float)y-yo+0.5;
874 r = rscale*sqrt(xc*xc+yc*yc)+1.2;
875 a = (PI+atan2(yc,xc))*ascale;
876 v = saturate(128+100*(PerlinNoise_2D(a,r)));
877 /* v=saturate(120+12*PerlinNoise_2D(xo+(float)x/scale,yo+(float)y/scale)); Good soft marble */
878 for(ch=0; ch<im->channels; ch++) val.channel[ch]=v;
879 i_ppix(im, x, y, &val);
885 =item i_turbnoise(im, xo, yo, scale)
887 Perlin-like 2d noise noise.
890 xo - x coordinate translation
891 yo - y coordinate translation
892 scale - scale of noise
898 i_turbnoise(i_img *im, float xo, float yo, float scale) {
903 for(y = 0; y < im->ysize; y++) for(x = 0; x < im->xsize; x++) {
904 /* v=saturate(125*(1.0+PerlinNoise_2D(xo+(float)x/scale,yo+(float)y/scale))); */
905 v = saturate(120*(1.0+sin(xo+(float)x/scale+PerlinNoise_2D(xo+(float)x/scale,yo+(float)y/scale))));
906 for(ch=0; ch<im->channels; ch++) val.channel[ch] = v;
907 i_ppix(im, x, y, &val);
914 =item i_gradgen(im, num, xo, yo, ival, dmeasure)
916 Gradient generating function.
919 num - number of points given
920 xo - array of x coordinates
921 yo - array of y coordinates
922 ival - array of i_color objects
923 dmeasure - distance measure to be used.
925 1 = Euclidean squared
926 2 = Manhattan distance
933 i_gradgen(i_img *im, int num, int *xo, int *yo, i_color *ival, int dmeasure) {
937 int channels = im->channels;
938 int xsize = im->xsize;
939 int ysize = im->ysize;
943 mm_log((1,"i_gradgen(im %p, num %d, xo %p, yo %p, ival %p, dmeasure %d)\n", im, num, xo, yo, ival, dmeasure));
945 for(p = 0; p<num; p++) {
946 mm_log((1,"i_gradgen: (%d, %d)\n", xo[p], yo[p]));
950 fdist = mymalloc( sizeof(float) * num );
952 for(y = 0; y<ysize; y++) for(x = 0; x<xsize; x++) {
955 for(p = 0; p<num; p++) {
959 case 0: /* euclidean */
960 fdist[p] = sqrt(xd*xd + yd*yd); /* euclidean distance */
962 case 1: /* euclidean squared */
963 fdist[p] = xd*xd + yd*yd; /* euclidean distance */
965 case 2: /* euclidean squared */
966 fdist[p] = i_max(xd*xd, yd*yd); /* manhattan distance */
969 m_fatal(3,"i_gradgen: Unknown distance measure\n");
974 csd = 1/((num-1)*cs);
976 for(p = 0; p<num; p++) fdist[p] = (cs-fdist[p])*csd;
978 for(ch = 0; ch<channels; ch++) {
980 for(p = 0; p<num; p++) tres += ival[p].channel[ch] * fdist[p];
981 val.channel[ch] = saturate(tres);
983 i_ppix(im, x, y, &val);
990 i_nearest_color_foo(i_img *im, int num, int *xo, int *yo, i_color *ival, int dmeasure) {
993 int xsize = im->xsize;
994 int ysize = im->ysize;
996 mm_log((1,"i_gradgen(im %p, num %d, xo %p, yo %p, ival %p, dmeasure %d)\n", im, num, xo, yo, ival, dmeasure));
998 for(p = 0; p<num; p++) {
999 mm_log((1,"i_gradgen: (%d, %d)\n", xo[p], yo[p]));
1003 for(y = 0; y<ysize; y++) for(x = 0; x<xsize; x++) {
1012 case 0: /* euclidean */
1013 mindist = sqrt(xd*xd + yd*yd); /* euclidean distance */
1015 case 1: /* euclidean squared */
1016 mindist = xd*xd + yd*yd; /* euclidean distance */
1018 case 2: /* euclidean squared */
1019 mindist = i_max(xd*xd, yd*yd); /* manhattan distance */
1022 m_fatal(3,"i_nearest_color: Unknown distance measure\n");
1025 for(p = 1; p<num; p++) {
1029 case 0: /* euclidean */
1030 curdist = sqrt(xd*xd + yd*yd); /* euclidean distance */
1032 case 1: /* euclidean squared */
1033 curdist = xd*xd + yd*yd; /* euclidean distance */
1035 case 2: /* euclidean squared */
1036 curdist = i_max(xd*xd, yd*yd); /* manhattan distance */
1039 m_fatal(3,"i_nearest_color: Unknown distance measure\n");
1041 if (curdist < mindist) {
1046 i_ppix(im, x, y, &ival[midx]);
1051 i_nearest_color(i_img *im, int num, int *xo, int *yo, i_color *oval, int dmeasure) {
1057 int xsize = im->xsize;
1058 int ysize = im->ysize;
1061 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));
1063 tval = mymalloc( sizeof(float)*num*im->channels );
1064 ival = mymalloc( sizeof(i_color)*num );
1065 cmatch = mymalloc( sizeof(int)*num );
1067 for(p = 0; p<num; p++) {
1068 for(ch = 0; ch<im->channels; ch++) tval[ p * im->channels + ch] = 0;
1073 for(y = 0; y<ysize; y++) for(x = 0; x<xsize; x++) {
1082 case 0: /* euclidean */
1083 mindist = sqrt(xd*xd + yd*yd); /* euclidean distance */
1085 case 1: /* euclidean squared */
1086 mindist = xd*xd + yd*yd; /* euclidean distance */
1088 case 2: /* euclidean squared */
1089 mindist = i_max(xd*xd, yd*yd); /* manhattan distance */
1092 m_fatal(3,"i_nearest_color: Unknown distance measure\n");
1095 for(p = 1; p<num; p++) {
1099 case 0: /* euclidean */
1100 curdist = sqrt(xd*xd + yd*yd); /* euclidean distance */
1102 case 1: /* euclidean squared */
1103 curdist = xd*xd + yd*yd; /* euclidean distance */
1105 case 2: /* euclidean squared */
1106 curdist = i_max(xd*xd, yd*yd); /* manhattan distance */
1109 m_fatal(3,"i_nearest_color: Unknown distance measure\n");
1111 if (curdist < mindist) {
1118 i_gpix(im, x, y, &val);
1119 c2 = 1.0/(float)(cmatch[midx]);
1122 for(ch = 0; ch<im->channels; ch++)
1123 tval[midx*im->channels + ch] = c1*tval[midx*im->channels + ch] + c2 * (float) val.channel[ch];
1128 for(p = 0; p<num; p++) for(ch = 0; ch<im->channels; ch++) ival[p].channel[ch] = tval[p*im->channels + ch];
1130 i_nearest_color_foo(im, num, xo, yo, ival, dmeasure);
1134 =item i_unsharp_mask(im, stddev, scale)
1136 Perform an usharp mask, which is defined as subtracting the blurred
1137 image from double the original.
1141 void i_unsharp_mask(i_img *im, double stddev, double scale) {
1147 /* it really shouldn't ever be more than 1.0, but maybe ... */
1152 i_gaussian(©, stddev);
1153 if (im->bits == i_8_bits) {
1154 i_color *blur = mymalloc(im->xsize * sizeof(i_color) * 2);
1155 i_color *out = blur + im->xsize;
1157 for (y = 0; y < im->ysize; ++y) {
1158 i_glin(©, 0, copy.xsize, y, blur);
1159 i_glin(im, 0, im->xsize, y, out);
1160 for (x = 0; x < im->xsize; ++x) {
1161 for (ch = 0; ch < im->channels; ++ch) {
1162 /*int temp = out[x].channel[ch] +
1163 scale * (out[x].channel[ch] - blur[x].channel[ch]);*/
1164 int temp = out[x].channel[ch] * 2 - blur[x].channel[ch];
1167 else if (temp > 255)
1169 out[x].channel[ch] = temp;
1172 i_plin(im, 0, im->xsize, y, out);
1178 i_fcolor *blur = mymalloc(im->xsize * sizeof(i_fcolor) * 2);
1179 i_fcolor *out = blur + im->xsize;
1181 for (y = 0; y < im->ysize; ++y) {
1182 i_glinf(©, 0, copy.xsize, y, blur);
1183 i_glinf(im, 0, im->xsize, y, out);
1184 for (x = 0; x < im->xsize; ++x) {
1185 for (ch = 0; ch < im->channels; ++ch) {
1186 double temp = out[x].channel[ch] +
1187 scale * (out[x].channel[ch] - blur[x].channel[ch]);
1190 else if (temp > 1.0)
1192 out[x].channel[ch] = temp;
1195 i_plinf(im, 0, im->xsize, y, out);
1200 i_img_exorcise(©);
1204 =item i_diff_image(im1, im2, mindiff)
1206 Creates a new image that is transparent, except where the pixel in im2
1207 is different from im1, where it is the pixel from im2.
1209 The samples must differ by at least mindiff to be considered different.
1215 i_diff_image(i_img *im1, i_img *im2, int mindiff) {
1217 int outchans, diffchans;
1222 if (im1->channels != im2->channels) {
1223 i_push_error(0, "different number of channels");
1227 outchans = diffchans = im1->channels;
1228 if (outchans == 1 || outchans == 3)
1231 xsize = i_min(im1->xsize, im2->xsize);
1232 ysize = i_min(im1->ysize, im2->ysize);
1234 out = i_sametype_chans(im1, xsize, ysize, outchans);
1236 if (im1->bits == i_8_bits && im2->bits == i_8_bits) {
1237 i_color *line1 = mymalloc(2 * xsize * sizeof(*line1));
1238 i_color *line2 = line1 + xsize;
1242 for (ch = 0; ch < MAXCHANNELS; ++ch)
1243 empty.channel[ch] = 0;
1245 for (y = 0; y < ysize; ++y) {
1246 i_glin(im1, 0, xsize, y, line1);
1247 i_glin(im2, 0, xsize, y, line2);
1248 if (outchans != diffchans) {
1249 /* give the output an alpha channel since it doesn't have one */
1250 for (x = 0; x < xsize; ++x)
1251 line2[x].channel[diffchans] = 255;
1253 for (x = 0; x < xsize; ++x) {
1255 for (ch = 0; ch < diffchans; ++ch) {
1256 if (line1[x].channel[ch] != line2[x].channel[ch]
1257 && abs(line1[x].channel[ch] - line2[x].channel[ch]) > mindiff) {
1265 i_plin(out, 0, xsize, y, line2);
1270 i_fcolor *line1 = mymalloc(2 * xsize * sizeof(*line1));
1271 i_fcolor *line2 = line1 + xsize;
1274 double dist = mindiff / 255;
1276 for (ch = 0; ch < MAXCHANNELS; ++ch)
1277 empty.channel[ch] = 0;
1279 for (y = 0; y < ysize; ++y) {
1280 i_glinf(im1, 0, xsize, y, line1);
1281 i_glinf(im2, 0, xsize, y, line2);
1282 if (outchans != diffchans) {
1283 /* give the output an alpha channel since it doesn't have one */
1284 for (x = 0; x < xsize; ++x)
1285 line2[x].channel[diffchans] = 1.0;
1287 for (x = 0; x < xsize; ++x) {
1289 for (ch = 0; ch < diffchans; ++ch) {
1290 if (line1[x].channel[ch] != line2[x].channel[ch]
1291 && abs(line1[x].channel[ch] - line2[x].channel[ch]) > dist) {
1299 i_plinf(out, 0, xsize, y, line2);
1308 static double linear_fount_f(double x, double y, struct fount_state *state);
1309 static double bilinear_fount_f(double x, double y, struct fount_state *state);
1310 static double radial_fount_f(double x, double y, struct fount_state *state);
1311 static double square_fount_f(double x, double y, struct fount_state *state);
1312 static double revolution_fount_f(double x, double y,
1313 struct fount_state *state);
1314 static double conical_fount_f(double x, double y, struct fount_state *state);
1316 typedef double (*fount_func)(double, double, struct fount_state *);
1317 static fount_func fount_funcs[] =
1327 static double linear_interp(double pos, i_fountain_seg *seg);
1328 static double sine_interp(double pos, i_fountain_seg *seg);
1329 static double sphereup_interp(double pos, i_fountain_seg *seg);
1330 static double spheredown_interp(double pos, i_fountain_seg *seg);
1331 typedef double (*fount_interp)(double pos, i_fountain_seg *seg);
1332 static fount_interp fount_interps[] =
1341 static void direct_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg);
1342 static void hue_up_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg);
1343 static void hue_down_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg);
1344 typedef void (*fount_cinterp)(i_fcolor *out, double pos, i_fountain_seg *seg);
1345 static fount_cinterp fount_cinterps[] =
1352 typedef double (*fount_repeat)(double v);
1353 static double fount_r_none(double v);
1354 static double fount_r_sawtooth(double v);
1355 static double fount_r_triangle(double v);
1356 static double fount_r_saw_both(double v);
1357 static double fount_r_tri_both(double v);
1358 static fount_repeat fount_repeats[] =
1367 static int simple_ssample(i_fcolor *out, double x, double y,
1368 struct fount_state *state);
1369 static int random_ssample(i_fcolor *out, double x, double y,
1370 struct fount_state *state);
1371 static int circle_ssample(i_fcolor *out, double x, double y,
1372 struct fount_state *state);
1373 typedef int (*fount_ssample)(i_fcolor *out, double x, double y,
1374 struct fount_state *state);
1375 static fount_ssample fount_ssamples[] =
1384 fount_getat(i_fcolor *out, double x, double y, struct fount_state *state);
1387 Keep state information used by each type of fountain fill
1389 struct fount_state {
1390 /* precalculated for the equation of the line perpendicular to the line AB */
1401 fount_repeat rpfunc;
1402 fount_ssample ssfunc;
1404 i_fountain_seg *segs;
1409 fount_init_state(struct fount_state *state, double xa, double ya,
1410 double xb, double yb, i_fountain_type type,
1411 i_fountain_repeat repeat, int combine, int super_sample,
1412 double ssample_param, int count, i_fountain_seg *segs);
1415 fount_finish_state(struct fount_state *state);
1417 #define EPSILON (1e-6)
1420 =item i_fountain(im, xa, ya, xb, yb, type, repeat, combine, super_sample, ssample_param, count, segs)
1422 Draws a fountain fill using A(xa, ya) and B(xb, yb) as reference points.
1424 I<type> controls how the reference points are used:
1430 linear, where A is 0 and B is 1.
1434 linear in both directions from A.
1438 circular, where A is the centre of the fill, and B is a point
1441 =item i_ft_radial_square
1443 where A is the centre of the fill and B is the centre of
1444 one side of the square.
1446 =item i_ft_revolution
1448 where A is the centre of the fill and B defines the 0/1.0
1453 similar to i_ft_revolution, except that the revolution goes in both
1458 I<repeat> can be one of:
1464 values < 0 are treated as zero, values > 1 are treated as 1.
1468 negative values are treated as 0, positive values are modulo 1.0
1472 negative values are treated as zero, if (int)value is odd then the value is treated as 1-(value
1473 mod 1.0), otherwise the same as for sawtooth.
1477 like i_fr_sawtooth, except that the sawtooth pattern repeats into
1482 Like i_fr_triangle, except that negative values are handled as their
1487 If combine is non-zero then non-opaque values are combined with the
1490 I<super_sample> controls super sampling, if any. At some point I'll
1491 probably add a adaptive super-sampler. Current possible values are:
1497 No super-sampling is done.
1501 A square grid of points withing the pixel are sampled.
1505 Random points within the pixel are sampled.
1509 Points on the radius of a circle are sampled. This produces fairly
1510 good results, but is fairly slow since sin() and cos() are evaluated
1515 I<ssample_param> is intended to be roughly the number of points
1516 sampled within the pixel.
1518 I<count> and I<segs> define the segments of the fill.
1525 i_fountain(i_img *im, double xa, double ya, double xb, double yb,
1526 i_fountain_type type, i_fountain_repeat repeat,
1527 int combine, int super_sample, double ssample_param,
1528 int count, i_fountain_seg *segs) {
1529 struct fount_state state;
1531 i_fcolor *line = mymalloc(sizeof(i_fcolor) * im->xsize);
1532 i_fcolor *work = NULL;
1534 i_fountain_seg *my_segs;
1535 i_fill_combine_f combine_func = NULL;
1536 i_fill_combinef_f combinef_func = NULL;
1538 i_get_combine(combine, &combine_func, &combinef_func);
1540 work = mymalloc(sizeof(i_fcolor) * im->xsize);
1542 fount_init_state(&state, xa, ya, xb, yb, type, repeat, combine,
1543 super_sample, ssample_param, count, segs);
1544 my_segs = state.segs;
1546 for (y = 0; y < im->ysize; ++y) {
1547 i_glinf(im, 0, im->xsize, y, line);
1548 for (x = 0; x < im->xsize; ++x) {
1551 if (super_sample == i_fts_none)
1552 got_one = fount_getat(&c, x, y, &state);
1554 got_one = state.ssfunc(&c, x, y, &state);
1563 combinef_func(line, work, im->channels, im->xsize);
1564 i_plinf(im, 0, im->xsize, y, line);
1566 fount_finish_state(&state);
1567 if (work) myfree(work);
1573 struct fount_state state;
1574 } i_fill_fountain_t;
1577 fill_fountf(i_fill_t *fill, int x, int y, int width, int channels,
1580 fount_fill_destroy(i_fill_t *fill);
1583 =item i_new_fount(xa, ya, xb, yb, type, repeat, combine, super_sample, ssample_param, count, segs)
1585 Creates a new general fill which fills with a fountain fill.
1591 i_new_fill_fount(double xa, double ya, double xb, double yb,
1592 i_fountain_type type, i_fountain_repeat repeat,
1593 int combine, int super_sample, double ssample_param,
1594 int count, i_fountain_seg *segs) {
1595 i_fill_fountain_t *fill = mymalloc(sizeof(i_fill_fountain_t));
1597 fill->base.fill_with_color = NULL;
1598 fill->base.fill_with_fcolor = fill_fountf;
1599 fill->base.destroy = fount_fill_destroy;
1601 i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
1603 fill->base.combine = NULL;
1604 fill->base.combinef = NULL;
1606 fount_init_state(&fill->state, xa, ya, xb, yb, type, repeat, combine,
1607 super_sample, ssample_param, count, segs);
1615 =head1 INTERNAL FUNCTIONS
1619 =item fount_init_state(...)
1621 Used by both the fountain fill filter and the fountain fill.
1627 fount_init_state(struct fount_state *state, double xa, double ya,
1628 double xb, double yb, i_fountain_type type,
1629 i_fountain_repeat repeat, int combine, int super_sample,
1630 double ssample_param, int count, i_fountain_seg *segs) {
1632 i_fountain_seg *my_segs = mymalloc(sizeof(i_fountain_seg) * count);
1633 /*int have_alpha = im->channels == 2 || im->channels == 4;*/
1635 memset(state, 0, sizeof(*state));
1636 /* we keep a local copy that we can adjust for speed */
1637 for (i = 0; i < count; ++i) {
1638 i_fountain_seg *seg = my_segs + i;
1641 if (seg->type < 0 || seg->type >= i_fst_end)
1642 seg->type = i_fst_linear;
1643 if (seg->color < 0 || seg->color >= i_fc_end)
1644 seg->color = i_fc_direct;
1645 if (seg->color == i_fc_hue_up || seg->color == i_fc_hue_down) {
1646 /* so we don't have to translate to HSV on each request, do it here */
1647 for (j = 0; j < 2; ++j) {
1648 i_rgb_to_hsvf(seg->c+j);
1650 if (seg->color == i_fc_hue_up) {
1651 if (seg->c[1].channel[0] <= seg->c[0].channel[0])
1652 seg->c[1].channel[0] += 1.0;
1655 if (seg->c[0].channel[0] <= seg->c[0].channel[1])
1656 seg->c[0].channel[0] += 1.0;
1659 /*printf("start %g mid %g end %g c0(%g,%g,%g,%g) c1(%g,%g,%g,%g) type %d color %d\n",
1660 seg->start, seg->middle, seg->end, seg->c[0].channel[0],
1661 seg->c[0].channel[1], seg->c[0].channel[2], seg->c[0].channel[3],
1662 seg->c[1].channel[0], seg->c[1].channel[1], seg->c[1].channel[2],
1663 seg->c[1].channel[3], seg->type, seg->color);*/
1667 /* initialize each engine */
1668 /* these are so common ... */
1669 state->lA = xb - xa;
1670 state->lB = yb - ya;
1671 state->AB = sqrt(state->lA * state->lA + state->lB * state->lB);
1676 type = i_ft_linear; /* make the invalid value valid */
1679 state->lC = ya * ya - ya * yb + xa * xa - xa * xb;
1681 state->mult = 1/linear_fount_f(xb, yb, state);
1685 state->mult = 1.0 / sqrt((double)(xb-xa)*(xb-xa)
1686 + (double)(yb-ya)*(yb-ya));
1689 case i_ft_radial_square:
1690 state->cos = state->lA / state->AB;
1691 state->sin = state->lB / state->AB;
1692 state->mult = 1.0 / state->AB;
1695 case i_ft_revolution:
1696 state->theta = atan2(yb-ya, xb-xa);
1697 state->mult = 1.0 / (PI * 2);
1701 state->theta = atan2(yb-ya, xb-xa);
1702 state->mult = 1.0 / PI;
1705 state->ffunc = fount_funcs[type];
1706 if (super_sample < 0
1707 || super_sample >= (int)(sizeof(fount_ssamples)/sizeof(*fount_ssamples))) {
1710 state->ssample_data = NULL;
1711 switch (super_sample) {
1713 ssample_param = floor(0.5 + sqrt(ssample_param));
1714 state->ssample_data = mymalloc(sizeof(i_fcolor) * ssample_param * ssample_param);
1719 ssample_param = floor(0.5+ssample_param);
1720 state->ssample_data = mymalloc(sizeof(i_fcolor) * ssample_param);
1723 state->parm = ssample_param;
1724 state->ssfunc = fount_ssamples[super_sample];
1725 if (repeat < 0 || repeat >= (sizeof(fount_repeats)/sizeof(*fount_repeats)))
1727 state->rpfunc = fount_repeats[repeat];
1728 state->segs = my_segs;
1729 state->count = count;
1733 fount_finish_state(struct fount_state *state) {
1734 if (state->ssample_data)
1735 myfree(state->ssample_data);
1736 myfree(state->segs);
1741 =item fount_getat(out, x, y, ffunc, rpfunc, state, segs, count)
1743 Evaluates the fountain fill at the given point.
1745 This is called by both the non-super-sampling and super-sampling code.
1747 You might think that it would make sense to sample the fill parameter
1748 instead, and combine those, but this breaks badly.
1754 fount_getat(i_fcolor *out, double x, double y, struct fount_state *state) {
1755 double v = (state->rpfunc)((state->ffunc)(x, y, state));
1759 while (i < state->count
1760 && (v < state->segs[i].start || v > state->segs[i].end)) {
1763 if (i < state->count) {
1764 v = (fount_interps[state->segs[i].type])(v, state->segs+i);
1765 (fount_cinterps[state->segs[i].color])(out, v, state->segs+i);
1773 =item linear_fount_f(x, y, state)
1775 Calculate the fill parameter for a linear fountain fill.
1777 Uses the point to line distance function, with some precalculation
1778 done in i_fountain().
1783 linear_fount_f(double x, double y, struct fount_state *state) {
1784 return (state->lA * x + state->lB * y + state->lC) / state->AB * state->mult;
1788 =item bilinear_fount_f(x, y, state)
1790 Calculate the fill parameter for a bi-linear fountain fill.
1795 bilinear_fount_f(double x, double y, struct fount_state *state) {
1796 return fabs((state->lA * x + state->lB * y + state->lC) / state->AB * state->mult);
1800 =item radial_fount_f(x, y, state)
1802 Calculate the fill parameter for a radial fountain fill.
1804 Simply uses the distance function.
1809 radial_fount_f(double x, double y, struct fount_state *state) {
1810 return sqrt((double)(state->xa-x)*(state->xa-x)
1811 + (double)(state->ya-y)*(state->ya-y)) * state->mult;
1815 =item square_fount_f(x, y, state)
1817 Calculate the fill parameter for a square fountain fill.
1819 Works by rotating the reference co-ordinate around the centre of the
1825 square_fount_f(double x, double y, struct fount_state *state) {
1826 int xc, yc; /* centred on A */
1827 double xt, yt; /* rotated by theta */
1830 xt = fabs(xc * state->cos + yc * state->sin);
1831 yt = fabs(-xc * state->sin + yc * state->cos);
1832 return (xt > yt ? xt : yt) * state->mult;
1836 =item revolution_fount_f(x, y, state)
1838 Calculates the fill parameter for the revolution fountain fill.
1843 revolution_fount_f(double x, double y, struct fount_state *state) {
1844 double angle = atan2(y - state->ya, x - state->xa);
1846 angle -= state->theta;
1848 angle = fmod(angle+ PI * 4, PI*2);
1851 return angle * state->mult;
1855 =item conical_fount_f(x, y, state)
1857 Calculates the fill parameter for the conical fountain fill.
1862 conical_fount_f(double x, double y, struct fount_state *state) {
1863 double angle = atan2(y - state->ya, x - state->xa);
1865 angle -= state->theta;
1868 else if (angle > PI)
1871 return fabs(angle) * state->mult;
1875 =item linear_interp(pos, seg)
1877 Calculates linear interpolation on the fill parameter. Breaks the
1878 segment into 2 regions based in the I<middle> value.
1883 linear_interp(double pos, i_fountain_seg *seg) {
1884 if (pos < seg->middle) {
1885 double len = seg->middle - seg->start;
1889 return (pos - seg->start) / len / 2;
1892 double len = seg->end - seg->middle;
1896 return 0.5 + (pos - seg->middle) / len / 2;
1901 =item sine_interp(pos, seg)
1903 Calculates sine function interpolation on the fill parameter.
1908 sine_interp(double pos, i_fountain_seg *seg) {
1909 /* I wonder if there's a simple way to smooth the transition for this */
1910 double work = linear_interp(pos, seg);
1912 return (1-cos(work * PI))/2;
1916 =item sphereup_interp(pos, seg)
1918 Calculates spherical interpolation on the fill parameter, with the cusp
1924 sphereup_interp(double pos, i_fountain_seg *seg) {
1925 double work = linear_interp(pos, seg);
1927 return sqrt(1.0 - (1-work) * (1-work));
1931 =item spheredown_interp(pos, seg)
1933 Calculates spherical interpolation on the fill parameter, with the cusp
1939 spheredown_interp(double pos, i_fountain_seg *seg) {
1940 double work = linear_interp(pos, seg);
1942 return 1-sqrt(1.0 - work * work);
1946 =item direct_cinterp(out, pos, seg)
1948 Calculates the fountain color based on direct scaling of the channels
1949 of the color channels.
1954 direct_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg) {
1956 for (ch = 0; ch < MAXCHANNELS; ++ch) {
1957 out->channel[ch] = seg->c[0].channel[ch] * (1 - pos)
1958 + seg->c[1].channel[ch] * pos;
1963 =item hue_up_cinterp(put, pos, seg)
1965 Calculates the fountain color based on scaling a HSV value. The hue
1966 increases as the fill parameter increases.
1971 hue_up_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg) {
1973 for (ch = 0; ch < MAXCHANNELS; ++ch) {
1974 out->channel[ch] = seg->c[0].channel[ch] * (1 - pos)
1975 + seg->c[1].channel[ch] * pos;
1981 =item hue_down_cinterp(put, pos, seg)
1983 Calculates the fountain color based on scaling a HSV value. The hue
1984 decreases as the fill parameter increases.
1989 hue_down_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg) {
1991 for (ch = 0; ch < MAXCHANNELS; ++ch) {
1992 out->channel[ch] = seg->c[0].channel[ch] * (1 - pos)
1993 + seg->c[1].channel[ch] * pos;
1999 =item simple_ssample(out, parm, x, y, state, ffunc, rpfunc, segs, count)
2001 Simple grid-based super-sampling.
2006 simple_ssample(i_fcolor *out, double x, double y, struct fount_state *state) {
2007 i_fcolor *work = state->ssample_data;
2009 int grid = state->parm;
2010 double base = -0.5 + 0.5 / grid;
2011 double step = 1.0 / grid;
2015 for (dx = 0; dx < grid; ++dx) {
2016 for (dy = 0; dy < grid; ++dy) {
2017 if (fount_getat(work+samp_count, x + base + step * dx,
2018 y + base + step * dy, state)) {
2023 for (ch = 0; ch < MAXCHANNELS; ++ch) {
2024 out->channel[ch] = 0;
2025 for (i = 0; i < samp_count; ++i) {
2026 out->channel[ch] += work[i].channel[ch];
2028 /* we divide by 4 rather than samp_count since if there's only one valid
2029 sample it should be mostly transparent */
2030 out->channel[ch] /= grid * grid;
2036 =item random_ssample(out, parm, x, y, state, ffunc, rpfunc, segs, count)
2038 Random super-sampling.
2043 random_ssample(i_fcolor *out, double x, double y,
2044 struct fount_state *state) {
2045 i_fcolor *work = state->ssample_data;
2047 int maxsamples = state->parm;
2048 double rand_scale = 1.0 / RAND_MAX;
2050 for (i = 0; i < maxsamples; ++i) {
2051 if (fount_getat(work+samp_count, x - 0.5 + rand() * rand_scale,
2052 y - 0.5 + rand() * rand_scale, state)) {
2056 for (ch = 0; ch < MAXCHANNELS; ++ch) {
2057 out->channel[ch] = 0;
2058 for (i = 0; i < samp_count; ++i) {
2059 out->channel[ch] += work[i].channel[ch];
2061 /* we divide by maxsamples rather than samp_count since if there's
2062 only one valid sample it should be mostly transparent */
2063 out->channel[ch] /= maxsamples;
2069 =item circle_ssample(out, parm, x, y, state, ffunc, rpfunc, segs, count)
2071 Super-sampling around the circumference of a circle.
2073 I considered saving the sin()/cos() values and transforming step-size
2074 around the circle, but that's inaccurate, though it may not matter
2080 circle_ssample(i_fcolor *out, double x, double y,
2081 struct fount_state *state) {
2082 i_fcolor *work = state->ssample_data;
2084 int maxsamples = state->parm;
2085 double angle = 2 * PI / maxsamples;
2086 double radius = 0.3; /* semi-random */
2088 for (i = 0; i < maxsamples; ++i) {
2089 if (fount_getat(work+samp_count, x + radius * cos(angle * i),
2090 y + radius * sin(angle * i), state)) {
2094 for (ch = 0; ch < MAXCHANNELS; ++ch) {
2095 out->channel[ch] = 0;
2096 for (i = 0; i < samp_count; ++i) {
2097 out->channel[ch] += work[i].channel[ch];
2099 /* we divide by maxsamples rather than samp_count since if there's
2100 only one valid sample it should be mostly transparent */
2101 out->channel[ch] /= maxsamples;
2107 =item fount_r_none(v)
2109 Implements no repeats. Simply clamps the fill value.
2114 fount_r_none(double v) {
2115 return v < 0 ? 0 : v > 1 ? 1 : v;
2119 =item fount_r_sawtooth(v)
2121 Implements sawtooth repeats. Clamps negative values and uses fmod()
2127 fount_r_sawtooth(double v) {
2128 return v < 0 ? 0 : fmod(v, 1.0);
2132 =item fount_r_triangle(v)
2134 Implements triangle repeats. Clamps negative values, uses fmod to get
2135 a range 0 through 2 and then adjusts values > 1.
2140 fount_r_triangle(double v) {
2145 return v > 1.0 ? 2.0 - v : v;
2150 =item fount_r_saw_both(v)
2152 Implements sawtooth repeats in the both postive and negative directions.
2154 Adjusts the value to be postive and then just uses fmod().
2159 fount_r_saw_both(double v) {
2162 return fmod(v, 1.0);
2166 =item fount_r_tri_both(v)
2168 Implements triangle repeats in the both postive and negative directions.
2170 Uses fmod on the absolute value, and then adjusts values > 1.
2175 fount_r_tri_both(double v) {
2176 v = fmod(fabs(v), 2.0);
2177 return v > 1.0 ? 2.0 - v : v;
2181 =item fill_fountf(fill, x, y, width, channels, data)
2183 The fill function for fountain fills.
2188 fill_fountf(i_fill_t *fill, int x, int y, int width, int channels,
2190 i_fill_fountain_t *f = (i_fill_fountain_t *)fill;
2196 if (f->state.ssfunc)
2197 got_one = f->state.ssfunc(&c, x, y, &f->state);
2199 got_one = fount_getat(&c, x, y, &f->state);
2208 =item fount_fill_destroy(fill)
2213 fount_fill_destroy(i_fill_t *fill) {
2214 i_fill_fountain_t *f = (i_fill_fountain_t *)fill;
2215 fount_finish_state(&f->state);
2223 Arnar M. Hrafnkelsson <addi@umich.edu>
2225 Tony Cook <tony@develop-help.com> (i_fountain())