implementing read/put horizontal line of pixels
[imager.git] / image.c
1 #include "image.h"
2 #include "io.h"
3
4 /*
5 =head1 NAME
6
7 image.c - implements most of the basic functions of Imager and much of the rest
8
9 =head1 SYNOPSIS
10
11   i_img *i;
12   i_color *c;
13   c = i_color_new(red, green, blue, alpha);
14   ICL_DESTROY(c);
15   i = i_img_new();
16   i_img_destroy(i);
17   // and much more
18
19 =head1 DESCRIPTION
20
21 image.c implements the basic functions to create and destroy image and
22 color objects for Imager.
23
24 =head1 FUNCTION REFERENCE
25
26 Some of these functions are internal.
27
28 =over 4
29
30 =cut
31 */
32
33 #define XAXIS 0
34 #define YAXIS 1
35 #define XYAXIS 2
36
37 #define minmax(a,b,i) ( ((a>=i)?a: ( (b<=i)?b:i   )) )
38
39 /* Hack around an obscure linker bug on solaris - probably due to builtin gcc thingies */
40 void fake() { ceil(1); }
41
42 /* 
43 =item ICL_new_internal(r, g, b, a)
44
45 Return a new color object with values passed to it.
46
47    r - red   component (range: 0 - 255)
48    g - green component (range: 0 - 255)
49    b - blue  component (range: 0 - 255)
50    a - alpha component (range: 0 - 255)
51
52 =cut
53 */
54
55 i_color *
56 ICL_new_internal(unsigned char r,unsigned char g,unsigned char b,unsigned char a) {
57   i_color *cl = NULL;
58
59   mm_log((1,"ICL_new_internal(r %d,g %d,b %d,a %d)\n", r, g, b, a));
60
61   if ( (cl=mymalloc(sizeof(i_color))) == NULL) m_fatal(2,"malloc() error\n");
62   cl->rgba.r = r;
63   cl->rgba.g = g;
64   cl->rgba.b = b;
65   cl->rgba.a = a;
66   mm_log((1,"(%p) <- ICL_new_internal\n",cl));
67   return cl;
68 }
69
70
71 /*
72 =item ICL_set_internal(cl, r, g, b, a)
73
74  Overwrite a color with new values.
75
76    cl - pointer to color object
77    r - red   component (range: 0 - 255)
78    g - green component (range: 0 - 255)
79    b - blue  component (range: 0 - 255)
80    a - alpha component (range: 0 - 255)
81
82 =cut
83 */
84
85 i_color *
86 ICL_set_internal(i_color *cl,unsigned char r,unsigned char g,unsigned char b,unsigned char a) {
87   mm_log((1,"ICL_set_internal(cl* %p,r %d,g %d,b %d,a %d)\n",cl,r,g,b,a));
88   if (cl == NULL)
89     if ( (cl=mymalloc(sizeof(i_color))) == NULL)
90       m_fatal(2,"malloc() error\n");
91   cl->rgba.r=r;
92   cl->rgba.g=g;
93   cl->rgba.b=b;
94   cl->rgba.a=a;
95   mm_log((1,"(%p) <- ICL_set_internal\n",cl));
96   return cl;
97 }
98
99
100 /* 
101 =item ICL_add(dst, src, ch)
102
103 Add src to dst inplace - dst is modified.
104
105    dst - pointer to destination color object
106    src - pointer to color object that is added
107    ch - number of channels
108
109 =cut
110 */
111
112 void
113 ICL_add(i_color *dst,i_color *src,int ch) {
114   int tmp,i;
115   for(i=0;i<ch;i++) {
116     tmp=dst->channel[i]+src->channel[i];
117     dst->channel[i]= tmp>255 ? 255:tmp;
118   }
119 }
120
121 /* 
122 =item ICL_info(cl)
123
124 Dump color information to log - strictly for debugging.
125
126    cl - pointer to color object
127
128 =cut
129 */
130
131 void
132 ICL_info(i_color *cl) {
133   mm_log((1,"i_color_info(cl* %p)\n",cl));
134   mm_log((1,"i_color_info: (%d,%d,%d,%d)\n",cl->rgba.r,cl->rgba.g,cl->rgba.b,cl->rgba.a));
135 }
136
137 /* 
138 =item ICL_DESTROY
139
140 Destroy ancillary data for Color object.
141
142    cl - pointer to color object
143
144 =cut
145 */
146
147 void
148 ICL_DESTROY(i_color *cl) {
149   mm_log((1,"ICL_DESTROY(cl* %p)\n",cl));
150   myfree(cl);
151 }
152
153 /*
154 =item IIM_new(x, y, ch)
155
156 Creates a new image object I<x> pixels wide, and I<y> pixels high with I<ch> channels.
157
158 =cut
159 */
160
161
162 i_img *
163 IIM_new(int x,int y,int ch) {
164   i_img *im;
165   mm_log((1,"IIM_new(x %d,y %d,ch %d)\n",x,y,ch));
166
167   im=i_img_empty_ch(NULL,x,y,ch);
168   
169   mm_log((1,"(%p) <- IIM_new\n",im));
170   return im;
171 }
172
173
174 void
175 IIM_DESTROY(i_img *im) {
176   mm_log((1,"IIM_DESTROY(im* %p)\n",im));
177   /*   myfree(cl); */
178 }
179
180
181
182 /* 
183 =item i_img_new()
184
185 Create new image reference - notice that this isn't an object yet and
186 this should be fixed asap.
187
188 =cut
189 */
190
191
192 i_img *
193 i_img_new() {
194   i_img *im;
195   
196   mm_log((1,"i_img_struct()\n"));
197   if ( (im=mymalloc(sizeof(i_img))) == NULL)
198     m_fatal(2,"malloc() error\n");
199   
200   im->xsize=0;
201   im->ysize=0;
202   im->channels=3;
203   im->ch_mask=MAXINT;
204   im->bytes=0;
205   im->data=NULL;
206
207   im->i_f_ppix=i_ppix_d;
208   im->i_f_gpix=i_gpix_d;
209   im->i_f_plin=i_plin_d;
210   im->i_f_glin=i_glin_d;
211   im->ext_data=NULL;
212   
213   mm_log((1,"(%p) <- i_img_struct\n",im));
214   return im;
215 }
216
217 /* 
218 =item i_img_empty(im, x, y)
219
220 Re-new image reference (assumes 3 channels)
221
222    im - Image pointer
223    x - xsize of destination image
224    y - ysize of destination image
225
226 =cut
227 */
228
229 i_img *
230 i_img_empty(i_img *im,int x,int y) {
231   mm_log((1,"i_img_empty(*im %p, x %d, y %d)\n",im, x, y));
232   if (im==NULL)
233     if ( (im=mymalloc(sizeof(i_img))) == NULL)
234       m_fatal(2,"malloc() error\n");
235   
236   im->xsize    = x;
237   im->ysize    = y;
238   im->channels = 3;
239   im->ch_mask  = MAXINT;
240   im->bytes=x*y*im->channels;
241   if ( (im->data = mymalloc(im->bytes)) == NULL) m_fatal(2,"malloc() error\n"); 
242   memset(im->data, 0, (size_t)im->bytes);
243
244   im->i_f_ppix = i_ppix_d;
245   im->i_f_gpix = i_gpix_d;
246   im->i_f_plin = i_plin_d;
247   im->i_f_glin = i_glin_d;
248   im->ext_data = NULL;
249   
250   mm_log((1,"(%p) <- i_img_empty\n", im));
251   return im;
252 }
253
254 /* 
255 =item i_img_empty_ch(im, x, y, ch)
256
257 Re-new image reference 
258
259    im - Image pointer
260    x  - xsize of destination image
261    y  - ysize of destination image
262    ch - number of channels
263
264 =cut
265 */
266
267 i_img *
268 i_img_empty_ch(i_img *im,int x,int y,int ch) {
269   mm_log((1,"i_img_empty_ch(*im %p, x %d, y %d, ch %d)\n", im, x, y, ch));
270   if (im == NULL)
271     if ( (im=mymalloc(sizeof(i_img))) == NULL)
272       m_fatal(2,"malloc() error\n");
273   
274   im->xsize    = x;
275   im->ysize    = y;
276   im->channels = ch;
277   im->ch_mask  = MAXINT;
278   im->bytes=x*y*im->channels;
279   if ( (im->data=mymalloc(im->bytes)) == NULL) m_fatal(2,"malloc() error\n"); 
280   memset(im->data,0,(size_t)im->bytes);
281   
282   im->i_f_ppix = i_ppix_d;
283   im->i_f_gpix = i_gpix_d;
284   im->i_f_plin = i_plin_d;
285   im->i_f_glin = i_glin_d;
286   im->ext_data = NULL;
287   
288   mm_log((1,"(%p) <- i_img_empty_ch\n",im));
289   return im;
290 }
291
292 /* 
293 =item i_img_exorcise(im)
294
295 Free image data.
296
297    im - Image pointer
298
299 =cut
300 */
301
302 void
303 i_img_exorcise(i_img *im) {
304   mm_log((1,"i_img_exorcise(im* 0x%x)\n",im));
305   if (im->data != NULL) { myfree(im->data); }
306   im->data     = NULL;
307   im->xsize    = 0;
308   im->ysize    = 0;
309   im->channels = 0;
310
311   im->i_f_ppix=i_ppix_d;
312   im->i_f_gpix=i_gpix_d;
313   im->i_f_plin=i_plin_d;
314   im->i_f_glin=i_glin_d;
315   im->ext_data=NULL;
316 }
317
318 /* 
319 =item i_img_destroy(im)
320
321 Destroy image and free data via exorcise.
322
323    im - Image pointer
324
325 =cut
326 */
327
328 void
329 i_img_destroy(i_img *im) {
330   mm_log((1,"i_img_destroy(im* 0x%x)\n",im));
331   i_img_exorcise(im);
332   if (im) { myfree(im); }
333 }
334
335 /* 
336 =item i_img_info(im, info)
337
338 Return image information
339
340    im - Image pointer
341    info - pointer to array to return data
342
343 info is an array of 4 integers with the following values:
344
345  info[0] - width
346  info[1] - height
347  info[2] - channels
348  info[3] - channel mask
349
350 =cut
351 */
352
353
354 void
355 i_img_info(i_img *im,int *info) {
356   mm_log((1,"i_img_info(im 0x%x)\n",im));
357   if (im != NULL) {
358     mm_log((1,"i_img_info: xsize=%d ysize=%d channels=%d mask=%ud\n",im->xsize,im->ysize,im->channels,im->ch_mask));
359     mm_log((1,"i_img_info: data=0x%d\n",im->data));
360     info[0] = im->xsize;
361     info[1] = im->ysize;
362     info[2] = im->channels;
363     info[3] = im->ch_mask;
364   } else {
365     info[0] = 0;
366     info[1] = 0;
367     info[2] = 0;
368     info[3] = 0;
369   }
370 }
371
372 /*
373 =item i_img_setmask(im, ch_mask)
374
375 Set the image channel mask for I<im> to I<ch_mask>.
376
377 =cut
378 */
379 void
380 i_img_setmask(i_img *im,int ch_mask) { im->ch_mask=ch_mask; }
381
382
383 /*
384 =item i_img_getmask(im)
385
386 Get the image channel mask for I<im>.
387
388 =cut
389 */
390 int
391 i_img_getmask(i_img *im) { return im->ch_mask; }
392
393 /*
394 =item i_img_getchannels(im)
395
396 Get the number of channels in I<im>.
397
398 =cut
399 */
400 int
401 i_img_getchannels(i_img *im) { return im->channels; }
402
403
404 /*
405 =item i_ppix(im, x, y, col)
406
407 Sets the pixel at (I<x>,I<y>) in I<im> to I<col>.
408
409 Returns true if the pixel could be set, false if x or y is out of
410 range.
411
412 =cut
413 */
414 int
415 i_ppix(i_img *im, int x, int y, i_color *val) { return im->i_f_ppix(im, x, y, val); }
416
417 /*
418 =item i_gpix(im, x, y, &col)
419
420 Get the pixel at (I<x>,I<y>) in I<im> into I<col>.
421
422 Returns true if the pixel could be retrieved, false otherwise.
423
424 =cut
425 */
426 int
427 i_gpix(i_img *im, int x, int y, i_color *val) { return im->i_f_gpix(im, x, y, val); }
428
429 /*
430 =item i_ppix_d(im, x, y, col)
431
432 Internal function.
433
434 This is the function kept in the i_f_ppix member of an i_img object.
435 It does a normal store of a pixel into the image with range checking.
436
437 Returns true if the pixel could be set, false otherwise.
438
439 =cut
440 */
441 int
442 i_ppix_d(i_img *im, int x, int y, i_color *val) {
443   int ch;
444   
445   if ( x>-1 && x<im->xsize && y>-1 && y<im->ysize ) {
446     for(ch=0;ch<im->channels;ch++)
447       if (im->ch_mask&(1<<ch)) 
448         im->data[(x+y*im->xsize)*im->channels+ch]=val->channel[ch];
449     return 0;
450   }
451   return -1; /* error was clipped */
452 }
453
454 /*
455 =item i_gpix_d(im, x, y, &col)
456
457 Internal function.
458
459 This is the function kept in the i_f_gpix member of an i_img object.
460 It does normal retrieval of a pixel from the image with range checking.
461
462 Returns true if the pixel could be set, false otherwise.
463
464 =cut
465 */
466 int 
467 i_gpix_d(i_img *im, int x, int y, i_color *val) {
468   int ch;
469   if (x>-1 && x<im->xsize && y>-1 && y<im->ysize) {
470     for(ch=0;ch<im->channels;ch++) 
471         val->channel[ch]=im->data[(x+y*im->xsize)*im->channels+ch];
472     return 0;
473   }
474   return -1; /* error was cliped */
475 }
476
477 /*
478 =item i_glin_d(im, l, r, y, vals)
479
480 Reads a line of data from the image, storing the pixels at vals.
481
482 The line runs from (l,y) inclusive to (r,y) non-inclusive
483
484 vals should point at space for (r-l) pixels.
485
486 l should never be less than zero (to avoid confusion about where to
487 put the pixels in vals).
488
489 Returns the number of pixels copied (eg. if r, l or y is out of range)
490
491 =cut */
492 int
493 i_glin_d(i_img *im, int l, int r, int y, i_color *vals) {
494   int x, ch;
495   int count;
496   int i;
497   unsigned char *data;
498   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
499     if (r > im->xsize)
500       r = im->xsize;
501     data = im->data + (l+y*im->xsize) * im->channels;
502     count = r - l;
503     for (i = 0; i < count; ++i) {
504       for (ch = 0; ch < im->channels; ++ch)
505         vals[i].channel[ch] = *data++;
506     }
507     return count;
508   }
509   else {
510     return 0;
511   }
512 }
513 /*
514 =item i_plin_d(im, l, r, y, vals)
515
516 Writes a line of data into the image, using the pixels at vals.
517
518 The line runs from (l,y) inclusive to (r,y) non-inclusive
519
520 vals should point at (r-l) pixels.
521
522 l should never be less than zero (to avoid confusion about where to
523 get the pixels in vals).
524
525 Returns the number of pixels copied (eg. if r, l or y is out of range)
526
527 =cut */
528 int
529 i_plin_d(i_img *im, int l, int r, int y, i_color *vals) {
530   int x, ch;
531   int count;
532   int i;
533   unsigned char *data;
534   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
535     if (r > im->xsize)
536       r = im->xsize;
537     data = im->data + (l+y*im->xsize) * im->channels;
538     count = r - l;
539     for (i = 0; i < count; ++i) {
540       for (ch = 0; ch < im->channels; ++ch) {
541         if (im->ch_mask & (1 << ch)) 
542           *data = vals[i].channel[ch];
543         ++data;
544       }
545     }
546     return count;
547   }
548   else {
549     return 0;
550   }
551 }
552
553 /*
554 =item i_ppix_pch(im, x, y, ch)
555
556 Get the value from the channel I<ch> for pixel (I<x>,I<y>) from I<im>
557 scaled to [0,1].
558
559 Returns zero if x or y is out of range.
560
561 Warning: this ignores the vptr interface for images.
562
563 =cut
564 */
565 float
566 i_gpix_pch(i_img *im,int x,int y,int ch) {
567   if (x>-1 && x<im->xsize && y>-1 && y<im->ysize) return ((float)im->data[(x+y*im->xsize)*im->channels+ch]/255);
568   else return 0;
569 }
570
571
572 /*
573 =item i_copyto_trans(im, src, x1, y1, x2, y2, tx, ty, trans)
574
575 (x1,y1) (x2,y2) specifies the region to copy (in the source coordinates)
576 (tx,ty) specifies the upper left corner for the target image.
577 pass NULL in trans for non transparent i_colors.
578
579 =cut
580 */
581
582 void
583 i_copyto_trans(i_img *im,i_img *src,int x1,int y1,int x2,int y2,int tx,int ty,i_color *trans) {
584   i_color pv;
585   int x,y,t,ttx,tty,tt,ch;
586
587   mm_log((1,"i_copyto_trans(im* %p,src 0x%x, x1 %d, y1 %d, x2 %d, y2 %d, tx %d, ty %d, trans* 0x%x)\n",
588           im, src, x1, y1, x2, y2, tx, ty, trans));
589   
590   if (x2<x1) { t=x1; x1=x2; x2=t; }
591   if (y2<y1) { t=y1; y1=y2; y2=t; }
592
593   ttx=tx;
594   for(x=x1;x<x2;x++)
595     {
596       tty=ty;
597       for(y=y1;y<y2;y++)
598         {
599           i_gpix(src,x,y,&pv);
600           if ( trans != NULL)
601           {
602             tt=0;
603             for(ch=0;ch<im->channels;ch++) if (trans->channel[ch]!=pv.channel[ch]) tt++;
604             if (tt) i_ppix(im,ttx,tty,&pv);
605           } else i_ppix(im,ttx,tty,&pv);
606           tty++;
607         }
608       ttx++;
609     }
610 }
611
612 /*
613 =item i_copyto(dest, src, x1, y1, x2, y2, tx, ty)
614
615 Copies image data from the area (x1,y1)-[x2,y2] in the source image to
616 a rectangle the same size with it's top-left corner at (tx,ty) in the
617 destination image.
618
619 If x1 > x2 or y1 > y2 then the corresponding co-ordinates are swapped.
620
621 =cut
622 */
623
624 void
625 i_copyto(i_img *im, i_img *src, int x1, int y1, int x2, int y2, int tx, int ty) {
626   i_color pv;
627   int x, y, t, ttx, tty;
628
629   if (x2<x1) { t=x1; x1=x2; x2=t; }
630   if (y2<y1) { t=y1; y1=y2; y2=t; }
631
632   mm_log((1,"i_copyto(im* %p, src %p, x1 %d, y1 %d, x2 %d, y2 %d, tx %d, ty %d)\n",
633           im, src, x1, y1, x2, y2, tx, ty));
634
635     tty = ty;
636     for(y=y1; y<y2; y++) {
637     ttx = tx;
638     for(x=x1; x<x2; x++) {
639       i_gpix(src, x,   y,   &pv);
640       i_ppix(im,  ttx, tty, &pv);
641       ttx++;
642     }
643     tty++;
644   }
645 }
646
647 /*
648 =item i_copy(im, src)
649
650 Copies the contents of the image I<src> over the image I<im>.
651
652 =cut
653 */
654
655 void
656 i_copy(i_img *im, i_img *src) {
657   i_color *pv;
658   int x,y,y1,x1;
659
660   mm_log((1,"i_copy(im* %p,src %p)\n", im, src));
661
662   x1 = src->xsize;
663   y1 = src->ysize;
664   i_img_empty_ch(im, x1, y1, src->channels);
665   pv = mymalloc(sizeof(i_color) * x1);
666   
667   for (y = 0; y < y1; ++y) {
668     i_glin(src, 0, x1, y, pv);
669     i_plin(im, 0, x1, y, pv);
670   }
671 }
672
673
674 /*
675 =item i_rubthru(im, src, tx, ty)
676
677 Takes the image I<src> and applies it at an original (I<tx>,I<ty>) in I<im>.
678
679 The alpha channel of each pixel in I<src> is used to control how much
680 the existing colour in I<im> is replaced, if it is 255 then the colour
681 is completely replaced, if it is 0 then the original colour is left 
682 unmodified.
683
684 =cut
685 */
686
687 void
688 i_rubthru(i_img *im,i_img *src,int tx,int ty) {
689   i_color pv, orig, dest;
690   int x, y, ttx, tty;
691
692   mm_log((1,"i_rubthru(im %p, src %p, tx %d, ty %d)\n", im, src, tx, ty));
693
694   if (im->channels  != 3) { fprintf(stderr,"Destination is not in rgb mode.\n"); exit(3); }
695   if (src->channels != 4) { fprintf(stderr,"Source is not in rgba mode.\n"); exit(3); }
696
697   ttx = tx;
698   for(x=0; x<src->xsize; x++) {
699     tty=ty;
700     for(y=0;y<src->ysize;y++) {
701       /* fprintf(stderr,"reading (%d,%d) writing (%d,%d).\n",x,y,ttx,tty); */
702       i_gpix(src, x,   y,   &pv);
703       i_gpix(im,  ttx, tty, &orig);
704       dest.rgb.r = (pv.rgba.a*pv.rgba.r+(255-pv.rgba.a)*orig.rgb.r)/255;
705       dest.rgb.g = (pv.rgba.a*pv.rgba.g+(255-pv.rgba.a)*orig.rgb.g)/255;
706       dest.rgb.b = (pv.rgba.a*pv.rgba.b+(255-pv.rgba.a)*orig.rgb.b)/255;
707       i_ppix(im, ttx, tty, &dest);
708       tty++;
709     }
710     ttx++;
711   }
712 }
713
714
715 /*
716 =item i_flipxy(im, axis)
717
718 Flips the image inplace around the axis specified.
719 Returns 0 if parameters are invalid.
720
721    im   - Image pointer
722    axis - 0 = x, 1 = y, 2 = both
723
724 =cut
725 */
726
727 undef_int
728 i_flipxy(i_img *im, int direction) {
729   int x, x2, y, y2, xm, ym;
730   int xs = im->xsize;
731   int ys = im->ysize;
732   
733   mm_log((1, "i_flipxy(im %p, direction %d)\n", im, direction ));
734
735   if (!im) return 0;
736
737   switch (direction) {
738   case XAXIS: /* Horizontal flip */
739     xm = xs/2;
740     ym = ys;
741     for(y=0; y<ym; y++) {
742       x2 = xs-1;
743       for(x=0; x<xm; x++) {
744         i_color val1, val2;
745         i_gpix(im, x,  y,  &val1);
746         i_gpix(im, x2, y,  &val2);
747         i_ppix(im, x,  y,  &val2);
748         i_ppix(im, x2, y,  &val1);
749         x2--;
750       }
751     }
752     break;
753   case YAXIS: /* Vertical flip */
754     xm = xs;
755     ym = ys/2;
756     y2 = ys-1;
757     for(y=0; y<ym; y++) {
758       for(x=0; x<xm; x++) {
759         i_color val1, val2;
760         i_gpix(im, x,  y,  &val1);
761         i_gpix(im, x,  y2, &val2);
762         i_ppix(im, x,  y,  &val2);
763         i_ppix(im, x,  y2, &val1);
764       }
765       y2--;
766     }
767     break;
768   case XYAXIS: /* Horizontal and Vertical flip */
769     xm = xs/2;
770     ym = ys/2;
771     y2 = ys-1;
772     for(y=0; y<ym; y++) {
773       x2 = xs-1;
774       for(x=0; x<xm; x++) {
775         i_color val1, val2;
776         i_gpix(im, x,  y,  &val1);
777         i_gpix(im, x2, y2, &val2);
778         i_ppix(im, x,  y,  &val2);
779         i_ppix(im, x2, y2, &val1);
780
781         i_gpix(im, x2, y,  &val1);
782         i_gpix(im, x,  y2, &val2);
783         i_ppix(im, x2, y,  &val2);
784         i_ppix(im, x,  y2, &val1);
785         x2--;
786       }
787       y2--;
788     }
789     if (xm*2 != xs) { /* odd number of column */
790       mm_log((1, "i_flipxy: odd number of columns\n"));
791       x = xm;
792       y2 = ys-1;
793       for(y=0; y<ym; y++) {
794         i_color val1, val2;
795         i_gpix(im, x,  y,  &val1);
796         i_gpix(im, x,  y2, &val2);
797         i_ppix(im, x,  y,  &val2);
798         i_ppix(im, x,  y2, &val1);
799         y2--;
800       }
801     }
802     if (ym*2 != ys) { /* odd number of rows */
803       mm_log((1, "i_flipxy: odd number of rows\n"));
804       y = ym;
805       x2 = xs-1;
806       for(x=0; x<xm; x++) {
807         i_color val1, val2;
808         i_gpix(im, x,  y,  &val1);
809         i_gpix(im, x2, y,  &val2);
810         i_ppix(im, x,  y,  &val2);
811         i_ppix(im, x2, y,  &val1);
812         x2--;
813       }
814     }
815     break;
816   default:
817     mm_log((1, "i_flipxy: direction is invalid\n" ));
818     return 0;
819   }
820   return 1;
821 }
822
823
824
825
826
827 static
828 float
829 Lanczos(float x) {
830   float PIx, PIx2;
831   
832   PIx = PI * x;
833   PIx2 = PIx / 2.0;
834   
835   if ((x >= 2.0) || (x <= -2.0)) return (0.0);
836   else if (x == 0.0) return (1.0);
837   else return(sin(PIx) / PIx * sin(PIx2) / PIx2);
838 }
839
840 /*
841 =item i_scaleaxis(im, value, axis)
842
843 Returns a new image object which is I<im> scaled by I<value> along
844 wither the x-axis (I<axis> == 0) or the y-axis (I<axis> == 1).
845
846 =cut
847 */
848
849 i_img*
850 i_scaleaxis(i_img *im, float Value, int Axis) {
851   int hsize, vsize, i, j, k, l, lMax, iEnd, jEnd;
852   int LanczosWidthFactor;
853   float *l0, *l1, OldLocation;
854   int T, TempJump1, TempJump2;
855   float F, PictureValue[MAXCHANNELS];
856   short psave;
857   i_color val,val1,val2;
858   i_img *new_img;
859
860   mm_log((1,"i_scaleaxis(im 0x%x,Value %.2f,Axis %d)\n",im,Value,Axis));
861
862   if (Axis == XAXIS) {
863     hsize = (int) ((float) im->xsize * Value);
864     vsize = im->ysize;
865     
866     jEnd = hsize;
867     iEnd = vsize;
868     
869     TempJump1 = (hsize - 1) * 3;
870     TempJump2 = hsize * (vsize - 1) * 3 + TempJump1;
871   } else {
872     hsize = im->xsize;
873     vsize = (int) ((float) im->ysize * Value);
874     
875     jEnd = vsize;
876     iEnd = hsize;
877     
878     TempJump1 = 0;
879     TempJump2 = 0;
880   }
881   
882   new_img=i_img_empty_ch(NULL,hsize,vsize,im->channels);
883   
884   if (Value >=1) LanczosWidthFactor = 1;
885   else LanczosWidthFactor = (int) (1.0/Value);
886   
887   lMax = LanczosWidthFactor << 1;
888   
889   l0 = (float *) mymalloc(lMax * sizeof(float));
890   l1 = (float *) mymalloc(lMax * sizeof(float));
891   
892   for (j=0; j<jEnd; j++) {
893     OldLocation = ((float) j) / Value;
894     T = (int) (OldLocation);
895     F = OldLocation - (float) T;
896     
897     for (l = 0; l < lMax; l++) {
898       l0[lMax-l-1] = Lanczos(((float) (lMax-l-1) + F) / (float) LanczosWidthFactor);
899       l1[l] = Lanczos(((float) (l + 1) - F) / (float) LanczosWidthFactor);
900     }
901     
902     if (Axis== XAXIS) {
903       
904       for (i=0; i<iEnd; i++) {
905         for (k=0; k<im->channels; k++) PictureValue[k] = 0.0;
906         for (l=0; l < lMax; l++) {
907           i_gpix(im,T+l+1, i, &val1);
908           i_gpix(im,T-lMax+l+1, i, &val2);
909           for (k=0; k<im->channels; k++) {
910             PictureValue[k] += l1[l] * val1.channel[k];
911             PictureValue[k] += l0[lMax-l-1] * val2.channel[k];
912           }
913         }
914         for(k=0;k<im->channels;k++) {
915           psave = (short)( PictureValue[k] / LanczosWidthFactor);
916           val.channel[k]=minmax(0,255,psave);
917         }
918         i_ppix(new_img,j,i,&val);
919       }
920       
921     } else {
922       
923       for (i=0; i<iEnd; i++) {
924         for (k=0; k<im->channels; k++) PictureValue[k] = 0.0;
925         for (l=0; l < lMax; l++) {
926           i_gpix(im,i, T+l+1, &val1);
927           i_gpix(im,i, T-lMax+l+1, &val2);
928           for (k=0; k<im->channels; k++) {
929             PictureValue[k] += l1[l] * val1.channel[k];
930             PictureValue[k] += l0[lMax-l-1] * val2.channel[k]; 
931           }
932         }
933         for (k=0; k<im->channels; k++) {
934           psave = (short)( PictureValue[k] / LanczosWidthFactor);
935           val.channel[k]=minmax(0,255,psave);
936         }
937         i_ppix(new_img,i,j,&val);
938       }
939       
940     }
941   }
942   myfree(l0);
943   myfree(l1);
944
945   mm_log((1,"(0x%x) <- i_scaleaxis\n",new_img));
946
947   return new_img;
948 }
949
950
951 /* 
952 =item i_scale_nn(im, scx, scy)
953
954 Scale by using nearest neighbor 
955 Both axes scaled at the same time since 
956 nothing is gained by doing it in two steps 
957
958 =cut
959 */
960
961
962 i_img*
963 i_scale_nn(i_img *im, float scx, float scy) {
964
965   int nxsize,nysize,nx,ny;
966   i_img *new_img;
967   i_color val;
968
969   mm_log((1,"i_scale_nn(im 0x%x,scx %.2f,scy %.2f)\n",im,scx,scy));
970
971   nxsize = (int) ((float) im->xsize * scx);
972   nysize = (int) ((float) im->ysize * scy);
973     
974   new_img=i_img_empty_ch(NULL,nxsize,nysize,im->channels);
975   
976   for(ny=0;ny<nysize;ny++) for(nx=0;nx<nxsize;nx++) {
977     i_gpix(im,((float)nx)/scx,((float)ny)/scy,&val);
978     i_ppix(new_img,nx,ny,&val);
979   }
980
981   mm_log((1,"(0x%x) <- i_scale_nn\n",new_img));
982
983   return new_img;
984 }
985
986
987 /*
988 =item i_transform(im, opx, opxl, opy, opyl, parm, parmlen)
989
990 Spatially transforms I<im> returning a new image.
991
992 opx for a length of opxl and opy for a length of opy are arrays of
993 operators that modify the x and y positions to retreive the pixel data from.
994
995 parm and parmlen define extra parameters that the operators may use.
996
997 Note that this function is largely superseded by the more flexible
998 L<transform.c/i_transform2>.
999
1000 Returns the new image.
1001
1002 The operators for this function are defined in L<stackmach.c>.
1003
1004 =cut
1005 */
1006 i_img*
1007 i_transform(i_img *im, int *opx,int opxl,int *opy,int opyl,double parm[],int parmlen) {
1008   double rx,ry;
1009   int nxsize,nysize,nx,ny;
1010   i_img *new_img;
1011   i_color val;
1012   
1013   mm_log((1,"i_transform(im 0x%x, opx 0x%x, opxl %d, opy 0x%x, opyl %d, parm 0x%x, parmlen %d)\n",im,opx,opxl,opy,opyl,parm,parmlen));
1014
1015   nxsize = im->xsize;
1016   nysize = im->ysize ;
1017   
1018   new_img=i_img_empty_ch(NULL,nxsize,nysize,im->channels);
1019   /*   fprintf(stderr,"parm[2]=%f\n",parm[2]);   */
1020   for(ny=0;ny<nysize;ny++) for(nx=0;nx<nxsize;nx++) {
1021     /*     parm[parmlen-2]=(double)nx;
1022            parm[parmlen-1]=(double)ny; */
1023
1024     parm[0]=(double)nx;
1025     parm[1]=(double)ny;
1026
1027     /*     fprintf(stderr,"(%d,%d) ->",nx,ny);  */
1028     rx=op_run(opx,opxl,parm,parmlen);
1029     ry=op_run(opy,opyl,parm,parmlen);
1030     /*    fprintf(stderr,"(%f,%f)\n",rx,ry); */
1031     i_gpix(im,rx,ry,&val);
1032     i_ppix(new_img,nx,ny,&val);
1033   }
1034
1035   mm_log((1,"(0x%x) <- i_transform\n",new_img));
1036   return new_img;
1037 }
1038
1039 /*
1040 =item i_img_diff(im1, im2)
1041
1042 Calculates the sum of the squares of the differences between
1043 correspoding channels in two images.
1044
1045 If the images are not the same size then only the common area is 
1046 compared, hence even if images are different sizes this function 
1047 can return zero.
1048
1049 =cut
1050 */
1051 float
1052 i_img_diff(i_img *im1,i_img *im2) {
1053   int x,y,ch,xb,yb,chb;
1054   float tdiff;
1055   i_color val1,val2;
1056
1057   mm_log((1,"i_img_diff(im1 0x%x,im2 0x%x)\n",im1,im2));
1058
1059   xb=(im1->xsize<im2->xsize)?im1->xsize:im2->xsize;
1060   yb=(im1->ysize<im2->ysize)?im1->ysize:im2->ysize;
1061   chb=(im1->channels<im2->channels)?im1->channels:im2->channels;
1062
1063   mm_log((1,"i_img_diff: xb=%d xy=%d chb=%d\n",xb,yb,chb));
1064
1065   tdiff=0;
1066   for(y=0;y<yb;y++) for(x=0;x<xb;x++) {
1067     i_gpix(im1,x,y,&val1);
1068     i_gpix(im2,x,y,&val2);
1069
1070     for(ch=0;ch<chb;ch++) tdiff+=(val1.channel[ch]-val2.channel[ch])*(val1.channel[ch]-val2.channel[ch]);
1071   }
1072   mm_log((1,"i_img_diff <- (%.2f)\n",tdiff));
1073   return tdiff;
1074 }
1075
1076 /* just a tiny demo of haar wavelets */
1077
1078 i_img*
1079 i_haar(i_img *im) {
1080   int mx,my;
1081   int fx,fy;
1082   int x,y;
1083   int ch,c;
1084   i_img *new_img,*new_img2;
1085   i_color val1,val2,dval1,dval2;
1086   
1087   mx=im->xsize;
1088   my=im->ysize;
1089   fx=(mx+1)/2;
1090   fy=(my+1)/2;
1091
1092
1093   /* horizontal pass */
1094   
1095   new_img=i_img_empty_ch(NULL,fx*2,fy*2,im->channels);
1096   new_img2=i_img_empty_ch(NULL,fx*2,fy*2,im->channels);
1097
1098   c=0; 
1099   for(y=0;y<my;y++) for(x=0;x<fx;x++) {
1100     i_gpix(im,x*2,y,&val1);
1101     i_gpix(im,x*2+1,y,&val2);
1102     for(ch=0;ch<im->channels;ch++) {
1103       dval1.channel[ch]=(val1.channel[ch]+val2.channel[ch])/2;
1104       dval2.channel[ch]=(255+val1.channel[ch]-val2.channel[ch])/2;
1105     }
1106     i_ppix(new_img,x,y,&dval1);
1107     i_ppix(new_img,x+fx,y,&dval2);
1108   }
1109
1110   for(y=0;y<fy;y++) for(x=0;x<mx;x++) {
1111     i_gpix(new_img,x,y*2,&val1);
1112     i_gpix(new_img,x,y*2+1,&val2);
1113     for(ch=0;ch<im->channels;ch++) {
1114       dval1.channel[ch]=(val1.channel[ch]+val2.channel[ch])/2;
1115       dval2.channel[ch]=(255+val1.channel[ch]-val2.channel[ch])/2;
1116     }
1117     i_ppix(new_img2,x,y,&dval1);
1118     i_ppix(new_img2,x,y+fy,&dval2);
1119   }
1120
1121   i_img_destroy(new_img);
1122   return new_img2;
1123 }
1124
1125 /* 
1126 =item i_count_colors(im, maxc)
1127
1128 returns number of colors or -1 
1129 to indicate that it was more than max colors
1130
1131 =cut
1132 */
1133 int
1134 i_count_colors(i_img *im,int maxc) {
1135   struct octt *ct;
1136   int x,y;
1137   int xsize,ysize;
1138   i_color val;
1139   int colorcnt;
1140
1141   mm_log((1,"i_count_colors(im 0x%08X,maxc %d)\n"));
1142
1143   xsize=im->xsize; 
1144   ysize=im->ysize;
1145   ct=octt_new();
1146  
1147   colorcnt=0;
1148   for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
1149     i_gpix(im,x,y,&val);
1150     colorcnt+=octt_add(ct,val.rgb.r,val.rgb.g,val.rgb.b);
1151     if (colorcnt > maxc) { octt_delete(ct); return -1; }
1152   }
1153   octt_delete(ct);
1154   return colorcnt;
1155 }
1156
1157
1158 symbol_table_t symbol_table={i_has_format,ICL_set_internal,ICL_info,
1159                              i_img_new,i_img_empty,i_img_empty_ch,i_img_exorcise,
1160                              i_img_info,i_img_setmask,i_img_getmask,i_ppix,i_gpix,
1161                              i_box,i_draw,i_arc,i_copyto,i_copyto_trans,i_rubthru};
1162
1163
1164 /*
1165 =item i_gen_reader(i_gen_read_data *info, char *buf, int length)
1166
1167 Performs general read buffering for file readers that permit reading
1168 to be done through a callback.
1169
1170 The final callback gets two parameters, a I<need> value, and a I<want>
1171 value, where I<need> is the amount of data that the file library needs
1172 to read, and I<want> is the amount of space available in the buffer
1173 maintained by these functions.
1174
1175 This means if you need to read from a stream that you don't know the
1176 length of, you can return I<need> bytes, taking the performance hit of
1177 possibly expensive callbacks (eg. back to perl code), or if you are
1178 reading from a stream where it doesn't matter if some data is lost, or
1179 if the total length of the stream is known, you can return I<want>
1180 bytes.
1181
1182 =cut 
1183 */
1184
1185 int
1186 i_gen_reader(i_gen_read_data *gci, char *buf, int length) {
1187   int total;
1188
1189   if (length < gci->length - gci->cpos) {
1190     /* simplest case */
1191     memcpy(buf, gci->buffer+gci->cpos, length);
1192     gci->cpos += length;
1193     return length;
1194   }
1195   
1196   total = 0;
1197   memcpy(buf, gci->buffer+gci->cpos, gci->length-gci->cpos);
1198   total  += gci->length - gci->cpos;
1199   length -= gci->length - gci->cpos;
1200   buf    += gci->length - gci->cpos;
1201   if (length < (int)sizeof(gci->buffer)) {
1202     int did_read;
1203     int copy_size;
1204     while (length
1205            && (did_read = (gci->cb)(gci->userdata, gci->buffer, length, 
1206                                     sizeof(gci->buffer))) > 0) {
1207       gci->cpos = 0;
1208       gci->length = did_read;
1209
1210       copy_size = min(length, gci->length);
1211       memcpy(buf, gci->buffer, copy_size);
1212       gci->cpos += copy_size;
1213       buf += copy_size;
1214       total += copy_size;
1215       length -= copy_size;
1216     }
1217   }
1218   else {
1219     /* just read the rest - too big for our buffer*/
1220     int did_read;
1221     while ((did_read = (gci->cb)(gci->userdata, buf, length, length)) > 0) {
1222       length -= did_read;
1223       total += did_read;
1224       buf += did_read;
1225     }
1226   }
1227   return total;
1228 }
1229
1230 /*
1231 =item i_gen_read_data_new(i_read_callback_t cb, char *userdata)
1232
1233 For use by callback file readers to initialize the reader buffer.
1234
1235 Allocates, initializes and returns the reader buffer.
1236
1237 See also L<image.c/free_gen_read_data> and L<image.c/i_gen_reader>.
1238
1239 =cut
1240 */
1241 i_gen_read_data *
1242 i_gen_read_data_new(i_read_callback_t cb, char *userdata) {
1243   i_gen_read_data *self = mymalloc(sizeof(i_gen_read_data));
1244   self->cb = cb;
1245   self->userdata = userdata;
1246   self->length = 0;
1247   self->cpos = 0;
1248
1249   return self;
1250 }
1251
1252 /*
1253 =item free_gen_read_data(i_gen_read_data *)
1254
1255 Cleans up.
1256
1257 =cut
1258 */
1259 void free_gen_read_data(i_gen_read_data *self) {
1260   myfree(self);
1261 }
1262
1263 /*
1264 =item i_gen_writer(i_gen_write_data *info, char const *data, int size)
1265
1266 Performs write buffering for a callback based file writer.
1267
1268 Failures are considered fatal, if a write fails then data will be
1269 dropped.
1270
1271 =cut
1272 */
1273 int 
1274 i_gen_writer(
1275 i_gen_write_data *self, 
1276 char const *data, 
1277 int size)
1278 {
1279   if (self->filledto && self->filledto+size > self->maxlength) {
1280     if (self->cb(self->userdata, self->buffer, self->filledto)) {
1281       self->filledto = 0;
1282     }
1283     else {
1284       self->filledto = 0;
1285       return 0;
1286     }
1287   }
1288   if (self->filledto+size <= self->maxlength) {
1289     /* just save it */
1290     memcpy(self->buffer+self->filledto, data, size);
1291     self->filledto += size;
1292     return 1;
1293   }
1294   /* doesn't fit - hand it off */
1295   return self->cb(self->userdata, data, size);
1296 }
1297
1298 /*
1299 =item i_gen_write_data_new(i_write_callback_t cb, char *userdata, int max_length)
1300
1301 Allocates and initializes the data structure used by i_gen_writer.
1302
1303 This should be released with L<image.c/free_gen_write_data>
1304
1305 =cut
1306 */
1307 i_gen_write_data *i_gen_write_data_new(i_write_callback_t cb, 
1308                                        char *userdata, int max_length)
1309 {
1310   i_gen_write_data *self = mymalloc(sizeof(i_gen_write_data));
1311   self->cb = cb;
1312   self->userdata = userdata;
1313   self->maxlength = min(max_length, sizeof(self->buffer));
1314   if (self->maxlength < 0)
1315     self->maxlength = sizeof(self->buffer);
1316   self->filledto = 0;
1317
1318   return self;
1319 }
1320
1321 /*
1322 =item free_gen_write_data(i_gen_write_data *info, int flush)
1323
1324 Cleans up the write buffer.
1325
1326 Will flush any left-over data if I<flush> is non-zero.
1327
1328 Returns non-zero if flush is zero or if info->cb() returns non-zero.
1329
1330 Return zero only if flush is non-zero and info->cb() returns zero.
1331 ie. if it fails.
1332
1333 =cut
1334 */
1335
1336 int free_gen_write_data(i_gen_write_data *info, int flush)
1337 {
1338   int result = !flush || 
1339     info->filledto == 0 ||
1340     info->cb(info->userdata, info->buffer, info->filledto);
1341   myfree(info);
1342
1343   return result;
1344 }
1345
1346 /*
1347 =back
1348
1349 =head1 SEE ALSO
1350
1351 L<Imager>, L<gif.c>
1352
1353 =cut
1354 */