]>
Commit | Line | Data |
---|---|---|
92bda632 | 1 | #include "imager.h" |
5ff415bb AMH |
2 | #include "log.h" |
3 | #include "iolayer.h" | |
4 | ||
5 | #include <stdlib.h> | |
6 | #include <errno.h> | |
7 | ||
8 | ||
9 | /* | |
10 | =head1 NAME | |
11 | ||
12 | rgb.c - implements reading and writing sgi image files, uses io layer. | |
13 | ||
14 | =head1 SYNOPSIS | |
15 | ||
16 | io_glue *ig = io_new_fd( fd ); | |
17 | i_img *im = i_readrgb_wiol(ig, -1); // no limit on how much is read | |
18 | // or | |
19 | io_glue *ig = io_new_fd( fd ); | |
20 | return_code = i_writergb_wiol(im, ig); | |
21 | ||
22 | =head1 DESCRIPTION | |
23 | ||
24 | rgb.c implements the basic functions to read and write portable targa | |
25 | files. It uses the iolayer and needs either a seekable source or an | |
26 | entire memory mapped buffer. | |
27 | ||
28 | =head1 FUNCTION REFERENCE | |
29 | ||
30 | Some of these functions are internal. | |
31 | ||
32 | =over | |
33 | ||
34 | =cut | |
35 | */ | |
36 | ||
37 | typedef struct { | |
38 | unsigned short imagic; | |
39 | unsigned char storagetype; | |
40 | unsigned char BPC; | |
41 | unsigned short dimensions; | |
42 | unsigned short xsize, ysize, zsize; | |
43 | unsigned int min, max; | |
44 | char name[80]; | |
45 | unsigned int colormap; | |
46 | } rgb_header; | |
47 | ||
da1c841c AMH |
48 | typedef struct { |
49 | int start, length; | |
50 | } stlen_pair; | |
5ff415bb AMH |
51 | |
52 | typedef enum { NoInit, Raw, Rle } rle_state; | |
53 | ||
5ff415bb AMH |
54 | |
55 | ||
56 | typedef struct { | |
57 | int compressed; | |
58 | int bytepp; | |
59 | io_glue *ig; | |
60 | } rgb_dest; | |
61 | ||
62 | ||
63 | ||
64 | ||
5ff415bb AMH |
65 | |
66 | ||
67 | /* | |
68 | =item rgb_header_unpack(header, headbuf) | |
69 | ||
70 | Unpacks the header structure into from buffer and stores | |
71 | in the header structure. | |
72 | ||
73 | header - header structure | |
74 | headbuf - buffer to unpack from | |
75 | ||
76 | =cut | |
77 | */ | |
78 | ||
79 | ||
80 | static | |
81 | void | |
82 | rgb_header_unpack(rgb_header *header, unsigned char *headbuf) { | |
83 | header->imagic = (headbuf[0]<<8) + headbuf[1]; | |
84 | header->storagetype = headbuf[2]; | |
85 | header->BPC = headbuf[3]; | |
86 | header->dimensions = (headbuf[4]<<8) + headbuf[5]; | |
87 | header->xsize = (headbuf[6]<<8) + headbuf[7]; | |
88 | header->ysize = (headbuf[8]<<8) + headbuf[9]; | |
89 | header->zsize = (headbuf[10]<<8) + headbuf[11]; | |
90 | header->min = (headbuf[12]<<24) + (headbuf[13]<<16)+(headbuf[14]<<8)+headbuf[15]; | |
91 | header->max = (headbuf[16]<<24) + (headbuf[17]<<16)+(headbuf[18]<<8)+headbuf[19]; | |
92 | memcpy(header->name,headbuf+20,80); | |
93 | header->colormap = (headbuf[100]<<24) + (headbuf[101]<<16)+(headbuf[102]<<8)+headbuf[103]; | |
94 | } | |
95 | ||
e4bf9335 | 96 | #if 0 /* this is currently unused */ |
5ff415bb AMH |
97 | |
98 | /* | |
99 | =item rgb_header_pack(header, headbuf) | |
100 | ||
101 | Packs header structure into buffer for writing. | |
102 | ||
103 | header - header structure | |
104 | headbuf - buffer to pack into | |
105 | ||
106 | =cut | |
107 | */ | |
108 | ||
109 | static | |
110 | void | |
111 | rgb_header_pack(rgb_header *header, unsigned char headbuf[512]) { | |
112 | ||
113 | header->imagic = (headbuf[0]<<8) + headbuf[1]; | |
114 | header->storagetype = headbuf[2]; | |
115 | header->BPC = headbuf[3]; | |
116 | header->dimensions = (headbuf[4]<<8) + headbuf[5]; | |
117 | header->xsize = (headbuf[6]<<8) + headbuf[7]; | |
118 | header->ysize = (headbuf[8]<<8) + headbuf[9]; | |
119 | header->zsize = (headbuf[10]<<8) + headbuf[11]; | |
120 | header->min = (headbuf[12]<<24) + (headbuf[13]<<16)+(headbuf[14]<<8)+headbuf[15]; | |
121 | header->max = (headbuf[16]<<24) + (headbuf[17]<<16)+(headbuf[18]<<8)+headbuf[19]; | |
122 | memcpy(header->name,headbuf+20,80); | |
123 | header->colormap = (headbuf[100]<<24) + (headbuf[101]<<16)+(headbuf[102]<<8)+headbuf[103]; | |
124 | ||
125 | } | |
126 | ||
127 | ||
5ff415bb AMH |
128 | |
129 | ||
130 | ||
131 | /* | |
132 | =item rgb_dest_write(s, buf, pixels) | |
133 | ||
134 | Writes pixels from buf to destination s. Takes care of compressing if the | |
135 | destination is compressed. | |
136 | ||
137 | s - data destination | |
138 | buf - source buffer | |
139 | pixels - number of pixels to put write to destination | |
140 | ||
141 | =cut | |
142 | */ | |
143 | ||
144 | static | |
145 | int | |
146 | rgb_dest_write(rgb_dest *s, unsigned char *buf, size_t pixels) { | |
af070d99 | 147 | return -1; |
5ff415bb AMH |
148 | } |
149 | ||
e4bf9335 | 150 | #endif |
5ff415bb AMH |
151 | |
152 | ||
153 | ||
154 | ||
155 | ||
156 | ||
157 | /* | |
158 | =item i_readrgb_wiol(ig, length) | |
159 | ||
160 | Read in an image from the iolayer data source and return the image structure to it. | |
161 | Returns NULL on error. | |
162 | ||
163 | ig - io_glue object | |
164 | length - maximum length to read from data source, before closing it -1 | |
165 | signifies no limit. | |
166 | ||
167 | =cut | |
168 | */ | |
169 | ||
170 | i_img * | |
171 | i_readrgb_wiol(io_glue *ig, int length) { | |
172 | i_img *img; | |
a659442a | 173 | int y, c,i; |
5ff415bb AMH |
174 | int width, height, channels; |
175 | unsigned long maxlen; | |
da1c841c AMH |
176 | |
177 | int savemask; | |
5ff415bb | 178 | |
5ff415bb AMH |
179 | rgb_header header; |
180 | unsigned char headbuf[512]; | |
181 | unsigned char *databuf; | |
5ff415bb AMH |
182 | unsigned long *starttab, *lengthtab; |
183 | i_color *linebuf = NULL; | |
184 | i_mempool mp; | |
185 | ||
186 | mm_log((1,"i_readrgb(ig %p, length %d)\n", ig, length)); | |
187 | i_clear_error(); | |
188 | i_mempool_init(&mp); | |
189 | ||
190 | io_glue_commit_types(ig); | |
191 | ||
192 | if (ig->readcb(ig, headbuf, 512) != 512) { | |
193 | i_push_error(errno, "could not read SGI rgb header"); | |
194 | return NULL; | |
195 | } | |
196 | ||
197 | rgb_header_unpack(&header, headbuf); | |
198 | ||
199 | ||
200 | mm_log((1,"imagic: %d\n", header.imagic)); | |
201 | mm_log((1,"storagetype: %d\n", header.storagetype)); | |
202 | mm_log((1,"BPC: %d\n", header.BPC)); | |
203 | mm_log((1,"dimensions: %d\n", header.dimensions)); | |
204 | mm_log((1,"xsize: %d\n", header.xsize)); | |
205 | mm_log((1,"ysize: %d\n", header.ysize)); | |
206 | mm_log((1,"zsize: %d\n", header.zsize)); | |
207 | mm_log((1,"min: %d\n", header.min)); | |
208 | mm_log((1,"max: %d\n", header.max)); | |
209 | mm_log((1,"name [skipped]\n")); | |
210 | mm_log((1,"colormap: %d\n", header.colormap)); | |
211 | ||
212 | if (header.colormap != 0) { | |
213 | i_push_error(0, "SGI rgb image has a non zero colormap entry - obsolete format"); | |
214 | return NULL; | |
215 | } | |
216 | ||
217 | if (header.storagetype != 0 && header.storagetype != 1) { | |
218 | i_push_error(0, "SGI rgb image has has invalid storage field"); | |
219 | return NULL; | |
220 | } | |
221 | ||
222 | width = header.xsize; | |
223 | height = header.ysize; | |
224 | channels = header.zsize; | |
225 | ||
226 | img = i_img_empty_ch(NULL, width, height, channels); | |
50dc291e TC |
227 | if (!img) |
228 | return NULL; | |
5ff415bb AMH |
229 | |
230 | i_tags_add(&img->tags, "rgb_namestr", 0, header.name, 80, 0); | |
50dc291e | 231 | i_tags_add(&img->tags, "i_format", 0, "rgb", -1, 0); |
da1c841c | 232 | |
5ff415bb AMH |
233 | switch (header.storagetype) { |
234 | case 0: /* uncompressed */ | |
235 | ||
da1c841c AMH |
236 | linebuf = i_mempool_alloc(&mp, width*sizeof(i_color)); |
237 | databuf = i_mempool_alloc(&mp, width); | |
238 | ||
239 | savemask = i_img_getmask(img); | |
240 | ||
241 | for(c=0; c<channels; c++) { | |
242 | i_img_setmask(img, 1<<c); | |
243 | for(y=0; y<height; y++) { | |
244 | int x; | |
245 | ||
246 | if (ig->readcb(ig, databuf, width) != width) { | |
247 | i_push_error(0, "SGI rgb: cannot read"); | |
248 | goto ErrorReturn; | |
249 | } | |
250 | ||
251 | for(x=0; x<width; x++) | |
252 | linebuf[x].channel[c] = databuf[x]; | |
253 | ||
254 | i_plin(img, 0, width, height-1-y, linebuf); | |
255 | } | |
256 | } | |
257 | i_img_setmask(img, savemask); | |
5ff415bb AMH |
258 | break; |
259 | case 1: /* RLE compressed */ | |
260 | ||
261 | databuf = i_mempool_alloc(&mp, height*channels*4); | |
262 | starttab = i_mempool_alloc(&mp, height*channels*sizeof(unsigned long)); | |
263 | lengthtab = i_mempool_alloc(&mp, height*channels*sizeof(unsigned long)); | |
264 | linebuf = i_mempool_alloc(&mp, width*sizeof(i_color)); | |
265 | ||
266 | /* Read offset table */ | |
267 | if (ig->readcb(ig, databuf, height*channels*4) != height*channels*4) goto ErrorReturn; | |
268 | for(i=0; i<height*channels; i++) starttab[i] = | |
269 | (databuf[i*4]<<24) | | |
270 | (databuf[i*4+1]<<16) | | |
271 | (databuf[i*4+2]<<8) | | |
272 | (databuf[i*4+3]); | |
273 | ||
274 | ||
275 | /* Read length table */ | |
276 | if (ig->readcb(ig, databuf, height*channels*4) != height*channels*4) goto ErrorReturn; | |
277 | for(i=0; i<height*channels; i++) lengthtab[i] = | |
278 | (databuf[i*4]<<24)+ | |
279 | (databuf[i*4+1]<<16)+ | |
280 | (databuf[i*4+2]<<8)+ | |
281 | (databuf[i*4+3]); | |
282 | ||
283 | mm_log((3, "Offset/length table:\n")); | |
284 | for(i=0; i<height*channels; i++) | |
285 | mm_log((3, "%d: %d/%d\n", i, starttab[i], lengthtab[i])); | |
286 | ||
287 | ||
288 | /* Find max spanlength if someone is making very badly formed RLE data */ | |
289 | maxlen = 0; | |
290 | for(y=0; y<height; y++) maxlen = (maxlen>lengthtab[y])?maxlen:lengthtab[y]; | |
291 | ||
292 | mm_log((1, "maxlen for an rle buffer: %d\n", maxlen)); | |
293 | ||
294 | databuf = i_mempool_alloc(&mp, maxlen); | |
295 | ||
296 | for(y=0; y<height; y++) { | |
297 | for(c=0; c<channels; c++) { | |
298 | unsigned long iidx = 0, oidx = 0, span = 0; | |
e4bf9335 | 299 | unsigned char cval = 0; |
5ff415bb AMH |
300 | int rle = 0; |
301 | int ci = height*c+y; | |
302 | int datalen = lengthtab[ci]; | |
303 | ||
304 | if (ig->seekcb(ig, starttab[ci], SEEK_SET) != starttab[ci]) { | |
305 | i_push_error(0, "SGI rgb: cannot seek"); | |
306 | goto ErrorReturn; | |
307 | } | |
308 | if (ig->readcb(ig, databuf, datalen) != datalen) { | |
309 | i_push_error(0, "SGI rgb: cannot read"); | |
310 | goto ErrorReturn; | |
311 | } | |
312 | ||
313 | /* | |
314 | mm_log((1, "Buffer length %d\n", datalen)); | |
315 | for(i=0; i<datalen; i++) | |
316 | mm_log((1, "0x%x\n", databuf[i])); | |
317 | */ | |
318 | ||
319 | while( iidx <= datalen && oidx < width ) { | |
320 | if (!span) { | |
321 | span = databuf[iidx] & 0x7f; | |
322 | rle = !(databuf[iidx++] & 0x80); | |
323 | /* mm_log((1,"new span %d, rle %d\n", span, rle)); */ | |
324 | if (rle) { | |
325 | if (iidx==datalen) { | |
326 | i_push_error(0, "SGI rgb: bad rle data"); | |
327 | goto ErrorReturn; | |
328 | } | |
329 | cval = databuf[iidx++]; | |
330 | /* mm_log((1, "rle value %d\n", cval)); */ | |
331 | } | |
332 | } | |
333 | linebuf[oidx++].channel[c] = rle ? cval : databuf[iidx++]; | |
334 | span--; | |
335 | /* | |
336 | mm_log((1,"iidx=%d/%d, oidx=%d/%d, linebuf[%d].channel[%d] %d\n", iidx-1, datalen, oidx-1, width, oidx-1, c, linebuf[oidx-1].channel[c])); | |
337 | */ | |
338 | } | |
339 | } | |
340 | i_plin(img, 0, width, height-1-y, linebuf); | |
341 | } | |
342 | ||
343 | break; | |
344 | } | |
345 | ||
50dc291e TC |
346 | i_tags_add(&img->tags, "i_format", 0, "rgb", -1, 0); |
347 | ||
5ff415bb AMH |
348 | i_mempool_destroy(&mp); |
349 | return img; | |
350 | ||
351 | ErrorReturn: | |
352 | i_mempool_destroy(&mp); | |
353 | if (img) i_img_destroy(img); | |
354 | return NULL; | |
355 | } | |
356 | ||
357 | ||
358 | ||
359 | /* | |
360 | =item i_writergb_wiol(img, ig) | |
361 | ||
362 | Writes an image in targa format. Returns 0 on error. | |
363 | ||
364 | img - image to store | |
365 | ig - io_glue object | |
366 | ||
367 | =cut | |
368 | */ | |
369 | ||
370 | undef_int | |
371 | i_writergb_wiol(i_img *img, io_glue *ig, int wierdpack, int compress, char *idstring, size_t idlen) { | |
50dc291e TC |
372 | i_clear_error(); |
373 | i_push_error(0, "writing SGI RGB files is not implemented"); | |
5ff415bb | 374 | |
50dc291e | 375 | return 0; |
5ff415bb AMH |
376 | } |
377 |