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;
1221 if (im1->channels != im2->channels) {
1222 i_push_error(0, "different number of channels");
1226 outchans = diffchans = im1->channels;
1227 if (outchans == 1 || outchans == 3)
1230 xsize = i_min(im1->xsize, im2->xsize);
1231 ysize = i_min(im1->ysize, im2->ysize);
1233 out = i_sametype_chans(im1, xsize, ysize, outchans);
1235 if (im1->bits == i_8_bits && im2->bits == i_8_bits) {
1236 i_color *line1 = mymalloc(2 * xsize * sizeof(*line1));
1237 i_color *line2 = line1 + xsize;
1241 for (ch = 0; ch < MAXCHANNELS; ++ch)
1242 empty.channel[ch] = 0;
1244 for (y = 0; y < ysize; ++y) {
1245 i_glin(im1, 0, xsize, y, line1);
1246 i_glin(im2, 0, xsize, y, line2);
1247 if (outchans != diffchans) {
1248 /* give the output an alpha channel since it doesn't have one */
1249 for (x = 0; x < xsize; ++x)
1250 line2[x].channel[diffchans] = 255;
1252 for (x = 0; x < xsize; ++x) {
1254 for (ch = 0; ch < diffchans; ++ch) {
1255 if (line1[x].channel[ch] != line2[x].channel[ch]
1256 && abs(line1[x].channel[ch] - line2[x].channel[ch]) > mindiff) {
1264 i_plin(out, 0, xsize, y, line2);
1269 i_fcolor *line1 = mymalloc(2 * xsize * sizeof(*line1));
1270 i_fcolor *line2 = line1 + xsize;
1273 double dist = mindiff / 255;
1275 for (ch = 0; ch < MAXCHANNELS; ++ch)
1276 empty.channel[ch] = 0;
1278 for (y = 0; y < ysize; ++y) {
1279 i_glinf(im1, 0, xsize, y, line1);
1280 i_glinf(im2, 0, xsize, y, line2);
1281 if (outchans != diffchans) {
1282 /* give the output an alpha channel since it doesn't have one */
1283 for (x = 0; x < xsize; ++x)
1284 line2[x].channel[diffchans] = 1.0;
1286 for (x = 0; x < xsize; ++x) {
1288 for (ch = 0; ch < diffchans; ++ch) {
1289 if (line1[x].channel[ch] != line2[x].channel[ch]
1290 && abs(line1[x].channel[ch] - line2[x].channel[ch]) > dist) {
1298 i_plinf(out, 0, xsize, y, line2);
1307 static double linear_fount_f(double x, double y, struct fount_state *state);
1308 static double bilinear_fount_f(double x, double y, struct fount_state *state);
1309 static double radial_fount_f(double x, double y, struct fount_state *state);
1310 static double square_fount_f(double x, double y, struct fount_state *state);
1311 static double revolution_fount_f(double x, double y,
1312 struct fount_state *state);
1313 static double conical_fount_f(double x, double y, struct fount_state *state);
1315 typedef double (*fount_func)(double, double, struct fount_state *);
1316 static fount_func fount_funcs[] =
1326 static double linear_interp(double pos, i_fountain_seg *seg);
1327 static double sine_interp(double pos, i_fountain_seg *seg);
1328 static double sphereup_interp(double pos, i_fountain_seg *seg);
1329 static double spheredown_interp(double pos, i_fountain_seg *seg);
1330 typedef double (*fount_interp)(double pos, i_fountain_seg *seg);
1331 static fount_interp fount_interps[] =
1340 static void direct_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg);
1341 static void hue_up_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg);
1342 static void hue_down_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg);
1343 typedef void (*fount_cinterp)(i_fcolor *out, double pos, i_fountain_seg *seg);
1344 static fount_cinterp fount_cinterps[] =
1351 typedef double (*fount_repeat)(double v);
1352 static double fount_r_none(double v);
1353 static double fount_r_sawtooth(double v);
1354 static double fount_r_triangle(double v);
1355 static double fount_r_saw_both(double v);
1356 static double fount_r_tri_both(double v);
1357 static fount_repeat fount_repeats[] =
1366 static int simple_ssample(i_fcolor *out, double x, double y,
1367 struct fount_state *state);
1368 static int random_ssample(i_fcolor *out, double x, double y,
1369 struct fount_state *state);
1370 static int circle_ssample(i_fcolor *out, double x, double y,
1371 struct fount_state *state);
1372 typedef int (*fount_ssample)(i_fcolor *out, double x, double y,
1373 struct fount_state *state);
1374 static fount_ssample fount_ssamples[] =
1383 fount_getat(i_fcolor *out, double x, double y, struct fount_state *state);
1386 Keep state information used by each type of fountain fill
1388 struct fount_state {
1389 /* precalculated for the equation of the line perpendicular to the line AB */
1400 fount_repeat rpfunc;
1401 fount_ssample ssfunc;
1403 i_fountain_seg *segs;
1408 fount_init_state(struct fount_state *state, double xa, double ya,
1409 double xb, double yb, i_fountain_type type,
1410 i_fountain_repeat repeat, int combine, int super_sample,
1411 double ssample_param, int count, i_fountain_seg *segs);
1414 fount_finish_state(struct fount_state *state);
1416 #define EPSILON (1e-6)
1419 =item i_fountain(im, xa, ya, xb, yb, type, repeat, combine, super_sample, ssample_param, count, segs)
1421 Draws a fountain fill using A(xa, ya) and B(xb, yb) as reference points.
1423 I<type> controls how the reference points are used:
1429 linear, where A is 0 and B is 1.
1433 linear in both directions from A.
1437 circular, where A is the centre of the fill, and B is a point
1440 =item i_ft_radial_square
1442 where A is the centre of the fill and B is the centre of
1443 one side of the square.
1445 =item i_ft_revolution
1447 where A is the centre of the fill and B defines the 0/1.0
1452 similar to i_ft_revolution, except that the revolution goes in both
1457 I<repeat> can be one of:
1463 values < 0 are treated as zero, values > 1 are treated as 1.
1467 negative values are treated as 0, positive values are modulo 1.0
1471 negative values are treated as zero, if (int)value is odd then the value is treated as 1-(value
1472 mod 1.0), otherwise the same as for sawtooth.
1476 like i_fr_sawtooth, except that the sawtooth pattern repeats into
1481 Like i_fr_triangle, except that negative values are handled as their
1486 If combine is non-zero then non-opaque values are combined with the
1489 I<super_sample> controls super sampling, if any. At some point I'll
1490 probably add a adaptive super-sampler. Current possible values are:
1496 No super-sampling is done.
1500 A square grid of points withing the pixel are sampled.
1504 Random points within the pixel are sampled.
1508 Points on the radius of a circle are sampled. This produces fairly
1509 good results, but is fairly slow since sin() and cos() are evaluated
1514 I<ssample_param> is intended to be roughly the number of points
1515 sampled within the pixel.
1517 I<count> and I<segs> define the segments of the fill.
1524 i_fountain(i_img *im, double xa, double ya, double xb, double yb,
1525 i_fountain_type type, i_fountain_repeat repeat,
1526 int combine, int super_sample, double ssample_param,
1527 int count, i_fountain_seg *segs) {
1528 struct fount_state state;
1530 i_fcolor *line = mymalloc(sizeof(i_fcolor) * im->xsize);
1531 i_fcolor *work = NULL;
1533 i_fountain_seg *my_segs;
1534 i_fill_combine_f combine_func = NULL;
1535 i_fill_combinef_f combinef_func = NULL;
1537 i_get_combine(combine, &combine_func, &combinef_func);
1539 work = mymalloc(sizeof(i_fcolor) * im->xsize);
1541 fount_init_state(&state, xa, ya, xb, yb, type, repeat, combine,
1542 super_sample, ssample_param, count, segs);
1543 my_segs = state.segs;
1545 for (y = 0; y < im->ysize; ++y) {
1546 i_glinf(im, 0, im->xsize, y, line);
1547 for (x = 0; x < im->xsize; ++x) {
1550 if (super_sample == i_fts_none)
1551 got_one = fount_getat(&c, x, y, &state);
1553 got_one = state.ssfunc(&c, x, y, &state);
1562 combinef_func(line, work, im->channels, im->xsize);
1563 i_plinf(im, 0, im->xsize, y, line);
1565 fount_finish_state(&state);
1566 if (work) myfree(work);
1572 struct fount_state state;
1573 } i_fill_fountain_t;
1576 fill_fountf(i_fill_t *fill, int x, int y, int width, int channels,
1579 fount_fill_destroy(i_fill_t *fill);
1582 =item i_new_fount(xa, ya, xb, yb, type, repeat, combine, super_sample, ssample_param, count, segs)
1584 Creates a new general fill which fills with a fountain fill.
1590 i_new_fill_fount(double xa, double ya, double xb, double yb,
1591 i_fountain_type type, i_fountain_repeat repeat,
1592 int combine, int super_sample, double ssample_param,
1593 int count, i_fountain_seg *segs) {
1594 i_fill_fountain_t *fill = mymalloc(sizeof(i_fill_fountain_t));
1596 fill->base.fill_with_color = NULL;
1597 fill->base.fill_with_fcolor = fill_fountf;
1598 fill->base.destroy = fount_fill_destroy;
1600 i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
1602 fill->base.combine = NULL;
1603 fill->base.combinef = NULL;
1605 fount_init_state(&fill->state, xa, ya, xb, yb, type, repeat, combine,
1606 super_sample, ssample_param, count, segs);
1614 =head1 INTERNAL FUNCTIONS
1618 =item fount_init_state(...)
1620 Used by both the fountain fill filter and the fountain fill.
1626 fount_init_state(struct fount_state *state, double xa, double ya,
1627 double xb, double yb, i_fountain_type type,
1628 i_fountain_repeat repeat, int combine, int super_sample,
1629 double ssample_param, int count, i_fountain_seg *segs) {
1631 i_fountain_seg *my_segs = mymalloc(sizeof(i_fountain_seg) * count);
1632 /*int have_alpha = im->channels == 2 || im->channels == 4;*/
1634 memset(state, 0, sizeof(*state));
1635 /* we keep a local copy that we can adjust for speed */
1636 for (i = 0; i < count; ++i) {
1637 i_fountain_seg *seg = my_segs + i;
1640 if (seg->type < 0 || seg->type >= i_fst_end)
1641 seg->type = i_fst_linear;
1642 if (seg->color < 0 || seg->color >= i_fc_end)
1643 seg->color = i_fc_direct;
1644 if (seg->color == i_fc_hue_up || seg->color == i_fc_hue_down) {
1645 /* so we don't have to translate to HSV on each request, do it here */
1646 for (j = 0; j < 2; ++j) {
1647 i_rgb_to_hsvf(seg->c+j);
1649 if (seg->color == i_fc_hue_up) {
1650 if (seg->c[1].channel[0] <= seg->c[0].channel[0])
1651 seg->c[1].channel[0] += 1.0;
1654 if (seg->c[0].channel[0] <= seg->c[0].channel[1])
1655 seg->c[0].channel[0] += 1.0;
1658 /*printf("start %g mid %g end %g c0(%g,%g,%g,%g) c1(%g,%g,%g,%g) type %d color %d\n",
1659 seg->start, seg->middle, seg->end, seg->c[0].channel[0],
1660 seg->c[0].channel[1], seg->c[0].channel[2], seg->c[0].channel[3],
1661 seg->c[1].channel[0], seg->c[1].channel[1], seg->c[1].channel[2],
1662 seg->c[1].channel[3], seg->type, seg->color);*/
1666 /* initialize each engine */
1667 /* these are so common ... */
1668 state->lA = xb - xa;
1669 state->lB = yb - ya;
1670 state->AB = sqrt(state->lA * state->lA + state->lB * state->lB);
1675 type = i_ft_linear; /* make the invalid value valid */
1678 state->lC = ya * ya - ya * yb + xa * xa - xa * xb;
1680 state->mult = 1/linear_fount_f(xb, yb, state);
1684 state->mult = 1.0 / sqrt((double)(xb-xa)*(xb-xa)
1685 + (double)(yb-ya)*(yb-ya));
1688 case i_ft_radial_square:
1689 state->cos = state->lA / state->AB;
1690 state->sin = state->lB / state->AB;
1691 state->mult = 1.0 / state->AB;
1694 case i_ft_revolution:
1695 state->theta = atan2(yb-ya, xb-xa);
1696 state->mult = 1.0 / (PI * 2);
1700 state->theta = atan2(yb-ya, xb-xa);
1701 state->mult = 1.0 / PI;
1704 state->ffunc = fount_funcs[type];
1705 if (super_sample < 0
1706 || super_sample >= (int)(sizeof(fount_ssamples)/sizeof(*fount_ssamples))) {
1709 state->ssample_data = NULL;
1710 switch (super_sample) {
1712 ssample_param = floor(0.5 + sqrt(ssample_param));
1713 state->ssample_data = mymalloc(sizeof(i_fcolor) * ssample_param * ssample_param);
1718 ssample_param = floor(0.5+ssample_param);
1719 state->ssample_data = mymalloc(sizeof(i_fcolor) * ssample_param);
1722 state->parm = ssample_param;
1723 state->ssfunc = fount_ssamples[super_sample];
1724 if (repeat < 0 || repeat >= (sizeof(fount_repeats)/sizeof(*fount_repeats)))
1726 state->rpfunc = fount_repeats[repeat];
1727 state->segs = my_segs;
1728 state->count = count;
1732 fount_finish_state(struct fount_state *state) {
1733 if (state->ssample_data)
1734 myfree(state->ssample_data);
1735 myfree(state->segs);
1740 =item fount_getat(out, x, y, ffunc, rpfunc, state, segs, count)
1742 Evaluates the fountain fill at the given point.
1744 This is called by both the non-super-sampling and super-sampling code.
1746 You might think that it would make sense to sample the fill parameter
1747 instead, and combine those, but this breaks badly.
1753 fount_getat(i_fcolor *out, double x, double y, struct fount_state *state) {
1754 double v = (state->rpfunc)((state->ffunc)(x, y, state));
1758 while (i < state->count
1759 && (v < state->segs[i].start || v > state->segs[i].end)) {
1762 if (i < state->count) {
1763 v = (fount_interps[state->segs[i].type])(v, state->segs+i);
1764 (fount_cinterps[state->segs[i].color])(out, v, state->segs+i);
1772 =item linear_fount_f(x, y, state)
1774 Calculate the fill parameter for a linear fountain fill.
1776 Uses the point to line distance function, with some precalculation
1777 done in i_fountain().
1782 linear_fount_f(double x, double y, struct fount_state *state) {
1783 return (state->lA * x + state->lB * y + state->lC) / state->AB * state->mult;
1787 =item bilinear_fount_f(x, y, state)
1789 Calculate the fill parameter for a bi-linear fountain fill.
1794 bilinear_fount_f(double x, double y, struct fount_state *state) {
1795 return fabs((state->lA * x + state->lB * y + state->lC) / state->AB * state->mult);
1799 =item radial_fount_f(x, y, state)
1801 Calculate the fill parameter for a radial fountain fill.
1803 Simply uses the distance function.
1808 radial_fount_f(double x, double y, struct fount_state *state) {
1809 return sqrt((double)(state->xa-x)*(state->xa-x)
1810 + (double)(state->ya-y)*(state->ya-y)) * state->mult;
1814 =item square_fount_f(x, y, state)
1816 Calculate the fill parameter for a square fountain fill.
1818 Works by rotating the reference co-ordinate around the centre of the
1824 square_fount_f(double x, double y, struct fount_state *state) {
1825 int xc, yc; /* centred on A */
1826 double xt, yt; /* rotated by theta */
1829 xt = fabs(xc * state->cos + yc * state->sin);
1830 yt = fabs(-xc * state->sin + yc * state->cos);
1831 return (xt > yt ? xt : yt) * state->mult;
1835 =item revolution_fount_f(x, y, state)
1837 Calculates the fill parameter for the revolution fountain fill.
1842 revolution_fount_f(double x, double y, struct fount_state *state) {
1843 double angle = atan2(y - state->ya, x - state->xa);
1845 angle -= state->theta;
1847 angle = fmod(angle+ PI * 4, PI*2);
1850 return angle * state->mult;
1854 =item conical_fount_f(x, y, state)
1856 Calculates the fill parameter for the conical fountain fill.
1861 conical_fount_f(double x, double y, struct fount_state *state) {
1862 double angle = atan2(y - state->ya, x - state->xa);
1864 angle -= state->theta;
1867 else if (angle > PI)
1870 return fabs(angle) * state->mult;
1874 =item linear_interp(pos, seg)
1876 Calculates linear interpolation on the fill parameter. Breaks the
1877 segment into 2 regions based in the I<middle> value.
1882 linear_interp(double pos, i_fountain_seg *seg) {
1883 if (pos < seg->middle) {
1884 double len = seg->middle - seg->start;
1888 return (pos - seg->start) / len / 2;
1891 double len = seg->end - seg->middle;
1895 return 0.5 + (pos - seg->middle) / len / 2;
1900 =item sine_interp(pos, seg)
1902 Calculates sine function interpolation on the fill parameter.
1907 sine_interp(double pos, i_fountain_seg *seg) {
1908 /* I wonder if there's a simple way to smooth the transition for this */
1909 double work = linear_interp(pos, seg);
1911 return (1-cos(work * PI))/2;
1915 =item sphereup_interp(pos, seg)
1917 Calculates spherical interpolation on the fill parameter, with the cusp
1923 sphereup_interp(double pos, i_fountain_seg *seg) {
1924 double work = linear_interp(pos, seg);
1926 return sqrt(1.0 - (1-work) * (1-work));
1930 =item spheredown_interp(pos, seg)
1932 Calculates spherical interpolation on the fill parameter, with the cusp
1938 spheredown_interp(double pos, i_fountain_seg *seg) {
1939 double work = linear_interp(pos, seg);
1941 return 1-sqrt(1.0 - work * work);
1945 =item direct_cinterp(out, pos, seg)
1947 Calculates the fountain color based on direct scaling of the channels
1948 of the color channels.
1953 direct_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg) {
1955 for (ch = 0; ch < MAXCHANNELS; ++ch) {
1956 out->channel[ch] = seg->c[0].channel[ch] * (1 - pos)
1957 + seg->c[1].channel[ch] * pos;
1962 =item hue_up_cinterp(put, pos, seg)
1964 Calculates the fountain color based on scaling a HSV value. The hue
1965 increases as the fill parameter increases.
1970 hue_up_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg) {
1972 for (ch = 0; ch < MAXCHANNELS; ++ch) {
1973 out->channel[ch] = seg->c[0].channel[ch] * (1 - pos)
1974 + seg->c[1].channel[ch] * pos;
1980 =item hue_down_cinterp(put, pos, seg)
1982 Calculates the fountain color based on scaling a HSV value. The hue
1983 decreases as the fill parameter increases.
1988 hue_down_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg) {
1990 for (ch = 0; ch < MAXCHANNELS; ++ch) {
1991 out->channel[ch] = seg->c[0].channel[ch] * (1 - pos)
1992 + seg->c[1].channel[ch] * pos;
1998 =item simple_ssample(out, parm, x, y, state, ffunc, rpfunc, segs, count)
2000 Simple grid-based super-sampling.
2005 simple_ssample(i_fcolor *out, double x, double y, struct fount_state *state) {
2006 i_fcolor *work = state->ssample_data;
2008 int grid = state->parm;
2009 double base = -0.5 + 0.5 / grid;
2010 double step = 1.0 / grid;
2014 for (dx = 0; dx < grid; ++dx) {
2015 for (dy = 0; dy < grid; ++dy) {
2016 if (fount_getat(work+samp_count, x + base + step * dx,
2017 y + base + step * dy, state)) {
2022 for (ch = 0; ch < MAXCHANNELS; ++ch) {
2023 out->channel[ch] = 0;
2024 for (i = 0; i < samp_count; ++i) {
2025 out->channel[ch] += work[i].channel[ch];
2027 /* we divide by 4 rather than samp_count since if there's only one valid
2028 sample it should be mostly transparent */
2029 out->channel[ch] /= grid * grid;
2035 =item random_ssample(out, parm, x, y, state, ffunc, rpfunc, segs, count)
2037 Random super-sampling.
2042 random_ssample(i_fcolor *out, double x, double y,
2043 struct fount_state *state) {
2044 i_fcolor *work = state->ssample_data;
2046 int maxsamples = state->parm;
2047 double rand_scale = 1.0 / RAND_MAX;
2049 for (i = 0; i < maxsamples; ++i) {
2050 if (fount_getat(work+samp_count, x - 0.5 + rand() * rand_scale,
2051 y - 0.5 + rand() * rand_scale, state)) {
2055 for (ch = 0; ch < MAXCHANNELS; ++ch) {
2056 out->channel[ch] = 0;
2057 for (i = 0; i < samp_count; ++i) {
2058 out->channel[ch] += work[i].channel[ch];
2060 /* we divide by maxsamples rather than samp_count since if there's
2061 only one valid sample it should be mostly transparent */
2062 out->channel[ch] /= maxsamples;
2068 =item circle_ssample(out, parm, x, y, state, ffunc, rpfunc, segs, count)
2070 Super-sampling around the circumference of a circle.
2072 I considered saving the sin()/cos() values and transforming step-size
2073 around the circle, but that's inaccurate, though it may not matter
2079 circle_ssample(i_fcolor *out, double x, double y,
2080 struct fount_state *state) {
2081 i_fcolor *work = state->ssample_data;
2083 int maxsamples = state->parm;
2084 double angle = 2 * PI / maxsamples;
2085 double radius = 0.3; /* semi-random */
2087 for (i = 0; i < maxsamples; ++i) {
2088 if (fount_getat(work+samp_count, x + radius * cos(angle * i),
2089 y + radius * sin(angle * i), state)) {
2093 for (ch = 0; ch < MAXCHANNELS; ++ch) {
2094 out->channel[ch] = 0;
2095 for (i = 0; i < samp_count; ++i) {
2096 out->channel[ch] += work[i].channel[ch];
2098 /* we divide by maxsamples rather than samp_count since if there's
2099 only one valid sample it should be mostly transparent */
2100 out->channel[ch] /= maxsamples;
2106 =item fount_r_none(v)
2108 Implements no repeats. Simply clamps the fill value.
2113 fount_r_none(double v) {
2114 return v < 0 ? 0 : v > 1 ? 1 : v;
2118 =item fount_r_sawtooth(v)
2120 Implements sawtooth repeats. Clamps negative values and uses fmod()
2126 fount_r_sawtooth(double v) {
2127 return v < 0 ? 0 : fmod(v, 1.0);
2131 =item fount_r_triangle(v)
2133 Implements triangle repeats. Clamps negative values, uses fmod to get
2134 a range 0 through 2 and then adjusts values > 1.
2139 fount_r_triangle(double v) {
2144 return v > 1.0 ? 2.0 - v : v;
2149 =item fount_r_saw_both(v)
2151 Implements sawtooth repeats in the both postive and negative directions.
2153 Adjusts the value to be postive and then just uses fmod().
2158 fount_r_saw_both(double v) {
2161 return fmod(v, 1.0);
2165 =item fount_r_tri_both(v)
2167 Implements triangle repeats in the both postive and negative directions.
2169 Uses fmod on the absolute value, and then adjusts values > 1.
2174 fount_r_tri_both(double v) {
2175 v = fmod(fabs(v), 2.0);
2176 return v > 1.0 ? 2.0 - v : v;
2180 =item fill_fountf(fill, x, y, width, channels, data)
2182 The fill function for fountain fills.
2187 fill_fountf(i_fill_t *fill, int x, int y, int width, int channels,
2189 i_fill_fountain_t *f = (i_fill_fountain_t *)fill;
2195 if (f->state.ssfunc)
2196 got_one = f->state.ssfunc(&c, x, y, &f->state);
2198 got_one = fount_getat(&c, x, y, &f->state);
2207 =item fount_fill_destroy(fill)
2212 fount_fill_destroy(i_fill_t *fill) {
2213 i_fill_fountain_t *f = (i_fill_fountain_t *)fill;
2214 fount_finish_state(&f->state);
2222 Arnar M. Hrafnkelsson <addi@umich.edu>
2224 Tony Cook <tony@develop-help.com> (i_fountain())