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 if (outchans != diffchans) {
1246 /* give the output an alpha channel since it doesn't have one */
1247 for (x = 0; x < xsize; ++x)
1248 line2[x].channel[diffchans] = 255;
1250 for (x = 0; x < xsize; ++x) {
1252 for (ch = 0; ch < diffchans; ++ch) {
1253 if (line1[x].channel[ch] != line2[x].channel[ch]
1254 && abs(line1[x].channel[ch] - line2[x].channel[ch]) > mindiff) {
1262 i_plin(out, 0, xsize, y, line2);
1267 i_fcolor *line1 = mymalloc(2 * xsize * sizeof(*line1));
1268 i_fcolor *line2 = line1 + xsize;
1271 double dist = mindiff / 255;
1273 for (ch = 0; ch < MAXCHANNELS; ++ch)
1274 empty.channel[ch] = 0;
1276 for (y = 0; y < ysize; ++y) {
1277 i_glinf(im1, 0, xsize, y, line1);
1278 i_glinf(im2, 0, xsize, y, line2);
1279 if (outchans != diffchans) {
1280 /* give the output an alpha channel since it doesn't have one */
1281 for (x = 0; x < xsize; ++x)
1282 line2[x].channel[diffchans] = 1.0;
1284 for (x = 0; x < xsize; ++x) {
1286 for (ch = 0; ch < diffchans; ++ch) {
1287 if (line1[x].channel[ch] != line2[x].channel[ch]
1288 && abs(line1[x].channel[ch] - line2[x].channel[ch]) > dist) {
1296 i_plinf(out, 0, xsize, y, line2);
1305 static double linear_fount_f(double x, double y, struct fount_state *state);
1306 static double bilinear_fount_f(double x, double y, struct fount_state *state);
1307 static double radial_fount_f(double x, double y, struct fount_state *state);
1308 static double square_fount_f(double x, double y, struct fount_state *state);
1309 static double revolution_fount_f(double x, double y,
1310 struct fount_state *state);
1311 static double conical_fount_f(double x, double y, struct fount_state *state);
1313 typedef double (*fount_func)(double, double, struct fount_state *);
1314 static fount_func fount_funcs[] =
1324 static double linear_interp(double pos, i_fountain_seg *seg);
1325 static double sine_interp(double pos, i_fountain_seg *seg);
1326 static double sphereup_interp(double pos, i_fountain_seg *seg);
1327 static double spheredown_interp(double pos, i_fountain_seg *seg);
1328 typedef double (*fount_interp)(double pos, i_fountain_seg *seg);
1329 static fount_interp fount_interps[] =
1338 static void direct_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg);
1339 static void hue_up_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg);
1340 static void hue_down_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg);
1341 typedef void (*fount_cinterp)(i_fcolor *out, double pos, i_fountain_seg *seg);
1342 static fount_cinterp fount_cinterps[] =
1349 typedef double (*fount_repeat)(double v);
1350 static double fount_r_none(double v);
1351 static double fount_r_sawtooth(double v);
1352 static double fount_r_triangle(double v);
1353 static double fount_r_saw_both(double v);
1354 static double fount_r_tri_both(double v);
1355 static fount_repeat fount_repeats[] =
1364 static int simple_ssample(i_fcolor *out, double x, double y,
1365 struct fount_state *state);
1366 static int random_ssample(i_fcolor *out, double x, double y,
1367 struct fount_state *state);
1368 static int circle_ssample(i_fcolor *out, double x, double y,
1369 struct fount_state *state);
1370 typedef int (*fount_ssample)(i_fcolor *out, double x, double y,
1371 struct fount_state *state);
1372 static fount_ssample fount_ssamples[] =
1381 fount_getat(i_fcolor *out, double x, double y, struct fount_state *state);
1384 Keep state information used by each type of fountain fill
1386 struct fount_state {
1387 /* precalculated for the equation of the line perpendicular to the line AB */
1398 fount_repeat rpfunc;
1399 fount_ssample ssfunc;
1401 i_fountain_seg *segs;
1406 fount_init_state(struct fount_state *state, double xa, double ya,
1407 double xb, double yb, i_fountain_type type,
1408 i_fountain_repeat repeat, int combine, int super_sample,
1409 double ssample_param, int count, i_fountain_seg *segs);
1412 fount_finish_state(struct fount_state *state);
1414 #define EPSILON (1e-6)
1417 =item i_fountain(im, xa, ya, xb, yb, type, repeat, combine, super_sample, ssample_param, count, segs)
1419 Draws a fountain fill using A(xa, ya) and B(xb, yb) as reference points.
1421 I<type> controls how the reference points are used:
1427 linear, where A is 0 and B is 1.
1431 linear in both directions from A.
1435 circular, where A is the centre of the fill, and B is a point
1438 =item i_ft_radial_square
1440 where A is the centre of the fill and B is the centre of
1441 one side of the square.
1443 =item i_ft_revolution
1445 where A is the centre of the fill and B defines the 0/1.0
1450 similar to i_ft_revolution, except that the revolution goes in both
1455 I<repeat> can be one of:
1461 values < 0 are treated as zero, values > 1 are treated as 1.
1465 negative values are treated as 0, positive values are modulo 1.0
1469 negative values are treated as zero, if (int)value is odd then the value is treated as 1-(value
1470 mod 1.0), otherwise the same as for sawtooth.
1474 like i_fr_sawtooth, except that the sawtooth pattern repeats into
1479 Like i_fr_triangle, except that negative values are handled as their
1484 If combine is non-zero then non-opaque values are combined with the
1487 I<super_sample> controls super sampling, if any. At some point I'll
1488 probably add a adaptive super-sampler. Current possible values are:
1494 No super-sampling is done.
1498 A square grid of points withing the pixel are sampled.
1502 Random points within the pixel are sampled.
1506 Points on the radius of a circle are sampled. This produces fairly
1507 good results, but is fairly slow since sin() and cos() are evaluated
1512 I<ssample_param> is intended to be roughly the number of points
1513 sampled within the pixel.
1515 I<count> and I<segs> define the segments of the fill.
1522 i_fountain(i_img *im, double xa, double ya, double xb, double yb,
1523 i_fountain_type type, i_fountain_repeat repeat,
1524 int combine, int super_sample, double ssample_param,
1525 int count, i_fountain_seg *segs) {
1526 struct fount_state state;
1528 i_fcolor *line = mymalloc(sizeof(i_fcolor) * im->xsize);
1529 i_fcolor *work = NULL;
1531 i_fountain_seg *my_segs;
1532 i_fill_combine_f combine_func = NULL;
1533 i_fill_combinef_f combinef_func = NULL;
1535 i_get_combine(combine, &combine_func, &combinef_func);
1537 work = mymalloc(sizeof(i_fcolor) * im->xsize);
1539 fount_init_state(&state, xa, ya, xb, yb, type, repeat, combine,
1540 super_sample, ssample_param, count, segs);
1541 my_segs = state.segs;
1543 for (y = 0; y < im->ysize; ++y) {
1544 i_glinf(im, 0, im->xsize, y, line);
1545 for (x = 0; x < im->xsize; ++x) {
1548 if (super_sample == i_fts_none)
1549 got_one = fount_getat(&c, x, y, &state);
1551 got_one = state.ssfunc(&c, x, y, &state);
1560 combinef_func(line, work, im->channels, im->xsize);
1561 i_plinf(im, 0, im->xsize, y, line);
1563 fount_finish_state(&state);
1564 if (work) myfree(work);
1570 struct fount_state state;
1571 } i_fill_fountain_t;
1574 fill_fountf(i_fill_t *fill, int x, int y, int width, int channels,
1577 fount_fill_destroy(i_fill_t *fill);
1580 =item i_new_fount(xa, ya, xb, yb, type, repeat, combine, super_sample, ssample_param, count, segs)
1582 Creates a new general fill which fills with a fountain fill.
1588 i_new_fill_fount(double xa, double ya, double xb, double yb,
1589 i_fountain_type type, i_fountain_repeat repeat,
1590 int combine, int super_sample, double ssample_param,
1591 int count, i_fountain_seg *segs) {
1592 i_fill_fountain_t *fill = mymalloc(sizeof(i_fill_fountain_t));
1594 fill->base.fill_with_color = NULL;
1595 fill->base.fill_with_fcolor = fill_fountf;
1596 fill->base.destroy = fount_fill_destroy;
1598 i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
1600 fill->base.combine = NULL;
1601 fill->base.combinef = NULL;
1603 fount_init_state(&fill->state, xa, ya, xb, yb, type, repeat, combine,
1604 super_sample, ssample_param, count, segs);
1612 =head1 INTERNAL FUNCTIONS
1616 =item fount_init_state(...)
1618 Used by both the fountain fill filter and the fountain fill.
1624 fount_init_state(struct fount_state *state, double xa, double ya,
1625 double xb, double yb, i_fountain_type type,
1626 i_fountain_repeat repeat, int combine, int super_sample,
1627 double ssample_param, int count, i_fountain_seg *segs) {
1629 i_fountain_seg *my_segs = mymalloc(sizeof(i_fountain_seg) * count);
1630 /*int have_alpha = im->channels == 2 || im->channels == 4;*/
1632 memset(state, 0, sizeof(*state));
1633 /* we keep a local copy that we can adjust for speed */
1634 for (i = 0; i < count; ++i) {
1635 i_fountain_seg *seg = my_segs + i;
1638 if (seg->type < 0 || seg->type >= i_fst_end)
1639 seg->type = i_fst_linear;
1640 if (seg->color < 0 || seg->color >= i_fc_end)
1641 seg->color = i_fc_direct;
1642 if (seg->color == i_fc_hue_up || seg->color == i_fc_hue_down) {
1643 /* so we don't have to translate to HSV on each request, do it here */
1644 for (j = 0; j < 2; ++j) {
1645 i_rgb_to_hsvf(seg->c+j);
1647 if (seg->color == i_fc_hue_up) {
1648 if (seg->c[1].channel[0] <= seg->c[0].channel[0])
1649 seg->c[1].channel[0] += 1.0;
1652 if (seg->c[0].channel[0] <= seg->c[0].channel[1])
1653 seg->c[0].channel[0] += 1.0;
1656 /*printf("start %g mid %g end %g c0(%g,%g,%g,%g) c1(%g,%g,%g,%g) type %d color %d\n",
1657 seg->start, seg->middle, seg->end, seg->c[0].channel[0],
1658 seg->c[0].channel[1], seg->c[0].channel[2], seg->c[0].channel[3],
1659 seg->c[1].channel[0], seg->c[1].channel[1], seg->c[1].channel[2],
1660 seg->c[1].channel[3], seg->type, seg->color);*/
1664 /* initialize each engine */
1665 /* these are so common ... */
1666 state->lA = xb - xa;
1667 state->lB = yb - ya;
1668 state->AB = sqrt(state->lA * state->lA + state->lB * state->lB);
1673 type = i_ft_linear; /* make the invalid value valid */
1676 state->lC = ya * ya - ya * yb + xa * xa - xa * xb;
1678 state->mult = 1/linear_fount_f(xb, yb, state);
1682 state->mult = 1.0 / sqrt((double)(xb-xa)*(xb-xa)
1683 + (double)(yb-ya)*(yb-ya));
1686 case i_ft_radial_square:
1687 state->cos = state->lA / state->AB;
1688 state->sin = state->lB / state->AB;
1689 state->mult = 1.0 / state->AB;
1692 case i_ft_revolution:
1693 state->theta = atan2(yb-ya, xb-xa);
1694 state->mult = 1.0 / (PI * 2);
1698 state->theta = atan2(yb-ya, xb-xa);
1699 state->mult = 1.0 / PI;
1702 state->ffunc = fount_funcs[type];
1703 if (super_sample < 0
1704 || super_sample >= (int)(sizeof(fount_ssamples)/sizeof(*fount_ssamples))) {
1707 state->ssample_data = NULL;
1708 switch (super_sample) {
1710 ssample_param = floor(0.5 + sqrt(ssample_param));
1711 state->ssample_data = mymalloc(sizeof(i_fcolor) * ssample_param * ssample_param);
1716 ssample_param = floor(0.5+ssample_param);
1717 state->ssample_data = mymalloc(sizeof(i_fcolor) * ssample_param);
1720 state->parm = ssample_param;
1721 state->ssfunc = fount_ssamples[super_sample];
1722 if (repeat < 0 || repeat >= (sizeof(fount_repeats)/sizeof(*fount_repeats)))
1724 state->rpfunc = fount_repeats[repeat];
1725 state->segs = my_segs;
1726 state->count = count;
1730 fount_finish_state(struct fount_state *state) {
1731 if (state->ssample_data)
1732 myfree(state->ssample_data);
1733 myfree(state->segs);
1738 =item fount_getat(out, x, y, ffunc, rpfunc, state, segs, count)
1740 Evaluates the fountain fill at the given point.
1742 This is called by both the non-super-sampling and super-sampling code.
1744 You might think that it would make sense to sample the fill parameter
1745 instead, and combine those, but this breaks badly.
1751 fount_getat(i_fcolor *out, double x, double y, struct fount_state *state) {
1752 double v = (state->rpfunc)((state->ffunc)(x, y, state));
1756 while (i < state->count
1757 && (v < state->segs[i].start || v > state->segs[i].end)) {
1760 if (i < state->count) {
1761 v = (fount_interps[state->segs[i].type])(v, state->segs+i);
1762 (fount_cinterps[state->segs[i].color])(out, v, state->segs+i);
1770 =item linear_fount_f(x, y, state)
1772 Calculate the fill parameter for a linear fountain fill.
1774 Uses the point to line distance function, with some precalculation
1775 done in i_fountain().
1780 linear_fount_f(double x, double y, struct fount_state *state) {
1781 return (state->lA * x + state->lB * y + state->lC) / state->AB * state->mult;
1785 =item bilinear_fount_f(x, y, state)
1787 Calculate the fill parameter for a bi-linear fountain fill.
1792 bilinear_fount_f(double x, double y, struct fount_state *state) {
1793 return fabs((state->lA * x + state->lB * y + state->lC) / state->AB * state->mult);
1797 =item radial_fount_f(x, y, state)
1799 Calculate the fill parameter for a radial fountain fill.
1801 Simply uses the distance function.
1806 radial_fount_f(double x, double y, struct fount_state *state) {
1807 return sqrt((double)(state->xa-x)*(state->xa-x)
1808 + (double)(state->ya-y)*(state->ya-y)) * state->mult;
1812 =item square_fount_f(x, y, state)
1814 Calculate the fill parameter for a square fountain fill.
1816 Works by rotating the reference co-ordinate around the centre of the
1822 square_fount_f(double x, double y, struct fount_state *state) {
1823 int xc, yc; /* centred on A */
1824 double xt, yt; /* rotated by theta */
1827 xt = fabs(xc * state->cos + yc * state->sin);
1828 yt = fabs(-xc * state->sin + yc * state->cos);
1829 return (xt > yt ? xt : yt) * state->mult;
1833 =item revolution_fount_f(x, y, state)
1835 Calculates the fill parameter for the revolution fountain fill.
1840 revolution_fount_f(double x, double y, struct fount_state *state) {
1841 double angle = atan2(y - state->ya, x - state->xa);
1843 angle -= state->theta;
1845 angle = fmod(angle+ PI * 4, PI*2);
1848 return angle * state->mult;
1852 =item conical_fount_f(x, y, state)
1854 Calculates the fill parameter for the conical fountain fill.
1859 conical_fount_f(double x, double y, struct fount_state *state) {
1860 double angle = atan2(y - state->ya, x - state->xa);
1862 angle -= state->theta;
1865 else if (angle > PI)
1868 return fabs(angle) * state->mult;
1872 =item linear_interp(pos, seg)
1874 Calculates linear interpolation on the fill parameter. Breaks the
1875 segment into 2 regions based in the I<middle> value.
1880 linear_interp(double pos, i_fountain_seg *seg) {
1881 if (pos < seg->middle) {
1882 double len = seg->middle - seg->start;
1886 return (pos - seg->start) / len / 2;
1889 double len = seg->end - seg->middle;
1893 return 0.5 + (pos - seg->middle) / len / 2;
1898 =item sine_interp(pos, seg)
1900 Calculates sine function interpolation on the fill parameter.
1905 sine_interp(double pos, i_fountain_seg *seg) {
1906 /* I wonder if there's a simple way to smooth the transition for this */
1907 double work = linear_interp(pos, seg);
1909 return (1-cos(work * PI))/2;
1913 =item sphereup_interp(pos, seg)
1915 Calculates spherical interpolation on the fill parameter, with the cusp
1921 sphereup_interp(double pos, i_fountain_seg *seg) {
1922 double work = linear_interp(pos, seg);
1924 return sqrt(1.0 - (1-work) * (1-work));
1928 =item spheredown_interp(pos, seg)
1930 Calculates spherical interpolation on the fill parameter, with the cusp
1936 spheredown_interp(double pos, i_fountain_seg *seg) {
1937 double work = linear_interp(pos, seg);
1939 return 1-sqrt(1.0 - work * work);
1943 =item direct_cinterp(out, pos, seg)
1945 Calculates the fountain color based on direct scaling of the channels
1946 of the color channels.
1951 direct_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg) {
1953 for (ch = 0; ch < MAXCHANNELS; ++ch) {
1954 out->channel[ch] = seg->c[0].channel[ch] * (1 - pos)
1955 + seg->c[1].channel[ch] * pos;
1960 =item hue_up_cinterp(put, pos, seg)
1962 Calculates the fountain color based on scaling a HSV value. The hue
1963 increases as the fill parameter increases.
1968 hue_up_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg) {
1970 for (ch = 0; ch < MAXCHANNELS; ++ch) {
1971 out->channel[ch] = seg->c[0].channel[ch] * (1 - pos)
1972 + seg->c[1].channel[ch] * pos;
1978 =item hue_down_cinterp(put, pos, seg)
1980 Calculates the fountain color based on scaling a HSV value. The hue
1981 decreases as the fill parameter increases.
1986 hue_down_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg) {
1988 for (ch = 0; ch < MAXCHANNELS; ++ch) {
1989 out->channel[ch] = seg->c[0].channel[ch] * (1 - pos)
1990 + seg->c[1].channel[ch] * pos;
1996 =item simple_ssample(out, parm, x, y, state, ffunc, rpfunc, segs, count)
1998 Simple grid-based super-sampling.
2003 simple_ssample(i_fcolor *out, double x, double y, struct fount_state *state) {
2004 i_fcolor *work = state->ssample_data;
2006 int grid = state->parm;
2007 double base = -0.5 + 0.5 / grid;
2008 double step = 1.0 / grid;
2012 for (dx = 0; dx < grid; ++dx) {
2013 for (dy = 0; dy < grid; ++dy) {
2014 if (fount_getat(work+samp_count, x + base + step * dx,
2015 y + base + step * dy, state)) {
2020 for (ch = 0; ch < MAXCHANNELS; ++ch) {
2021 out->channel[ch] = 0;
2022 for (i = 0; i < samp_count; ++i) {
2023 out->channel[ch] += work[i].channel[ch];
2025 /* we divide by 4 rather than samp_count since if there's only one valid
2026 sample it should be mostly transparent */
2027 out->channel[ch] /= grid * grid;
2033 =item random_ssample(out, parm, x, y, state, ffunc, rpfunc, segs, count)
2035 Random super-sampling.
2040 random_ssample(i_fcolor *out, double x, double y,
2041 struct fount_state *state) {
2042 i_fcolor *work = state->ssample_data;
2044 int maxsamples = state->parm;
2045 double rand_scale = 1.0 / RAND_MAX;
2047 for (i = 0; i < maxsamples; ++i) {
2048 if (fount_getat(work+samp_count, x - 0.5 + rand() * rand_scale,
2049 y - 0.5 + rand() * rand_scale, state)) {
2053 for (ch = 0; ch < MAXCHANNELS; ++ch) {
2054 out->channel[ch] = 0;
2055 for (i = 0; i < samp_count; ++i) {
2056 out->channel[ch] += work[i].channel[ch];
2058 /* we divide by maxsamples rather than samp_count since if there's
2059 only one valid sample it should be mostly transparent */
2060 out->channel[ch] /= maxsamples;
2066 =item circle_ssample(out, parm, x, y, state, ffunc, rpfunc, segs, count)
2068 Super-sampling around the circumference of a circle.
2070 I considered saving the sin()/cos() values and transforming step-size
2071 around the circle, but that's inaccurate, though it may not matter
2077 circle_ssample(i_fcolor *out, double x, double y,
2078 struct fount_state *state) {
2079 i_fcolor *work = state->ssample_data;
2081 int maxsamples = state->parm;
2082 double angle = 2 * PI / maxsamples;
2083 double radius = 0.3; /* semi-random */
2085 for (i = 0; i < maxsamples; ++i) {
2086 if (fount_getat(work+samp_count, x + radius * cos(angle * i),
2087 y + radius * sin(angle * i), state)) {
2091 for (ch = 0; ch < MAXCHANNELS; ++ch) {
2092 out->channel[ch] = 0;
2093 for (i = 0; i < samp_count; ++i) {
2094 out->channel[ch] += work[i].channel[ch];
2096 /* we divide by maxsamples rather than samp_count since if there's
2097 only one valid sample it should be mostly transparent */
2098 out->channel[ch] /= maxsamples;
2104 =item fount_r_none(v)
2106 Implements no repeats. Simply clamps the fill value.
2111 fount_r_none(double v) {
2112 return v < 0 ? 0 : v > 1 ? 1 : v;
2116 =item fount_r_sawtooth(v)
2118 Implements sawtooth repeats. Clamps negative values and uses fmod()
2124 fount_r_sawtooth(double v) {
2125 return v < 0 ? 0 : fmod(v, 1.0);
2129 =item fount_r_triangle(v)
2131 Implements triangle repeats. Clamps negative values, uses fmod to get
2132 a range 0 through 2 and then adjusts values > 1.
2137 fount_r_triangle(double v) {
2142 return v > 1.0 ? 2.0 - v : v;
2147 =item fount_r_saw_both(v)
2149 Implements sawtooth repeats in the both postive and negative directions.
2151 Adjusts the value to be postive and then just uses fmod().
2156 fount_r_saw_both(double v) {
2159 return fmod(v, 1.0);
2163 =item fount_r_tri_both(v)
2165 Implements triangle repeats in the both postive and negative directions.
2167 Uses fmod on the absolute value, and then adjusts values > 1.
2172 fount_r_tri_both(double v) {
2173 v = fmod(fabs(v), 2.0);
2174 return v > 1.0 ? 2.0 - v : v;
2178 =item fill_fountf(fill, x, y, width, channels, data)
2180 The fill function for fountain fills.
2185 fill_fountf(i_fill_t *fill, int x, int y, int width, int channels,
2187 i_fill_fountain_t *f = (i_fill_fountain_t *)fill;
2193 if (f->state.ssfunc)
2194 got_one = f->state.ssfunc(&c, x, y, &f->state);
2196 got_one = fount_getat(&c, x, y, &f->state);
2205 =item fount_fill_destroy(fill)
2210 fount_fill_destroy(i_fill_t *fill) {
2211 i_fill_fountain_t *f = (i_fill_fountain_t *)fill;
2212 fount_finish_state(&f->state);
2220 Arnar M. Hrafnkelsson <addi@umich.edu>
2222 Tony Cook <tony@develop-help.com> (i_fountain())