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