document the new test image functions
[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
185 if (ig->readcb(ig, headbuf, 512) != 512) {
186 i_push_error(errno, "SGI image: could not read header");
187 return NULL;
188 }
189
190 rgb_header_unpack(&header, headbuf);
191
192 if (header.imagic != SGI_MAGIC) {
193 i_push_error(0, "SGI image: invalid magic number");
194 return NULL;
195 }
196
197 mm_log((1,"imagic: %d\n", header.imagic));
198 mm_log((1,"storagetype: %d\n", header.storagetype));
199 mm_log((1,"BPC: %d\n", header.BPC));
200 mm_log((1,"dimensions: %d\n", header.dimensions));
201 mm_log((1,"xsize: %d\n", header.xsize));
202 mm_log((1,"ysize: %d\n", header.ysize));
203 mm_log((1,"zsize: %d\n", header.zsize));
204 mm_log((1,"min: %d\n", header.pixmin));
205 mm_log((1,"max: %d\n", header.pixmax));
206 mm_log((1,"name [skipped]\n"));
207 mm_log((1,"colormap: %d\n", header.colormap));
208
209 if (header.colormap != SGI_COLORMAP_NORMAL) {
210 i_push_errorf(0, "SGI image: invalid value for colormap (%d)", header.colormap);
211 return NULL;
212 }
213
214 if (header.BPC != 1 && header.BPC != 2) {
215 i_push_errorf(0, "SGI image: invalid value for BPC (%d)", header.BPC);
216 return NULL;
217 }
218
219 if (header.storagetype != SGI_STORAGE_VERBATIM
220 && header.storagetype != SGI_STORAGE_RLE) {
221 i_push_error(0, "SGI image: invalid storage type field");
222 return NULL;
223 }
224
225 if (header.pixmin >= header.pixmax) {
226 i_push_error(0, "SGI image: invalid pixmin >= pixmax");
227 return NULL;
228 }
229
230 width = header.xsize;
231 height = header.ysize;
232 channels = header.zsize;
233
234 switch (header.dimensions) {
235 case 1:
236 channels = 1;
237 height = 1;
238 break;
239
240 case 2:
241 channels = 1;
242 break;
243
244 case 3:
245 /* fall through and use all of the dimensions */
246 break;
247
248 default:
249 i_push_error(0, "SGI image: invalid dimension field");
250 return NULL;
251 }
252
253 if (!i_int_check_image_file_limits(width, height, channels, header.BPC)) {
254 mm_log((1, "i_readsgi_wiol: image size exceeds limits\n"));
255 return NULL;
256 }
257
258 if (header.BPC == 1) {
259 img = i_img_8_new(width, height, channels);
260 if (!img)
261 goto ErrorReturn;
262
263 switch (header.storagetype) {
264 case SGI_STORAGE_VERBATIM:
265 img = read_rgb_8_verbatim(img, ig, &header);
266 break;
267
268 case SGI_STORAGE_RLE:
269 img = read_rgb_8_rle(img, ig, &header);
270 break;
271
272 default:
273 goto ErrorReturn;
274 }
275 }
276 else {
277 img = i_img_16_new(width, height, channels);
278 if (!img)
279 goto ErrorReturn;
280
281 switch (header.storagetype) {
282 case SGI_STORAGE_VERBATIM:
283 img = read_rgb_16_verbatim(img, ig, &header);
284 break;
285
286 case SGI_STORAGE_RLE:
287 img = read_rgb_16_rle(img, ig, &header);
288 break;
289
290 default:
291 goto ErrorReturn;
292 }
293 }
294
295 if (!img)
296 goto ErrorReturn;
297
298 if (*header.name)
299 i_tags_set(&img->tags, "i_comment", header.name, -1);
300 i_tags_setn(&img->tags, "sgi_pixmin", header.pixmin);
301 i_tags_setn(&img->tags, "sgi_pixmax", header.pixmax);
302 i_tags_setn(&img->tags, "sgi_bpc", header.BPC);
303 i_tags_setn(&img->tags, "sgi_rle", header.storagetype == SGI_STORAGE_RLE);
304 i_tags_set(&img->tags, "i_format", "sgi", -1);
305
306 return img;
307
308 ErrorReturn:
309 if (img) i_img_destroy(img);
310 return NULL;
311}
312
313/*
314=item i_writergb_wiol(img, ig)
315
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
378 if (ig->readcb(ig, databuf, width) != width) {
379 i_push_error(0, "SGI image: cannot read image data");
380 i_img_destroy(img);
381 myfree(linebuf);
382 myfree(databuf);
383 return NULL;
384 }
385
386 if (pixmin == 0 && pixmax == 255) {
387 for(x = 0; x < img->xsize; x++)
388 linebuf[x].channel[c] = databuf[x];
389 }
390 else {
391 for(x = 0; x < img->xsize; x++) {
392 int sample = databuf[x];
393 if (sample < pixmin)
394 sample = 0;
395 else if (sample > pixmax)
396 sample = outmax;
397 else
398 sample -= pixmin;
399
400 linebuf[x].channel[c] = sample * 255 / outmax;
401 }
402 }
403
404 i_plin(img, 0, width, height-1-y, linebuf);
405 }
406 }
407 i_img_setmask(img, savemask);
408
409 myfree(linebuf);
410 myfree(databuf);
411
412 return img;
413}
414
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 */
439 if (ig->readcb(ig, databuf, height * channels * 4) != height * channels * 4) {
440 i_push_error(0, "SGI image: short read reading RLE start table");
441 goto ErrorReturn;
442 }
443
444 for(i = 0; i < height * channels; i++)
445 start_tab[i] = (databuf[i*4] << 24) | (databuf[i*4+1] << 16) |
446 (databuf[i*4+2] << 8) | (databuf[i*4+3]);
447
448
449 /* Read length table */
450 if (ig->readcb(ig, databuf, height*channels*4) != height*channels*4) {
451 i_push_error(0, "SGI image: short read reading RLE length table");
452 goto ErrorReturn;
453 }
454
455 for(i=0; i < height * channels; i++) {
456 length_tab[i] = (databuf[i*4] << 24) + (databuf[i*4+1] << 16)+
457 (databuf[i*4+2] << 8) + (databuf[i*4+3]);
458 if (length_tab[i] > max_length)
459 max_length = length_tab[i];
460 }
461
462 mm_log((3, "Offset/length table:\n"));
463 for(i=0; i < height * channels; i++)
464 mm_log((3, "%d: %d/%d\n", i, start_tab[i], length_tab[i]));
465
466 *pstart_tab = start_tab;
467 *plength_tab = length_tab;
468 *pmax_length = max_length;
469
470 myfree(databuf);
471
472 return 1;
473
474 ErrorReturn:
475 myfree(databuf);
476 myfree(start_tab);
477 myfree(length_tab);
478
479 return 0;
480}
481
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
503 mm_log((1, "maxlen for an rle buffer: %d\n", max_length));
504
505 if (max_length > (img->xsize + 1) * 2) {
506 i_push_errorf(0, "SGI image: ridiculous RLE line length %lu", max_length);
507 goto ErrorReturn;
508 }
509
510 linebuf = mymalloc(width*sizeof(i_color)); /* checked 31Jul07 TonyC */
511 databuf = mymalloc(max_length); /* checked 31Jul07 TonyC */
512
513 for(y = 0; y < img->ysize; y++) {
514 for(c = 0; c < channels; c++) {
515 int ci = height * c + y;
516 int datalen = length_tab[ci];
517 unsigned char *inp;
518 i_color *outp;
519 int data_left = datalen;
520 int pixels_left = width;
521 i_sample_t sample;
522
523 if (ig->seekcb(ig, start_tab[ci], SEEK_SET) != start_tab[ci]) {
524 i_push_error(0, "SGI image: cannot seek to RLE data");
525 goto ErrorReturn;
526 }
527 if (ig->readcb(ig, databuf, datalen) != datalen) {
528 i_push_error(0, "SGI image: cannot read RLE data");
529 goto ErrorReturn;
530 }
531
532 inp = databuf;
533 outp = linebuf;
534 while (data_left) {
535 int code = *inp++;
536 int count = code & 0x7f;
537 --data_left;
538
539 if (count == 0)
540 break;
541 if (code & 0x80) {
542 /* literal run */
543 /* sanity checks */
544 if (count > pixels_left) {
545 i_push_error(0, "SGI image: literal run overflows scanline");
546 goto ErrorReturn;
547 }
548 if (count > data_left) {
549 i_push_error(0, "SGI image: literal run consumes more data than available");
550 goto ErrorReturn;
551 }
552 /* copy the run */
553 pixels_left -= count;
554 data_left -= count;
555 if (pixmin == 0 && pixmax == 255) {
556 while (count-- > 0) {
557 outp->channel[c] = *inp++;
558 ++outp;
559 }
560 }
561 else {
562 while (count-- > 0) {
563 int sample = *inp++;
564 if (sample < pixmin)
565 sample = 0;
566 else if (sample > pixmax)
567 sample = outmax;
568 else
569 sample -= pixmin;
570 outp->channel[c] = sample * 255 / outmax;
571 ++outp;
572 }
573 }
574 }
575 else {
576 /* RLE run */
577 if (count > pixels_left) {
578 i_push_error(0, "SGI image: RLE run overflows scanline");
579 mm_log((2, "RLE run overflows scanline (y %d chan %d offset %ld len %ld)\n", y, c, start_tab[ci], length_tab[ci]));
580 goto ErrorReturn;
581 }
582 if (data_left < 1) {
583 i_push_error(0, "SGI image: RLE run has no data for pixel");
584 goto ErrorReturn;
585 }
586 sample = *inp++;
587 if (pixmin != 0 || pixmax != 255) {
588 if (sample < pixmin)
589 sample = 0;
590 else if (sample > pixmax)
591 sample = outmax;
592 else
593 sample -= pixmin;
594 sample = sample * 255 / outmax;
595 }
596 --data_left;
597 pixels_left -= count;
598 while (count-- > 0) {
599 outp->channel[c] = sample;
600 ++outp;
601 }
602 }
603 }
604 /* must have a full scanline */
605 if (pixels_left) {
606 i_push_error(0, "SGI image: incomplete RLE scanline");
607 goto ErrorReturn;
608 }
609 /* must have used all of the data */
610 if (data_left) {
611 i_push_errorf(0, "SGI image: unused RLE data");
612 goto ErrorReturn;
613 }
614 }
615 i_plin(img, 0, width, height-1-y, linebuf);
616 }
617
618 myfree(linebuf);
619 myfree(databuf);
620 myfree(start_tab);
621 myfree(length_tab);
622
623 return img;
624
625 ErrorReturn:
626 if (linebuf)
627 myfree(linebuf);
628 if (databuf)
629 myfree(databuf);
630 myfree(start_tab);
631 myfree(length_tab);
632 i_img_destroy(img);
633 return NULL;
634}
635
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
659 if (ig->readcb(ig, databuf, width*2) != width*2) {
660 i_push_error(0, "SGI image: cannot read image data");
661 i_img_destroy(img);
662 myfree(linebuf);
663 myfree(databuf);
664 return NULL;
665 }
666
667 if (pixmin == 0 && pixmax == 65535) {
668 for(x = 0; x < img->xsize; x++)
669 linebuf[x].channel[c] = (databuf[x*2] * 256 + databuf[x*2+1]) / 65535.0;
670 }
671 else {
672 for(x = 0; x < img->xsize; x++) {
673 int sample = databuf[x*2] * 256 + databuf[x*2+1];
674 if (sample < pixmin)
675 sample = 0;
676 else if (sample > pixmax)
677 sample = outmax;
678 else
679 sample -= pixmin;
680
681 linebuf[x].channel[c] = (double)sample / outmax;
682 }
683 }
684
685 i_plinf(img, 0, width, height-1-y, linebuf);
686 }
687 }
688 i_img_setmask(img, savemask);
689
690 myfree(linebuf);
691 myfree(databuf);
692
693 return img;
694}
695
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 }
741 if (ig->seekcb(ig, start_tab[ci], SEEK_SET) != start_tab[ci]) {
742 i_push_error(0, "SGI image: cannot seek to RLE data");
743 goto ErrorReturn;
744 }
745 if (ig->readcb(ig, databuf, datalen) != datalen) {
746 i_push_error(0, "SGI image: cannot read RLE data");
747 goto ErrorReturn;
748 }
749
750 inp = databuf;
751 outp = linebuf;
752 while (data_left > 0) {
753 int code = inp[0] * 256 + inp[1];
754 int count = code & 0x7f;
755 inp += 2;
756 data_left -= 2;
757
758 if (count == 0)
759 break;
760 if (code & 0x80) {
761 /* literal run */
762 /* sanity checks */
763 if (count > pixels_left) {
764 i_push_error(0, "SGI image: literal run overflows scanline");
765 goto ErrorReturn;
766 }
767 if (count > data_left) {
768 i_push_error(0, "SGI image: literal run consumes more data than available");
769 goto ErrorReturn;
770 }
771 /* copy the run */
772 pixels_left -= count;
773 data_left -= count * 2;
774 if (pixmin == 0 && pixmax == 65535) {
775 while (count-- > 0) {
776 outp->channel[c] = (inp[0] * 256 + inp[1]) / 65535.0;
777 inp += 2;
778 ++outp;
779 }
780 }
781 else {
782 while (count-- > 0) {
783 int sample = inp[0] * 256 + inp[1];
784 if (sample < pixmin)
785 sample = 0;
786 else if (sample > pixmax)
787 sample = outmax;
788 else
789 sample -= pixmin;
790 outp->channel[c] = (double)sample / outmax;
791 ++outp;
792 inp += 2;
793 }
794 }
795 }
796 else {
797 double fsample;
798 /* RLE run */
799 if (count > pixels_left) {
800 i_push_error(0, "SGI image: RLE run overflows scanline");
801 goto ErrorReturn;
802 }
803 if (data_left < 2) {
804 i_push_error(0, "SGI image: RLE run has no data for pixel");
805 goto ErrorReturn;
806 }
807 sample = inp[0] * 256 + inp[1];
808 inp += 2;
809 data_left -= 2;
810 if (pixmin != 0 || pixmax != 65535) {
811 if (sample < pixmin)
812 sample = 0;
813 else if (sample > pixmax)
814 sample = outmax;
815 else
816 sample -= pixmin;
817 fsample = (double)sample / outmax;
818 }
819 else {
820 fsample = (double)sample / 65535.0;
821 }
822 pixels_left -= count;
823 while (count-- > 0) {
824 outp->channel[c] = fsample;
825 ++outp;
826 }
827 }
828 }
829 /* must have a full scanline */
830 if (pixels_left) {
831 i_push_error(0, "SGI image: incomplete RLE scanline");
832 goto ErrorReturn;
833 }
834 /* must have used all of the data */
835 if (data_left) {
836 i_push_errorf(0, "SGI image: unused RLE data");
837 goto ErrorReturn;
838 }
839 }
840 i_plinf(img, 0, width, height-1-y, linebuf);
841 }
842
843 myfree(linebuf);
844 myfree(databuf);
845 myfree(start_tab);
846 myfree(length_tab);
847
848 return img;
849
850 ErrorReturn:
851 if (linebuf)
852 myfree(linebuf);
853 if (databuf)
854 myfree(databuf);
855 myfree(start_tab);
856 myfree(length_tab);
857 i_img_destroy(img);
858 return NULL;
859}
860
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);
915 if (ig->writecb(ig, linebuf, width) != width) {
916 i_push_error(errno, "SGI image: error writing image data");
917 myfree(linebuf);
918 return 0;
919 }
920 }
921 }
922 myfree(linebuf);
923
924 return 1;
925}
926
927static int
928write_sgi_8_rle(i_img *img, io_glue *ig) {
929 i_sample_t *linebuf;
930 unsigned char *comp_buf;
931 i_img_dim width = img->xsize;
932 int c;
933 i_img_dim y;
934 unsigned char *offsets;
935 unsigned char *lengths;
936 int offset_pos = 0;
937 size_t offsets_size = (size_t)4 * img->ysize * img->channels * 2;
938 unsigned long start_offset = 512 + offsets_size;
939 unsigned long current_offset = start_offset;
940 int in_left;
941 unsigned char *outp;
942 i_sample_t *inp;
943 size_t comp_size;
944
945 if (offsets_size / 2 / 4 / img->channels != img->ysize) {
946 i_push_error(0, "SGI image: integer overflow calculating allocation size");
947 return 0;
948 }
949
950 linebuf = mymalloc(width); /* checked 31Jul07 TonyC */
951 comp_buf = mymalloc((width + 1) * 2); /* checked 31Jul07 TonyC */
952 offsets = mymalloc(offsets_size);
953 memset(offsets, 0, offsets_size);
954 if (i_io_write(ig, offsets, offsets_size) != offsets_size) {
955 i_push_error(errno, "SGI image: error writing offsets/lengths");
956 goto Error;
957 }
958 lengths = offsets + img->ysize * img->channels * 4;
959 for (c = 0; c < img->channels; ++c) {
960 for (y = img->ysize - 1; y >= 0; --y) {
961 i_gsamp(img, 0, width, y, linebuf, &c, 1);
962 in_left = width;
963 outp = comp_buf;
964 inp = linebuf;
965 while (in_left) {
966 unsigned char *run_start = inp;
967
968 /* first try for an RLE run */
969 int run_length = 1;
970 while (in_left - run_length >= 2 && inp[0] == inp[1] && run_length < 127) {
971 ++run_length;
972 ++inp;
973 }
974 if (in_left - run_length == 1 && inp[0] == inp[1] && run_length < 127) {
975 ++run_length;
976 ++inp;
977 }
978 if (run_length > 2) {
979 *outp++ = run_length;
980 *outp++ = inp[0];
981 inp++;
982 in_left -= run_length;
983 }
984 else {
985 inp = run_start;
986
987 /* scan for a literal run */
988 run_length = 1;
989 run_start = inp;
990 while (in_left - run_length > 1 && (inp[0] != inp[1] || inp[1] != inp[2]) && run_length < 127) {
991 ++run_length;
992 ++inp;
993 }
994 ++inp;
995
996 /* fill out the run if 2 or less samples left and there's space */
997 if (in_left - run_length <= 2
ed107438
TC
998 && in_left <= 127) {
999 run_length = in_left;
d5477d3d
TC
1000 }
1001 in_left -= run_length;
1002 *outp++ = run_length | 0x80;
1003 while (run_length--) {
1004 *outp++ = *run_start++;
1005 }
1006 }
1007 }
1008 *outp++ = 0;
1009 comp_size = outp - comp_buf;
1010 store_32(offsets + offset_pos, current_offset);
1011 store_32(lengths + offset_pos, comp_size);
1012 offset_pos += 4;
1013 current_offset += comp_size;
1014 if (ig->writecb(ig, comp_buf, comp_size) != comp_size) {
1015 i_push_error(errno, "SGI image: error writing RLE data");
1016 goto Error;
1017 }
1018 }
1019 }
1020
1021 /* seek back to store the offsets and lengths */
1022 if (i_io_seek(ig, 512, SEEK_SET) != 512) {
1023 i_push_error(errno, "SGI image: cannot seek to RLE table");
1024 goto Error;
1025 }
1026
1027 if (i_io_write(ig, offsets, offsets_size) != offsets_size) {
1028 i_push_error(errno, "SGI image: cannot write final RLE table");
1029 goto Error;
1030 }
1031
1032 myfree(offsets);
1033 myfree(comp_buf);
1034 myfree(linebuf);
1035
1036 return 1;
1037
1038 Error:
1039 myfree(offsets);
1040 myfree(comp_buf);
1041 myfree(linebuf);
1042 return 0;
1043}
1044
1045static int
1046write_sgi_16_verb(i_img *img, io_glue *ig) {
1047 i_fsample_t *linebuf;
1048 unsigned char *encbuf;
1049 unsigned char *outp;
1050 i_img_dim width = img->xsize;
1051 int c;
1052 i_img_dim x;
1053 i_img_dim y;
1054
1055 linebuf = mymalloc(width * sizeof(i_fsample_t)); /* checked 31Jul07 TonyC */
1056 encbuf = mymalloc(width * 2); /* checked 31Jul07 TonyC */
1057 for (c = 0; c < img->channels; ++c) {
1058 for (y = img->ysize - 1; y >= 0; --y) {
1059 i_gsampf(img, 0, width, y, linebuf, &c, 1);
1060 for (x = 0, outp = encbuf; x < width; ++x, outp+=2) {
1061 unsigned short samp16 = SampleFTo16(linebuf[x]);
1062 store_16(outp, samp16);
1063 }
1064 if (ig->writecb(ig, encbuf, width * 2) != width * 2) {
1065 i_push_error(errno, "SGI image: error writing image data");
1066 myfree(linebuf);
1067 myfree(encbuf);
1068 return 0;
1069 }
1070 }
1071 }
1072 myfree(linebuf);
1073 myfree(encbuf);
1074
1075 return 1;
1076}
1077
1078static int
1079write_sgi_16_rle(i_img *img, io_glue *ig) {
1080 i_fsample_t *sampbuf;
1081 unsigned short *linebuf;
1082 unsigned char *comp_buf;
1083 i_img_dim width = img->xsize;
1084 int c;
1085 i_img_dim y;
1086 unsigned char *offsets;
1087 unsigned char *lengths;
1088 int offset_pos = 0;
1089 size_t offsets_size = (size_t)4 * img->ysize * img->channels * 2;
1090 unsigned long start_offset = 512 + offsets_size;
1091 unsigned long current_offset = start_offset;
1092 int in_left;
1093 unsigned char *outp;
1094 unsigned short *inp;
1095 size_t comp_size;
1096 i_img_dim x;
1097
1098 if (offsets_size / 4 / 2 / img->channels != img->ysize) {
1099 i_push_error(0, "SGI image: integer overflow calculating allocation size");
1100 return 0;
1101 }
1102
1103 sampbuf = mymalloc(width * sizeof(i_fsample_t)); /* checked 31Jul07 TonyC */
1104 linebuf = mymalloc(width * sizeof(unsigned short)); /* checked 31Jul07 TonyC */
1105 comp_buf = mymalloc((width + 1) * 2 * 2); /* checked 31Jul07 TonyC */
1106 offsets = mymalloc(offsets_size);
1107 memset(offsets, 0, offsets_size);
1108 if (i_io_write(ig, offsets, offsets_size) != offsets_size) {
1109 i_push_error(errno, "SGI image: error writing offsets/lengths");
1110 goto Error;
1111 }
1112 lengths = offsets + img->ysize * img->channels * 4;
1113 for (c = 0; c < img->channels; ++c) {
1114 for (y = img->ysize - 1; y >= 0; --y) {
1115 i_gsampf(img, 0, width, y, sampbuf, &c, 1);
1116 for (x = 0; x < width; ++x)
1117 linebuf[x] = (unsigned short)(SampleFTo16(sampbuf[x]));
1118 in_left = width;
1119 outp = comp_buf;
1120 inp = linebuf;
1121 while (in_left) {
1122 unsigned short *run_start = inp;
1123
1124 /* first try for an RLE run */
1125 int run_length = 1;
1126 while (in_left - run_length >= 2 && inp[0] == inp[1] && run_length < 127) {
1127 ++run_length;
1128 ++inp;
1129 }
1130 if (in_left - run_length == 1 && inp[0] == inp[1] && run_length < 127) {
1131 ++run_length;
1132 ++inp;
1133 }
1134 if (run_length > 2) {
1135 store_16(outp, run_length);
1136 store_16(outp+2, inp[0]);
1137 outp += 4;
1138 inp++;
1139 in_left -= run_length;
1140 }
1141 else {
1142 inp = run_start;
1143
1144 /* scan for a literal run */
1145 run_length = 1;
1146 run_start = inp;
1147 while (in_left - run_length > 1 && (inp[0] != inp[1] || inp[1] != inp[2]) && run_length < 127) {
1148 ++run_length;
1149 ++inp;
1150 }
1151 ++inp;
1152
1153 /* fill out the run if 2 or less samples left and there's space */
1154 if (in_left - run_length <= 2
ed107438
TC
1155 && in_left <= 127) {
1156 run_length = in_left;
d5477d3d
TC
1157 }
1158 in_left -= run_length;
1159 store_16(outp, run_length | 0x80);
1160 outp += 2;
1161 while (run_length--) {
1162 store_16(outp, *run_start++);
1163 outp += 2;
1164 }
1165 }
1166 }
1167 store_16(outp, 0);
1168 outp += 2;
1169 comp_size = outp - comp_buf;
1170 store_32(offsets + offset_pos, current_offset);
1171 store_32(lengths + offset_pos, comp_size);
1172 offset_pos += 4;
1173 current_offset += comp_size;
1174 if (ig->writecb(ig, comp_buf, comp_size) != comp_size) {
1175 i_push_error(errno, "SGI image: error writing RLE data");
1176 goto Error;
1177 }
1178 }
1179 }
1180
1181 /* seek back to store the offsets and lengths */
1182 if (i_io_seek(ig, 512, SEEK_SET) != 512) {
1183 i_push_error(errno, "SGI image: cannot seek to RLE table");
1184 goto Error;
1185 }
1186
1187 if (i_io_write(ig, offsets, offsets_size) != offsets_size) {
1188 i_push_error(errno, "SGI image: cannot write final RLE table");
1189 goto Error;
1190 }
1191
1192 myfree(offsets);
1193 myfree(comp_buf);
1194 myfree(linebuf);
1195 myfree(sampbuf);
1196
1197 return 1;
1198
1199 Error:
1200 myfree(offsets);
1201 myfree(comp_buf);
1202 myfree(linebuf);
1203 myfree(sampbuf);
1204
1205 return 0;
1206}