switch to using size_t and i_img_dim strictly
[imager.git] / SGI / imsgi.c
1 #include "imsgi.h"
2
3 #include <stdlib.h>
4 #include <errno.h>
5 #include <string.h>
6
7 /* value for imagic */
8 #define SGI_MAGIC 474
9
10 /* values for the storage field */
11 #define SGI_STORAGE_VERBATIM 0
12 #define SGI_STORAGE_RLE 1
13
14 /* values for the colormap field */
15 #define SGI_COLORMAP_NORMAL 0
16 #define SGI_COLORMAP_DITHERED 1
17 #define SGI_COLORMAP_SCREEN 2
18 #define SGI_COLORMAP_COLORMAP 3
19
20 /* we add that little bit to avoid rounding issues */
21 #define SampleFTo16(num) ((int)((num) * 65535.0 + 0.01))
22
23 /* maximum size of an SGI image */
24 #define SGI_DIM_LIMIT 0xFFFF
25
26 typedef struct {
27   unsigned short imagic;
28   unsigned char storagetype;
29   unsigned char BPC;
30   unsigned short dimensions;
31   unsigned short xsize, ysize, zsize;
32   unsigned int pixmin, pixmax;
33   char name[80];
34   unsigned int colormap;
35 } rgb_header;
36
37 static i_img *
38 read_rgb_8_verbatim(i_img *im, io_glue *ig, rgb_header const *hdr);
39 static i_img *
40 read_rgb_8_rle(i_img *im, io_glue *ig, rgb_header const *hdr);
41 static i_img *
42 read_rgb_16_verbatim(i_img *im, io_glue *ig, rgb_header const *hdr);
43 static i_img *
44 read_rgb_16_rle(i_img *im, io_glue *ig, rgb_header const *hdr);
45 static int
46 write_sgi_header(i_img *img, io_glue *ig, int *rle, int *bpc2);
47 static int
48 write_sgi_8_rle(i_img *img, io_glue *ig);
49 static int
50 write_sgi_8_verb(i_img *img, io_glue *ig);
51 static int
52 write_sgi_16_rle(i_img *img, io_glue *ig);
53 static int
54 write_sgi_16_verb(i_img *img, io_glue *ig);
55
56 #define Sample16ToF(num) ((num) / 65535.0)
57
58 #define _STRING(x) #x
59 #define STRING(x) _STRING(x)
60
61 /*
62 =head1 NAME
63
64 rgb.c - implements reading and writing sgi image files, uses io layer.
65
66 =head1 SYNOPSIS
67
68    io_glue *ig = io_new_fd( fd );
69    i_img *im   = i_readrgb_wiol(ig, 0); // disallow partial reads
70    // or 
71    io_glue *ig = io_new_fd( fd );
72    return_code = i_writergb_wiol(im, ig); 
73
74 =head1 DESCRIPTION
75
76 imsgi.c implements the basic functions to read and write portable SGI
77 files.  It uses the iolayer and needs either a seekable source or an
78 entire memory mapped buffer.
79
80 =head1 FUNCTION REFERENCE
81
82 Some of these functions are internal.
83
84 =over
85
86 =cut
87 */
88
89 /*
90 =item rgb_header_unpack(header, headbuf)
91
92 Unpacks the header structure into from buffer and stores
93 in the header structure.
94
95     header - header structure
96     headbuf - buffer to unpack from
97
98 =cut
99 */
100
101
102 static
103 void
104 rgb_header_unpack(rgb_header *header, const unsigned char *headbuf) {
105   header->imagic      = (headbuf[0]<<8) + headbuf[1];
106   header->storagetype = headbuf[2];
107   header->BPC         = headbuf[3];
108   header->dimensions  = (headbuf[4]<<8) + headbuf[5];
109   header->xsize       = (headbuf[6]<<8) + headbuf[7];
110   header->ysize       = (headbuf[8]<<8) + headbuf[9];
111   header->zsize       = (headbuf[10]<<8) + headbuf[11];
112   header->pixmin      = (headbuf[12]<<24) + (headbuf[13]<<16)+(headbuf[14]<<8)+headbuf[15];
113   header->pixmax      = (headbuf[16]<<24) + (headbuf[17]<<16)+(headbuf[18]<<8)+headbuf[19];
114   memcpy(header->name,headbuf+24,80);
115   header->name[79] = '\0';
116   header->colormap    = (headbuf[104]<<24) + (headbuf[105]<<16)+(headbuf[106]<<8)+headbuf[107];
117 }
118
119 /* don't make this a macro */
120 static void
121 store_16(unsigned char *buf, unsigned short value) {
122   buf[0] = value >> 8;
123   buf[1] = value & 0xFF;
124 }
125
126 static void
127 store_32(unsigned char *buf, unsigned long value) {
128   buf[0] = value >> 24;
129   buf[1] = (value >> 16) & 0xFF;
130   buf[2] = (value >> 8) & 0xFF;
131   buf[3] = value & 0xFF;
132 }
133
134 /*
135 =item rgb_header_pack(header, headbuf)
136
137 Packs header structure into buffer for writing.
138
139     header - header structure
140     headbuf - buffer to pack into
141
142 =cut
143 */
144
145 static
146 void
147 rgb_header_pack(const rgb_header *header, unsigned char headbuf[512]) {
148   memset(headbuf, 0, 512);
149   store_16(headbuf, header->imagic);
150   headbuf[2] = header->storagetype;
151   headbuf[3] = header->BPC;
152   store_16(headbuf+4, header->dimensions);
153   store_16(headbuf+6, header->xsize);
154   store_16(headbuf+8, header->ysize);
155   store_16(headbuf+10, header->zsize);
156   store_32(headbuf+12, header->pixmin);
157   store_32(headbuf+16, header->pixmax);
158   memccpy(headbuf+24, header->name, '\0', 80);
159   store_32(headbuf+104, header->colormap);
160 }
161
162 /*
163 =item i_readsgi_wiol(ig, partial)
164
165 Read in an image from the iolayer data source and return the image structure to it.
166 Returns NULL on error.
167
168    ig     - io_glue object
169    length - maximum length to read from data source, before closing it -1 
170             signifies no limit.
171
172 =cut
173 */
174
175 i_img *
176 i_readsgi_wiol(io_glue *ig, int partial) {
177   i_img *img = NULL;
178   int width, height, channels;
179   rgb_header header;
180   unsigned char headbuf[512];
181
182   mm_log((1,"i_readsgi(ig %p, partial %d)\n", ig, partial));
183   i_clear_error();
184
185   if (ig->readcb(ig, headbuf, 512) != 512) {
186     i_push_error(errno, "SGI image: could not read header");
187     return NULL;
188   }
189
190   rgb_header_unpack(&header, headbuf);
191
192   if (header.imagic != SGI_MAGIC) {
193     i_push_error(0, "SGI image: invalid magic number");
194     return NULL;
195   }
196
197   mm_log((1,"imagic:         %d\n", header.imagic));
198   mm_log((1,"storagetype:    %d\n", header.storagetype));
199   mm_log((1,"BPC:            %d\n", header.BPC));
200   mm_log((1,"dimensions:     %d\n", header.dimensions));
201   mm_log((1,"xsize:          %d\n", header.xsize));
202   mm_log((1,"ysize:          %d\n", header.ysize));
203   mm_log((1,"zsize:          %d\n", header.zsize));
204   mm_log((1,"min:            %d\n", header.pixmin));
205   mm_log((1,"max:            %d\n", header.pixmax));
206   mm_log((1,"name [skipped]\n"));
207   mm_log((1,"colormap:       %d\n", header.colormap));
208
209   if (header.colormap != SGI_COLORMAP_NORMAL) {
210     i_push_errorf(0, "SGI image: invalid value for colormap (%d)", header.colormap);
211     return NULL;
212   }
213
214   if (header.BPC != 1 && header.BPC != 2) {
215     i_push_errorf(0, "SGI image: invalid value for BPC (%d)", header.BPC);
216     return NULL;
217   }
218
219   if (header.storagetype != SGI_STORAGE_VERBATIM 
220       && header.storagetype != SGI_STORAGE_RLE) {
221     i_push_error(0, "SGI image: invalid storage type field");
222     return NULL;
223   }
224
225   if (header.pixmin >= header.pixmax) {
226     i_push_error(0, "SGI image: invalid pixmin >= pixmax");
227     return NULL;
228   }
229
230   width    = header.xsize;
231   height   = header.ysize;
232   channels = header.zsize;
233
234   switch (header.dimensions) {
235   case 1:
236     channels = 1;
237     height = 1;
238     break;
239
240   case 2:
241     channels = 1;
242     break;
243
244   case 3:
245     /* fall through and use all of the dimensions */
246     break;
247
248   default:
249     i_push_error(0, "SGI image: invalid dimension field");
250     return NULL;
251   }
252
253   if (!i_int_check_image_file_limits(width, height, channels, header.BPC)) {
254     mm_log((1, "i_readsgi_wiol: image size exceeds limits\n"));
255     return NULL;
256   }
257
258   if (header.BPC == 1) {
259     img = i_img_8_new(width, height, channels);
260     if (!img)
261       goto ErrorReturn;
262
263     switch (header.storagetype) {
264     case SGI_STORAGE_VERBATIM:
265       img = read_rgb_8_verbatim(img, ig, &header);
266       break;
267
268     case SGI_STORAGE_RLE:
269       img = read_rgb_8_rle(img, ig, &header);
270       break;
271
272     default:
273       goto ErrorReturn;
274     }
275   }
276   else {
277     img = i_img_16_new(width, height, channels);
278     if (!img)
279       goto ErrorReturn;
280
281     switch (header.storagetype) {
282     case SGI_STORAGE_VERBATIM:
283       img = read_rgb_16_verbatim(img, ig, &header);
284       break;
285
286     case SGI_STORAGE_RLE:
287       img = read_rgb_16_rle(img, ig, &header);
288       break;
289
290     default:
291       goto ErrorReturn;
292     }
293   }
294
295   if (!img)
296     goto ErrorReturn;
297
298   if (*header.name)
299     i_tags_set(&img->tags, "i_comment", header.name, -1);
300   i_tags_setn(&img->tags, "sgi_pixmin", header.pixmin);
301   i_tags_setn(&img->tags, "sgi_pixmax", header.pixmax);
302   i_tags_setn(&img->tags, "sgi_bpc", header.BPC);
303   i_tags_setn(&img->tags, "sgi_rle", header.storagetype == SGI_STORAGE_RLE);
304   i_tags_set(&img->tags, "i_format", "sgi", -1);
305
306   return img;
307
308  ErrorReturn:
309   if (img) i_img_destroy(img);
310   return NULL;
311 }
312
313 /*
314 =item i_writergb_wiol(img, ig)
315
316 Writes an image in targa format.  Returns 0 on error.
317
318    img    - image to store
319    ig     - io_glue object
320
321 =cut
322 */
323
324 int
325 i_writesgi_wiol(io_glue *ig, i_img *img) {
326   int rle;
327   int bpc2;
328
329   i_clear_error();
330
331   if (img->xsize > SGI_DIM_LIMIT || img->ysize > SGI_DIM_LIMIT) {
332     i_push_error(0, "image too large for SGI");
333     return 0;
334   }
335
336   if (!write_sgi_header(img, ig, &rle, &bpc2))
337     return 0;
338
339   mm_log((1, "format rle %d bpc2 %d\n", rle, bpc2));
340
341   if (bpc2) {
342     if (rle)
343       return write_sgi_16_rle(img, ig);
344     else
345       return write_sgi_16_verb(img, ig);
346   }
347   else {
348     if (rle)
349       return write_sgi_8_rle(img, ig);
350     else
351       return write_sgi_8_verb(img, ig);
352   }
353 }
354
355 static i_img *
356 read_rgb_8_verbatim(i_img *img, io_glue *ig, rgb_header const *header) {
357   i_color *linebuf;
358   unsigned char *databuf;
359   int c, y;
360   int savemask;
361   i_img_dim width = i_img_get_width(img);
362   i_img_dim height = i_img_get_height(img);
363   int channels = i_img_getchannels(img);
364   int pixmin = header->pixmin;
365   int pixmax = header->pixmax;
366   int outmax = pixmax - pixmin;
367   
368   linebuf   = mymalloc(width * sizeof(i_color)); /* checked 31Jul07 TonyC */
369   databuf   = mymalloc(width); /* checked 31Jul07 TonyC */
370
371   savemask = i_img_getmask(img);
372
373   for(c = 0; c < channels; c++) {
374     i_img_setmask(img, 1<<c);
375     for(y = 0; y < height; y++) {
376       int x;
377       
378       if (ig->readcb(ig, databuf, width) != width) {
379         i_push_error(0, "SGI image: cannot read image data");
380         i_img_destroy(img);
381         myfree(linebuf);
382         myfree(databuf);
383         return NULL;
384       }
385
386       if (pixmin == 0 && pixmax == 255) {
387         for(x = 0; x < img->xsize; x++)
388           linebuf[x].channel[c] = databuf[x];
389       }
390       else {
391         for(x = 0; x < img->xsize; x++) {
392           int sample = databuf[x];
393           if (sample < pixmin)
394             sample = 0;
395           else if (sample > pixmax)
396             sample = outmax;
397           else
398             sample -= pixmin;
399             
400           linebuf[x].channel[c] = sample * 255 / outmax;
401         }
402       }
403       
404       i_plin(img, 0, width, height-1-y, linebuf);
405     }
406   }
407   i_img_setmask(img, savemask);
408
409   myfree(linebuf);
410   myfree(databuf);
411   
412   return img;
413 }
414
415 static int
416 read_rle_tables(io_glue *ig, i_img *img,
417                 unsigned long **pstart_tab, unsigned long **plength_tab, 
418                 unsigned long *pmax_length) {
419   i_img_dim height = i_img_get_height(img);
420   int channels = i_img_getchannels(img);
421   unsigned char *databuf;
422   unsigned long *start_tab, *length_tab;
423   unsigned long max_length = 0;
424   int i;
425   size_t databuf_size = (size_t)height * channels * 4;
426   size_t tab_size = (size_t)height * channels * sizeof(unsigned long);
427
428   /* assumption: that the lengths are in bytes rather than in pixels */
429   if (databuf_size / height / channels != 4
430       || tab_size / height / channels != sizeof(unsigned long)) {
431     i_push_error(0, "SGI image: integer overflow calculating allocation size");
432     return 0;
433   }
434   databuf    = mymalloc(height * channels * 4);  /* checked 31Jul07 TonyC */
435   start_tab  = mymalloc(height*channels*sizeof(unsigned long));
436   length_tab = mymalloc(height*channels*sizeof(unsigned long));
437     
438     /* Read offset table */
439   if (ig->readcb(ig, databuf, height * channels * 4) != height * channels * 4) {
440     i_push_error(0, "SGI image: short read reading RLE start table");
441     goto ErrorReturn;
442   }
443
444   for(i = 0; i < height * channels; i++) 
445     start_tab[i] = (databuf[i*4] << 24) | (databuf[i*4+1] << 16) | 
446       (databuf[i*4+2] << 8) | (databuf[i*4+3]);
447
448
449   /* Read length table */
450   if (ig->readcb(ig, databuf, height*channels*4) != height*channels*4) {
451     i_push_error(0, "SGI image: short read reading RLE length table");
452     goto ErrorReturn;
453   }
454
455   for(i=0; i < height * channels; i++) {
456     length_tab[i] = (databuf[i*4] << 24) + (databuf[i*4+1] << 16)+
457       (databuf[i*4+2] << 8) + (databuf[i*4+3]);
458     if (length_tab[i] > max_length)
459       max_length = length_tab[i];
460   }
461
462   mm_log((3, "Offset/length table:\n"));
463   for(i=0; i < height * channels; i++)
464     mm_log((3, "%d: %d/%d\n", i, start_tab[i], length_tab[i]));
465
466   *pstart_tab = start_tab;
467   *plength_tab = length_tab;
468   *pmax_length = max_length;
469
470   myfree(databuf);
471
472   return 1;
473
474  ErrorReturn:
475   myfree(databuf);
476   myfree(start_tab);
477   myfree(length_tab);
478
479   return 0;
480 }
481
482 static i_img *
483 read_rgb_8_rle(i_img *img, io_glue *ig, rgb_header const *header) {
484   i_color *linebuf = NULL;
485   unsigned char *databuf = NULL;
486   unsigned long *start_tab, *length_tab;
487   unsigned long max_length;
488   i_img_dim width = i_img_get_width(img);
489   i_img_dim height = i_img_get_height(img);
490   int channels = i_img_getchannels(img);
491   i_img_dim y;
492   int c;
493   int pixmin = header->pixmin;
494   int pixmax = header->pixmax;
495   int outmax = pixmax - pixmin;
496
497   if (!read_rle_tables(ig, img,  
498                        &start_tab, &length_tab, &max_length)) {
499     i_img_destroy(img);
500     return NULL;
501   }
502
503   mm_log((1, "maxlen for an rle buffer: %d\n", max_length));
504
505   if (max_length > (img->xsize + 1) * 2) {
506     i_push_errorf(0, "SGI image: ridiculous RLE line length %lu", max_length);
507     goto ErrorReturn;
508   }
509
510   linebuf = mymalloc(width*sizeof(i_color)); /* checked 31Jul07 TonyC */
511   databuf = mymalloc(max_length); /* checked 31Jul07 TonyC */
512
513   for(y = 0; y < img->ysize; y++) {
514     for(c = 0; c < channels; c++) {
515       int ci = height * c + y;
516       int datalen = length_tab[ci];
517       unsigned char *inp;
518       i_color *outp;
519       int data_left = datalen;
520       int pixels_left = width;
521       i_sample_t sample;
522       
523       if (ig->seekcb(ig, start_tab[ci], SEEK_SET) != start_tab[ci]) {
524         i_push_error(0, "SGI image: cannot seek to RLE data");
525         goto ErrorReturn;
526       }
527       if (ig->readcb(ig, databuf, datalen) != datalen) {
528         i_push_error(0, "SGI image: cannot read RLE data");
529         goto ErrorReturn;
530       }
531       
532       inp = databuf;
533       outp = linebuf;
534       while (data_left) {
535         int code = *inp++;
536         int count = code & 0x7f;
537         --data_left;
538
539         if (count == 0)
540           break;
541         if (code & 0x80) {
542           /* literal run */
543           /* sanity checks */
544           if (count > pixels_left) {
545             i_push_error(0, "SGI image: literal run overflows scanline");
546             goto ErrorReturn;
547           }
548           if (count > data_left) {
549             i_push_error(0, "SGI image: literal run consumes more data than available");
550             goto ErrorReturn;
551           }
552           /* copy the run */
553           pixels_left -= count;
554           data_left -= count;
555           if (pixmin == 0 && pixmax == 255) {
556             while (count-- > 0) {
557               outp->channel[c] = *inp++;
558               ++outp;
559             }
560           }
561           else {
562             while (count-- > 0) {
563               int sample = *inp++;
564               if (sample < pixmin)
565                 sample = 0;
566               else if (sample > pixmax)
567                 sample = outmax;
568               else
569                 sample -= pixmin;
570               outp->channel[c] = sample * 255 / outmax;
571               ++outp;
572             }
573           }
574         }
575         else {
576           /* RLE run */
577           if (count > pixels_left) {
578             i_push_error(0, "SGI image: RLE run overflows scanline");
579             mm_log((2, "RLE run overflows scanline (y %d chan %d offset %ld len %ld)\n", y, c, start_tab[ci], length_tab[ci]));
580             goto ErrorReturn;
581           }
582           if (data_left < 1) {
583             i_push_error(0, "SGI image: RLE run has no data for pixel");
584             goto ErrorReturn;
585           }
586           sample = *inp++;
587           if (pixmin != 0 || pixmax != 255) {
588             if (sample < pixmin)
589               sample = 0;
590             else if (sample > pixmax)
591               sample = outmax;
592             else
593               sample -= pixmin;
594             sample = sample * 255 / outmax;
595           }
596           --data_left;
597           pixels_left -= count;
598           while (count-- > 0) {
599             outp->channel[c] = sample;
600             ++outp;
601           }
602         }
603       }
604       /* must have a full scanline */
605       if (pixels_left) {
606         i_push_error(0, "SGI image: incomplete RLE scanline");
607         goto ErrorReturn;
608       }
609       /* must have used all of the data */
610       if (data_left) {
611         i_push_errorf(0, "SGI image: unused RLE data");
612         goto ErrorReturn;
613       }
614     }
615     i_plin(img, 0, width, height-1-y, linebuf);
616   }
617
618   myfree(linebuf);
619   myfree(databuf);
620   myfree(start_tab);
621   myfree(length_tab);
622
623   return img;
624
625  ErrorReturn:
626   if (linebuf)
627     myfree(linebuf);
628   if (databuf)
629     myfree(databuf);
630   myfree(start_tab);
631   myfree(length_tab);
632   i_img_destroy(img);
633   return NULL;
634 }
635
636 static i_img *
637 read_rgb_16_verbatim(i_img *img, io_glue *ig, rgb_header const *header) {
638   i_fcolor *linebuf;
639   unsigned char *databuf;
640   int c, y;
641   int savemask;
642   i_img_dim width = i_img_get_width(img);
643   i_img_dim height = i_img_get_height(img);
644   int channels = i_img_getchannels(img);
645   int pixmin = header->pixmin;
646   int pixmax = header->pixmax;
647   int outmax = pixmax - pixmin;
648   
649   linebuf   = mymalloc(width * sizeof(i_fcolor));  /* checked 31Jul07 TonyC */
650   databuf   = mymalloc(width * 2);  /* checked 31Jul07 TonyC */
651
652   savemask = i_img_getmask(img);
653
654   for(c = 0; c < channels; c++) {
655     i_img_setmask(img, 1<<c);
656     for(y = 0; y < height; y++) {
657       int x;
658       
659       if (ig->readcb(ig, databuf, width*2) != width*2) {
660         i_push_error(0, "SGI image: cannot read image data");
661         i_img_destroy(img);
662         myfree(linebuf);
663         myfree(databuf);
664         return NULL;
665       }
666
667       if (pixmin == 0 && pixmax == 65535) {
668         for(x = 0; x < img->xsize; x++)
669           linebuf[x].channel[c] = (databuf[x*2] * 256 + databuf[x*2+1]) / 65535.0;
670       }
671       else {
672         for(x = 0; x < img->xsize; x++) {
673           int sample = databuf[x*2] * 256 + databuf[x*2+1];
674           if (sample < pixmin)
675             sample = 0;
676           else if (sample > pixmax)
677             sample = outmax;
678           else
679             sample -= pixmin;
680             
681           linebuf[x].channel[c] = (double)sample / outmax;
682         }
683       }
684       
685       i_plinf(img, 0, width, height-1-y, linebuf);
686     }
687   }
688   i_img_setmask(img, savemask);
689
690   myfree(linebuf);
691   myfree(databuf);
692   
693   return img;
694 }
695
696 static i_img *
697 read_rgb_16_rle(i_img *img, io_glue *ig, rgb_header const *header) {
698   i_fcolor *linebuf = NULL;
699   unsigned char *databuf = NULL;
700   unsigned long *start_tab, *length_tab;
701   unsigned long max_length;
702   i_img_dim width = i_img_get_width(img);
703   i_img_dim height = i_img_get_height(img);
704   int channels = i_img_getchannels(img);
705   i_img_dim y;
706   int c;
707   int pixmin = header->pixmin;
708   int pixmax = header->pixmax;
709   int outmax = pixmax - pixmin;
710
711   if (!read_rle_tables(ig, img,  
712                        &start_tab, &length_tab, &max_length)) {
713     i_img_destroy(img);
714     return NULL;
715   }
716
717   mm_log((1, "maxlen for an rle buffer: %lu\n", max_length));
718
719   if (max_length > (img->xsize * 2 + 1) * 2) {
720     i_push_errorf(0, "SGI image: ridiculous RLE line length %lu", max_length);
721     goto ErrorReturn;
722   }
723
724   linebuf = mymalloc(width*sizeof(i_fcolor)); /* checked 31Jul07 TonyC */
725   databuf = mymalloc(max_length); /* checked 31Jul07 TonyC */
726
727   for(y = 0; y < img->ysize; y++) {
728     for(c = 0; c < channels; c++) {
729       int ci = height * c + y;
730       int datalen = length_tab[ci];
731       unsigned char *inp;
732       i_fcolor *outp;
733       int data_left = datalen;
734       int pixels_left = width;
735       int sample;
736       
737       if (datalen & 1) {
738         i_push_error(0, "SGI image: invalid RLE length value for BPC=2");
739         goto ErrorReturn;
740       }
741       if (ig->seekcb(ig, start_tab[ci], SEEK_SET) != start_tab[ci]) {
742         i_push_error(0, "SGI image: cannot seek to RLE data");
743         goto ErrorReturn;
744       }
745       if (ig->readcb(ig, databuf, datalen) != datalen) {
746         i_push_error(0, "SGI image: cannot read RLE data");
747         goto ErrorReturn;
748       }
749       
750       inp = databuf;
751       outp = linebuf;
752       while (data_left > 0) {
753         int code = inp[0] * 256 + inp[1];
754         int count = code & 0x7f;
755         inp += 2;
756         data_left -= 2;
757
758         if (count == 0)
759           break;
760         if (code & 0x80) {
761           /* literal run */
762           /* sanity checks */
763           if (count > pixels_left) {
764             i_push_error(0, "SGI image: literal run overflows scanline");
765             goto ErrorReturn;
766           }
767           if (count > data_left) {
768             i_push_error(0, "SGI image: literal run consumes more data than available");
769             goto ErrorReturn;
770           }
771           /* copy the run */
772           pixels_left -= count;
773           data_left -= count * 2;
774           if (pixmin == 0 && pixmax == 65535) {
775             while (count-- > 0) {
776               outp->channel[c] = (inp[0] * 256 + inp[1]) / 65535.0;
777               inp += 2;
778               ++outp;
779             }
780           }
781           else {
782             while (count-- > 0) {
783               int sample = inp[0] * 256 + inp[1];
784               if (sample < pixmin)
785                 sample = 0;
786               else if (sample > pixmax)
787                 sample = outmax;
788               else
789                 sample -= pixmin;
790               outp->channel[c] = (double)sample / outmax;
791               ++outp;
792               inp += 2;
793             }
794           }
795         }
796         else {
797           double fsample;
798           /* RLE run */
799           if (count > pixels_left) {
800             i_push_error(0, "SGI image: RLE run overflows scanline");
801             goto ErrorReturn;
802           }
803           if (data_left < 2) {
804             i_push_error(0, "SGI image: RLE run has no data for pixel");
805             goto ErrorReturn;
806           }
807           sample = inp[0] * 256 + inp[1];
808           inp += 2;
809           data_left -= 2;
810           if (pixmin != 0 || pixmax != 65535) {
811             if (sample < pixmin)
812               sample = 0;
813             else if (sample > pixmax)
814               sample = outmax;
815             else
816               sample -= pixmin;
817             fsample = (double)sample / outmax;
818           }
819           else {
820             fsample = (double)sample / 65535.0;
821           }
822           pixels_left -= count;
823           while (count-- > 0) {
824             outp->channel[c] = fsample;
825             ++outp;
826           }
827         }
828       }
829       /* must have a full scanline */
830       if (pixels_left) {
831         i_push_error(0, "SGI image: incomplete RLE scanline");
832         goto ErrorReturn;
833       }
834       /* must have used all of the data */
835       if (data_left) {
836         i_push_errorf(0, "SGI image: unused RLE data");
837         goto ErrorReturn;
838       }
839     }
840     i_plinf(img, 0, width, height-1-y, linebuf);
841   }
842
843   myfree(linebuf);
844   myfree(databuf);
845   myfree(start_tab);
846   myfree(length_tab);
847
848   return img;
849
850  ErrorReturn:
851   if (linebuf)
852     myfree(linebuf);
853   if (databuf)
854     myfree(databuf);
855   myfree(start_tab);
856   myfree(length_tab);
857   i_img_destroy(img);
858   return NULL;
859 }
860
861 static int
862 write_sgi_header(i_img *img, io_glue *ig, int *rle, int *bpc2) {
863   rgb_header header;
864   unsigned char headbuf[512] = { 0 };
865
866   header.imagic = SGI_MAGIC;
867   if (!i_tags_get_int(&img->tags, "sgi_rle", 0, rle))
868     *rle = 0;
869   header.storagetype = *rle ? SGI_STORAGE_RLE : SGI_STORAGE_VERBATIM;
870   header.pixmin = 0;
871   header.colormap = SGI_COLORMAP_NORMAL;
872   *bpc2 = img->bits > 8;
873   if (*bpc2) {
874     header.BPC = 2;
875     header.pixmax = 65535;
876   }
877   else {
878     header.BPC = 1;
879     header.pixmax = 255;
880   }
881   if (img->channels == 1) {
882     header.dimensions = 2;
883   }
884   else {
885     header.dimensions = 3;
886   }
887   header.xsize = img->xsize;
888   header.ysize = img->ysize;
889   header.zsize = img->channels;
890   memset(header.name, 0, sizeof(header.name));
891   i_tags_get_string(&img->tags, "i_comment",  0, 
892                     header.name, sizeof(header.name));
893
894   rgb_header_pack(&header, headbuf);
895
896   if (i_io_write(ig, headbuf, sizeof(headbuf)) != sizeof(headbuf)) {
897     i_push_error(0, "SGI image: cannot write header");
898     return 0;
899   }
900
901   return 1;
902 }
903
904 static int
905 write_sgi_8_verb(i_img *img, io_glue *ig) {
906   i_sample_t *linebuf;
907   i_img_dim width = img->xsize;
908   int c;
909   i_img_dim y;
910
911   linebuf = mymalloc(width);  /* checked 31Jul07 TonyC */
912   for (c = 0; c < img->channels; ++c) {
913     for (y = img->ysize - 1; y >= 0; --y) {
914       i_gsamp(img, 0, width, y, linebuf, &c, 1);
915       if (ig->writecb(ig, linebuf, width) != width) {
916         i_push_error(errno, "SGI image: error writing image data");
917         myfree(linebuf);
918         return 0;
919       }
920     }
921   }
922   myfree(linebuf);
923
924   return 1;
925 }
926
927 static int
928 write_sgi_8_rle(i_img *img, io_glue *ig) {
929   i_sample_t *linebuf;
930   unsigned char *comp_buf;
931   i_img_dim width = img->xsize;
932   int c;
933   i_img_dim y;
934   unsigned char *offsets;
935   unsigned char *lengths;
936   int offset_pos = 0;
937   size_t offsets_size = (size_t)4 * img->ysize * img->channels * 2;
938   unsigned long start_offset = 512 + offsets_size;
939   unsigned long current_offset = start_offset;
940   int in_left;
941   unsigned char *outp;
942   i_sample_t *inp;
943   size_t comp_size;
944
945   if (offsets_size / 2 / 4 / img->channels != img->ysize) {
946     i_push_error(0, "SGI image: integer overflow calculating allocation size");
947     return 0;
948   }
949
950   linebuf = mymalloc(width);  /* checked 31Jul07 TonyC */
951   comp_buf = mymalloc((width + 1) * 2);  /* checked 31Jul07 TonyC */
952   offsets = mymalloc(offsets_size);
953   memset(offsets, 0, offsets_size);
954   if (i_io_write(ig, offsets, offsets_size) != offsets_size) {
955     i_push_error(errno, "SGI image: error writing offsets/lengths");
956     goto Error;
957   }
958   lengths = offsets + img->ysize * img->channels * 4;
959   for (c = 0; c < img->channels; ++c) {
960     for (y = img->ysize - 1; y >= 0; --y) {
961       i_gsamp(img, 0, width, y, linebuf, &c, 1);
962       in_left = width;
963       outp = comp_buf;
964       inp = linebuf;
965       while (in_left) {
966         unsigned char *run_start = inp;
967
968         /* first try for an RLE run */
969         int run_length = 1;
970         while (in_left - run_length >= 2 && inp[0] == inp[1] && run_length < 127) {
971           ++run_length;
972           ++inp;
973         }
974         if (in_left - run_length == 1 && inp[0] == inp[1] && run_length < 127) {
975           ++run_length;
976           ++inp;
977         }
978         if (run_length > 2) {
979           *outp++ = run_length;
980           *outp++ = inp[0];
981           inp++;
982           in_left -= run_length;
983         }
984         else {
985           inp = run_start;
986
987           /* scan for a literal run */
988           run_length = 1;
989           run_start = inp;
990           while (in_left - run_length > 1 && (inp[0] != inp[1] || inp[1] != inp[2]) && run_length < 127) {
991             ++run_length;
992             ++inp;
993           }
994           ++inp;
995           
996           /* fill out the run if 2 or less samples left and there's space */
997           if (in_left - run_length <= 2 
998               && in_left <= 127) {
999             run_length = in_left;
1000           }
1001           in_left -= run_length;
1002           *outp++ = run_length | 0x80;
1003           while (run_length--) {
1004             *outp++ = *run_start++;
1005           }
1006         }
1007       }
1008       *outp++ = 0;
1009       comp_size = outp - comp_buf;
1010       store_32(offsets + offset_pos, current_offset);
1011       store_32(lengths + offset_pos, comp_size);
1012       offset_pos += 4;
1013       current_offset += comp_size;
1014       if (ig->writecb(ig, comp_buf, comp_size) != comp_size) {
1015         i_push_error(errno, "SGI image: error writing RLE data");
1016         goto Error;
1017       }
1018     }
1019   }
1020
1021   /* seek back to store the offsets and lengths */
1022   if (i_io_seek(ig, 512, SEEK_SET) != 512) {
1023     i_push_error(errno, "SGI image: cannot seek to RLE table");
1024     goto Error;
1025   }
1026
1027   if (i_io_write(ig, offsets, offsets_size) != offsets_size) {
1028     i_push_error(errno, "SGI image: cannot write final RLE table");
1029     goto Error;
1030   }
1031
1032   myfree(offsets);
1033   myfree(comp_buf);
1034   myfree(linebuf);
1035
1036   return 1;
1037
1038  Error:
1039   myfree(offsets);
1040   myfree(comp_buf);
1041   myfree(linebuf);
1042   return 0;
1043 }
1044
1045 static int
1046 write_sgi_16_verb(i_img *img, io_glue *ig) {
1047   i_fsample_t *linebuf;
1048   unsigned char *encbuf;
1049   unsigned char *outp;
1050   i_img_dim width = img->xsize;
1051   int c;
1052   i_img_dim x;
1053   i_img_dim y;
1054
1055   linebuf = mymalloc(width * sizeof(i_fsample_t));  /* checked 31Jul07 TonyC */
1056   encbuf = mymalloc(width * 2);  /* checked 31Jul07 TonyC */
1057   for (c = 0; c < img->channels; ++c) {
1058     for (y = img->ysize - 1; y >= 0; --y) {
1059       i_gsampf(img, 0, width, y, linebuf, &c, 1);
1060       for (x = 0, outp = encbuf; x < width; ++x, outp+=2) {
1061         unsigned short samp16 = SampleFTo16(linebuf[x]);
1062         store_16(outp, samp16);
1063       }
1064       if (ig->writecb(ig, encbuf, width * 2) != width * 2) {
1065         i_push_error(errno, "SGI image: error writing image data");
1066         myfree(linebuf);
1067         myfree(encbuf);
1068         return 0;
1069       }
1070     }
1071   }
1072   myfree(linebuf);
1073   myfree(encbuf);
1074
1075   return 1;
1076 }
1077
1078 static int
1079 write_sgi_16_rle(i_img *img, io_glue *ig) {
1080   i_fsample_t *sampbuf;
1081   unsigned short *linebuf;
1082   unsigned char *comp_buf;
1083   i_img_dim width = img->xsize;
1084   int c;
1085   i_img_dim y;
1086   unsigned char *offsets;
1087   unsigned char *lengths;
1088   int offset_pos = 0;
1089   size_t offsets_size = (size_t)4 * img->ysize * img->channels * 2;
1090   unsigned long start_offset = 512 + offsets_size;
1091   unsigned long current_offset = start_offset;
1092   int in_left;
1093   unsigned char *outp;
1094   unsigned short *inp;
1095   size_t comp_size;
1096   i_img_dim x;
1097
1098   if (offsets_size / 4 / 2 / img->channels != img->ysize) {
1099     i_push_error(0, "SGI image: integer overflow calculating allocation size");
1100     return 0;
1101   }
1102
1103   sampbuf = mymalloc(width * sizeof(i_fsample_t));  /* checked 31Jul07 TonyC */
1104   linebuf = mymalloc(width * sizeof(unsigned short));  /* checked 31Jul07 TonyC */
1105   comp_buf = mymalloc((width + 1) * 2 * 2);  /* checked 31Jul07 TonyC */
1106   offsets = mymalloc(offsets_size);
1107   memset(offsets, 0, offsets_size);
1108   if (i_io_write(ig, offsets, offsets_size) != offsets_size) {
1109     i_push_error(errno, "SGI image: error writing offsets/lengths");
1110     goto Error;
1111   }
1112   lengths = offsets + img->ysize * img->channels * 4;
1113   for (c = 0; c < img->channels; ++c) {
1114     for (y = img->ysize - 1; y >= 0; --y) {
1115       i_gsampf(img, 0, width, y, sampbuf, &c, 1);
1116       for (x = 0; x < width; ++x)
1117         linebuf[x] = (unsigned short)(SampleFTo16(sampbuf[x]));
1118       in_left = width;
1119       outp = comp_buf;
1120       inp = linebuf;
1121       while (in_left) {
1122         unsigned short *run_start = inp;
1123
1124         /* first try for an RLE run */
1125         int run_length = 1;
1126         while (in_left - run_length >= 2 && inp[0] == inp[1] && run_length < 127) {
1127           ++run_length;
1128           ++inp;
1129         }
1130         if (in_left - run_length == 1 && inp[0] == inp[1] && run_length < 127) {
1131           ++run_length;
1132           ++inp;
1133         }
1134         if (run_length > 2) {
1135           store_16(outp, run_length);
1136           store_16(outp+2, inp[0]);
1137           outp += 4;
1138           inp++;
1139           in_left -= run_length;
1140         }
1141         else {
1142           inp = run_start;
1143
1144           /* scan for a literal run */
1145           run_length = 1;
1146           run_start = inp;
1147           while (in_left - run_length > 1 && (inp[0] != inp[1] || inp[1] != inp[2]) && run_length < 127) {
1148             ++run_length;
1149             ++inp;
1150           }
1151           ++inp;
1152           
1153           /* fill out the run if 2 or less samples left and there's space */
1154           if (in_left - run_length <= 2 
1155               && in_left <= 127) {
1156             run_length = in_left;
1157           }
1158           in_left -= run_length;
1159           store_16(outp, run_length | 0x80);
1160           outp += 2;
1161           while (run_length--) {
1162             store_16(outp, *run_start++);
1163             outp += 2;
1164           }
1165         }
1166       }
1167       store_16(outp, 0);
1168       outp += 2;
1169       comp_size = outp - comp_buf;
1170       store_32(offsets + offset_pos, current_offset);
1171       store_32(lengths + offset_pos, comp_size);
1172       offset_pos += 4;
1173       current_offset += comp_size;
1174       if (ig->writecb(ig, comp_buf, comp_size) != comp_size) {
1175         i_push_error(errno, "SGI image: error writing RLE data");
1176         goto Error;
1177       }
1178     }
1179   }
1180
1181   /* seek back to store the offsets and lengths */
1182   if (i_io_seek(ig, 512, SEEK_SET) != 512) {
1183     i_push_error(errno, "SGI image: cannot seek to RLE table");
1184     goto Error;
1185   }
1186
1187   if (i_io_write(ig, offsets, offsets_size) != offsets_size) {
1188     i_push_error(errno, "SGI image: cannot write final RLE table");
1189     goto Error;
1190   }
1191
1192   myfree(offsets);
1193   myfree(comp_buf);
1194   myfree(linebuf);
1195   myfree(sampbuf);
1196
1197   return 1;
1198
1199  Error:
1200   myfree(offsets);
1201   myfree(comp_buf);
1202   myfree(linebuf);
1203   myfree(sampbuf);
1204
1205   return 0;
1206 }