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