- Finished/rewrote Arnar's old SGI RGB file format support, so Imager
[imager.git] / SGI / imsgi.c
CommitLineData
d5477d3d
TC
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
23typedef 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
34static i_img *
35read_rgb_8_verbatim(i_img *im, io_glue *ig, rgb_header const *hdr);
36static i_img *
37read_rgb_8_rle(i_img *im, io_glue *ig, rgb_header const *hdr);
38static i_img *
39read_rgb_16_verbatim(i_img *im, io_glue *ig, rgb_header const *hdr);
40static i_img *
41read_rgb_16_rle(i_img *im, io_glue *ig, rgb_header const *hdr);
42static int
43write_sgi_header(i_img *img, io_glue *ig, int *rle, int *bpc2);
44static int
45write_sgi_8_rle(i_img *img, io_glue *ig);
46static int
47write_sgi_8_verb(i_img *img, io_glue *ig);
48static int
49write_sgi_16_rle(i_img *img, io_glue *ig);
50static int
51write_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
61rgb.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
73imsgi.c implements the basic functions to read and write portable SGI
74files. It uses the iolayer and needs either a seekable source or an
75entire memory mapped buffer.
76
77=head1 FUNCTION REFERENCE
78
79Some of these functions are internal.
80
81=over
82
83=cut
84*/
85
86/*
87=item rgb_header_unpack(header, headbuf)
88
89Unpacks the header structure into from buffer and stores
90in the header structure.
91
92 header - header structure
93 headbuf - buffer to unpack from
94
95=cut
96*/
97
98
99static
100void
101rgb_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 */
117static void
118store_16(unsigned char *buf, unsigned short value) {
119 buf[0] = value >> 8;
120 buf[1] = value & 0xFF;
121}
122
123static void
124store_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
134Packs header structure into buffer for writing.
135
136 header - header structure
137 headbuf - buffer to pack into
138
139=cut
140*/
141
142static
143void
144rgb_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
162Read in an image from the iolayer data source and return the image structure to it.
163Returns 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
172i_img *
173i_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
313Writes 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
321int
322i_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
347static i_img *
348read_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
407static int
408read_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
474static i_img *
475read_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
628static i_img *
629read_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
688static i_img *
689read_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
853static int
854write_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
896static int
897write_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
919static int
920write_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
1038static int
1039write_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
1071static int
1072write_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}