- eliminate sign warning from image.c
[imager.git] / filters.c
CommitLineData
92bda632
TC
1#include "imager.h"
2#include "imageri.h"
02d1d628
AMH
3#include <stdlib.h>
4#include <math.h>
5
6
7/*
8=head1 NAME
9
10filters.c - implements filters that operate on images
11
12=head1 SYNOPSIS
13
14
15 i_contrast(im, 0.8);
16 i_hardinvert(im);
b6381851 17 i_unsharp_mask(im, 2.0, 1.0);
02d1d628
AMH
18 // and more
19
20=head1 DESCRIPTION
21
22filters.c implements basic filters for Imager. These filters
23should be accessible from the filter interface as defined in
24the pod for Imager.
25
26=head1 FUNCTION REFERENCE
27
28Some of these functions are internal.
29
b8c2033e 30=over
02d1d628
AMH
31
32=cut
33*/
34
35
36
37
b2778574
AMH
38/*
39=item saturate(in)
40
41Clamps the input value between 0 and 255. (internal)
42
43 in - input integer
44
45=cut
46*/
47
48static
49unsigned char
50saturate(int in) {
51 if (in>255) { return 255; }
52 else if (in>0) return in;
53 return 0;
54}
02d1d628
AMH
55
56
57
58/*
59=item i_contrast(im, intensity)
60
61Scales the pixel values by the amount specified.
62
63 im - image object
64 intensity - scalefactor
65
66=cut
67*/
68
69void
70i_contrast(i_img *im, float intensity) {
71 int x, y;
72 unsigned char ch;
73 unsigned int new_color;
74 i_color rcolor;
75
76 mm_log((1,"i_contrast(im %p, intensity %f)\n", im, intensity));
77
78 if(intensity < 0) return;
79
80 for(y = 0; y < im->ysize; y++) for(x = 0; x < im->xsize; x++) {
81 i_gpix(im, x, y, &rcolor);
82
83 for(ch = 0; ch < im->channels; ch++) {
84 new_color = (unsigned int) rcolor.channel[ch];
85 new_color *= intensity;
86
87 if(new_color > 255) {
88 new_color = 255;
89 }
90 rcolor.channel[ch] = (unsigned char) new_color;
91 }
92 i_ppix(im, x, y, &rcolor);
93 }
94}
95
96
97/*
98=item i_hardinvert(im)
99
100Inverts the pixel values of the input image.
101
102 im - image object
103
104=cut
105*/
106
107void
108i_hardinvert(i_img *im) {
109 int x, y;
110 unsigned char ch;
111
92bda632 112 i_color *row, *entry;
02d1d628 113
92bda632
TC
114 mm_log((1,"i_hardinvert(im %p)\n", im));
115
116 row = mymalloc(sizeof(i_color) * im->xsize);
02d1d628
AMH
117
118 for(y = 0; y < im->ysize; y++) {
92bda632
TC
119 i_glin(im, 0, im->xsize, y, row);
120 entry = row;
02d1d628 121 for(x = 0; x < im->xsize; x++) {
02d1d628 122 for(ch = 0; ch < im->channels; ch++) {
92bda632 123 entry->channel[ch] = 255 - entry->channel[ch];
02d1d628 124 }
92bda632 125 ++entry;
02d1d628 126 }
92bda632 127 i_plin(im, 0, im->xsize, y, row);
02d1d628 128 }
92bda632 129 myfree(row);
02d1d628
AMH
130}
131
132
133
134/*
135=item i_noise(im, amount, type)
136
137Inverts the pixel values by the amount specified.
138
139 im - image object
140 amount - deviation in pixel values
141 type - noise individual for each channel if true
142
143=cut
144*/
145
8a00cb26 146#ifdef WIN32
02d1d628
AMH
147/* random() is non-ASCII, even if it is better than rand() */
148#define random() rand()
149#endif
150
151void
152i_noise(i_img *im, float amount, unsigned char type) {
153 int x, y;
154 unsigned char ch;
155 int new_color;
156 float damount = amount * 2;
157 i_color rcolor;
158 int color_inc = 0;
159
160 mm_log((1,"i_noise(im %p, intensity %.2f\n", im, amount));
161
162 if(amount < 0) return;
163
164 for(y = 0; y < im->ysize; y++) for(x = 0; x < im->xsize; x++) {
165 i_gpix(im, x, y, &rcolor);
166
167 if(type == 0) {
168 color_inc = (amount - (damount * ((float)random() / RAND_MAX)));
169 }
170
171 for(ch = 0; ch < im->channels; ch++) {
172 new_color = (int) rcolor.channel[ch];
173
174 if(type != 0) {
175 new_color += (amount - (damount * ((float)random() / RAND_MAX)));
176 } else {
177 new_color += color_inc;
178 }
179
180 if(new_color < 0) {
181 new_color = 0;
182 }
183 if(new_color > 255) {
184 new_color = 255;
185 }
186
187 rcolor.channel[ch] = (unsigned char) new_color;
188 }
189
190 i_ppix(im, x, y, &rcolor);
191 }
192}
193
194
195/*
196=item i_noise(im, amount, type)
197
198Inverts the pixel values by the amount specified.
199
200 im - image object
201 amount - deviation in pixel values
202 type - noise individual for each channel if true
203
204=cut
205*/
206
207
208/*
209=item i_applyimage(im, add_im, mode)
210
211Apply's an image to another image
212
213 im - target image
214 add_im - image that is applied to target
215 mode - what method is used in applying:
216
217 0 Normal
218 1 Multiply
219 2 Screen
220 3 Overlay
221 4 Soft Light
222 5 Hard Light
223 6 Color dodge
224 7 Color Burn
225 8 Darker
226 9 Lighter
227 10 Add
228 11 Subtract
229 12 Difference
230 13 Exclusion
231
232=cut
233*/
234
235void i_applyimage(i_img *im, i_img *add_im, unsigned char mode) {
236 int x, y;
237 int mx, my;
238
239 mm_log((1, "i_applyimage(im %p, add_im %p, mode %d", im, add_im, mode));
240
241 mx = (add_im->xsize <= im->xsize) ? add_im->xsize : add_im->xsize;
242 my = (add_im->ysize <= im->ysize) ? add_im->ysize : add_im->ysize;
243
244 for(x = 0; x < mx; x++) {
245 for(y = 0; y < my; y++) {
246 }
247 }
248}
249
250
251/*
252=item i_bumpmap(im, bump, channel, light_x, light_y, st)
253
254Makes a bumpmap on image im using the bump image as the elevation map.
255
256 im - target image
257 bump - image that contains the elevation info
258 channel - to take the elevation information from
259 light_x - x coordinate of light source
260 light_y - y coordinate of light source
261 st - length of shadow
262
263=cut
264*/
265
266void
267i_bumpmap(i_img *im, i_img *bump, int channel, int light_x, int light_y, int st) {
268 int x, y, ch;
269 int mx, my;
270 i_color x1_color, y1_color, x2_color, y2_color, dst_color;
271 double nX, nY;
272 double tX, tY, tZ;
273 double aX, aY, aL;
274 double fZ;
275 unsigned char px1, px2, py1, py2;
276
277 i_img new_im;
278
279 mm_log((1, "i_bumpmap(im %p, add_im %p, channel %d, light_x %d, light_y %d, st %d)\n",
280 im, bump, channel, light_x, light_y, st));
281
282
283 if(channel >= bump->channels) {
284 mm_log((1, "i_bumpmap: channel = %d while bump image only has %d channels\n", channel, bump->channels));
285 return;
286 }
b2778574 287
02d1d628
AMH
288 mx = (bump->xsize <= im->xsize) ? bump->xsize : im->xsize;
289 my = (bump->ysize <= im->ysize) ? bump->ysize : im->ysize;
290
291 i_img_empty_ch(&new_im, im->xsize, im->ysize, im->channels);
b2778574 292
02d1d628
AMH
293 aX = (light_x > (mx >> 1)) ? light_x : mx - light_x;
294 aY = (light_y > (my >> 1)) ? light_y : my - light_y;
295
296 aL = sqrt((aX * aX) + (aY * aY));
297
298 for(y = 1; y < my - 1; y++) {
299 for(x = 1; x < mx - 1; x++) {
300 i_gpix(bump, x + st, y, &x1_color);
301 i_gpix(bump, x, y + st, &y1_color);
302 i_gpix(bump, x - st, y, &x2_color);
303 i_gpix(bump, x, y - st, &y2_color);
304
305 i_gpix(im, x, y, &dst_color);
306
307 px1 = x1_color.channel[channel];
308 py1 = y1_color.channel[channel];
309 px2 = x2_color.channel[channel];
310 py2 = y2_color.channel[channel];
311
312 nX = px1 - px2;
313 nY = py1 - py2;
314
315 nX += 128;
316 nY += 128;
317
318 fZ = (sqrt((nX * nX) + (nY * nY)) / aL);
319
320 tX = abs(x - light_x) / aL;
321 tY = abs(y - light_y) / aL;
322
323 tZ = 1 - (sqrt((tX * tX) + (tY * tY)) * fZ);
324
325 if(tZ < 0) tZ = 0;
326 if(tZ > 2) tZ = 2;
327
328 for(ch = 0; ch < im->channels; ch++)
329 dst_color.channel[ch] = (unsigned char) (float)(dst_color.channel[ch] * tZ);
330
331 i_ppix(&new_im, x, y, &dst_color);
332 }
333 }
334
335 i_copyto(im, &new_im, 0, 0, (int)im->xsize, (int)im->ysize, 0, 0);
336
337 i_img_exorcise(&new_im);
338}
339
340
341
b2778574
AMH
342
343typedef struct {
344 float x,y,z;
345} fvec;
346
347
348static
349float
350dotp(fvec *a, fvec *b) {
351 return a->x*b->x+a->y*b->y+a->z*b->z;
352}
353
354static
355void
356normalize(fvec *a) {
357 double d = sqrt(dotp(a,a));
358 a->x /= d;
359 a->y /= d;
360 a->z /= d;
361}
362
363
364/*
365 positive directions:
366
367 x - right,
368 y - down
369 z - out of the plane
370
371 I = Ia + Ip*( cd*Scol(N.L) + cs*(R.V)^n )
372
373 Here, the variables are:
374
375 * Ia - ambient colour
376 * Ip - intensity of the point light source
377 * cd - diffuse coefficient
378 * Scol - surface colour
379 * cs - specular coefficient
380 * n - objects shinyness
381 * N - normal vector
382 * L - lighting vector
383 * R - reflection vector
384 * V - vision vector
385
386 static void fvec_dump(fvec *x) {
387 printf("(%.2f %.2f %.2f)", x->x, x->y, x->z);
388 }
389*/
390
391/* XXX: Should these return a code for success? */
392
393
394
395
396/*
397=item i_bumpmap_complex(im, bump, channel, tx, ty, Lx, Ly, Lz, Ip, cd, cs, n, Ia, Il, Is)
398
399Makes a bumpmap on image im using the bump image as the elevation map.
400
401 im - target image
402 bump - image that contains the elevation info
403 channel - to take the elevation information from
404 tx - shift in x direction of where to start applying bumpmap
405 ty - shift in y direction of where to start applying bumpmap
406 Lx - x position/direction of light
407 Ly - y position/direction of light
408 Lz - z position/direction of light
409 Ip - light intensity
410 cd - diffuse coefficient
411 cs - specular coefficient
412 n - surface shinyness
413 Ia - ambient colour
414 Il - light colour
415 Is - specular colour
416
417if z<0 then the L is taken to be the direction the light is shining in. Otherwise
418the L is taken to be the position of the Light, Relative to the image.
419
420=cut
421*/
422
423
424void
425i_bumpmap_complex(i_img *im,
426 i_img *bump,
427 int channel,
428 int tx,
429 int ty,
430 float Lx,
431 float Ly,
432 float Lz,
433 float cd,
434 float cs,
435 float n,
436 i_color *Ia,
437 i_color *Il,
438 i_color *Is) {
439 i_img new_im;
440
441 int inflight;
442 int x, y, ch;
443 int mx, Mx, my, My;
444
445 float cdc[MAXCHANNELS];
446 float csc[MAXCHANNELS];
447
448 i_color x1_color, y1_color, x2_color, y2_color;
449
450 i_color Scol; /* Surface colour */
451
452 fvec L; /* Light vector */
453 fvec N; /* surface normal */
454 fvec R; /* Reflection vector */
455 fvec V; /* Vision vector */
456
457 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",
458 im, bump, channel, tx, ty, Lx, Ly, Lz, cd, cs, n, Ia, Il, Is));
459
460 if (channel >= bump->channels) {
461 mm_log((1, "i_bumpmap_complex: channel = %d while bump image only has %d channels\n", channel, bump->channels));
462 return;
463 }
464
465 for(ch=0; ch<im->channels; ch++) {
466 cdc[ch] = (float)Il->channel[ch]*cd/255.f;
467 csc[ch] = (float)Is->channel[ch]*cs/255.f;
468 }
469
470 mx = 1;
471 my = 1;
472 Mx = bump->xsize-1;
473 My = bump->ysize-1;
474
475 V.x = 0;
476 V.y = 0;
477 V.z = 1;
478
479 if (Lz < 0) { /* Light specifies a direction vector, reverse it to get the vector from surface to light */
480 L.x = -Lx;
481 L.y = -Ly;
482 L.z = -Lz;
483 normalize(&L);
484 } else { /* Light is the position of the light source */
485 inflight = 0;
486 L.x = -0.2;
487 L.y = -0.4;
488 L.z = 1;
489 normalize(&L);
490 }
491
492 i_img_empty_ch(&new_im, im->xsize, im->ysize, im->channels);
493
494 for(y = 0; y < im->ysize; y++) {
495 for(x = 0; x < im->xsize; x++) {
496 double dp1, dp2;
497 double dx = 0, dy = 0;
498
499 /* Calculate surface normal */
500 if (mx<x && x<Mx && my<y && y<My) {
501 i_gpix(bump, x + 1, y, &x1_color);
502 i_gpix(bump, x - 1, y, &x2_color);
503 i_gpix(bump, x, y + 1, &y1_color);
504 i_gpix(bump, x, y - 1, &y2_color);
505 dx = x2_color.channel[channel] - x1_color.channel[channel];
506 dy = y2_color.channel[channel] - y1_color.channel[channel];
507 } else {
508 dx = 0;
509 dy = 0;
510 }
511 N.x = -dx * 0.015;
512 N.y = -dy * 0.015;
513 N.z = 1;
514 normalize(&N);
515
516 /* Calculate Light vector if needed */
517 if (Lz>=0) {
518 L.x = Lx - x;
519 L.y = Ly - y;
520 L.z = Lz;
521 normalize(&L);
522 }
523
524 dp1 = dotp(&L,&N);
525 R.x = -L.x + 2*dp1*N.x;
526 R.y = -L.y + 2*dp1*N.y;
527 R.z = -L.z + 2*dp1*N.z;
528
529 dp2 = dotp(&R,&V);
530
531 dp1 = dp1<0 ?0 : dp1;
532 dp2 = pow(dp2<0 ?0 : dp2,n);
533
534 i_gpix(im, x, y, &Scol);
535
536 for(ch = 0; ch < im->channels; ch++)
537 Scol.channel[ch] =
538 saturate( Ia->channel[ch] + cdc[ch]*Scol.channel[ch]*dp1 + csc[ch]*dp2 );
539
540 i_ppix(&new_im, x, y, &Scol);
541 }
542 }
543
544 i_copyto(im, &new_im, 0, 0, (int)im->xsize, (int)im->ysize, 0, 0);
545 i_img_exorcise(&new_im);
546}
547
548
02d1d628
AMH
549/*
550=item i_postlevels(im, levels)
551
552Quantizes Images to fewer levels.
553
554 im - target image
555 levels - number of levels
556
557=cut
558*/
559
560void
561i_postlevels(i_img *im, int levels) {
562 int x, y, ch;
563 float pv;
564 int rv;
565 float av;
566
567 i_color rcolor;
568
569 rv = (int) ((float)(256 / levels));
570 av = (float)levels;
571
572 for(y = 0; y < im->ysize; y++) for(x = 0; x < im->xsize; x++) {
573 i_gpix(im, x, y, &rcolor);
574
575 for(ch = 0; ch < im->channels; ch++) {
576 pv = (((float)rcolor.channel[ch] / 255)) * av;
577 pv = (int) ((int)pv * rv);
578
579 if(pv < 0) pv = 0;
580 else if(pv > 255) pv = 255;
581
582 rcolor.channel[ch] = (unsigned char) pv;
583 }
584 i_ppix(im, x, y, &rcolor);
585 }
586}
587
588
589/*
590=item i_mosaic(im, size)
591
592Makes an image looks like a mosaic with tilesize of size
593
594 im - target image
595 size - size of tiles
596
597=cut
598*/
599
600void
601i_mosaic(i_img *im, int size) {
602 int x, y, ch;
603 int lx, ly, z;
604 long sqrsize;
605
606 i_color rcolor;
607 long col[256];
608
609 sqrsize = size * size;
610
611 for(y = 0; y < im->ysize; y += size) for(x = 0; x < im->xsize; x += size) {
612 for(z = 0; z < 256; z++) col[z] = 0;
613
614 for(lx = 0; lx < size; lx++) {
615 for(ly = 0; ly < size; ly++) {
616 i_gpix(im, (x + lx), (y + ly), &rcolor);
617
618 for(ch = 0; ch < im->channels; ch++) {
619 col[ch] += rcolor.channel[ch];
620 }
621 }
622 }
623
624 for(ch = 0; ch < im->channels; ch++)
625 rcolor.channel[ch] = (int) ((float)col[ch] / sqrsize);
626
627
628 for(lx = 0; lx < size; lx++)
629 for(ly = 0; ly < size; ly++)
630 i_ppix(im, (x + lx), (y + ly), &rcolor);
631
632 }
633}
634
02d1d628
AMH
635
636/*
637=item i_watermark(im, wmark, tx, ty, pixdiff)
638
639Applies a watermark to the target image
640
641 im - target image
642 wmark - watermark image
643 tx - x coordinate of where watermark should be applied
644 ty - y coordinate of where watermark should be applied
645 pixdiff - the magnitude of the watermark, controls how visible it is
646
647=cut
648*/
649
650void
651i_watermark(i_img *im, i_img *wmark, int tx, int ty, int pixdiff) {
652 int vx, vy, ch;
653 i_color val, wval;
654
985ffb60
AMH
655 int mx = wmark->xsize;
656 int my = wmark->ysize;
657
658 for(vx=0;vx<mx;vx++) for(vy=0;vy<my;vy++) {
02d1d628
AMH
659
660 i_gpix(im, tx+vx, ty+vy,&val );
661 i_gpix(wmark, vx, vy, &wval);
662
663 for(ch=0;ch<im->channels;ch++)
664 val.channel[ch] = saturate( val.channel[ch] + (pixdiff* (wval.channel[0]-128) )/128 );
665
666 i_ppix(im,tx+vx,ty+vy,&val);
667 }
668}
669
670
671/*
672=item i_autolevels(im, lsat, usat, skew)
673
674Scales and translates each color such that it fills the range completely.
675Skew is not implemented yet - purpose is to control the color skew that can
676occur when changing the contrast.
677
678 im - target image
679 lsat - fraction of pixels that will be truncated at the lower end of the spectrum
680 usat - fraction of pixels that will be truncated at the higher end of the spectrum
681 skew - not used yet
682
683=cut
684*/
685
686void
687i_autolevels(i_img *im, float lsat, float usat, float skew) {
688 i_color val;
689 int i, x, y, rhist[256], ghist[256], bhist[256];
690 int rsum, rmin, rmax;
691 int gsum, gmin, gmax;
692 int bsum, bmin, bmax;
693 int rcl, rcu, gcl, gcu, bcl, bcu;
694
695 mm_log((1,"i_autolevels(im %p, lsat %f,usat %f,skew %f)\n", im, lsat,usat,skew));
696
697 rsum=gsum=bsum=0;
698 for(i=0;i<256;i++) rhist[i]=ghist[i]=bhist[i] = 0;
699 /* create histogram for each channel */
700 for(y = 0; y < im->ysize; y++) for(x = 0; x < im->xsize; x++) {
701 i_gpix(im, x, y, &val);
702 rhist[val.channel[0]]++;
703 ghist[val.channel[1]]++;
704 bhist[val.channel[2]]++;
705 }
706
707 for(i=0;i<256;i++) {
708 rsum+=rhist[i];
709 gsum+=ghist[i];
710 bsum+=bhist[i];
711 }
712
713 rmin = gmin = bmin = 0;
714 rmax = gmax = bmax = 255;
715
716 rcu = rcl = gcu = gcl = bcu = bcl = 0;
717
718 for(i=0; i<256; i++) {
719 rcl += rhist[i]; if ( (rcl<rsum*lsat) ) rmin=i;
720 rcu += rhist[255-i]; if ( (rcu<rsum*usat) ) rmax=255-i;
721
722 gcl += ghist[i]; if ( (gcl<gsum*lsat) ) gmin=i;
723 gcu += ghist[255-i]; if ( (gcu<gsum*usat) ) gmax=255-i;
724
725 bcl += bhist[i]; if ( (bcl<bsum*lsat) ) bmin=i;
726 bcu += bhist[255-i]; if ( (bcu<bsum*usat) ) bmax=255-i;
727 }
728
729 for(y = 0; y < im->ysize; y++) for(x = 0; x < im->xsize; x++) {
730 i_gpix(im, x, y, &val);
731 val.channel[0]=saturate((val.channel[0]-rmin)*255/(rmax-rmin));
732 val.channel[1]=saturate((val.channel[1]-gmin)*255/(gmax-gmin));
733 val.channel[2]=saturate((val.channel[2]-bmin)*255/(bmax-bmin));
734 i_ppix(im, x, y, &val);
735 }
736}
737
738/*
739=item Noise(x,y)
740
741Pseudo noise utility function used to generate perlin noise. (internal)
742
743 x - x coordinate
744 y - y coordinate
745
746=cut
747*/
748
749static
750float
751Noise(int x, int y) {
752 int n = x + y * 57;
753 n = (n<<13) ^ n;
754 return ( 1.0 - ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0);
755}
756
757/*
758=item SmoothedNoise1(x,y)
759
760Pseudo noise utility function used to generate perlin noise. (internal)
761
762 x - x coordinate
763 y - y coordinate
764
765=cut
766*/
767
768static
769float
770SmoothedNoise1(float x, float y) {
771 float corners = ( Noise(x-1, y-1)+Noise(x+1, y-1)+Noise(x-1, y+1)+Noise(x+1, y+1) ) / 16;
772 float sides = ( Noise(x-1, y) +Noise(x+1, y) +Noise(x, y-1) +Noise(x, y+1) ) / 8;
773 float center = Noise(x, y) / 4;
774 return corners + sides + center;
775}
776
777
778/*
779=item G_Interpolate(a, b, x)
780
781Utility function used to generate perlin noise. (internal)
782
783=cut
784*/
785
786static
787float C_Interpolate(float a, float b, float x) {
788 /* float ft = x * 3.1415927; */
789 float ft = x * PI;
790 float f = (1 - cos(ft)) * .5;
791 return a*(1-f) + b*f;
792}
793
794
795/*
796=item InterpolatedNoise(x, y)
797
798Utility function used to generate perlin noise. (internal)
799
800=cut
801*/
802
803static
804float
805InterpolatedNoise(float x, float y) {
806
807 int integer_X = x;
808 float fractional_X = x - integer_X;
809 int integer_Y = y;
810 float fractional_Y = y - integer_Y;
811
812 float v1 = SmoothedNoise1(integer_X, integer_Y);
813 float v2 = SmoothedNoise1(integer_X + 1, integer_Y);
814 float v3 = SmoothedNoise1(integer_X, integer_Y + 1);
815 float v4 = SmoothedNoise1(integer_X + 1, integer_Y + 1);
816
817 float i1 = C_Interpolate(v1 , v2 , fractional_X);
818 float i2 = C_Interpolate(v3 , v4 , fractional_X);
819
820 return C_Interpolate(i1 , i2 , fractional_Y);
821}
822
823
824
825/*
826=item PerlinNoise_2D(x, y)
827
828Utility function used to generate perlin noise. (internal)
829
830=cut
831*/
832
833static
834float
835PerlinNoise_2D(float x, float y) {
836 int i,frequency;
837 float amplitude;
838 float total = 0;
839 int Number_Of_Octaves=6;
840 int n = Number_Of_Octaves - 1;
841
842 for(i=0;i<n;i++) {
843 frequency = 2*i;
844 amplitude = PI;
845 total = total + InterpolatedNoise(x * frequency, y * frequency) * amplitude;
846 }
847
848 return total;
849}
850
851
852/*
853=item i_radnoise(im, xo, yo, rscale, ascale)
854
855Perlin-like radial noise.
856
857 im - target image
858 xo - x coordinate of center
859 yo - y coordinate of center
860 rscale - radial scale
861 ascale - angular scale
862
863=cut
864*/
865
866void
867i_radnoise(i_img *im, int xo, int yo, float rscale, float ascale) {
868 int x, y, ch;
869 i_color val;
870 unsigned char v;
871 float xc, yc, r;
872 double a;
873
874 for(y = 0; y < im->ysize; y++) for(x = 0; x < im->xsize; x++) {
875 xc = (float)x-xo+0.5;
876 yc = (float)y-yo+0.5;
877 r = rscale*sqrt(xc*xc+yc*yc)+1.2;
878 a = (PI+atan2(yc,xc))*ascale;
879 v = saturate(128+100*(PerlinNoise_2D(a,r)));
880 /* v=saturate(120+12*PerlinNoise_2D(xo+(float)x/scale,yo+(float)y/scale)); Good soft marble */
881 for(ch=0; ch<im->channels; ch++) val.channel[ch]=v;
882 i_ppix(im, x, y, &val);
883 }
884}
885
886
887/*
888=item i_turbnoise(im, xo, yo, scale)
889
890Perlin-like 2d noise noise.
891
892 im - target image
893 xo - x coordinate translation
894 yo - y coordinate translation
895 scale - scale of noise
896
897=cut
898*/
899
900void
901i_turbnoise(i_img *im, float xo, float yo, float scale) {
902 int x,y,ch;
903 unsigned char v;
904 i_color val;
905
906 for(y = 0; y < im->ysize; y++) for(x = 0; x < im->xsize; x++) {
907 /* v=saturate(125*(1.0+PerlinNoise_2D(xo+(float)x/scale,yo+(float)y/scale))); */
908 v = saturate(120*(1.0+sin(xo+(float)x/scale+PerlinNoise_2D(xo+(float)x/scale,yo+(float)y/scale))));
909 for(ch=0; ch<im->channels; ch++) val.channel[ch] = v;
910 i_ppix(im, x, y, &val);
911 }
912}
913
914
915
916/*
917=item i_gradgen(im, num, xo, yo, ival, dmeasure)
918
919Gradient generating function.
920
921 im - target image
922 num - number of points given
923 xo - array of x coordinates
924 yo - array of y coordinates
925 ival - array of i_color objects
926 dmeasure - distance measure to be used.
927 0 = Euclidean
928 1 = Euclidean squared
929 2 = Manhattan distance
930
931=cut
932*/
933
934
935void
936i_gradgen(i_img *im, int num, int *xo, int *yo, i_color *ival, int dmeasure) {
937
938 i_color val;
939 int p, x, y, ch;
940 int channels = im->channels;
941 int xsize = im->xsize;
942 int ysize = im->ysize;
f0960b14 943 int bytes;
02d1d628
AMH
944
945 float *fdist;
946
947 mm_log((1,"i_gradgen(im %p, num %d, xo %p, yo %p, ival %p, dmeasure %d)\n", im, num, xo, yo, ival, dmeasure));
948
949 for(p = 0; p<num; p++) {
950 mm_log((1,"i_gradgen: (%d, %d)\n", xo[p], yo[p]));
951 ICL_info(&ival[p]);
952 }
953
f0960b14
TC
954 /* on the systems I have sizeof(float) == sizeof(int) and thus
955 this would be same size as the arrays xo and yo point at, but this
956 may not be true for other systems
957
958 since the arrays here are caller controlled, I assume that on
959 overflow is a programming error rather than an end-user error, so
960 calling exit() is justified.
961 */
962 bytes = sizeof(float) * num;
963 if (bytes / num != sizeof(float)) {
964 fprintf(stderr, "integer overflow calculating memory allocation");
965 exit(1);
966 }
967 fdist = mymalloc( bytes ); /* checked 14jul05 tonyc */
02d1d628
AMH
968
969 for(y = 0; y<ysize; y++) for(x = 0; x<xsize; x++) {
970 float cs = 0;
971 float csd = 0;
972 for(p = 0; p<num; p++) {
973 int xd = x-xo[p];
974 int yd = y-yo[p];
975 switch (dmeasure) {
976 case 0: /* euclidean */
977 fdist[p] = sqrt(xd*xd + yd*yd); /* euclidean distance */
978 break;
979 case 1: /* euclidean squared */
980 fdist[p] = xd*xd + yd*yd; /* euclidean distance */
981 break;
982 case 2: /* euclidean squared */
b33c08f8 983 fdist[p] = i_max(xd*xd, yd*yd); /* manhattan distance */
02d1d628
AMH
984 break;
985 default:
986 m_fatal(3,"i_gradgen: Unknown distance measure\n");
987 }
988 cs += fdist[p];
989 }
990
991 csd = 1/((num-1)*cs);
992
993 for(p = 0; p<num; p++) fdist[p] = (cs-fdist[p])*csd;
994
995 for(ch = 0; ch<channels; ch++) {
996 int tres = 0;
997 for(p = 0; p<num; p++) tres += ival[p].channel[ch] * fdist[p];
998 val.channel[ch] = saturate(tres);
999 }
1000 i_ppix(im, x, y, &val);
1001 }
a73aeb5f 1002 myfree(fdist);
02d1d628
AMH
1003
1004}
1005
02d1d628
AMH
1006void
1007i_nearest_color_foo(i_img *im, int num, int *xo, int *yo, i_color *ival, int dmeasure) {
1008
a743c0a6 1009 int p, x, y;
02d1d628
AMH
1010 int xsize = im->xsize;
1011 int ysize = im->ysize;
1012
1013 mm_log((1,"i_gradgen(im %p, num %d, xo %p, yo %p, ival %p, dmeasure %d)\n", im, num, xo, yo, ival, dmeasure));
1014
1015 for(p = 0; p<num; p++) {
1016 mm_log((1,"i_gradgen: (%d, %d)\n", xo[p], yo[p]));
1017 ICL_info(&ival[p]);
1018 }
1019
1020 for(y = 0; y<ysize; y++) for(x = 0; x<xsize; x++) {
1021 int midx = 0;
1022 float mindist = 0;
1023 float curdist = 0;
1024
1025 int xd = x-xo[0];
1026 int yd = y-yo[0];
1027
1028 switch (dmeasure) {
1029 case 0: /* euclidean */
1030 mindist = sqrt(xd*xd + yd*yd); /* euclidean distance */
1031 break;
1032 case 1: /* euclidean squared */
1033 mindist = xd*xd + yd*yd; /* euclidean distance */
1034 break;
1035 case 2: /* euclidean squared */
b33c08f8 1036 mindist = i_max(xd*xd, yd*yd); /* manhattan distance */
02d1d628
AMH
1037 break;
1038 default:
1039 m_fatal(3,"i_nearest_color: Unknown distance measure\n");
1040 }
1041
1042 for(p = 1; p<num; p++) {
1043 xd = x-xo[p];
1044 yd = y-yo[p];
1045 switch (dmeasure) {
1046 case 0: /* euclidean */
1047 curdist = sqrt(xd*xd + yd*yd); /* euclidean distance */
1048 break;
1049 case 1: /* euclidean squared */
1050 curdist = xd*xd + yd*yd; /* euclidean distance */
1051 break;
1052 case 2: /* euclidean squared */
b33c08f8 1053 curdist = i_max(xd*xd, yd*yd); /* manhattan distance */
02d1d628
AMH
1054 break;
1055 default:
1056 m_fatal(3,"i_nearest_color: Unknown distance measure\n");
1057 }
1058 if (curdist < mindist) {
1059 mindist = curdist;
1060 midx = p;
1061 }
1062 }
1063 i_ppix(im, x, y, &ival[midx]);
1064 }
1065}
1066
f0960b14
TC
1067/*
1068=item i_nearest_color(im, num, xo, yo, oval, dmeasure)
1069
1070This wasn't document - quoth Addi:
1071
1072 An arty type of filter
1073
1074FIXME: check IRC logs for actual text.
1075
1076Inputs:
1077
1078=over
1079
1080=item *
1081
1082i_img *im - image to render on.
1083
1084=item *
1085
1086int num - number of points/colors in xo, yo, oval
1087
1088=item *
1089
1090int *xo - array of I<num> x positions
1091
1092=item *
1093
1094int *yo - array of I<num> y positions
1095
1096=item *
1097
1098i_color *oval - array of I<num> colors
1099
1100xo, yo, oval correspond to each other, the point xo[i], yo[i] has a
1101color something like oval[i], at least closer to that color than other
1102points.
1103
1104=item *
1105
1106int dmeasure - how we measure the distance from some point P(x,y) to
1107any (xo[i], yo[i]).
1108
1109Valid values are:
1110
1111=over
1112
1113=item 0
1114
1115euclidean distance: sqrt((x2-x1)**2 + (y2-y1)**2)
1116
1117=item 1
1118
1119square of euclidean distance: ((x2-x1)**2 + (y2-y1)**2)
1120
1121=item 2
1122
1123manhattan distance: max((y2-y1)**2, (x2-x1)**2)
1124
1125=back
1126
1127An invalid value causes an error exit (the program is aborted).
1128
1129=back
1130
1131=cut
1132 */
02d1d628
AMH
1133void
1134i_nearest_color(i_img *im, int num, int *xo, int *yo, i_color *oval, int dmeasure) {
1135 i_color *ival;
1136 float *tval;
1137 float c1, c2;
1138 i_color val;
1139 int p, x, y, ch;
02d1d628
AMH
1140 int xsize = im->xsize;
1141 int ysize = im->ysize;
1142 int *cmatch;
1143
f0960b14 1144 mm_log((1,"i_nearest_color(im %p, num %d, xo %p, yo %p, oval %p, dmeasure %d)\n", im, num, xo, yo, oval, dmeasure));
02d1d628
AMH
1145
1146 tval = mymalloc( sizeof(float)*num*im->channels );
1147 ival = mymalloc( sizeof(i_color)*num );
1148 cmatch = mymalloc( sizeof(int)*num );
1149
1150 for(p = 0; p<num; p++) {
1151 for(ch = 0; ch<im->channels; ch++) tval[ p * im->channels + ch] = 0;
1152 cmatch[p] = 0;
1153 }
1154
1155
1156 for(y = 0; y<ysize; y++) for(x = 0; x<xsize; x++) {
1157 int midx = 0;
1158 float mindist = 0;
1159 float curdist = 0;
1160
1161 int xd = x-xo[0];
1162 int yd = y-yo[0];
1163
1164 switch (dmeasure) {
1165 case 0: /* euclidean */
1166 mindist = sqrt(xd*xd + yd*yd); /* euclidean distance */
1167 break;
1168 case 1: /* euclidean squared */
1169 mindist = xd*xd + yd*yd; /* euclidean distance */
1170 break;
1171 case 2: /* euclidean squared */
b33c08f8 1172 mindist = i_max(xd*xd, yd*yd); /* manhattan distance */
02d1d628
AMH
1173 break;
1174 default:
1175 m_fatal(3,"i_nearest_color: Unknown distance measure\n");
1176 }
1177
1178 for(p = 1; p<num; p++) {
1179 xd = x-xo[p];
1180 yd = y-yo[p];
1181 switch (dmeasure) {
1182 case 0: /* euclidean */
1183 curdist = sqrt(xd*xd + yd*yd); /* euclidean distance */
1184 break;
1185 case 1: /* euclidean squared */
1186 curdist = xd*xd + yd*yd; /* euclidean distance */
1187 break;
1188 case 2: /* euclidean squared */
b33c08f8 1189 curdist = i_max(xd*xd, yd*yd); /* manhattan distance */
02d1d628
AMH
1190 break;
1191 default:
1192 m_fatal(3,"i_nearest_color: Unknown distance measure\n");
1193 }
1194 if (curdist < mindist) {
1195 mindist = curdist;
1196 midx = p;
1197 }
1198 }
1199
1200 cmatch[midx]++;
1201 i_gpix(im, x, y, &val);
1202 c2 = 1.0/(float)(cmatch[midx]);
1203 c1 = 1.0-c2;
1204
02d1d628 1205 for(ch = 0; ch<im->channels; ch++)
f0960b14
TC
1206 tval[midx*im->channels + ch] =
1207 c1*tval[midx*im->channels + ch] + c2 * (float) val.channel[ch];
3bb1c1f3 1208
02d1d628
AMH
1209 }
1210
f0960b14
TC
1211 for(p = 0; p<num; p++) for(ch = 0; ch<im->channels; ch++)
1212 ival[p].channel[ch] = tval[p*im->channels + ch];
02d1d628
AMH
1213
1214 i_nearest_color_foo(im, num, xo, yo, ival, dmeasure);
1215}
6607600c 1216
b6381851
TC
1217/*
1218=item i_unsharp_mask(im, stddev, scale)
1219
1220Perform an usharp mask, which is defined as subtracting the blurred
1221image from double the original.
1222
1223=cut
1224*/
1225void i_unsharp_mask(i_img *im, double stddev, double scale) {
92bda632 1226 i_img *copy;
b6381851
TC
1227 int x, y, ch;
1228
1229 if (scale < 0)
1230 return;
1231 /* it really shouldn't ever be more than 1.0, but maybe ... */
1232 if (scale > 100)
1233 scale = 100;
1234
92bda632
TC
1235 copy = i_copy(im);
1236 i_gaussian(copy, stddev);
b6381851
TC
1237 if (im->bits == i_8_bits) {
1238 i_color *blur = mymalloc(im->xsize * sizeof(i_color) * 2);
1239 i_color *out = blur + im->xsize;
1240
1241 for (y = 0; y < im->ysize; ++y) {
92bda632 1242 i_glin(copy, 0, copy->xsize, y, blur);
b6381851
TC
1243 i_glin(im, 0, im->xsize, y, out);
1244 for (x = 0; x < im->xsize; ++x) {
1245 for (ch = 0; ch < im->channels; ++ch) {
1246 /*int temp = out[x].channel[ch] +
1247 scale * (out[x].channel[ch] - blur[x].channel[ch]);*/
1248 int temp = out[x].channel[ch] * 2 - blur[x].channel[ch];
1249 if (temp < 0)
1250 temp = 0;
1251 else if (temp > 255)
1252 temp = 255;
1253 out[x].channel[ch] = temp;
1254 }
1255 }
1256 i_plin(im, 0, im->xsize, y, out);
1257 }
1258
1259 myfree(blur);
1260 }
1261 else {
1262 i_fcolor *blur = mymalloc(im->xsize * sizeof(i_fcolor) * 2);
1263 i_fcolor *out = blur + im->xsize;
1264
1265 for (y = 0; y < im->ysize; ++y) {
92bda632 1266 i_glinf(copy, 0, copy->xsize, y, blur);
b6381851
TC
1267 i_glinf(im, 0, im->xsize, y, out);
1268 for (x = 0; x < im->xsize; ++x) {
1269 for (ch = 0; ch < im->channels; ++ch) {
1270 double temp = out[x].channel[ch] +
1271 scale * (out[x].channel[ch] - blur[x].channel[ch]);
1272 if (temp < 0)
1273 temp = 0;
1274 else if (temp > 1.0)
1275 temp = 1.0;
1276 out[x].channel[ch] = temp;
1277 }
1278 }
1279 i_plinf(im, 0, im->xsize, y, out);
1280 }
1281
1282 myfree(blur);
1283 }
92bda632 1284 i_img_destroy(copy);
b6381851
TC
1285}
1286
dff75dee
TC
1287/*
1288=item i_diff_image(im1, im2, mindiff)
1289
1290Creates a new image that is transparent, except where the pixel in im2
1291is different from im1, where it is the pixel from im2.
1292
1293The samples must differ by at least mindiff to be considered different.
1294
1295=cut
1296*/
1297
1298i_img *
1299i_diff_image(i_img *im1, i_img *im2, int mindiff) {
1300 i_img *out;
1301 int outchans, diffchans;
1302 int xsize, ysize;
dff75dee
TC
1303
1304 i_clear_error();
1305 if (im1->channels != im2->channels) {
1306 i_push_error(0, "different number of channels");
1307 return NULL;
1308 }
1309
1310 outchans = diffchans = im1->channels;
1311 if (outchans == 1 || outchans == 3)
1312 ++outchans;
1313
b33c08f8
TC
1314 xsize = i_min(im1->xsize, im2->xsize);
1315 ysize = i_min(im1->ysize, im2->ysize);
dff75dee
TC
1316
1317 out = i_sametype_chans(im1, xsize, ysize, outchans);
1318
1319 if (im1->bits == i_8_bits && im2->bits == i_8_bits) {
1320 i_color *line1 = mymalloc(2 * xsize * sizeof(*line1));
1321 i_color *line2 = line1 + xsize;
1322 i_color empty;
1323 int x, y, ch;
1324
1325 for (ch = 0; ch < MAXCHANNELS; ++ch)
1326 empty.channel[ch] = 0;
1327
1328 for (y = 0; y < ysize; ++y) {
1329 i_glin(im1, 0, xsize, y, line1);
1330 i_glin(im2, 0, xsize, y, line2);
35342ea0
TC
1331 if (outchans != diffchans) {
1332 /* give the output an alpha channel since it doesn't have one */
1333 for (x = 0; x < xsize; ++x)
1334 line2[x].channel[diffchans] = 255;
1335 }
dff75dee
TC
1336 for (x = 0; x < xsize; ++x) {
1337 int diff = 0;
1338 for (ch = 0; ch < diffchans; ++ch) {
1339 if (line1[x].channel[ch] != line2[x].channel[ch]
1340 && abs(line1[x].channel[ch] - line2[x].channel[ch]) > mindiff) {
1341 diff = 1;
1342 break;
1343 }
1344 }
1345 if (!diff)
1346 line2[x] = empty;
1347 }
1348 i_plin(out, 0, xsize, y, line2);
1349 }
1350 myfree(line1);
1351 }
1352 else {
1353 i_fcolor *line1 = mymalloc(2 * xsize * sizeof(*line1));
1354 i_fcolor *line2 = line1 + xsize;
1355 i_fcolor empty;
1356 int x, y, ch;
1357 double dist = mindiff / 255;
1358
1359 for (ch = 0; ch < MAXCHANNELS; ++ch)
1360 empty.channel[ch] = 0;
1361
1362 for (y = 0; y < ysize; ++y) {
1363 i_glinf(im1, 0, xsize, y, line1);
1364 i_glinf(im2, 0, xsize, y, line2);
35342ea0
TC
1365 if (outchans != diffchans) {
1366 /* give the output an alpha channel since it doesn't have one */
1367 for (x = 0; x < xsize; ++x)
1368 line2[x].channel[diffchans] = 1.0;
1369 }
dff75dee
TC
1370 for (x = 0; x < xsize; ++x) {
1371 int diff = 0;
1372 for (ch = 0; ch < diffchans; ++ch) {
1373 if (line1[x].channel[ch] != line2[x].channel[ch]
1374 && abs(line1[x].channel[ch] - line2[x].channel[ch]) > dist) {
1375 diff = 1;
1376 break;
1377 }
1378 }
1379 if (!diff)
1380 line2[x] = empty;
1381 }
1382 i_plinf(out, 0, xsize, y, line2);
1383 }
1384 myfree(line1);
1385 }
1386
1387 return out;
1388}
1389
f1ac5027 1390struct fount_state;
6607600c
TC
1391static double linear_fount_f(double x, double y, struct fount_state *state);
1392static double bilinear_fount_f(double x, double y, struct fount_state *state);
1393static double radial_fount_f(double x, double y, struct fount_state *state);
1394static double square_fount_f(double x, double y, struct fount_state *state);
1395static double revolution_fount_f(double x, double y,
1396 struct fount_state *state);
1397static double conical_fount_f(double x, double y, struct fount_state *state);
1398
1399typedef double (*fount_func)(double, double, struct fount_state *);
1400static fount_func fount_funcs[] =
1401{
1402 linear_fount_f,
1403 bilinear_fount_f,
1404 radial_fount_f,
1405 square_fount_f,
1406 revolution_fount_f,
1407 conical_fount_f,
1408};
1409
1410static double linear_interp(double pos, i_fountain_seg *seg);
1411static double sine_interp(double pos, i_fountain_seg *seg);
1412static double sphereup_interp(double pos, i_fountain_seg *seg);
1413static double spheredown_interp(double pos, i_fountain_seg *seg);
1414typedef double (*fount_interp)(double pos, i_fountain_seg *seg);
1415static fount_interp fount_interps[] =
1416{
1417 linear_interp,
1418 linear_interp,
1419 sine_interp,
1420 sphereup_interp,
1421 spheredown_interp,
1422};
1423
1424static void direct_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg);
1425static void hue_up_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg);
1426static void hue_down_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg);
1427typedef void (*fount_cinterp)(i_fcolor *out, double pos, i_fountain_seg *seg);
1428static fount_cinterp fount_cinterps[] =
1429{
1430 direct_cinterp,
1431 hue_up_cinterp,
1432 hue_down_cinterp,
1433};
1434
1435typedef double (*fount_repeat)(double v);
1436static double fount_r_none(double v);
1437static double fount_r_sawtooth(double v);
1438static double fount_r_triangle(double v);
1439static double fount_r_saw_both(double v);
1440static double fount_r_tri_both(double v);
1441static fount_repeat fount_repeats[] =
1442{
1443 fount_r_none,
1444 fount_r_sawtooth,
1445 fount_r_triangle,
1446 fount_r_saw_both,
1447 fount_r_tri_both,
1448};
1449
f1ac5027
TC
1450static int simple_ssample(i_fcolor *out, double x, double y,
1451 struct fount_state *state);
1452static int random_ssample(i_fcolor *out, double x, double y,
1453 struct fount_state *state);
1454static int circle_ssample(i_fcolor *out, double x, double y,
1455 struct fount_state *state);
1456typedef int (*fount_ssample)(i_fcolor *out, double x, double y,
1457 struct fount_state *state);
6607600c
TC
1458static fount_ssample fount_ssamples[] =
1459{
1460 NULL,
1461 simple_ssample,
1462 random_ssample,
1463 circle_ssample,
1464};
1465
1466static int
f1ac5027
TC
1467fount_getat(i_fcolor *out, double x, double y, struct fount_state *state);
1468
1469/*
1470 Keep state information used by each type of fountain fill
1471*/
1472struct fount_state {
1473 /* precalculated for the equation of the line perpendicular to the line AB */
1474 double lA, lB, lC;
1475 double AB;
1476 double sqrtA2B2;
1477 double mult;
1478 double cos;
1479 double sin;
1480 double theta;
1481 int xa, ya;
1482 void *ssample_data;
1483 fount_func ffunc;
1484 fount_repeat rpfunc;
1485 fount_ssample ssfunc;
1486 double parm;
1487 i_fountain_seg *segs;
1488 int count;
1489};
1490
1491static void
1492fount_init_state(struct fount_state *state, double xa, double ya,
1493 double xb, double yb, i_fountain_type type,
1494 i_fountain_repeat repeat, int combine, int super_sample,
1495 double ssample_param, int count, i_fountain_seg *segs);
1496
1497static void
1498fount_finish_state(struct fount_state *state);
6607600c
TC
1499
1500#define EPSILON (1e-6)
1501
1502/*
1503=item i_fountain(im, xa, ya, xb, yb, type, repeat, combine, super_sample, ssample_param, count, segs)
1504
1505Draws a fountain fill using A(xa, ya) and B(xb, yb) as reference points.
1506
1507I<type> controls how the reference points are used:
1508
1509=over
1510
1511=item i_ft_linear
1512
1513linear, where A is 0 and B is 1.
1514
1515=item i_ft_bilinear
1516
1517linear in both directions from A.
1518
1519=item i_ft_radial
1520
1521circular, where A is the centre of the fill, and B is a point
1522on the radius.
1523
1524=item i_ft_radial_square
1525
1526where A is the centre of the fill and B is the centre of
1527one side of the square.
1528
1529=item i_ft_revolution
1530
1531where A is the centre of the fill and B defines the 0/1.0
1532angle of the fill.
1533
1534=item i_ft_conical
1535
1536similar to i_ft_revolution, except that the revolution goes in both
1537directions
1538
1539=back
1540
1541I<repeat> can be one of:
1542
1543=over
1544
1545=item i_fr_none
1546
1547values < 0 are treated as zero, values > 1 are treated as 1.
1548
1549=item i_fr_sawtooth
1550
1551negative values are treated as 0, positive values are modulo 1.0
1552
1553=item i_fr_triangle
1554
1555negative values are treated as zero, if (int)value is odd then the value is treated as 1-(value
1556mod 1.0), otherwise the same as for sawtooth.
1557
1558=item i_fr_saw_both
1559
1560like i_fr_sawtooth, except that the sawtooth pattern repeats into
1561negative values.
1562
1563=item i_fr_tri_both
1564
1565Like i_fr_triangle, except that negative values are handled as their
1566absolute values.
1567
1568=back
1569
1570If combine is non-zero then non-opaque values are combined with the
1571underlying color.
1572
1573I<super_sample> controls super sampling, if any. At some point I'll
1574probably add a adaptive super-sampler. Current possible values are:
1575
1576=over
1577
1578=item i_fts_none
1579
1580No super-sampling is done.
1581
1582=item i_fts_grid
1583
1584A square grid of points withing the pixel are sampled.
1585
1586=item i_fts_random
1587
1588Random points within the pixel are sampled.
1589
1590=item i_fts_circle
1591
1592Points on the radius of a circle are sampled. This produces fairly
1593good results, but is fairly slow since sin() and cos() are evaluated
1594for each point.
1595
1596=back
1597
1598I<ssample_param> is intended to be roughly the number of points
1599sampled within the pixel.
1600
1601I<count> and I<segs> define the segments of the fill.
1602
1603=cut
1604
1605*/
1606
1607void
1608i_fountain(i_img *im, double xa, double ya, double xb, double yb,
1609 i_fountain_type type, i_fountain_repeat repeat,
1610 int combine, int super_sample, double ssample_param,
1611 int count, i_fountain_seg *segs) {
1612 struct fount_state state;
6607600c
TC
1613 int x, y;
1614 i_fcolor *line = mymalloc(sizeof(i_fcolor) * im->xsize);
efdc2568 1615 i_fcolor *work = NULL;
290bdf77 1616
f1ac5027 1617 i_fountain_seg *my_segs;
efdc2568
TC
1618 i_fill_combine_f combine_func = NULL;
1619 i_fill_combinef_f combinef_func = NULL;
1620
1621 i_get_combine(combine, &combine_func, &combinef_func);
1622 if (combinef_func)
1623 work = mymalloc(sizeof(i_fcolor) * im->xsize);
f1ac5027
TC
1624
1625 fount_init_state(&state, xa, ya, xb, yb, type, repeat, combine,
1626 super_sample, ssample_param, count, segs);
1627 my_segs = state.segs;
1628
1629 for (y = 0; y < im->ysize; ++y) {
1630 i_glinf(im, 0, im->xsize, y, line);
1631 for (x = 0; x < im->xsize; ++x) {
1632 i_fcolor c;
1633 int got_one;
f1ac5027
TC
1634 if (super_sample == i_fts_none)
1635 got_one = fount_getat(&c, x, y, &state);
1636 else
1637 got_one = state.ssfunc(&c, x, y, &state);
1638 if (got_one) {
efdc2568
TC
1639 if (combine)
1640 work[x] = c;
f1ac5027
TC
1641 else
1642 line[x] = c;
1643 }
1644 }
efdc2568
TC
1645 if (combine)
1646 combinef_func(line, work, im->channels, im->xsize);
f1ac5027
TC
1647 i_plinf(im, 0, im->xsize, y, line);
1648 }
1649 fount_finish_state(&state);
a73aeb5f 1650 if (work) myfree(work);
f1ac5027
TC
1651 myfree(line);
1652}
1653
1654typedef struct {
1655 i_fill_t base;
1656 struct fount_state state;
1657} i_fill_fountain_t;
1658
1659static void
1660fill_fountf(i_fill_t *fill, int x, int y, int width, int channels,
43c5dacb 1661 i_fcolor *data);
f1ac5027
TC
1662static void
1663fount_fill_destroy(i_fill_t *fill);
1664
1665/*
92bda632
TC
1666=item i_new_fill_fount(xa, ya, xb, yb, type, repeat, combine, super_sample, ssample_param, count, segs)
1667
1668=category Fills
1669=synopsis fill = i_new_fill_fount(0, 0, 100, 100, i_ft_linear, i_ft_linear,
1670=synopsis i_fr_triangle, 0, i_fts_grid, 9, 1, segs);
1671
f1ac5027 1672
773bc121
TC
1673Creates a new general fill which fills with a fountain fill.
1674
f1ac5027
TC
1675=cut
1676*/
1677
1678i_fill_t *
1679i_new_fill_fount(double xa, double ya, double xb, double yb,
1680 i_fountain_type type, i_fountain_repeat repeat,
1681 int combine, int super_sample, double ssample_param,
1682 int count, i_fountain_seg *segs) {
1683 i_fill_fountain_t *fill = mymalloc(sizeof(i_fill_fountain_t));
1684
1685 fill->base.fill_with_color = NULL;
1686 fill->base.fill_with_fcolor = fill_fountf;
1687 fill->base.destroy = fount_fill_destroy;
efdc2568
TC
1688 if (combine)
1689 i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
1690 else {
1691 fill->base.combine = NULL;
1692 fill->base.combinef = NULL;
1693 }
f1ac5027
TC
1694 fount_init_state(&fill->state, xa, ya, xb, yb, type, repeat, combine,
1695 super_sample, ssample_param, count, segs);
1696
1697 return &fill->base;
1698}
1699
1700/*
1701=back
1702
1703=head1 INTERNAL FUNCTIONS
1704
1705=over
1706
1707=item fount_init_state(...)
1708
1709Used by both the fountain fill filter and the fountain fill.
1710
1711=cut
1712*/
1713
1714static void
1715fount_init_state(struct fount_state *state, double xa, double ya,
1716 double xb, double yb, i_fountain_type type,
1717 i_fountain_repeat repeat, int combine, int super_sample,
1718 double ssample_param, int count, i_fountain_seg *segs) {
6607600c
TC
1719 int i, j;
1720 i_fountain_seg *my_segs = mymalloc(sizeof(i_fountain_seg) * count);
f1ac5027 1721 /*int have_alpha = im->channels == 2 || im->channels == 4;*/
f1ac5027
TC
1722
1723 memset(state, 0, sizeof(*state));
6607600c
TC
1724 /* we keep a local copy that we can adjust for speed */
1725 for (i = 0; i < count; ++i) {
1726 i_fountain_seg *seg = my_segs + i;
1727
1728 *seg = segs[i];
4c033fd4
TC
1729 if (seg->type < 0 || seg->type >= i_fst_end)
1730 seg->type = i_fst_linear;
6607600c
TC
1731 if (seg->color < 0 || seg->color >= i_fc_end)
1732 seg->color = i_fc_direct;
1733 if (seg->color == i_fc_hue_up || seg->color == i_fc_hue_down) {
1734 /* so we don't have to translate to HSV on each request, do it here */
1735 for (j = 0; j < 2; ++j) {
1736 i_rgb_to_hsvf(seg->c+j);
1737 }
1738 if (seg->color == i_fc_hue_up) {
1739 if (seg->c[1].channel[0] <= seg->c[0].channel[0])
1740 seg->c[1].channel[0] += 1.0;
1741 }
1742 else {
1743 if (seg->c[0].channel[0] <= seg->c[0].channel[1])
1744 seg->c[0].channel[0] += 1.0;
1745 }
1746 }
1747 /*printf("start %g mid %g end %g c0(%g,%g,%g,%g) c1(%g,%g,%g,%g) type %d color %d\n",
1748 seg->start, seg->middle, seg->end, seg->c[0].channel[0],
1749 seg->c[0].channel[1], seg->c[0].channel[2], seg->c[0].channel[3],
1750 seg->c[1].channel[0], seg->c[1].channel[1], seg->c[1].channel[2],
1751 seg->c[1].channel[3], seg->type, seg->color);*/
1752
1753 }
1754
1755 /* initialize each engine */
1756 /* these are so common ... */
f1ac5027
TC
1757 state->lA = xb - xa;
1758 state->lB = yb - ya;
1759 state->AB = sqrt(state->lA * state->lA + state->lB * state->lB);
1760 state->xa = xa;
1761 state->ya = ya;
6607600c
TC
1762 switch (type) {
1763 default:
1764 type = i_ft_linear; /* make the invalid value valid */
1765 case i_ft_linear:
1766 case i_ft_bilinear:
f1ac5027
TC
1767 state->lC = ya * ya - ya * yb + xa * xa - xa * xb;
1768 state->mult = 1;
1769 state->mult = 1/linear_fount_f(xb, yb, state);
6607600c
TC
1770 break;
1771
1772 case i_ft_radial:
f1ac5027
TC
1773 state->mult = 1.0 / sqrt((double)(xb-xa)*(xb-xa)
1774 + (double)(yb-ya)*(yb-ya));
6607600c
TC
1775 break;
1776
1777 case i_ft_radial_square:
f1ac5027
TC
1778 state->cos = state->lA / state->AB;
1779 state->sin = state->lB / state->AB;
1780 state->mult = 1.0 / state->AB;
6607600c
TC
1781 break;
1782
1783 case i_ft_revolution:
f1ac5027
TC
1784 state->theta = atan2(yb-ya, xb-xa);
1785 state->mult = 1.0 / (PI * 2);
6607600c
TC
1786 break;
1787
1788 case i_ft_conical:
f1ac5027
TC
1789 state->theta = atan2(yb-ya, xb-xa);
1790 state->mult = 1.0 / PI;
6607600c
TC
1791 break;
1792 }
f1ac5027 1793 state->ffunc = fount_funcs[type];
6607600c 1794 if (super_sample < 0
290bdf77 1795 || super_sample >= (int)(sizeof(fount_ssamples)/sizeof(*fount_ssamples))) {
6607600c
TC
1796 super_sample = 0;
1797 }
f1ac5027 1798 state->ssample_data = NULL;
6607600c
TC
1799 switch (super_sample) {
1800 case i_fts_grid:
1801 ssample_param = floor(0.5 + sqrt(ssample_param));
f1ac5027 1802 state->ssample_data = mymalloc(sizeof(i_fcolor) * ssample_param * ssample_param);
6607600c
TC
1803 break;
1804
1805 case i_fts_random:
1806 case i_fts_circle:
1807 ssample_param = floor(0.5+ssample_param);
f1ac5027 1808 state->ssample_data = mymalloc(sizeof(i_fcolor) * ssample_param);
6607600c
TC
1809 break;
1810 }
f1ac5027
TC
1811 state->parm = ssample_param;
1812 state->ssfunc = fount_ssamples[super_sample];
6607600c
TC
1813 if (repeat < 0 || repeat >= (sizeof(fount_repeats)/sizeof(*fount_repeats)))
1814 repeat = 0;
f1ac5027
TC
1815 state->rpfunc = fount_repeats[repeat];
1816 state->segs = my_segs;
1817 state->count = count;
6607600c
TC
1818}
1819
f1ac5027
TC
1820static void
1821fount_finish_state(struct fount_state *state) {
1822 if (state->ssample_data)
1823 myfree(state->ssample_data);
1824 myfree(state->segs);
1825}
6607600c 1826
6607600c 1827
f1ac5027 1828/*
6607600c
TC
1829=item fount_getat(out, x, y, ffunc, rpfunc, state, segs, count)
1830
1831Evaluates the fountain fill at the given point.
1832
1833This is called by both the non-super-sampling and super-sampling code.
1834
1835You might think that it would make sense to sample the fill parameter
1836instead, and combine those, but this breaks badly.
1837
1838=cut
1839*/
1840
1841static int
f1ac5027
TC
1842fount_getat(i_fcolor *out, double x, double y, struct fount_state *state) {
1843 double v = (state->rpfunc)((state->ffunc)(x, y, state));
6607600c
TC
1844 int i;
1845
1846 i = 0;
f1ac5027
TC
1847 while (i < state->count
1848 && (v < state->segs[i].start || v > state->segs[i].end)) {
6607600c
TC
1849 ++i;
1850 }
f1ac5027
TC
1851 if (i < state->count) {
1852 v = (fount_interps[state->segs[i].type])(v, state->segs+i);
1853 (fount_cinterps[state->segs[i].color])(out, v, state->segs+i);
6607600c
TC
1854 return 1;
1855 }
1856 else
1857 return 0;
1858}
1859
1860/*
1861=item linear_fount_f(x, y, state)
1862
1863Calculate the fill parameter for a linear fountain fill.
1864
1865Uses the point to line distance function, with some precalculation
1866done in i_fountain().
1867
1868=cut
1869*/
1870static double
1871linear_fount_f(double x, double y, struct fount_state *state) {
1872 return (state->lA * x + state->lB * y + state->lC) / state->AB * state->mult;
1873}
1874
1875/*
1876=item bilinear_fount_f(x, y, state)
1877
1878Calculate the fill parameter for a bi-linear fountain fill.
1879
1880=cut
1881*/
1882static double
1883bilinear_fount_f(double x, double y, struct fount_state *state) {
1884 return fabs((state->lA * x + state->lB * y + state->lC) / state->AB * state->mult);
1885}
1886
1887/*
1888=item radial_fount_f(x, y, state)
1889
1890Calculate the fill parameter for a radial fountain fill.
1891
1892Simply uses the distance function.
1893
1894=cut
1895 */
1896static double
1897radial_fount_f(double x, double y, struct fount_state *state) {
1898 return sqrt((double)(state->xa-x)*(state->xa-x)
1899 + (double)(state->ya-y)*(state->ya-y)) * state->mult;
1900}
1901
1902/*
1903=item square_fount_f(x, y, state)
1904
1905Calculate the fill parameter for a square fountain fill.
1906
1907Works by rotating the reference co-ordinate around the centre of the
1908square.
1909
1910=cut
1911*/
1912static double
1913square_fount_f(double x, double y, struct fount_state *state) {
1914 int xc, yc; /* centred on A */
1915 double xt, yt; /* rotated by theta */
1916 xc = x - state->xa;
1917 yc = y - state->ya;
1918 xt = fabs(xc * state->cos + yc * state->sin);
1919 yt = fabs(-xc * state->sin + yc * state->cos);
1920 return (xt > yt ? xt : yt) * state->mult;
1921}
1922
1923/*
1924=item revolution_fount_f(x, y, state)
1925
1926Calculates the fill parameter for the revolution fountain fill.
1927
1928=cut
1929*/
1930static double
1931revolution_fount_f(double x, double y, struct fount_state *state) {
1932 double angle = atan2(y - state->ya, x - state->xa);
1933
1934 angle -= state->theta;
1935 if (angle < 0) {
1936 angle = fmod(angle+ PI * 4, PI*2);
1937 }
1938
1939 return angle * state->mult;
1940}
1941
1942/*
1943=item conical_fount_f(x, y, state)
1944
1945Calculates the fill parameter for the conical fountain fill.
1946
1947=cut
1948*/
1949static double
1950conical_fount_f(double x, double y, struct fount_state *state) {
1951 double angle = atan2(y - state->ya, x - state->xa);
1952
1953 angle -= state->theta;
1954 if (angle < -PI)
1955 angle += PI * 2;
1956 else if (angle > PI)
1957 angle -= PI * 2;
1958
1959 return fabs(angle) * state->mult;
1960}
1961
1962/*
1963=item linear_interp(pos, seg)
1964
1965Calculates linear interpolation on the fill parameter. Breaks the
1966segment into 2 regions based in the I<middle> value.
1967
1968=cut
1969*/
1970static double
1971linear_interp(double pos, i_fountain_seg *seg) {
1972 if (pos < seg->middle) {
1973 double len = seg->middle - seg->start;
1974 if (len < EPSILON)
1975 return 0.0;
1976 else
1977 return (pos - seg->start) / len / 2;
1978 }
1979 else {
1980 double len = seg->end - seg->middle;
1981 if (len < EPSILON)
1982 return 1.0;
1983 else
1984 return 0.5 + (pos - seg->middle) / len / 2;
1985 }
1986}
1987
1988/*
1989=item sine_interp(pos, seg)
1990
1991Calculates sine function interpolation on the fill parameter.
1992
1993=cut
1994*/
1995static double
1996sine_interp(double pos, i_fountain_seg *seg) {
1997 /* I wonder if there's a simple way to smooth the transition for this */
1998 double work = linear_interp(pos, seg);
1999
2000 return (1-cos(work * PI))/2;
2001}
2002
2003/*
2004=item sphereup_interp(pos, seg)
2005
2006Calculates spherical interpolation on the fill parameter, with the cusp
2007at the low-end.
2008
2009=cut
2010*/
2011static double
2012sphereup_interp(double pos, i_fountain_seg *seg) {
2013 double work = linear_interp(pos, seg);
2014
2015 return sqrt(1.0 - (1-work) * (1-work));
2016}
2017
2018/*
2019=item spheredown_interp(pos, seg)
2020
2021Calculates spherical interpolation on the fill parameter, with the cusp
2022at the high-end.
2023
2024=cut
2025*/
2026static double
2027spheredown_interp(double pos, i_fountain_seg *seg) {
2028 double work = linear_interp(pos, seg);
2029
2030 return 1-sqrt(1.0 - work * work);
2031}
2032
2033/*
2034=item direct_cinterp(out, pos, seg)
2035
2036Calculates the fountain color based on direct scaling of the channels
2037of the color channels.
2038
2039=cut
2040*/
2041static void
2042direct_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg) {
2043 int ch;
2044 for (ch = 0; ch < MAXCHANNELS; ++ch) {
2045 out->channel[ch] = seg->c[0].channel[ch] * (1 - pos)
2046 + seg->c[1].channel[ch] * pos;
2047 }
2048}
2049
2050/*
2051=item hue_up_cinterp(put, pos, seg)
2052
2053Calculates the fountain color based on scaling a HSV value. The hue
2054increases as the fill parameter increases.
2055
2056=cut
2057*/
2058static void
2059hue_up_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg) {
2060 int ch;
2061 for (ch = 0; ch < MAXCHANNELS; ++ch) {
2062 out->channel[ch] = seg->c[0].channel[ch] * (1 - pos)
2063 + seg->c[1].channel[ch] * pos;
2064 }
2065 i_hsv_to_rgbf(out);
2066}
2067
2068/*
2069=item hue_down_cinterp(put, pos, seg)
2070
2071Calculates the fountain color based on scaling a HSV value. The hue
2072decreases as the fill parameter increases.
2073
2074=cut
2075*/
2076static void
2077hue_down_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg) {
2078 int ch;
2079 for (ch = 0; ch < MAXCHANNELS; ++ch) {
2080 out->channel[ch] = seg->c[0].channel[ch] * (1 - pos)
2081 + seg->c[1].channel[ch] * pos;
2082 }
2083 i_hsv_to_rgbf(out);
2084}
2085
2086/*
2087=item simple_ssample(out, parm, x, y, state, ffunc, rpfunc, segs, count)
2088
2089Simple grid-based super-sampling.
2090
2091=cut
2092*/
2093static int
f1ac5027 2094simple_ssample(i_fcolor *out, double x, double y, struct fount_state *state) {
6607600c
TC
2095 i_fcolor *work = state->ssample_data;
2096 int dx, dy;
f1ac5027 2097 int grid = state->parm;
6607600c
TC
2098 double base = -0.5 + 0.5 / grid;
2099 double step = 1.0 / grid;
2100 int ch, i;
2101 int samp_count = 0;
2102
2103 for (dx = 0; dx < grid; ++dx) {
2104 for (dy = 0; dy < grid; ++dy) {
2105 if (fount_getat(work+samp_count, x + base + step * dx,
f1ac5027 2106 y + base + step * dy, state)) {
6607600c
TC
2107 ++samp_count;
2108 }
2109 }
2110 }
2111 for (ch = 0; ch < MAXCHANNELS; ++ch) {
2112 out->channel[ch] = 0;
2113 for (i = 0; i < samp_count; ++i) {
2114 out->channel[ch] += work[i].channel[ch];
2115 }
2116 /* we divide by 4 rather than samp_count since if there's only one valid
2117 sample it should be mostly transparent */
2118 out->channel[ch] /= grid * grid;
2119 }
2120 return samp_count;
2121}
2122
2123/*
2124=item random_ssample(out, parm, x, y, state, ffunc, rpfunc, segs, count)
2125
2126Random super-sampling.
2127
2128=cut
2129*/
2130static int
f1ac5027
TC
2131random_ssample(i_fcolor *out, double x, double y,
2132 struct fount_state *state) {
6607600c
TC
2133 i_fcolor *work = state->ssample_data;
2134 int i, ch;
f1ac5027 2135 int maxsamples = state->parm;
6607600c
TC
2136 double rand_scale = 1.0 / RAND_MAX;
2137 int samp_count = 0;
2138 for (i = 0; i < maxsamples; ++i) {
2139 if (fount_getat(work+samp_count, x - 0.5 + rand() * rand_scale,
f1ac5027 2140 y - 0.5 + rand() * rand_scale, state)) {
6607600c
TC
2141 ++samp_count;
2142 }
2143 }
2144 for (ch = 0; ch < MAXCHANNELS; ++ch) {
2145 out->channel[ch] = 0;
2146 for (i = 0; i < samp_count; ++i) {
2147 out->channel[ch] += work[i].channel[ch];
2148 }
2149 /* we divide by maxsamples rather than samp_count since if there's
2150 only one valid sample it should be mostly transparent */
2151 out->channel[ch] /= maxsamples;
2152 }
2153 return samp_count;
2154}
2155
2156/*
2157=item circle_ssample(out, parm, x, y, state, ffunc, rpfunc, segs, count)
2158
2159Super-sampling around the circumference of a circle.
2160
2161I considered saving the sin()/cos() values and transforming step-size
2162around the circle, but that's inaccurate, though it may not matter
2163much.
2164
2165=cut
2166 */
2167static int
f1ac5027
TC
2168circle_ssample(i_fcolor *out, double x, double y,
2169 struct fount_state *state) {
6607600c
TC
2170 i_fcolor *work = state->ssample_data;
2171 int i, ch;
f1ac5027 2172 int maxsamples = state->parm;
6607600c
TC
2173 double angle = 2 * PI / maxsamples;
2174 double radius = 0.3; /* semi-random */
2175 int samp_count = 0;
2176 for (i = 0; i < maxsamples; ++i) {
2177 if (fount_getat(work+samp_count, x + radius * cos(angle * i),
f1ac5027 2178 y + radius * sin(angle * i), state)) {
6607600c
TC
2179 ++samp_count;
2180 }
2181 }
2182 for (ch = 0; ch < MAXCHANNELS; ++ch) {
2183 out->channel[ch] = 0;
2184 for (i = 0; i < samp_count; ++i) {
2185 out->channel[ch] += work[i].channel[ch];
2186 }
2187 /* we divide by maxsamples rather than samp_count since if there's
2188 only one valid sample it should be mostly transparent */
2189 out->channel[ch] /= maxsamples;
2190 }
2191 return samp_count;
2192}
2193
2194/*
2195=item fount_r_none(v)
2196
2197Implements no repeats. Simply clamps the fill value.
2198
2199=cut
2200*/
2201static double
2202fount_r_none(double v) {
2203 return v < 0 ? 0 : v > 1 ? 1 : v;
2204}
2205
2206/*
2207=item fount_r_sawtooth(v)
2208
2209Implements sawtooth repeats. Clamps negative values and uses fmod()
2210on others.
2211
2212=cut
2213*/
2214static double
2215fount_r_sawtooth(double v) {
2216 return v < 0 ? 0 : fmod(v, 1.0);
2217}
2218
2219/*
2220=item fount_r_triangle(v)
2221
2222Implements triangle repeats. Clamps negative values, uses fmod to get
2223a range 0 through 2 and then adjusts values > 1.
2224
2225=cut
2226*/
2227static double
2228fount_r_triangle(double v) {
2229 if (v < 0)
2230 return 0;
2231 else {
2232 v = fmod(v, 2.0);
2233 return v > 1.0 ? 2.0 - v : v;
2234 }
2235}
2236
2237/*
2238=item fount_r_saw_both(v)
2239
2240Implements sawtooth repeats in the both postive and negative directions.
2241
2242Adjusts the value to be postive and then just uses fmod().
2243
2244=cut
2245*/
2246static double
2247fount_r_saw_both(double v) {
2248 if (v < 0)
2249 v += 1+(int)(-v);
2250 return fmod(v, 1.0);
2251}
2252
2253/*
2254=item fount_r_tri_both(v)
2255
2256Implements triangle repeats in the both postive and negative directions.
2257
2258Uses fmod on the absolute value, and then adjusts values > 1.
2259
2260=cut
2261*/
2262static double
2263fount_r_tri_both(double v) {
2264 v = fmod(fabs(v), 2.0);
2265 return v > 1.0 ? 2.0 - v : v;
2266}
2267
f1ac5027
TC
2268/*
2269=item fill_fountf(fill, x, y, width, channels, data)
2270
2271The fill function for fountain fills.
2272
2273=cut
2274*/
2275static void
2276fill_fountf(i_fill_t *fill, int x, int y, int width, int channels,
43c5dacb 2277 i_fcolor *data) {
f1ac5027 2278 i_fill_fountain_t *f = (i_fill_fountain_t *)fill;
efdc2568 2279
43c5dacb
TC
2280 while (width--) {
2281 i_fcolor c;
2282 int got_one;
2283
2284 if (f->state.ssfunc)
2285 got_one = f->state.ssfunc(&c, x, y, &f->state);
2286 else
2287 got_one = fount_getat(&c, x, y, &f->state);
2288
2289 *data++ = c;
2290
2291 ++x;
f1ac5027
TC
2292 }
2293}
2294
2295/*
2296=item fount_fill_destroy(fill)
2297
2298=cut
2299*/
2300static void
2301fount_fill_destroy(i_fill_t *fill) {
2302 i_fill_fountain_t *f = (i_fill_fountain_t *)fill;
2303 fount_finish_state(&f->state);
2304}
2305
6607600c
TC
2306/*
2307=back
2308
2309=head1 AUTHOR
2310
2311Arnar M. Hrafnkelsson <addi@umich.edu>
2312
2313Tony Cook <tony@develop-help.com> (i_fountain())
2314
2315=head1 SEE ALSO
2316
2317Imager(3)
2318
2319=cut
2320*/