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