Extra ways of calling translate(), shear()
[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
1716/*
92bda632
TC
1717=item i_new_fill_fount(xa, ya, xb, yb, type, repeat, combine, super_sample, ssample_param, count, segs)
1718
1719=category Fills
1720=synopsis fill = i_new_fill_fount(0, 0, 100, 100, i_ft_linear, i_ft_linear,
1721=synopsis i_fr_triangle, 0, i_fts_grid, 9, 1, segs);
1722
f1ac5027 1723
773bc121
TC
1724Creates a new general fill which fills with a fountain fill.
1725
f1ac5027
TC
1726=cut
1727*/
1728
1729i_fill_t *
1730i_new_fill_fount(double xa, double ya, double xb, double yb,
1731 i_fountain_type type, i_fountain_repeat repeat,
1732 int combine, int super_sample, double ssample_param,
1733 int count, i_fountain_seg *segs) {
1734 i_fill_fountain_t *fill = mymalloc(sizeof(i_fill_fountain_t));
1735
1736 fill->base.fill_with_color = NULL;
1737 fill->base.fill_with_fcolor = fill_fountf;
1738 fill->base.destroy = fount_fill_destroy;
efdc2568
TC
1739 if (combine)
1740 i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
1741 else {
1742 fill->base.combine = NULL;
1743 fill->base.combinef = NULL;
1744 }
f1ac5027
TC
1745 fount_init_state(&fill->state, xa, ya, xb, yb, type, repeat, combine,
1746 super_sample, ssample_param, count, segs);
1747
1748 return &fill->base;
1749}
1750
1751/*
1752=back
1753
1754=head1 INTERNAL FUNCTIONS
1755
1756=over
1757
1758=item fount_init_state(...)
1759
1760Used by both the fountain fill filter and the fountain fill.
1761
1762=cut
1763*/
1764
1765static void
1766fount_init_state(struct fount_state *state, double xa, double ya,
1767 double xb, double yb, i_fountain_type type,
1768 i_fountain_repeat repeat, int combine, int super_sample,
1769 double ssample_param, int count, i_fountain_seg *segs) {
6607600c 1770 int i, j;
d3d01aeb
TC
1771 int bytes;
1772 i_fountain_seg *my_segs = mymalloc(sizeof(i_fountain_seg) * count); /* checked 2jul06 - duplicating original */
f1ac5027 1773 /*int have_alpha = im->channels == 2 || im->channels == 4;*/
f1ac5027
TC
1774
1775 memset(state, 0, sizeof(*state));
6607600c
TC
1776 /* we keep a local copy that we can adjust for speed */
1777 for (i = 0; i < count; ++i) {
1778 i_fountain_seg *seg = my_segs + i;
1779
1780 *seg = segs[i];
4c033fd4
TC
1781 if (seg->type < 0 || seg->type >= i_fst_end)
1782 seg->type = i_fst_linear;
6607600c
TC
1783 if (seg->color < 0 || seg->color >= i_fc_end)
1784 seg->color = i_fc_direct;
1785 if (seg->color == i_fc_hue_up || seg->color == i_fc_hue_down) {
1786 /* so we don't have to translate to HSV on each request, do it here */
1787 for (j = 0; j < 2; ++j) {
1788 i_rgb_to_hsvf(seg->c+j);
1789 }
1790 if (seg->color == i_fc_hue_up) {
1791 if (seg->c[1].channel[0] <= seg->c[0].channel[0])
1792 seg->c[1].channel[0] += 1.0;
1793 }
1794 else {
1795 if (seg->c[0].channel[0] <= seg->c[0].channel[1])
1796 seg->c[0].channel[0] += 1.0;
1797 }
1798 }
1799 /*printf("start %g mid %g end %g c0(%g,%g,%g,%g) c1(%g,%g,%g,%g) type %d color %d\n",
1800 seg->start, seg->middle, seg->end, seg->c[0].channel[0],
1801 seg->c[0].channel[1], seg->c[0].channel[2], seg->c[0].channel[3],
1802 seg->c[1].channel[0], seg->c[1].channel[1], seg->c[1].channel[2],
1803 seg->c[1].channel[3], seg->type, seg->color);*/
1804
1805 }
1806
1807 /* initialize each engine */
1808 /* these are so common ... */
f1ac5027
TC
1809 state->lA = xb - xa;
1810 state->lB = yb - ya;
1811 state->AB = sqrt(state->lA * state->lA + state->lB * state->lB);
1812 state->xa = xa;
1813 state->ya = ya;
6607600c
TC
1814 switch (type) {
1815 default:
1816 type = i_ft_linear; /* make the invalid value valid */
1817 case i_ft_linear:
1818 case i_ft_bilinear:
f1ac5027
TC
1819 state->lC = ya * ya - ya * yb + xa * xa - xa * xb;
1820 state->mult = 1;
1821 state->mult = 1/linear_fount_f(xb, yb, state);
6607600c
TC
1822 break;
1823
1824 case i_ft_radial:
f1ac5027
TC
1825 state->mult = 1.0 / sqrt((double)(xb-xa)*(xb-xa)
1826 + (double)(yb-ya)*(yb-ya));
6607600c
TC
1827 break;
1828
1829 case i_ft_radial_square:
f1ac5027
TC
1830 state->cos = state->lA / state->AB;
1831 state->sin = state->lB / state->AB;
1832 state->mult = 1.0 / state->AB;
6607600c
TC
1833 break;
1834
1835 case i_ft_revolution:
f1ac5027
TC
1836 state->theta = atan2(yb-ya, xb-xa);
1837 state->mult = 1.0 / (PI * 2);
6607600c
TC
1838 break;
1839
1840 case i_ft_conical:
f1ac5027
TC
1841 state->theta = atan2(yb-ya, xb-xa);
1842 state->mult = 1.0 / PI;
6607600c
TC
1843 break;
1844 }
f1ac5027 1845 state->ffunc = fount_funcs[type];
6607600c 1846 if (super_sample < 0
290bdf77 1847 || super_sample >= (int)(sizeof(fount_ssamples)/sizeof(*fount_ssamples))) {
6607600c
TC
1848 super_sample = 0;
1849 }
f1ac5027 1850 state->ssample_data = NULL;
6607600c
TC
1851 switch (super_sample) {
1852 case i_fts_grid:
1853 ssample_param = floor(0.5 + sqrt(ssample_param));
d3d01aeb
TC
1854 bytes = ssample_param * ssample_param * sizeof(i_fcolor);
1855 if (bytes / sizeof(i_fcolor) == ssample_param * ssample_param) {
1856 state->ssample_data = mymalloc(sizeof(i_fcolor) * ssample_param * ssample_param); /* checked 1jul06 tonyc */
1857 }
1858 else {
1859 super_sample = i_fts_none;
1860 }
6607600c
TC
1861 break;
1862
1863 case i_fts_random:
1864 case i_fts_circle:
1865 ssample_param = floor(0.5+ssample_param);
d3d01aeb
TC
1866 bytes = sizeof(i_fcolor) * ssample_param;
1867 if (bytes / sizeof(i_fcolor) == ssample_param) {
1868 state->ssample_data = mymalloc(sizeof(i_fcolor) * ssample_param);
1869 }
1870 else {
1871 super_sample = i_fts_none;
1872 }
6607600c
TC
1873 break;
1874 }
f1ac5027
TC
1875 state->parm = ssample_param;
1876 state->ssfunc = fount_ssamples[super_sample];
6607600c
TC
1877 if (repeat < 0 || repeat >= (sizeof(fount_repeats)/sizeof(*fount_repeats)))
1878 repeat = 0;
f1ac5027
TC
1879 state->rpfunc = fount_repeats[repeat];
1880 state->segs = my_segs;
1881 state->count = count;
6607600c
TC
1882}
1883
f1ac5027
TC
1884static void
1885fount_finish_state(struct fount_state *state) {
1886 if (state->ssample_data)
1887 myfree(state->ssample_data);
1888 myfree(state->segs);
1889}
6607600c 1890
6607600c 1891
f1ac5027 1892/*
6607600c
TC
1893=item fount_getat(out, x, y, ffunc, rpfunc, state, segs, count)
1894
1895Evaluates the fountain fill at the given point.
1896
1897This is called by both the non-super-sampling and super-sampling code.
1898
1899You might think that it would make sense to sample the fill parameter
1900instead, and combine those, but this breaks badly.
1901
1902=cut
1903*/
1904
1905static int
f1ac5027
TC
1906fount_getat(i_fcolor *out, double x, double y, struct fount_state *state) {
1907 double v = (state->rpfunc)((state->ffunc)(x, y, state));
6607600c
TC
1908 int i;
1909
1910 i = 0;
f1ac5027
TC
1911 while (i < state->count
1912 && (v < state->segs[i].start || v > state->segs[i].end)) {
6607600c
TC
1913 ++i;
1914 }
f1ac5027
TC
1915 if (i < state->count) {
1916 v = (fount_interps[state->segs[i].type])(v, state->segs+i);
1917 (fount_cinterps[state->segs[i].color])(out, v, state->segs+i);
6607600c
TC
1918 return 1;
1919 }
1920 else
1921 return 0;
1922}
1923
1924/*
1925=item linear_fount_f(x, y, state)
1926
1927Calculate the fill parameter for a linear fountain fill.
1928
1929Uses the point to line distance function, with some precalculation
1930done in i_fountain().
1931
1932=cut
1933*/
1934static double
1935linear_fount_f(double x, double y, struct fount_state *state) {
1936 return (state->lA * x + state->lB * y + state->lC) / state->AB * state->mult;
1937}
1938
1939/*
1940=item bilinear_fount_f(x, y, state)
1941
1942Calculate the fill parameter for a bi-linear fountain fill.
1943
1944=cut
1945*/
1946static double
1947bilinear_fount_f(double x, double y, struct fount_state *state) {
1948 return fabs((state->lA * x + state->lB * y + state->lC) / state->AB * state->mult);
1949}
1950
1951/*
1952=item radial_fount_f(x, y, state)
1953
1954Calculate the fill parameter for a radial fountain fill.
1955
1956Simply uses the distance function.
1957
1958=cut
1959 */
1960static double
1961radial_fount_f(double x, double y, struct fount_state *state) {
1962 return sqrt((double)(state->xa-x)*(state->xa-x)
1963 + (double)(state->ya-y)*(state->ya-y)) * state->mult;
1964}
1965
1966/*
1967=item square_fount_f(x, y, state)
1968
1969Calculate the fill parameter for a square fountain fill.
1970
1971Works by rotating the reference co-ordinate around the centre of the
1972square.
1973
1974=cut
1975*/
1976static double
1977square_fount_f(double x, double y, struct fount_state *state) {
1978 int xc, yc; /* centred on A */
1979 double xt, yt; /* rotated by theta */
1980 xc = x - state->xa;
1981 yc = y - state->ya;
1982 xt = fabs(xc * state->cos + yc * state->sin);
1983 yt = fabs(-xc * state->sin + yc * state->cos);
1984 return (xt > yt ? xt : yt) * state->mult;
1985}
1986
1987/*
1988=item revolution_fount_f(x, y, state)
1989
1990Calculates the fill parameter for the revolution fountain fill.
1991
1992=cut
1993*/
1994static double
1995revolution_fount_f(double x, double y, struct fount_state *state) {
1996 double angle = atan2(y - state->ya, x - state->xa);
1997
1998 angle -= state->theta;
1999 if (angle < 0) {
2000 angle = fmod(angle+ PI * 4, PI*2);
2001 }
2002
2003 return angle * state->mult;
2004}
2005
2006/*
2007=item conical_fount_f(x, y, state)
2008
2009Calculates the fill parameter for the conical fountain fill.
2010
2011=cut
2012*/
2013static double
2014conical_fount_f(double x, double y, struct fount_state *state) {
2015 double angle = atan2(y - state->ya, x - state->xa);
2016
2017 angle -= state->theta;
2018 if (angle < -PI)
2019 angle += PI * 2;
2020 else if (angle > PI)
2021 angle -= PI * 2;
2022
2023 return fabs(angle) * state->mult;
2024}
2025
2026/*
2027=item linear_interp(pos, seg)
2028
2029Calculates linear interpolation on the fill parameter. Breaks the
2030segment into 2 regions based in the I<middle> value.
2031
2032=cut
2033*/
2034static double
2035linear_interp(double pos, i_fountain_seg *seg) {
2036 if (pos < seg->middle) {
2037 double len = seg->middle - seg->start;
2038 if (len < EPSILON)
2039 return 0.0;
2040 else
2041 return (pos - seg->start) / len / 2;
2042 }
2043 else {
2044 double len = seg->end - seg->middle;
2045 if (len < EPSILON)
2046 return 1.0;
2047 else
2048 return 0.5 + (pos - seg->middle) / len / 2;
2049 }
2050}
2051
2052/*
2053=item sine_interp(pos, seg)
2054
2055Calculates sine function interpolation on the fill parameter.
2056
2057=cut
2058*/
2059static double
2060sine_interp(double pos, i_fountain_seg *seg) {
2061 /* I wonder if there's a simple way to smooth the transition for this */
2062 double work = linear_interp(pos, seg);
2063
2064 return (1-cos(work * PI))/2;
2065}
2066
2067/*
2068=item sphereup_interp(pos, seg)
2069
2070Calculates spherical interpolation on the fill parameter, with the cusp
2071at the low-end.
2072
2073=cut
2074*/
2075static double
2076sphereup_interp(double pos, i_fountain_seg *seg) {
2077 double work = linear_interp(pos, seg);
2078
2079 return sqrt(1.0 - (1-work) * (1-work));
2080}
2081
2082/*
2083=item spheredown_interp(pos, seg)
2084
2085Calculates spherical interpolation on the fill parameter, with the cusp
2086at the high-end.
2087
2088=cut
2089*/
2090static double
2091spheredown_interp(double pos, i_fountain_seg *seg) {
2092 double work = linear_interp(pos, seg);
2093
2094 return 1-sqrt(1.0 - work * work);
2095}
2096
2097/*
2098=item direct_cinterp(out, pos, seg)
2099
2100Calculates the fountain color based on direct scaling of the channels
2101of the color channels.
2102
2103=cut
2104*/
2105static void
2106direct_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg) {
2107 int ch;
2108 for (ch = 0; ch < MAXCHANNELS; ++ch) {
2109 out->channel[ch] = seg->c[0].channel[ch] * (1 - pos)
2110 + seg->c[1].channel[ch] * pos;
2111 }
2112}
2113
2114/*
2115=item hue_up_cinterp(put, pos, seg)
2116
2117Calculates the fountain color based on scaling a HSV value. The hue
2118increases as the fill parameter increases.
2119
2120=cut
2121*/
2122static void
2123hue_up_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg) {
2124 int ch;
2125 for (ch = 0; ch < MAXCHANNELS; ++ch) {
2126 out->channel[ch] = seg->c[0].channel[ch] * (1 - pos)
2127 + seg->c[1].channel[ch] * pos;
2128 }
2129 i_hsv_to_rgbf(out);
2130}
2131
2132/*
2133=item hue_down_cinterp(put, pos, seg)
2134
2135Calculates the fountain color based on scaling a HSV value. The hue
2136decreases as the fill parameter increases.
2137
2138=cut
2139*/
2140static void
2141hue_down_cinterp(i_fcolor *out, double pos, i_fountain_seg *seg) {
2142 int ch;
2143 for (ch = 0; ch < MAXCHANNELS; ++ch) {
2144 out->channel[ch] = seg->c[0].channel[ch] * (1 - pos)
2145 + seg->c[1].channel[ch] * pos;
2146 }
2147 i_hsv_to_rgbf(out);
2148}
2149
2150/*
2151=item simple_ssample(out, parm, x, y, state, ffunc, rpfunc, segs, count)
2152
2153Simple grid-based super-sampling.
2154
2155=cut
2156*/
2157static int
f1ac5027 2158simple_ssample(i_fcolor *out, double x, double y, struct fount_state *state) {
6607600c
TC
2159 i_fcolor *work = state->ssample_data;
2160 int dx, dy;
f1ac5027 2161 int grid = state->parm;
6607600c
TC
2162 double base = -0.5 + 0.5 / grid;
2163 double step = 1.0 / grid;
2164 int ch, i;
2165 int samp_count = 0;
2166
2167 for (dx = 0; dx < grid; ++dx) {
2168 for (dy = 0; dy < grid; ++dy) {
2169 if (fount_getat(work+samp_count, x + base + step * dx,
f1ac5027 2170 y + base + step * dy, state)) {
6607600c
TC
2171 ++samp_count;
2172 }
2173 }
2174 }
2175 for (ch = 0; ch < MAXCHANNELS; ++ch) {
2176 out->channel[ch] = 0;
2177 for (i = 0; i < samp_count; ++i) {
2178 out->channel[ch] += work[i].channel[ch];
2179 }
2180 /* we divide by 4 rather than samp_count since if there's only one valid
2181 sample it should be mostly transparent */
2182 out->channel[ch] /= grid * grid;
2183 }
2184 return samp_count;
2185}
2186
2187/*
2188=item random_ssample(out, parm, x, y, state, ffunc, rpfunc, segs, count)
2189
2190Random super-sampling.
2191
2192=cut
2193*/
2194static int
f1ac5027
TC
2195random_ssample(i_fcolor *out, double x, double y,
2196 struct fount_state *state) {
6607600c
TC
2197 i_fcolor *work = state->ssample_data;
2198 int i, ch;
f1ac5027 2199 int maxsamples = state->parm;
6607600c
TC
2200 double rand_scale = 1.0 / RAND_MAX;
2201 int samp_count = 0;
2202 for (i = 0; i < maxsamples; ++i) {
2203 if (fount_getat(work+samp_count, x - 0.5 + rand() * rand_scale,
f1ac5027 2204 y - 0.5 + rand() * rand_scale, state)) {
6607600c
TC
2205 ++samp_count;
2206 }
2207 }
2208 for (ch = 0; ch < MAXCHANNELS; ++ch) {
2209 out->channel[ch] = 0;
2210 for (i = 0; i < samp_count; ++i) {
2211 out->channel[ch] += work[i].channel[ch];
2212 }
2213 /* we divide by maxsamples rather than samp_count since if there's
2214 only one valid sample it should be mostly transparent */
2215 out->channel[ch] /= maxsamples;
2216 }
2217 return samp_count;
2218}
2219
2220/*
2221=item circle_ssample(out, parm, x, y, state, ffunc, rpfunc, segs, count)
2222
2223Super-sampling around the circumference of a circle.
2224
2225I considered saving the sin()/cos() values and transforming step-size
2226around the circle, but that's inaccurate, though it may not matter
2227much.
2228
2229=cut
2230 */
2231static int
f1ac5027
TC
2232circle_ssample(i_fcolor *out, double x, double y,
2233 struct fount_state *state) {
6607600c
TC
2234 i_fcolor *work = state->ssample_data;
2235 int i, ch;
f1ac5027 2236 int maxsamples = state->parm;
6607600c
TC
2237 double angle = 2 * PI / maxsamples;
2238 double radius = 0.3; /* semi-random */
2239 int samp_count = 0;
2240 for (i = 0; i < maxsamples; ++i) {
2241 if (fount_getat(work+samp_count, x + radius * cos(angle * i),
f1ac5027 2242 y + radius * sin(angle * i), state)) {
6607600c
TC
2243 ++samp_count;
2244 }
2245 }
2246 for (ch = 0; ch < MAXCHANNELS; ++ch) {
2247 out->channel[ch] = 0;
2248 for (i = 0; i < samp_count; ++i) {
2249 out->channel[ch] += work[i].channel[ch];
2250 }
2251 /* we divide by maxsamples rather than samp_count since if there's
2252 only one valid sample it should be mostly transparent */
2253 out->channel[ch] /= maxsamples;
2254 }
2255 return samp_count;
2256}
2257
2258/*
2259=item fount_r_none(v)
2260
2261Implements no repeats. Simply clamps the fill value.
2262
2263=cut
2264*/
2265static double
2266fount_r_none(double v) {
2267 return v < 0 ? 0 : v > 1 ? 1 : v;
2268}
2269
2270/*
2271=item fount_r_sawtooth(v)
2272
2273Implements sawtooth repeats. Clamps negative values and uses fmod()
2274on others.
2275
2276=cut
2277*/
2278static double
2279fount_r_sawtooth(double v) {
2280 return v < 0 ? 0 : fmod(v, 1.0);
2281}
2282
2283/*
2284=item fount_r_triangle(v)
2285
2286Implements triangle repeats. Clamps negative values, uses fmod to get
2287a range 0 through 2 and then adjusts values > 1.
2288
2289=cut
2290*/
2291static double
2292fount_r_triangle(double v) {
2293 if (v < 0)
2294 return 0;
2295 else {
2296 v = fmod(v, 2.0);
2297 return v > 1.0 ? 2.0 - v : v;
2298 }
2299}
2300
2301/*
2302=item fount_r_saw_both(v)
2303
2304Implements sawtooth repeats in the both postive and negative directions.
2305
2306Adjusts the value to be postive and then just uses fmod().
2307
2308=cut
2309*/
2310static double
2311fount_r_saw_both(double v) {
2312 if (v < 0)
2313 v += 1+(int)(-v);
2314 return fmod(v, 1.0);
2315}
2316
2317/*
2318=item fount_r_tri_both(v)
2319
2320Implements triangle repeats in the both postive and negative directions.
2321
2322Uses fmod on the absolute value, and then adjusts values > 1.
2323
2324=cut
2325*/
2326static double
2327fount_r_tri_both(double v) {
2328 v = fmod(fabs(v), 2.0);
2329 return v > 1.0 ? 2.0 - v : v;
2330}
2331
f1ac5027
TC
2332/*
2333=item fill_fountf(fill, x, y, width, channels, data)
2334
2335The fill function for fountain fills.
2336
2337=cut
2338*/
2339static void
2340fill_fountf(i_fill_t *fill, int x, int y, int width, int channels,
43c5dacb 2341 i_fcolor *data) {
f1ac5027 2342 i_fill_fountain_t *f = (i_fill_fountain_t *)fill;
efdc2568 2343
43c5dacb
TC
2344 while (width--) {
2345 i_fcolor c;
2346 int got_one;
2347
2348 if (f->state.ssfunc)
2349 got_one = f->state.ssfunc(&c, x, y, &f->state);
2350 else
2351 got_one = fount_getat(&c, x, y, &f->state);
2352
2353 *data++ = c;
2354
2355 ++x;
f1ac5027
TC
2356 }
2357}
2358
2359/*
2360=item fount_fill_destroy(fill)
2361
2362=cut
2363*/
2364static void
2365fount_fill_destroy(i_fill_t *fill) {
2366 i_fill_fountain_t *f = (i_fill_fountain_t *)fill;
2367 fount_finish_state(&f->state);
2368}
2369
6607600c
TC
2370/*
2371=back
2372
2373=head1 AUTHOR
2374
2375Arnar M. Hrafnkelsson <addi@umich.edu>
2376
2377Tony Cook <tony@develop-help.com> (i_fountain())
2378
2379=head1 SEE ALSO
2380
2381Imager(3)
2382
2383=cut
2384*/