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