]>
Commit | Line | Data |
---|---|---|
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 |
26 | typedef struct { |
27 | unsigned short imagic; | |
28 | unsigned char storagetype; | |
29 | unsigned char BPC; | |
30 | unsigned short dimensions; | |
31 | unsigned short xsize, ysize, zsize; | |
32 | unsigned int pixmin, pixmax; | |
33 | char name[80]; | |
34 | unsigned int colormap; | |
35 | } rgb_header; | |
36 | ||
37 | static i_img * | |
38 | read_rgb_8_verbatim(i_img *im, io_glue *ig, rgb_header const *hdr); | |
39 | static i_img * | |
40 | read_rgb_8_rle(i_img *im, io_glue *ig, rgb_header const *hdr); | |
41 | static i_img * | |
42 | read_rgb_16_verbatim(i_img *im, io_glue *ig, rgb_header const *hdr); | |
43 | static i_img * | |
44 | read_rgb_16_rle(i_img *im, io_glue *ig, rgb_header const *hdr); | |
45 | static int | |
46 | write_sgi_header(i_img *img, io_glue *ig, int *rle, int *bpc2); | |
47 | static int | |
48 | write_sgi_8_rle(i_img *img, io_glue *ig); | |
49 | static int | |
50 | write_sgi_8_verb(i_img *img, io_glue *ig); | |
51 | static int | |
52 | write_sgi_16_rle(i_img *img, io_glue *ig); | |
53 | static int | |
54 | write_sgi_16_verb(i_img *img, io_glue *ig); | |
55 | ||
56 | #define Sample16ToF(num) ((num) / 65535.0) | |
57 | ||
58 | #define _STRING(x) #x | |
59 | #define STRING(x) _STRING(x) | |
60 | ||
61 | /* | |
62 | =head1 NAME | |
63 | ||
64 | rgb.c - implements reading and writing sgi image files, uses io layer. | |
65 | ||
66 | =head1 SYNOPSIS | |
67 | ||
68 | io_glue *ig = io_new_fd( fd ); | |
69 | i_img *im = i_readrgb_wiol(ig, 0); // disallow partial reads | |
70 | // or | |
71 | io_glue *ig = io_new_fd( fd ); | |
72 | return_code = i_writergb_wiol(im, ig); | |
73 | ||
74 | =head1 DESCRIPTION | |
75 | ||
76 | imsgi.c implements the basic functions to read and write portable SGI | |
77 | files. It uses the iolayer and needs either a seekable source or an | |
78 | entire memory mapped buffer. | |
79 | ||
80 | =head1 FUNCTION REFERENCE | |
81 | ||
82 | Some of these functions are internal. | |
83 | ||
84 | =over | |
85 | ||
86 | =cut | |
87 | */ | |
88 | ||
89 | /* | |
90 | =item rgb_header_unpack(header, headbuf) | |
91 | ||
92 | Unpacks the header structure into from buffer and stores | |
93 | in the header structure. | |
94 | ||
95 | header - header structure | |
96 | headbuf - buffer to unpack from | |
97 | ||
98 | =cut | |
99 | */ | |
100 | ||
101 | ||
102 | static | |
103 | void | |
104 | rgb_header_unpack(rgb_header *header, const unsigned char *headbuf) { | |
105 | header->imagic = (headbuf[0]<<8) + headbuf[1]; | |
106 | header->storagetype = headbuf[2]; | |
107 | header->BPC = headbuf[3]; | |
108 | header->dimensions = (headbuf[4]<<8) + headbuf[5]; | |
109 | header->xsize = (headbuf[6]<<8) + headbuf[7]; | |
110 | header->ysize = (headbuf[8]<<8) + headbuf[9]; | |
111 | header->zsize = (headbuf[10]<<8) + headbuf[11]; | |
112 | header->pixmin = (headbuf[12]<<24) + (headbuf[13]<<16)+(headbuf[14]<<8)+headbuf[15]; | |
113 | header->pixmax = (headbuf[16]<<24) + (headbuf[17]<<16)+(headbuf[18]<<8)+headbuf[19]; | |
114 | memcpy(header->name,headbuf+24,80); | |
115 | header->name[79] = '\0'; | |
116 | header->colormap = (headbuf[104]<<24) + (headbuf[105]<<16)+(headbuf[106]<<8)+headbuf[107]; | |
117 | } | |
118 | ||
119 | /* don't make this a macro */ | |
120 | static void | |
121 | store_16(unsigned char *buf, unsigned short value) { | |
122 | buf[0] = value >> 8; | |
123 | buf[1] = value & 0xFF; | |
124 | } | |
125 | ||
126 | static void | |
ed107438 | 127 | store_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 | ||
137 | Packs header structure into buffer for writing. | |
138 | ||
139 | header - header structure | |
140 | headbuf - buffer to pack into | |
141 | ||
142 | =cut | |
143 | */ | |
144 | ||
145 | static | |
146 | void | |
147 | rgb_header_pack(const rgb_header *header, unsigned char headbuf[512]) { | |
148 | memset(headbuf, 0, 512); | |
149 | store_16(headbuf, header->imagic); | |
150 | headbuf[2] = header->storagetype; | |
151 | headbuf[3] = header->BPC; | |
152 | store_16(headbuf+4, header->dimensions); | |
153 | store_16(headbuf+6, header->xsize); | |
154 | store_16(headbuf+8, header->ysize); | |
155 | store_16(headbuf+10, header->zsize); | |
156 | store_32(headbuf+12, header->pixmin); | |
157 | store_32(headbuf+16, header->pixmax); | |
158 | memccpy(headbuf+24, header->name, '\0', 80); | |
159 | store_32(headbuf+104, header->colormap); | |
160 | } | |
161 | ||
162 | /* | |
163 | =item i_readsgi_wiol(ig, partial) | |
164 | ||
165 | Read in an image from the iolayer data source and return the image structure to it. | |
166 | Returns NULL on error. | |
167 | ||
168 | ig - io_glue object | |
169 | length - maximum length to read from data source, before closing it -1 | |
170 | signifies no limit. | |
171 | ||
172 | =cut | |
173 | */ | |
174 | ||
175 | i_img * | |
176 | i_readsgi_wiol(io_glue *ig, int partial) { | |
177 | i_img *img = NULL; | |
178 | int width, height, channels; | |
179 | rgb_header header; | |
180 | unsigned char headbuf[512]; | |
181 | ||
182 | mm_log((1,"i_readsgi(ig %p, partial %d)\n", ig, partial)); | |
183 | i_clear_error(); | |
184 | ||
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 | ||
316 | Writes an image in targa format. Returns 0 on error. | |
317 | ||
318 | img - image to store | |
319 | ig - io_glue object | |
320 | ||
321 | =cut | |
322 | */ | |
323 | ||
324 | int | |
325 | i_writesgi_wiol(io_glue *ig, i_img *img) { | |
326 | int rle; | |
327 | int bpc2; | |
328 | ||
329 | i_clear_error(); | |
330 | ||
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 | ||
355 | static i_img * | |
356 | read_rgb_8_verbatim(i_img *img, io_glue *ig, rgb_header const *header) { | |
357 | i_color *linebuf; | |
358 | unsigned char *databuf; | |
359 | int c, y; | |
360 | int savemask; | |
361 | i_img_dim width = i_img_get_width(img); | |
362 | i_img_dim height = i_img_get_height(img); | |
363 | int channels = i_img_getchannels(img); | |
364 | int pixmin = header->pixmin; | |
365 | int pixmax = header->pixmax; | |
366 | int outmax = pixmax - pixmin; | |
367 | ||
368 | linebuf = mymalloc(width * sizeof(i_color)); /* checked 31Jul07 TonyC */ | |
369 | databuf = mymalloc(width); /* checked 31Jul07 TonyC */ | |
370 | ||
371 | savemask = i_img_getmask(img); | |
372 | ||
373 | for(c = 0; c < channels; c++) { | |
374 | i_img_setmask(img, 1<<c); | |
375 | for(y = 0; y < height; y++) { | |
376 | int x; | |
377 | ||
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 | ||
415 | static int | |
416 | read_rle_tables(io_glue *ig, i_img *img, | |
417 | unsigned long **pstart_tab, unsigned long **plength_tab, | |
418 | unsigned long *pmax_length) { | |
419 | i_img_dim height = i_img_get_height(img); | |
420 | int channels = i_img_getchannels(img); | |
421 | unsigned char *databuf; | |
422 | unsigned long *start_tab, *length_tab; | |
423 | unsigned long max_length = 0; | |
424 | int i; | |
425 | size_t databuf_size = (size_t)height * channels * 4; | |
426 | size_t tab_size = (size_t)height * channels * sizeof(unsigned long); | |
427 | ||
428 | /* assumption: that the lengths are in bytes rather than in pixels */ | |
429 | if (databuf_size / height / channels != 4 | |
430 | || tab_size / height / channels != sizeof(unsigned long)) { | |
431 | i_push_error(0, "SGI image: integer overflow calculating allocation size"); | |
432 | return 0; | |
433 | } | |
434 | databuf = mymalloc(height * channels * 4); /* checked 31Jul07 TonyC */ | |
435 | start_tab = mymalloc(height*channels*sizeof(unsigned long)); | |
436 | length_tab = mymalloc(height*channels*sizeof(unsigned long)); | |
437 | ||
438 | /* Read offset table */ | |
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 | ||
482 | static i_img * | |
483 | read_rgb_8_rle(i_img *img, io_glue *ig, rgb_header const *header) { | |
484 | i_color *linebuf = NULL; | |
485 | unsigned char *databuf = NULL; | |
486 | unsigned long *start_tab, *length_tab; | |
487 | unsigned long max_length; | |
488 | i_img_dim width = i_img_get_width(img); | |
489 | i_img_dim height = i_img_get_height(img); | |
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 | ||
636 | static i_img * | |
637 | read_rgb_16_verbatim(i_img *img, io_glue *ig, rgb_header const *header) { | |
638 | i_fcolor *linebuf; | |
639 | unsigned char *databuf; | |
640 | int c, y; | |
641 | int savemask; | |
642 | i_img_dim width = i_img_get_width(img); | |
643 | i_img_dim height = i_img_get_height(img); | |
644 | int channels = i_img_getchannels(img); | |
645 | int pixmin = header->pixmin; | |
646 | int pixmax = header->pixmax; | |
647 | int outmax = pixmax - pixmin; | |
648 | ||
649 | linebuf = mymalloc(width * sizeof(i_fcolor)); /* checked 31Jul07 TonyC */ | |
650 | databuf = mymalloc(width * 2); /* checked 31Jul07 TonyC */ | |
651 | ||
652 | savemask = i_img_getmask(img); | |
653 | ||
654 | for(c = 0; c < channels; c++) { | |
655 | i_img_setmask(img, 1<<c); | |
656 | for(y = 0; y < height; y++) { | |
657 | int x; | |
658 | ||
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 | ||
696 | static i_img * | |
697 | read_rgb_16_rle(i_img *img, io_glue *ig, rgb_header const *header) { | |
698 | i_fcolor *linebuf = NULL; | |
699 | unsigned char *databuf = NULL; | |
700 | unsigned long *start_tab, *length_tab; | |
701 | unsigned long max_length; | |
702 | i_img_dim width = i_img_get_width(img); | |
703 | i_img_dim height = i_img_get_height(img); | |
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 | ||
861 | static int | |
862 | write_sgi_header(i_img *img, io_glue *ig, int *rle, int *bpc2) { | |
863 | rgb_header header; | |
864 | unsigned char headbuf[512] = { 0 }; | |
865 | ||
866 | header.imagic = SGI_MAGIC; | |
867 | if (!i_tags_get_int(&img->tags, "sgi_rle", 0, rle)) | |
868 | *rle = 0; | |
869 | header.storagetype = *rle ? SGI_STORAGE_RLE : SGI_STORAGE_VERBATIM; | |
870 | header.pixmin = 0; | |
871 | header.colormap = SGI_COLORMAP_NORMAL; | |
872 | *bpc2 = img->bits > 8; | |
873 | if (*bpc2) { | |
874 | header.BPC = 2; | |
875 | header.pixmax = 65535; | |
876 | } | |
877 | else { | |
878 | header.BPC = 1; | |
879 | header.pixmax = 255; | |
880 | } | |
881 | if (img->channels == 1) { | |
882 | header.dimensions = 2; | |
883 | } | |
884 | else { | |
885 | header.dimensions = 3; | |
886 | } | |
887 | header.xsize = img->xsize; | |
888 | header.ysize = img->ysize; | |
889 | header.zsize = img->channels; | |
890 | memset(header.name, 0, sizeof(header.name)); | |
891 | i_tags_get_string(&img->tags, "i_comment", 0, | |
892 | header.name, sizeof(header.name)); | |
893 | ||
894 | rgb_header_pack(&header, headbuf); | |
895 | ||
896 | if (i_io_write(ig, headbuf, sizeof(headbuf)) != sizeof(headbuf)) { | |
897 | i_push_error(0, "SGI image: cannot write header"); | |
898 | return 0; | |
899 | } | |
900 | ||
901 | return 1; | |
902 | } | |
903 | ||
904 | static int | |
905 | write_sgi_8_verb(i_img *img, io_glue *ig) { | |
906 | i_sample_t *linebuf; | |
907 | i_img_dim width = img->xsize; | |
908 | int c; | |
909 | i_img_dim y; | |
910 | ||
911 | linebuf = mymalloc(width); /* checked 31Jul07 TonyC */ | |
912 | for (c = 0; c < img->channels; ++c) { | |
913 | for (y = img->ysize - 1; y >= 0; --y) { | |
914 | i_gsamp(img, 0, width, y, linebuf, &c, 1); | |
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 | ||
930 | static int | |
931 | write_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 | ||
1051 | static int | |
1052 | write_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 | ||
1087 | static int | |
1088 | write_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 | } |