]> git.imager.perl.org - imager.git/blame - SGI/imsgi.c
avoid a possible sign-extension for offsets/sizes in SGI
[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
8d14daab
TC
23/* maximum size of an SGI image */
24#define SGI_DIM_LIMIT 0xFFFF
25
d5477d3d
TC
26typedef 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
37static i_img *
38read_rgb_8_verbatim(i_img *im, io_glue *ig, rgb_header const *hdr);
39static i_img *
40read_rgb_8_rle(i_img *im, io_glue *ig, rgb_header const *hdr);
41static i_img *
42read_rgb_16_verbatim(i_img *im, io_glue *ig, rgb_header const *hdr);
43static i_img *
44read_rgb_16_rle(i_img *im, io_glue *ig, rgb_header const *hdr);
45static int
46write_sgi_header(i_img *img, io_glue *ig, int *rle, int *bpc2);
47static int
48write_sgi_8_rle(i_img *img, io_glue *ig);
49static int
50write_sgi_8_verb(i_img *img, io_glue *ig);
51static int
52write_sgi_16_rle(i_img *img, io_glue *ig);
53static int
54write_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
64rgb.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
76imsgi.c implements the basic functions to read and write portable SGI
77files. It uses the iolayer and needs either a seekable source or an
78entire memory mapped buffer.
79
80=head1 FUNCTION REFERENCE
81
82Some of these functions are internal.
83
84=over
85
86=cut
87*/
88
89/*
90=item rgb_header_unpack(header, headbuf)
91
92Unpacks the header structure into from buffer and stores
93in the header structure.
94
95 header - header structure
96 headbuf - buffer to unpack from
97
98=cut
99*/
100
101
102static
103void
104rgb_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 */
120static void
121store_16(unsigned char *buf, unsigned short value) {
122 buf[0] = value >> 8;
123 buf[1] = value & 0xFF;
124}
125
126static void
ed107438 127store_32(unsigned char *buf, unsigned long value) {
d5477d3d
TC
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
137Packs header structure into buffer for writing.
138
139 header - header structure
140 headbuf - buffer to pack into
141
142=cut
143*/
144
145static
146void
147rgb_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
165Read in an image from the iolayer data source and return the image structure to it.
166Returns 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
175i_img *
176i_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
6d5c85a2 185 if (i_io_read(ig, headbuf, 512) != 512) {
d5477d3d
TC
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
316Writes 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
324int
325i_writesgi_wiol(io_glue *ig, i_img *img) {
326 int rle;
327 int bpc2;
328
329 i_clear_error();
330
8d14daab
TC
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
d5477d3d
TC
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
355static i_img *
356read_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
6d5c85a2 378 if (i_io_read(ig, databuf, width) != width) {
d5477d3d
TC
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
415static int
416read_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 */
6d5c85a2 439 if (i_io_read(ig, databuf, height * channels * 4) != height * channels * 4) {
d5477d3d
TC
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++)
e1c06929 445 start_tab[i] = ((unsigned long)databuf[i*4] << 24) | (databuf[i*4+1] << 16) |
d5477d3d
TC
446 (databuf[i*4+2] << 8) | (databuf[i*4+3]);
447
448
449 /* Read length table */
6d5c85a2 450 if (i_io_read(ig, databuf, height*channels*4) != height*channels*4) {
d5477d3d
TC
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++) {
e1c06929
TC
456 length_tab[i] = ((unsigned long)databuf[i*4] << 24) | (databuf[i*4+1] << 16) |
457 (databuf[i*4+2] << 8) | (databuf[i*4+3]);
d5477d3d
TC
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++)
5e84d110 464 mm_log((3, "%d: %lu/%lu\n", i, start_tab[i], length_tab[i]));
d5477d3d
TC
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
482static i_img *
483read_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);
62264261 490 int channels = i_img_getchannels(img);
d5477d3d
TC
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
5e84d110 503 mm_log((1, "maxlen for an rle buffer: %lu\n", max_length));
d5477d3d
TC
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
6d5c85a2 523 if (i_io_seek(ig, start_tab[ci], SEEK_SET) != start_tab[ci]) {
d5477d3d
TC
524 i_push_error(0, "SGI image: cannot seek to RLE data");
525 goto ErrorReturn;
526 }
6d5c85a2 527 if (i_io_read(ig, databuf, datalen) != datalen) {
d5477d3d
TC
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");
5e84d110 579 mm_log((2, "RLE run overflows scanline (y %" i_DF " chan %d offset %lu len %lu)\n", i_DFc(y), c, start_tab[ci], length_tab[ci]));
d5477d3d
TC
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
636static i_img *
637read_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
6d5c85a2 659 if (i_io_read(ig, databuf, width*2) != width*2) {
d5477d3d
TC
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
696static i_img *
697read_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);
62264261 704 int channels = i_img_getchannels(img);
d5477d3d
TC
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 }
6d5c85a2 741 if (i_io_seek(ig, start_tab[ci], SEEK_SET) != start_tab[ci]) {
d5477d3d
TC
742 i_push_error(0, "SGI image: cannot seek to RLE data");
743 goto ErrorReturn;
744 }
6d5c85a2 745 if (i_io_read(ig, databuf, datalen) != datalen) {
d5477d3d
TC
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
861static int
862write_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
904static int
905write_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);
6d5c85a2 915 if (i_io_write(ig, linebuf, width) != width) {
d5477d3d
TC
916 i_push_error(errno, "SGI image: error writing image data");
917 myfree(linebuf);
918 return 0;
919 }
920 }
921 }
922 myfree(linebuf);
923
6d5c85a2
TC
924 if (i_io_close(ig))
925 return 0;
926
d5477d3d
TC
927 return 1;
928}
929
930static int
931write_sgi_8_rle(i_img *img, io_glue *ig) {
932 i_sample_t *linebuf;
933 unsigned char *comp_buf;
934 i_img_dim width = img->xsize;
935 int c;
936 i_img_dim y;
937 unsigned char *offsets;
938 unsigned char *lengths;
939 int offset_pos = 0;
940 size_t offsets_size = (size_t)4 * img->ysize * img->channels * 2;
941 unsigned long start_offset = 512 + offsets_size;
942 unsigned long current_offset = start_offset;
943 int in_left;
944 unsigned char *outp;
945 i_sample_t *inp;
946 size_t comp_size;
947
948 if (offsets_size / 2 / 4 / img->channels != img->ysize) {
949 i_push_error(0, "SGI image: integer overflow calculating allocation size");
950 return 0;
951 }
952
953 linebuf = mymalloc(width); /* checked 31Jul07 TonyC */
954 comp_buf = mymalloc((width + 1) * 2); /* checked 31Jul07 TonyC */
955 offsets = mymalloc(offsets_size);
956 memset(offsets, 0, offsets_size);
957 if (i_io_write(ig, offsets, offsets_size) != offsets_size) {
958 i_push_error(errno, "SGI image: error writing offsets/lengths");
959 goto Error;
960 }
961 lengths = offsets + img->ysize * img->channels * 4;
962 for (c = 0; c < img->channels; ++c) {
963 for (y = img->ysize - 1; y >= 0; --y) {
964 i_gsamp(img, 0, width, y, linebuf, &c, 1);
965 in_left = width;
966 outp = comp_buf;
967 inp = linebuf;
968 while (in_left) {
969 unsigned char *run_start = inp;
970
971 /* first try for an RLE run */
972 int run_length = 1;
973 while (in_left - run_length >= 2 && inp[0] == inp[1] && run_length < 127) {
974 ++run_length;
975 ++inp;
976 }
977 if (in_left - run_length == 1 && inp[0] == inp[1] && run_length < 127) {
978 ++run_length;
979 ++inp;
980 }
981 if (run_length > 2) {
982 *outp++ = run_length;
983 *outp++ = inp[0];
984 inp++;
985 in_left -= run_length;
986 }
987 else {
988 inp = run_start;
989
990 /* scan for a literal run */
991 run_length = 1;
992 run_start = inp;
993 while (in_left - run_length > 1 && (inp[0] != inp[1] || inp[1] != inp[2]) && run_length < 127) {
994 ++run_length;
995 ++inp;
996 }
997 ++inp;
998
999 /* fill out the run if 2 or less samples left and there's space */
1000 if (in_left - run_length <= 2
ed107438
TC
1001 && in_left <= 127) {
1002 run_length = in_left;
d5477d3d
TC
1003 }
1004 in_left -= run_length;
1005 *outp++ = run_length | 0x80;
1006 while (run_length--) {
1007 *outp++ = *run_start++;
1008 }
1009 }
1010 }
1011 *outp++ = 0;
1012 comp_size = outp - comp_buf;
1013 store_32(offsets + offset_pos, current_offset);
1014 store_32(lengths + offset_pos, comp_size);
1015 offset_pos += 4;
1016 current_offset += comp_size;
6d5c85a2 1017 if (i_io_write(ig, comp_buf, comp_size) != comp_size) {
d5477d3d
TC
1018 i_push_error(errno, "SGI image: error writing RLE data");
1019 goto Error;
1020 }
1021 }
1022 }
1023
1024 /* seek back to store the offsets and lengths */
1025 if (i_io_seek(ig, 512, SEEK_SET) != 512) {
1026 i_push_error(errno, "SGI image: cannot seek to RLE table");
1027 goto Error;
1028 }
1029
1030 if (i_io_write(ig, offsets, offsets_size) != offsets_size) {
1031 i_push_error(errno, "SGI image: cannot write final RLE table");
1032 goto Error;
1033 }
1034
1035 myfree(offsets);
1036 myfree(comp_buf);
1037 myfree(linebuf);
1038
6d5c85a2
TC
1039 if (i_io_close(ig))
1040 return 0;
1041
d5477d3d
TC
1042 return 1;
1043
1044 Error:
1045 myfree(offsets);
1046 myfree(comp_buf);
1047 myfree(linebuf);
1048 return 0;
1049}
1050
1051static int
1052write_sgi_16_verb(i_img *img, io_glue *ig) {
1053 i_fsample_t *linebuf;
1054 unsigned char *encbuf;
1055 unsigned char *outp;
1056 i_img_dim width = img->xsize;
1057 int c;
1058 i_img_dim x;
1059 i_img_dim y;
1060
1061 linebuf = mymalloc(width * sizeof(i_fsample_t)); /* checked 31Jul07 TonyC */
1062 encbuf = mymalloc(width * 2); /* checked 31Jul07 TonyC */
1063 for (c = 0; c < img->channels; ++c) {
1064 for (y = img->ysize - 1; y >= 0; --y) {
1065 i_gsampf(img, 0, width, y, linebuf, &c, 1);
1066 for (x = 0, outp = encbuf; x < width; ++x, outp+=2) {
1067 unsigned short samp16 = SampleFTo16(linebuf[x]);
1068 store_16(outp, samp16);
1069 }
6d5c85a2 1070 if (i_io_write(ig, encbuf, width * 2) != width * 2) {
d5477d3d
TC
1071 i_push_error(errno, "SGI image: error writing image data");
1072 myfree(linebuf);
1073 myfree(encbuf);
1074 return 0;
1075 }
1076 }
1077 }
1078 myfree(linebuf);
1079 myfree(encbuf);
1080
6d5c85a2
TC
1081 if (i_io_close(ig))
1082 return 0;
1083
d5477d3d
TC
1084 return 1;
1085}
1086
1087static int
1088write_sgi_16_rle(i_img *img, io_glue *ig) {
1089 i_fsample_t *sampbuf;
1090 unsigned short *linebuf;
1091 unsigned char *comp_buf;
1092 i_img_dim width = img->xsize;
1093 int c;
1094 i_img_dim y;
1095 unsigned char *offsets;
1096 unsigned char *lengths;
1097 int offset_pos = 0;
1098 size_t offsets_size = (size_t)4 * img->ysize * img->channels * 2;
1099 unsigned long start_offset = 512 + offsets_size;
1100 unsigned long current_offset = start_offset;
1101 int in_left;
1102 unsigned char *outp;
1103 unsigned short *inp;
1104 size_t comp_size;
1105 i_img_dim x;
1106
1107 if (offsets_size / 4 / 2 / img->channels != img->ysize) {
1108 i_push_error(0, "SGI image: integer overflow calculating allocation size");
1109 return 0;
1110 }
1111
1112 sampbuf = mymalloc(width * sizeof(i_fsample_t)); /* checked 31Jul07 TonyC */
1113 linebuf = mymalloc(width * sizeof(unsigned short)); /* checked 31Jul07 TonyC */
1114 comp_buf = mymalloc((width + 1) * 2 * 2); /* checked 31Jul07 TonyC */
1115 offsets = mymalloc(offsets_size);
1116 memset(offsets, 0, offsets_size);
1117 if (i_io_write(ig, offsets, offsets_size) != offsets_size) {
1118 i_push_error(errno, "SGI image: error writing offsets/lengths");
1119 goto Error;
1120 }
1121 lengths = offsets + img->ysize * img->channels * 4;
1122 for (c = 0; c < img->channels; ++c) {
1123 for (y = img->ysize - 1; y >= 0; --y) {
1124 i_gsampf(img, 0, width, y, sampbuf, &c, 1);
1125 for (x = 0; x < width; ++x)
1126 linebuf[x] = (unsigned short)(SampleFTo16(sampbuf[x]));
1127 in_left = width;
1128 outp = comp_buf;
1129 inp = linebuf;
1130 while (in_left) {
1131 unsigned short *run_start = inp;
1132
1133 /* first try for an RLE run */
1134 int run_length = 1;
1135 while (in_left - run_length >= 2 && inp[0] == inp[1] && run_length < 127) {
1136 ++run_length;
1137 ++inp;
1138 }
1139 if (in_left - run_length == 1 && inp[0] == inp[1] && run_length < 127) {
1140 ++run_length;
1141 ++inp;
1142 }
1143 if (run_length > 2) {
1144 store_16(outp, run_length);
1145 store_16(outp+2, inp[0]);
1146 outp += 4;
1147 inp++;
1148 in_left -= run_length;
1149 }
1150 else {
1151 inp = run_start;
1152
1153 /* scan for a literal run */
1154 run_length = 1;
1155 run_start = inp;
1156 while (in_left - run_length > 1 && (inp[0] != inp[1] || inp[1] != inp[2]) && run_length < 127) {
1157 ++run_length;
1158 ++inp;
1159 }
1160 ++inp;
1161
1162 /* fill out the run if 2 or less samples left and there's space */
1163 if (in_left - run_length <= 2
ed107438
TC
1164 && in_left <= 127) {
1165 run_length = in_left;
d5477d3d
TC
1166 }
1167 in_left -= run_length;
1168 store_16(outp, run_length | 0x80);
1169 outp += 2;
1170 while (run_length--) {
1171 store_16(outp, *run_start++);
1172 outp += 2;
1173 }
1174 }
1175 }
1176 store_16(outp, 0);
1177 outp += 2;
1178 comp_size = outp - comp_buf;
1179 store_32(offsets + offset_pos, current_offset);
1180 store_32(lengths + offset_pos, comp_size);
1181 offset_pos += 4;
1182 current_offset += comp_size;
6d5c85a2 1183 if (i_io_write(ig, comp_buf, comp_size) != comp_size) {
d5477d3d
TC
1184 i_push_error(errno, "SGI image: error writing RLE data");
1185 goto Error;
1186 }
1187 }
1188 }
1189
1190 /* seek back to store the offsets and lengths */
1191 if (i_io_seek(ig, 512, SEEK_SET) != 512) {
1192 i_push_error(errno, "SGI image: cannot seek to RLE table");
1193 goto Error;
1194 }
1195
1196 if (i_io_write(ig, offsets, offsets_size) != offsets_size) {
1197 i_push_error(errno, "SGI image: cannot write final RLE table");
1198 goto Error;
1199 }
1200
1201 myfree(offsets);
1202 myfree(comp_buf);
1203 myfree(linebuf);
1204 myfree(sampbuf);
1205
6d5c85a2
TC
1206 if (i_io_close(ig))
1207 return 0;
1208
d5477d3d
TC
1209 return 1;
1210
1211 Error:
1212 myfree(offsets);
1213 myfree(comp_buf);
1214 myfree(linebuf);
1215 myfree(sampbuf);
1216
1217 return 0;
1218}