7 image.c - implements most of the basic functions of Imager and much of the rest
13 c = i_color_new(red, green, blue, alpha);
21 image.c implements the basic functions to create and destroy image and
22 color objects for Imager.
24 =head1 FUNCTION REFERENCE
26 Some of these functions are internal.
37 #define minmax(a,b,i) ( ((a>=i)?a: ( (b<=i)?b:i )) )
39 /* Hack around an obscure linker bug on solaris - probably due to builtin gcc thingies */
40 static void fake(void) { ceil(1); }
42 static int i_ppix_d(i_img *im, int x, int y, const i_color *val);
43 static int i_gpix_d(i_img *im, int x, int y, i_color *val);
44 static int i_glin_d(i_img *im, int l, int r, int y, i_color *vals);
45 static int i_plin_d(i_img *im, int l, int r, int y, const i_color *vals);
46 static int i_ppixf_d(i_img *im, int x, int y, const i_fcolor *val);
47 static int i_gpixf_d(i_img *im, int x, int y, i_fcolor *val);
48 static int i_glinf_d(i_img *im, int l, int r, int y, i_fcolor *vals);
49 static int i_plinf_d(i_img *im, int l, int r, int y, const i_fcolor *vals);
50 static int i_gsamp_d(i_img *im, int l, int r, int y, i_sample_t *samps, const int *chans, int chan_count);
51 static int i_gsampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps, const int *chans, int chan_count);
52 /*static int i_psamp_d(i_img *im, int l, int r, int y, i_sample_t *samps, int *chans, int chan_count);
53 static int i_psampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps, int *chans, int chan_count);*/
56 =item ICL_new_internal(r, g, b, a)
58 Return a new color object with values passed to it.
60 r - red component (range: 0 - 255)
61 g - green component (range: 0 - 255)
62 b - blue component (range: 0 - 255)
63 a - alpha component (range: 0 - 255)
69 ICL_new_internal(unsigned char r,unsigned char g,unsigned char b,unsigned char a) {
72 mm_log((1,"ICL_new_internal(r %d,g %d,b %d,a %d)\n", r, g, b, a));
74 if ( (cl=mymalloc(sizeof(i_color))) == NULL) m_fatal(2,"malloc() error\n");
79 mm_log((1,"(%p) <- ICL_new_internal\n",cl));
85 =item ICL_set_internal(cl, r, g, b, a)
87 Overwrite a color with new values.
89 cl - pointer to color object
90 r - red component (range: 0 - 255)
91 g - green component (range: 0 - 255)
92 b - blue component (range: 0 - 255)
93 a - alpha component (range: 0 - 255)
99 ICL_set_internal(i_color *cl,unsigned char r,unsigned char g,unsigned char b,unsigned char a) {
100 mm_log((1,"ICL_set_internal(cl* %p,r %d,g %d,b %d,a %d)\n",cl,r,g,b,a));
102 if ( (cl=mymalloc(sizeof(i_color))) == NULL)
103 m_fatal(2,"malloc() error\n");
108 mm_log((1,"(%p) <- ICL_set_internal\n",cl));
114 =item ICL_add(dst, src, ch)
116 Add src to dst inplace - dst is modified.
118 dst - pointer to destination color object
119 src - pointer to color object that is added
120 ch - number of channels
126 ICL_add(i_color *dst,i_color *src,int ch) {
129 tmp=dst->channel[i]+src->channel[i];
130 dst->channel[i]= tmp>255 ? 255:tmp;
137 Dump color information to log - strictly for debugging.
139 cl - pointer to color object
145 ICL_info(i_color const *cl) {
146 mm_log((1,"i_color_info(cl* %p)\n",cl));
147 mm_log((1,"i_color_info: (%d,%d,%d,%d)\n",cl->rgba.r,cl->rgba.g,cl->rgba.b,cl->rgba.a));
153 Destroy ancillary data for Color object.
155 cl - pointer to color object
161 ICL_DESTROY(i_color *cl) {
162 mm_log((1,"ICL_DESTROY(cl* %p)\n",cl));
167 =item i_fcolor_new(double r, double g, double b, double a)
171 i_fcolor *i_fcolor_new(double r, double g, double b, double a) {
174 mm_log((1,"i_fcolor_new(r %g,g %g,b %g,a %g)\n", r, g, b, a));
176 if ( (cl=mymalloc(sizeof(i_fcolor))) == NULL) m_fatal(2,"malloc() error\n");
181 mm_log((1,"(%p) <- i_fcolor_new\n",cl));
187 =item i_fcolor_destroy(i_fcolor *cl)
191 void i_fcolor_destroy(i_fcolor *cl) {
196 =item IIM_base_8bit_direct (static)
198 A static i_img object used to initialize direct 8-bit per sample images.
202 static i_img IIM_base_8bit_direct =
204 0, /* channels set */
205 0, 0, 0, /* xsize, ysize, bytes */
208 i_direct_type, /* type */
211 { 0, 0, NULL }, /* tags */
214 i_ppix_d, /* i_f_ppix */
215 i_ppixf_d, /* i_f_ppixf */
216 i_plin_d, /* i_f_plin */
217 i_plinf_d, /* i_f_plinf */
218 i_gpix_d, /* i_f_gpix */
219 i_gpixf_d, /* i_f_gpixf */
220 i_glin_d, /* i_f_glin */
221 i_glinf_d, /* i_f_glinf */
222 i_gsamp_d, /* i_f_gsamp */
223 i_gsampf_d, /* i_f_gsampf */
227 NULL, /* i_f_addcolors */
228 NULL, /* i_f_getcolors */
229 NULL, /* i_f_colorcount */
230 NULL, /* i_f_maxcolors */
231 NULL, /* i_f_findcolor */
232 NULL, /* i_f_setcolors */
234 NULL, /* i_f_destroy */
237 /*static void set_8bit_direct(i_img *im) {
238 im->i_f_ppix = i_ppix_d;
239 im->i_f_ppixf = i_ppixf_d;
240 im->i_f_plin = i_plin_d;
241 im->i_f_plinf = i_plinf_d;
242 im->i_f_gpix = i_gpix_d;
243 im->i_f_gpixf = i_gpixf_d;
244 im->i_f_glin = i_glin_d;
245 im->i_f_glinf = i_glinf_d;
248 im->i_f_addcolor = NULL;
249 im->i_f_getcolor = NULL;
250 im->i_f_colorcount = NULL;
251 im->i_f_findcolor = NULL;
255 =item IIM_new(x, y, ch)
257 =item i_img_8_new(x, y, ch)
259 =category Image creation
261 Creates a new image object I<x> pixels wide, and I<y> pixels high with
269 IIM_new(int x,int y,int ch) {
271 mm_log((1,"IIM_new(x %d,y %d,ch %d)\n",x,y,ch));
273 im=i_img_empty_ch(NULL,x,y,ch);
275 mm_log((1,"(%p) <- IIM_new\n",im));
281 IIM_DESTROY(i_img *im) {
282 mm_log((1,"IIM_DESTROY(im* %p)\n",im));
290 Create new image reference - notice that this isn't an object yet and
291 this should be fixed asap.
301 mm_log((1,"i_img_struct()\n"));
302 if ( (im=mymalloc(sizeof(i_img))) == NULL)
303 m_fatal(2,"malloc() error\n");
305 *im = IIM_base_8bit_direct;
313 mm_log((1,"(%p) <- i_img_struct\n",im));
318 =item i_img_empty(im, x, y)
320 Re-new image reference (assumes 3 channels)
323 x - xsize of destination image
324 y - ysize of destination image
326 **FIXME** what happens if a live image is passed in here?
328 Should this just call i_img_empty_ch()?
334 i_img_empty(i_img *im,int x,int y) {
335 mm_log((1,"i_img_empty(*im %p, x %d, y %d)\n",im, x, y));
336 return i_img_empty_ch(im, x, y, 3);
340 =item i_img_empty_ch(im, x, y, ch)
342 Re-new image reference
345 x - xsize of destination image
346 y - ysize of destination image
347 ch - number of channels
353 i_img_empty_ch(i_img *im,int x,int y,int ch) {
356 mm_log((1,"i_img_empty_ch(*im %p, x %d, y %d, ch %d)\n", im, x, y, ch));
358 if (x < 1 || y < 1) {
359 i_push_error(0, "Image sizes must be positive");
362 if (ch < 1 || ch > MAXCHANNELS) {
363 i_push_errorf(0, "channels must be between 1 and %d", MAXCHANNELS);
366 /* check this multiplication doesn't overflow */
368 if (bytes / y / ch != x) {
369 i_push_errorf(0, "integer overflow calculating image allocation");
374 if ( (im=mymalloc(sizeof(i_img))) == NULL)
375 m_fatal(2,"malloc() error\n");
377 memcpy(im, &IIM_base_8bit_direct, sizeof(i_img));
378 i_tags_new(&im->tags);
382 im->ch_mask = MAXINT;
384 if ( (im->idata=mymalloc(im->bytes)) == NULL)
385 m_fatal(2,"malloc() error\n");
386 memset(im->idata,0,(size_t)im->bytes);
390 mm_log((1,"(%p) <- i_img_empty_ch\n",im));
395 =item i_img_exorcise(im)
405 i_img_exorcise(i_img *im) {
406 mm_log((1,"i_img_exorcise(im* 0x%x)\n",im));
407 i_tags_destroy(&im->tags);
409 (im->i_f_destroy)(im);
410 if (im->idata != NULL) { myfree(im->idata); }
416 im->i_f_ppix=i_ppix_d;
417 im->i_f_gpix=i_gpix_d;
418 im->i_f_plin=i_plin_d;
419 im->i_f_glin=i_glin_d;
424 =item i_img_destroy(im)
428 Destroy image and free data via exorcise.
436 i_img_destroy(i_img *im) {
437 mm_log((1,"i_img_destroy(im %p)\n",im));
439 if (im) { myfree(im); }
443 =item i_img_info(im, info)
447 Return image information
450 info - pointer to array to return data
452 info is an array of 4 integers with the following values:
457 info[3] - channel mask
464 i_img_info(i_img *im,int *info) {
465 mm_log((1,"i_img_info(im 0x%x)\n",im));
467 mm_log((1,"i_img_info: xsize=%d ysize=%d channels=%d mask=%ud\n",im->xsize,im->ysize,im->channels,im->ch_mask));
468 mm_log((1,"i_img_info: idata=0x%d\n",im->idata));
471 info[2] = im->channels;
472 info[3] = im->ch_mask;
482 =item i_img_setmask(im, ch_mask)
484 Set the image channel mask for I<im> to I<ch_mask>.
489 i_img_setmask(i_img *im,int ch_mask) { im->ch_mask=ch_mask; }
493 =item i_img_getmask(im)
495 Get the image channel mask for I<im>.
500 i_img_getmask(i_img *im) { return im->ch_mask; }
503 =item i_img_getchannels(im)
505 Get the number of channels in I<im>.
510 i_img_getchannels(i_img *im) { return im->channels; }
515 =item i_copyto_trans(im, src, x1, y1, x2, y2, tx, ty, trans)
519 (x1,y1) (x2,y2) specifies the region to copy (in the source coordinates)
520 (tx,ty) specifies the upper left corner for the target image.
521 pass NULL in trans for non transparent i_colors.
527 i_copyto_trans(i_img *im,i_img *src,int x1,int y1,int x2,int y2,int tx,int ty,const i_color *trans) {
529 int x,y,t,ttx,tty,tt,ch;
531 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",
532 im, src, x1, y1, x2, y2, tx, ty, trans));
534 if (x2<x1) { t=x1; x1=x2; x2=t; }
535 if (y2<y1) { t=y1; y1=y2; y2=t; }
547 for(ch=0;ch<im->channels;ch++) if (trans->channel[ch]!=pv.channel[ch]) tt++;
548 if (tt) i_ppix(im,ttx,tty,&pv);
549 } else i_ppix(im,ttx,tty,&pv);
557 =item i_copyto(dest, src, x1, y1, x2, y2, tx, ty)
561 Copies image data from the area (x1,y1)-[x2,y2] in the source image to
562 a rectangle the same size with it's top-left corner at (tx,ty) in the
565 If x1 > x2 or y1 > y2 then the corresponding co-ordinates are swapped.
571 i_copyto(i_img *im, i_img *src, int x1, int y1, int x2, int y2, int tx, int ty) {
572 int x, y, t, ttx, tty;
574 if (x2<x1) { t=x1; x1=x2; x2=t; }
575 if (y2<y1) { t=y1; y1=y2; y2=t; }
577 /* adjust everything equally */
587 if (x1 >= src->xsize || y1 >= src->ysize)
588 return; /* nothing to do */
593 if (x1 == x2 || y1 == y2)
594 return; /* nothing to do */
596 mm_log((1,"i_copyto(im* %p, src %p, x1 %d, y1 %d, x2 %d, y2 %d, tx %d, ty %d)\n",
597 im, src, x1, y1, x2, y2, tx, ty));
599 if (im->bits == i_8_bits) {
600 i_color *row = mymalloc(sizeof(i_color) * (x2-x1));
602 for(y=y1; y<y2; y++) {
604 i_glin(src, x1, x2, y, row);
605 i_plin(im, tx, tx+x2-x1, tty, row);
613 for(y=y1; y<y2; y++) {
615 for(x=x1; x<x2; x++) {
616 i_gpixf(src, x, y, &pv);
617 i_ppixf(im, ttx, tty, &pv);
630 Creates a new image that is a copy of src.
632 Tags are not copied, only the image data.
642 i_img *im = i_sametype(src, src->xsize, src->ysize);
644 mm_log((1,"i_copy(src %p)\n", src));
651 if (src->type == i_direct_type) {
652 if (src->bits == i_8_bits) {
654 pv = mymalloc(sizeof(i_color) * x1);
656 for (y = 0; y < y1; ++y) {
657 i_glin(src, 0, x1, y, pv);
658 i_plin(im, 0, x1, y, pv);
665 pv = mymalloc(sizeof(i_fcolor) * x1);
666 for (y = 0; y < y1; ++y) {
667 i_glinf(src, 0, x1, y, pv);
668 i_plinf(im, 0, x1, y, pv);
680 i_img_pal_new_low(im, x1, y1, src->channels, i_maxcolors(src));
681 /* copy across the palette */
682 count = i_colorcount(src);
683 for (index = 0; index < count; ++index) {
684 i_getcolors(src, index, &temp, 1);
685 i_addcolors(im, &temp, 1);
688 vals = mymalloc(sizeof(i_palidx) * x1);
689 for (y = 0; y < y1; ++y) {
690 i_gpal(src, 0, x1, y, vals);
691 i_ppal(im, 0, x1, y, vals);
701 =item i_rubthru(im, src, tx, ty, src_minx, src_miny, src_maxx, src_maxy )
705 Takes the sub image I<src[src_minx, src_maxx)[src_miny, src_maxy)> and
706 overlays it at (I<tx>,I<ty>) on the image object.
708 The alpha channel of each pixel in I<src> is used to control how much
709 the existing colour in I<im> is replaced, if it is 255 then the colour
710 is completely replaced, if it is 0 then the original colour is left
717 i_rubthru(i_img *im, i_img *src, int tx, int ty, int src_minx, int src_miny,
718 int src_maxx, int src_maxy) {
725 mm_log((1,"i_rubthru(im %p, src %p, tx %d, ty %d, src_minx %d, "
726 "src_miny %d, src_maxx %d, src_maxy %d)\n",
727 im, src, tx, ty, src_minx, src_miny, src_maxx, src_maxy));
730 if (im->channels == 3 && src->channels == 4) {
732 chans[0] = 0; chans[1] = 1; chans[2] = 2;
735 else if (im->channels == 3 && src->channels == 2) {
737 chans[0] = chans[1] = chans[2] = 0;
740 else if (im->channels == 1 && src->channels == 2) {
746 i_push_error(0, "rubthru can only work where (dest, src) channels are (3,4), (3,2) or (1,2)");
751 /* if you change this code, please make sure the else branch is
752 changed in a similar fashion - TC */
754 i_color pv, orig, dest;
756 for(y = src_miny; y < src_maxy; y++) {
758 for(x = src_minx; x < src_maxx; x++) {
759 i_gpix(src, x, y, &pv);
760 i_gpix(im, ttx, tty, &orig);
761 alpha = pv.channel[alphachan];
762 for (ch = 0; ch < chancount; ++ch) {
763 dest.channel[ch] = (alpha * pv.channel[chans[ch]]
764 + (255 - alpha) * orig.channel[ch])/255;
766 i_ppix(im, ttx, tty, &dest);
774 i_fcolor pv, orig, dest;
777 for(y = src_miny; y < src_maxy; y++) {
779 for(x = src_minx; x < src_maxx; x++) {
780 i_gpixf(src, x, y, &pv);
781 i_gpixf(im, ttx, tty, &orig);
782 alpha = pv.channel[alphachan];
783 for (ch = 0; ch < chancount; ++ch) {
784 dest.channel[ch] = alpha * pv.channel[chans[ch]]
785 + (1 - alpha) * orig.channel[ch];
787 i_ppixf(im, ttx, tty, &dest);
799 =item i_flipxy(im, axis)
801 Flips the image inplace around the axis specified.
802 Returns 0 if parameters are invalid.
805 axis - 0 = x, 1 = y, 2 = both
811 i_flipxy(i_img *im, int direction) {
812 int x, x2, y, y2, xm, ym;
816 mm_log((1, "i_flipxy(im %p, direction %d)\n", im, direction ));
821 case XAXIS: /* Horizontal flip */
824 for(y=0; y<ym; y++) {
826 for(x=0; x<xm; x++) {
828 i_gpix(im, x, y, &val1);
829 i_gpix(im, x2, y, &val2);
830 i_ppix(im, x, y, &val2);
831 i_ppix(im, x2, y, &val1);
836 case YAXIS: /* Vertical flip */
840 for(y=0; y<ym; y++) {
841 for(x=0; x<xm; x++) {
843 i_gpix(im, x, y, &val1);
844 i_gpix(im, x, y2, &val2);
845 i_ppix(im, x, y, &val2);
846 i_ppix(im, x, y2, &val1);
851 case XYAXIS: /* Horizontal and Vertical flip */
855 for(y=0; y<ym; y++) {
857 for(x=0; x<xm; x++) {
859 i_gpix(im, x, y, &val1);
860 i_gpix(im, x2, y2, &val2);
861 i_ppix(im, x, y, &val2);
862 i_ppix(im, x2, y2, &val1);
864 i_gpix(im, x2, y, &val1);
865 i_gpix(im, x, y2, &val2);
866 i_ppix(im, x2, y, &val2);
867 i_ppix(im, x, y2, &val1);
872 if (xm*2 != xs) { /* odd number of column */
873 mm_log((1, "i_flipxy: odd number of columns\n"));
876 for(y=0; y<ym; y++) {
878 i_gpix(im, x, y, &val1);
879 i_gpix(im, x, y2, &val2);
880 i_ppix(im, x, y, &val2);
881 i_ppix(im, x, y2, &val1);
885 if (ym*2 != ys) { /* odd number of rows */
886 mm_log((1, "i_flipxy: odd number of rows\n"));
889 for(x=0; x<xm; x++) {
891 i_gpix(im, x, y, &val1);
892 i_gpix(im, x2, y, &val2);
893 i_ppix(im, x, y, &val2);
894 i_ppix(im, x2, y, &val1);
900 mm_log((1, "i_flipxy: direction is invalid\n" ));
918 if ((x >= 2.0) || (x <= -2.0)) return (0.0);
919 else if (x == 0.0) return (1.0);
920 else return(sin(PIx) / PIx * sin(PIx2) / PIx2);
925 =item i_scaleaxis(im, value, axis)
927 Returns a new image object which is I<im> scaled by I<value> along
928 wither the x-axis (I<axis> == 0) or the y-axis (I<axis> == 1).
934 i_scaleaxis(i_img *im, float Value, int Axis) {
935 int hsize, vsize, i, j, k, l, lMax, iEnd, jEnd;
936 int LanczosWidthFactor;
937 float *l0, *l1, OldLocation;
940 float F, PictureValue[MAXCHANNELS];
942 i_color val,val1,val2;
945 mm_log((1,"i_scaleaxis(im %p,Value %.2f,Axis %d)\n",im,Value,Axis));
949 hsize = (int)(0.5 + im->xsize * Value);
952 Value = 1.0 / im->xsize;
960 vsize = (int)(0.5 + im->ysize * Value);
964 Value = 1.0 / im->ysize;
971 new_img = i_img_empty_ch(NULL, hsize, vsize, im->channels);
973 /* 1.4 is a magic number, setting it to 2 will cause rather blurred images */
974 LanczosWidthFactor = (Value >= 1) ? 1 : (int) (1.4/Value);
975 lMax = LanczosWidthFactor << 1;
977 l0 = mymalloc(lMax * sizeof(float));
978 l1 = mymalloc(lMax * sizeof(float));
980 for (j=0; j<jEnd; j++) {
981 OldLocation = ((float) j) / Value;
982 T = (int) (OldLocation);
983 F = OldLocation - (float) T;
985 for (l = 0; l<lMax; l++) {
986 l0[lMax-l-1] = Lanczos(((float) (lMax-l-1) + F) / (float) LanczosWidthFactor);
987 l1[l] = Lanczos(((float) (l+1) - F) / (float) LanczosWidthFactor);
990 /* Make sure filter is normalized */
992 for(l=0; l<lMax; l++) {
996 t /= (float)LanczosWidthFactor;
998 for(l=0; l<lMax; l++) {
1003 if (Axis == XAXIS) {
1005 for (i=0; i<iEnd; i++) {
1006 for (k=0; k<im->channels; k++) PictureValue[k] = 0.0;
1007 for (l=0; l<lMax; l++) {
1008 int mx = T-lMax+l+1;
1010 mx = (mx < 0) ? 0 : mx;
1011 Mx = (Mx >= im->xsize) ? im->xsize-1 : Mx;
1013 i_gpix(im, Mx, i, &val1);
1014 i_gpix(im, mx, i, &val2);
1016 for (k=0; k<im->channels; k++) {
1017 PictureValue[k] += l1[l] * val1.channel[k];
1018 PictureValue[k] += l0[lMax-l-1] * val2.channel[k];
1021 for(k=0;k<im->channels;k++) {
1022 psave = (short)(0.5+(PictureValue[k] / LanczosWidthFactor));
1023 val.channel[k]=minmax(0,255,psave);
1025 i_ppix(new_img, j, i, &val);
1030 for (i=0; i<iEnd; i++) {
1031 for (k=0; k<im->channels; k++) PictureValue[k] = 0.0;
1032 for (l=0; l < lMax; l++) {
1033 int mx = T-lMax+l+1;
1035 mx = (mx < 0) ? 0 : mx;
1036 Mx = (Mx >= im->ysize) ? im->ysize-1 : Mx;
1038 i_gpix(im, i, Mx, &val1);
1039 i_gpix(im, i, mx, &val2);
1040 for (k=0; k<im->channels; k++) {
1041 PictureValue[k] += l1[l] * val1.channel[k];
1042 PictureValue[k] += l0[lMax-l-1] * val2.channel[k];
1045 for (k=0; k<im->channels; k++) {
1046 psave = (short)(0.5+(PictureValue[k] / LanczosWidthFactor));
1047 val.channel[k] = minmax(0, 255, psave);
1049 i_ppix(new_img, i, j, &val);
1057 mm_log((1,"(%p) <- i_scaleaxis\n", new_img));
1064 =item i_scale_nn(im, scx, scy)
1066 Scale by using nearest neighbor
1067 Both axes scaled at the same time since
1068 nothing is gained by doing it in two steps
1075 i_scale_nn(i_img *im, float scx, float scy) {
1077 int nxsize,nysize,nx,ny;
1081 mm_log((1,"i_scale_nn(im 0x%x,scx %.2f,scy %.2f)\n",im,scx,scy));
1083 nxsize = (int) ((float) im->xsize * scx);
1086 scx = 1 / im->xsize;
1088 nysize = (int) ((float) im->ysize * scy);
1091 scy = 1 / im->ysize;
1094 new_img=i_img_empty_ch(NULL,nxsize,nysize,im->channels);
1096 for(ny=0;ny<nysize;ny++) for(nx=0;nx<nxsize;nx++) {
1097 i_gpix(im,((float)nx)/scx,((float)ny)/scy,&val);
1098 i_ppix(new_img,nx,ny,&val);
1101 mm_log((1,"(0x%x) <- i_scale_nn\n",new_img));
1107 =item i_sametype(i_img *im, int xsize, int ysize)
1109 =category Image creation
1111 Returns an image of the same type (sample size, channels, paletted/direct).
1113 For paletted images the palette is copied from the source.
1118 i_img *i_sametype(i_img *src, int xsize, int ysize) {
1119 if (src->type == i_direct_type) {
1120 if (src->bits == 8) {
1121 return i_img_empty_ch(NULL, xsize, ysize, src->channels);
1123 else if (src->bits == i_16_bits) {
1124 return i_img_16_new(xsize, ysize, src->channels);
1126 else if (src->bits == i_double_bits) {
1127 return i_img_double_new(xsize, ysize, src->channels);
1130 i_push_error(0, "Unknown image bits");
1138 i_img *targ = i_img_pal_new(xsize, ysize, src->channels, i_maxcolors(src));
1139 for (i = 0; i < i_colorcount(src); ++i) {
1140 i_getcolors(src, i, &col, 1);
1141 i_addcolors(targ, &col, 1);
1149 =item i_sametype_chans(i_img *im, int xsize, int ysize, int channels)
1151 =category Image creation
1153 Returns an image of the same type (sample size).
1155 For paletted images the equivalent direct type is returned.
1160 i_img *i_sametype_chans(i_img *src, int xsize, int ysize, int channels) {
1161 if (src->bits == 8) {
1162 return i_img_empty_ch(NULL, xsize, ysize, channels);
1164 else if (src->bits == i_16_bits) {
1165 return i_img_16_new(xsize, ysize, channels);
1167 else if (src->bits == i_double_bits) {
1168 return i_img_double_new(xsize, ysize, channels);
1171 i_push_error(0, "Unknown image bits");
1177 =item i_transform(im, opx, opxl, opy, opyl, parm, parmlen)
1179 Spatially transforms I<im> returning a new image.
1181 opx for a length of opxl and opy for a length of opy are arrays of
1182 operators that modify the x and y positions to retreive the pixel data from.
1184 parm and parmlen define extra parameters that the operators may use.
1186 Note that this function is largely superseded by the more flexible
1187 L<transform.c/i_transform2>.
1189 Returns the new image.
1191 The operators for this function are defined in L<stackmach.c>.
1196 i_transform(i_img *im, int *opx,int opxl,int *opy,int opyl,double parm[],int parmlen) {
1198 int nxsize,nysize,nx,ny;
1202 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));
1205 nysize = im->ysize ;
1207 new_img=i_img_empty_ch(NULL,nxsize,nysize,im->channels);
1208 /* fprintf(stderr,"parm[2]=%f\n",parm[2]); */
1209 for(ny=0;ny<nysize;ny++) for(nx=0;nx<nxsize;nx++) {
1210 /* parm[parmlen-2]=(double)nx;
1211 parm[parmlen-1]=(double)ny; */
1216 /* fprintf(stderr,"(%d,%d) ->",nx,ny); */
1217 rx=i_op_run(opx,opxl,parm,parmlen);
1218 ry=i_op_run(opy,opyl,parm,parmlen);
1219 /* fprintf(stderr,"(%f,%f)\n",rx,ry); */
1220 i_gpix(im,rx,ry,&val);
1221 i_ppix(new_img,nx,ny,&val);
1224 mm_log((1,"(0x%x) <- i_transform\n",new_img));
1229 =item i_img_diff(im1, im2)
1231 Calculates the sum of the squares of the differences between
1232 correspoding channels in two images.
1234 If the images are not the same size then only the common area is
1235 compared, hence even if images are different sizes this function
1241 i_img_diff(i_img *im1,i_img *im2) {
1242 int x,y,ch,xb,yb,chb;
1246 mm_log((1,"i_img_diff(im1 0x%x,im2 0x%x)\n",im1,im2));
1248 xb=(im1->xsize<im2->xsize)?im1->xsize:im2->xsize;
1249 yb=(im1->ysize<im2->ysize)?im1->ysize:im2->ysize;
1250 chb=(im1->channels<im2->channels)?im1->channels:im2->channels;
1252 mm_log((1,"i_img_diff: xb=%d xy=%d chb=%d\n",xb,yb,chb));
1255 for(y=0;y<yb;y++) for(x=0;x<xb;x++) {
1256 i_gpix(im1,x,y,&val1);
1257 i_gpix(im2,x,y,&val2);
1259 for(ch=0;ch<chb;ch++) tdiff+=(val1.channel[ch]-val2.channel[ch])*(val1.channel[ch]-val2.channel[ch]);
1261 mm_log((1,"i_img_diff <- (%.2f)\n",tdiff));
1265 /* just a tiny demo of haar wavelets */
1273 i_img *new_img,*new_img2;
1274 i_color val1,val2,dval1,dval2;
1282 /* horizontal pass */
1284 new_img=i_img_empty_ch(NULL,fx*2,fy*2,im->channels);
1285 new_img2=i_img_empty_ch(NULL,fx*2,fy*2,im->channels);
1288 for(y=0;y<my;y++) for(x=0;x<fx;x++) {
1289 i_gpix(im,x*2,y,&val1);
1290 i_gpix(im,x*2+1,y,&val2);
1291 for(ch=0;ch<im->channels;ch++) {
1292 dval1.channel[ch]=(val1.channel[ch]+val2.channel[ch])/2;
1293 dval2.channel[ch]=(255+val1.channel[ch]-val2.channel[ch])/2;
1295 i_ppix(new_img,x,y,&dval1);
1296 i_ppix(new_img,x+fx,y,&dval2);
1299 for(y=0;y<fy;y++) for(x=0;x<mx;x++) {
1300 i_gpix(new_img,x,y*2,&val1);
1301 i_gpix(new_img,x,y*2+1,&val2);
1302 for(ch=0;ch<im->channels;ch++) {
1303 dval1.channel[ch]=(val1.channel[ch]+val2.channel[ch])/2;
1304 dval2.channel[ch]=(255+val1.channel[ch]-val2.channel[ch])/2;
1306 i_ppix(new_img2,x,y,&dval1);
1307 i_ppix(new_img2,x,y+fy,&dval2);
1310 i_img_destroy(new_img);
1315 =item i_count_colors(im, maxc)
1317 returns number of colors or -1
1318 to indicate that it was more than max colors
1323 i_count_colors(i_img *im,int maxc) {
1330 mm_log((1,"i_count_colors(im 0x%08X,maxc %d)\n"));
1337 for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
1338 i_gpix(im,x,y,&val);
1339 colorcnt+=octt_add(ct,val.rgb.r,val.rgb.g,val.rgb.b);
1340 if (colorcnt > maxc) { octt_delete(ct); return -1; }
1349 =head2 8-bit per sample image internal functions
1351 These are the functions installed in an 8-bit per sample image.
1355 =item i_ppix_d(im, x, y, col)
1359 This is the function kept in the i_f_ppix member of an i_img object.
1360 It does a normal store of a pixel into the image with range checking.
1362 Returns 0 if the pixel could be set, -1 otherwise.
1368 i_ppix_d(i_img *im, int x, int y, const i_color *val) {
1371 if ( x>-1 && x<im->xsize && y>-1 && y<im->ysize ) {
1372 for(ch=0;ch<im->channels;ch++)
1373 if (im->ch_mask&(1<<ch))
1374 im->idata[(x+y*im->xsize)*im->channels+ch]=val->channel[ch];
1377 return -1; /* error was clipped */
1381 =item i_gpix_d(im, x, y, &col)
1385 This is the function kept in the i_f_gpix member of an i_img object.
1386 It does normal retrieval of a pixel from the image with range checking.
1388 Returns 0 if the pixel could be set, -1 otherwise.
1394 i_gpix_d(i_img *im, int x, int y, i_color *val) {
1396 if (x>-1 && x<im->xsize && y>-1 && y<im->ysize) {
1397 for(ch=0;ch<im->channels;ch++)
1398 val->channel[ch]=im->idata[(x+y*im->xsize)*im->channels+ch];
1401 for(ch=0;ch<im->channels;ch++) val->channel[ch] = 0;
1402 return -1; /* error was cliped */
1406 =item i_glin_d(im, l, r, y, vals)
1408 Reads a line of data from the image, storing the pixels at vals.
1410 The line runs from (l,y) inclusive to (r,y) non-inclusive
1412 vals should point at space for (r-l) pixels.
1414 l should never be less than zero (to avoid confusion about where to
1415 put the pixels in vals).
1417 Returns the number of pixels copied (eg. if r, l or y is out of range)
1423 i_glin_d(i_img *im, int l, int r, int y, i_color *vals) {
1425 unsigned char *data;
1426 if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
1429 data = im->idata + (l+y*im->xsize) * im->channels;
1431 for (i = 0; i < count; ++i) {
1432 for (ch = 0; ch < im->channels; ++ch)
1433 vals[i].channel[ch] = *data++;
1443 =item i_plin_d(im, l, r, y, vals)
1445 Writes a line of data into the image, using the pixels at vals.
1447 The line runs from (l,y) inclusive to (r,y) non-inclusive
1449 vals should point at (r-l) pixels.
1451 l should never be less than zero (to avoid confusion about where to
1452 get the pixels in vals).
1454 Returns the number of pixels copied (eg. if r, l or y is out of range)
1460 i_plin_d(i_img *im, int l, int r, int y, const i_color *vals) {
1462 unsigned char *data;
1463 if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
1466 data = im->idata + (l+y*im->xsize) * im->channels;
1468 for (i = 0; i < count; ++i) {
1469 for (ch = 0; ch < im->channels; ++ch) {
1470 if (im->ch_mask & (1 << ch))
1471 *data = vals[i].channel[ch];
1483 =item i_ppixf_d(im, x, y, val)
1489 i_ppixf_d(i_img *im, int x, int y, const i_fcolor *val) {
1492 if ( x>-1 && x<im->xsize && y>-1 && y<im->ysize ) {
1493 for(ch=0;ch<im->channels;ch++)
1494 if (im->ch_mask&(1<<ch)) {
1495 im->idata[(x+y*im->xsize)*im->channels+ch] =
1496 SampleFTo8(val->channel[ch]);
1500 return -1; /* error was clipped */
1504 =item i_gpixf_d(im, x, y, val)
1510 i_gpixf_d(i_img *im, int x, int y, i_fcolor *val) {
1512 if (x>-1 && x<im->xsize && y>-1 && y<im->ysize) {
1513 for(ch=0;ch<im->channels;ch++) {
1515 Sample8ToF(im->idata[(x+y*im->xsize)*im->channels+ch]);
1519 return -1; /* error was cliped */
1523 =item i_glinf_d(im, l, r, y, vals)
1525 Reads a line of data from the image, storing the pixels at vals.
1527 The line runs from (l,y) inclusive to (r,y) non-inclusive
1529 vals should point at space for (r-l) pixels.
1531 l should never be less than zero (to avoid confusion about where to
1532 put the pixels in vals).
1534 Returns the number of pixels copied (eg. if r, l or y is out of range)
1540 i_glinf_d(i_img *im, int l, int r, int y, i_fcolor *vals) {
1542 unsigned char *data;
1543 if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
1546 data = im->idata + (l+y*im->xsize) * im->channels;
1548 for (i = 0; i < count; ++i) {
1549 for (ch = 0; ch < im->channels; ++ch)
1550 vals[i].channel[ch] = Sample8ToF(*data++);
1560 =item i_plinf_d(im, l, r, y, vals)
1562 Writes a line of data into the image, using the pixels at vals.
1564 The line runs from (l,y) inclusive to (r,y) non-inclusive
1566 vals should point at (r-l) pixels.
1568 l should never be less than zero (to avoid confusion about where to
1569 get the pixels in vals).
1571 Returns the number of pixels copied (eg. if r, l or y is out of range)
1577 i_plinf_d(i_img *im, int l, int r, int y, const i_fcolor *vals) {
1579 unsigned char *data;
1580 if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
1583 data = im->idata + (l+y*im->xsize) * im->channels;
1585 for (i = 0; i < count; ++i) {
1586 for (ch = 0; ch < im->channels; ++ch) {
1587 if (im->ch_mask & (1 << ch))
1588 *data = SampleFTo8(vals[i].channel[ch]);
1600 =item i_gsamp_d(i_img *im, int l, int r, int y, i_sample_t *samps, int *chans, int chan_count)
1602 Reads sample values from im for the horizontal line (l, y) to (r-1,y)
1603 for the channels specified by chans, an array of int with chan_count
1606 Returns the number of samples read (which should be (r-l) * bits_set(chan_mask)
1612 i_gsamp_d(i_img *im, int l, int r, int y, i_sample_t *samps,
1613 const int *chans, int chan_count) {
1614 int ch, count, i, w;
1615 unsigned char *data;
1617 if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
1620 data = im->idata + (l+y*im->xsize) * im->channels;
1625 /* make sure we have good channel numbers */
1626 for (ch = 0; ch < chan_count; ++ch) {
1627 if (chans[ch] < 0 || chans[ch] >= im->channels) {
1628 i_push_errorf(0, "No channel %d in this image", chans[ch]);
1632 for (i = 0; i < w; ++i) {
1633 for (ch = 0; ch < chan_count; ++ch) {
1634 *samps++ = data[chans[ch]];
1637 data += im->channels;
1641 for (i = 0; i < w; ++i) {
1642 for (ch = 0; ch < chan_count; ++ch) {
1643 *samps++ = data[ch];
1646 data += im->channels;
1658 =item i_gsampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps, int *chans, int chan_count)
1660 Reads sample values from im for the horizontal line (l, y) to (r-1,y)
1661 for the channels specified by chan_mask, where bit 0 is the first
1664 Returns the number of samples read (which should be (r-l) * bits_set(chan_mask)
1670 i_gsampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps,
1671 const int *chans, int chan_count) {
1672 int ch, count, i, w;
1673 unsigned char *data;
1674 for (ch = 0; ch < chan_count; ++ch) {
1675 if (chans[ch] < 0 || chans[ch] >= im->channels) {
1676 i_push_errorf(0, "No channel %d in this image", chans[ch]);
1679 if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
1682 data = im->idata + (l+y*im->xsize) * im->channels;
1687 /* make sure we have good channel numbers */
1688 for (ch = 0; ch < chan_count; ++ch) {
1689 if (chans[ch] < 0 || chans[ch] >= im->channels) {
1690 i_push_errorf(0, "No channel %d in this image", chans[ch]);
1694 for (i = 0; i < w; ++i) {
1695 for (ch = 0; ch < chan_count; ++ch) {
1696 *samps++ = Sample8ToF(data[chans[ch]]);
1699 data += im->channels;
1703 for (i = 0; i < w; ++i) {
1704 for (ch = 0; ch < chan_count; ++ch) {
1705 *samps++ = Sample8ToF(data[ch]);
1708 data += im->channels;
1721 =head2 Image method wrappers
1723 These functions provide i_fsample_t functions in terms of their
1724 i_sample_t versions.
1728 =item i_ppixf_fp(i_img *im, int x, int y, i_fcolor *pix)
1733 int i_ppixf_fp(i_img *im, int x, int y, const i_fcolor *pix) {
1737 for (ch = 0; ch < im->channels; ++ch)
1738 temp.channel[ch] = SampleFTo8(pix->channel[ch]);
1740 return i_ppix(im, x, y, &temp);
1744 =item i_gpixf_fp(i_img *im, int x, int y, i_fcolor *pix)
1748 int i_gpixf_fp(i_img *im, int x, int y, i_fcolor *pix) {
1752 if (i_gpix(im, x, y, &temp)) {
1753 for (ch = 0; ch < im->channels; ++ch)
1754 pix->channel[ch] = Sample8ToF(temp.channel[ch]);
1762 =item i_plinf_fp(i_img *im, int l, int r, int y, i_fcolor *pix)
1766 int i_plinf_fp(i_img *im, int l, int r, int y, const i_fcolor *pix) {
1769 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
1775 work = mymalloc(sizeof(i_color) * (r-l));
1776 for (i = 0; i < r-l; ++i) {
1777 for (ch = 0; ch < im->channels; ++ch)
1778 work[i].channel[ch] = SampleFTo8(pix[i].channel[ch]);
1780 ret = i_plin(im, l, r, y, work);
1795 =item i_glinf_fp(i_img *im, int l, int r, int y, i_fcolor *pix)
1799 int i_glinf_fp(i_img *im, int l, int r, int y, i_fcolor *pix) {
1802 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
1808 work = mymalloc(sizeof(i_color) * (r-l));
1809 ret = i_plin(im, l, r, y, work);
1810 for (i = 0; i < r-l; ++i) {
1811 for (ch = 0; ch < im->channels; ++ch)
1812 pix[i].channel[ch] = Sample8ToF(work[i].channel[ch]);
1828 =item i_gsampf_fp(i_img *im, int l, int r, int y, i_fsample_t *samp, int *chans, int chan_count)
1832 int i_gsampf_fp(i_img *im, int l, int r, int y, i_fsample_t *samp,
1833 int const *chans, int chan_count) {
1836 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
1842 work = mymalloc(sizeof(i_sample_t) * (r-l));
1843 ret = i_gsamp(im, l, r, y, work, chans, chan_count);
1844 for (i = 0; i < ret; ++i) {
1845 samp[i] = Sample8ToF(work[i]);
1863 =head2 Palette wrapper functions
1865 Used for virtual images, these forward palette calls to a wrapped image,
1866 assuming the wrapped image is the first pointer in the structure that
1867 im->ext_data points at.
1871 =item i_addcolors_forward(i_img *im, const i_color *colors, int count)
1875 int i_addcolors_forward(i_img *im, const i_color *colors, int count) {
1876 return i_addcolors(*(i_img **)im->ext_data, colors, count);
1880 =item i_getcolors_forward(i_img *im, int i, i_color *color, int count)
1884 int i_getcolors_forward(i_img *im, int i, i_color *color, int count) {
1885 return i_getcolors(*(i_img **)im->ext_data, i, color, count);
1889 =item i_setcolors_forward(i_img *im, int i, const i_color *color, int count)
1893 int i_setcolors_forward(i_img *im, int i, const i_color *color, int count) {
1894 return i_setcolors(*(i_img **)im->ext_data, i, color, count);
1898 =item i_colorcount_forward(i_img *im)
1902 int i_colorcount_forward(i_img *im) {
1903 return i_colorcount(*(i_img **)im->ext_data);
1907 =item i_maxcolors_forward(i_img *im)
1911 int i_maxcolors_forward(i_img *im) {
1912 return i_maxcolors(*(i_img **)im->ext_data);
1916 =item i_findcolor_forward(i_img *im, const i_color *color, i_palidx *entry)
1920 int i_findcolor_forward(i_img *im, const i_color *color, i_palidx *entry) {
1921 return i_findcolor(*(i_img **)im->ext_data, color, entry);
1927 =head2 Stream reading and writing wrapper functions
1931 =item i_gen_reader(i_gen_read_data *info, char *buf, int length)
1933 Performs general read buffering for file readers that permit reading
1934 to be done through a callback.
1936 The final callback gets two parameters, a I<need> value, and a I<want>
1937 value, where I<need> is the amount of data that the file library needs
1938 to read, and I<want> is the amount of space available in the buffer
1939 maintained by these functions.
1941 This means if you need to read from a stream that you don't know the
1942 length of, you can return I<need> bytes, taking the performance hit of
1943 possibly expensive callbacks (eg. back to perl code), or if you are
1944 reading from a stream where it doesn't matter if some data is lost, or
1945 if the total length of the stream is known, you can return I<want>
1952 i_gen_reader(i_gen_read_data *gci, char *buf, int length) {
1955 if (length < gci->length - gci->cpos) {
1957 memcpy(buf, gci->buffer+gci->cpos, length);
1958 gci->cpos += length;
1963 memcpy(buf, gci->buffer+gci->cpos, gci->length-gci->cpos);
1964 total += gci->length - gci->cpos;
1965 length -= gci->length - gci->cpos;
1966 buf += gci->length - gci->cpos;
1967 if (length < (int)sizeof(gci->buffer)) {
1971 && (did_read = (gci->cb)(gci->userdata, gci->buffer, length,
1972 sizeof(gci->buffer))) > 0) {
1974 gci->length = did_read;
1976 copy_size = i_min(length, gci->length);
1977 memcpy(buf, gci->buffer, copy_size);
1978 gci->cpos += copy_size;
1981 length -= copy_size;
1985 /* just read the rest - too big for our buffer*/
1987 while ((did_read = (gci->cb)(gci->userdata, buf, length, length)) > 0) {
1997 =item i_gen_read_data_new(i_read_callback_t cb, char *userdata)
1999 For use by callback file readers to initialize the reader buffer.
2001 Allocates, initializes and returns the reader buffer.
2003 See also L<image.c/free_gen_read_data> and L<image.c/i_gen_reader>.
2008 i_gen_read_data_new(i_read_callback_t cb, char *userdata) {
2009 i_gen_read_data *self = mymalloc(sizeof(i_gen_read_data));
2011 self->userdata = userdata;
2019 =item i_free_gen_read_data(i_gen_read_data *)
2025 void i_free_gen_read_data(i_gen_read_data *self) {
2030 =item i_gen_writer(i_gen_write_data *info, char const *data, int size)
2032 Performs write buffering for a callback based file writer.
2034 Failures are considered fatal, if a write fails then data will be
2041 i_gen_write_data *self,
2045 if (self->filledto && self->filledto+size > self->maxlength) {
2046 if (self->cb(self->userdata, self->buffer, self->filledto)) {
2054 if (self->filledto+size <= self->maxlength) {
2056 memcpy(self->buffer+self->filledto, data, size);
2057 self->filledto += size;
2060 /* doesn't fit - hand it off */
2061 return self->cb(self->userdata, data, size);
2065 =item i_gen_write_data_new(i_write_callback_t cb, char *userdata, int max_length)
2067 Allocates and initializes the data structure used by i_gen_writer.
2069 This should be released with L<image.c/i_free_gen_write_data>
2073 i_gen_write_data *i_gen_write_data_new(i_write_callback_t cb,
2074 char *userdata, int max_length)
2076 i_gen_write_data *self = mymalloc(sizeof(i_gen_write_data));
2078 self->userdata = userdata;
2079 self->maxlength = i_min(max_length, sizeof(self->buffer));
2080 if (self->maxlength < 0)
2081 self->maxlength = sizeof(self->buffer);
2088 =item i_free_gen_write_data(i_gen_write_data *info, int flush)
2090 Cleans up the write buffer.
2092 Will flush any left-over data if I<flush> is non-zero.
2094 Returns non-zero if flush is zero or if info->cb() returns non-zero.
2096 Return zero only if flush is non-zero and info->cb() returns zero.
2102 int i_free_gen_write_data(i_gen_write_data *info, int flush)
2104 int result = !flush ||
2105 info->filledto == 0 ||
2106 info->cb(info->userdata, info->buffer, info->filledto);
2115 =item i_test_format_probe(io_glue *data, int length)
2117 Check the beginning of the supplied file for a 'magic number'
2124 i_test_format_probe(io_glue *data, int length) {
2130 {"\xFF\xD8", "jpeg"},
2136 {"\x89PNG\x0d\x0a\x1a\x0a", "png"},
2149 io_glue_commit_types(data);
2150 rc = data->readcb(data, head, 18);
2151 if (rc == -1) return NULL;
2152 data->seekcb(data, -rc, SEEK_CUR);
2154 for(i=0; i<sizeof(formats)/sizeof(formats[0]); i++) {
2156 ssize_t len = strlen(formats[i].magic);
2157 if (rc<len) continue;
2158 c = !strncmp(formats[i].magic, head, len);
2160 match = formats[i].name;
2166 if (match && !strcmp(match, "jpeg")) {
2167 unsigned int x0, x1;
2168 rc = data->readcb(data, head, 18);
2169 if (rc == -1) return NULL;
2170 x0 = (unsigned char)head[0];
2171 x1 = (unsigned char)head[1];
2172 data->seekcb(data, -rc, SEEK_CUR);
2173 printf("Jpeg reread: %x %x\n", x0, x1);
2179 tga_header_verify(head)) return "tga";
2191 Arnar M. Hrafnkelsson <addi@umich.edu>
2193 Tony Cook <tony@develop-help.com>