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