13ca98e92592eed2bea890bc12d5d83bc6208825
[ffmpeg.git] / libavformat / sgi.c
1 /*
2  * SGI image format
3  * Todd Kirby <doubleshot@pacbell.net>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 #include "avformat.h"
21 #include "avio.h"
22
23 /* #define DEBUG */
24
25 /* sgi image file signature */
26 #define SGI_MAGIC 474
27
28 #define SGI_HEADER_SIZE 512
29
30 #define SGI_GRAYSCALE 1
31 #define SGI_RGB 3
32 #define SGI_RGBA 4
33
34 #define SGI_SINGLE_CHAN 2
35 #define SGI_MULTI_CHAN 3
36
37 typedef struct SGIInfo{
38     short magic;
39     char rle;
40     char bytes_per_channel;
41     unsigned short dimension;
42     unsigned short xsize;
43     unsigned short ysize;
44     unsigned short zsize;
45 } SGIInfo;
46
47
48 static int sgi_probe(AVProbeData *pd)
49 {
50     /* test for sgi magic */
51     if (pd->buf_size >= 2 && BE_16(&pd->buf[0]) == SGI_MAGIC) {
52         return AVPROBE_SCORE_MAX;
53     } else {
54         return 0;
55     }
56 }
57
58 /* read sgi header fields */
59 static void read_sgi_header(ByteIOContext *f, SGIInfo *info)
60 {
61     info->magic = (unsigned short) get_be16(f);
62     info->rle = get_byte(f);
63     info->bytes_per_channel = get_byte(f);
64     info->dimension = (unsigned short)get_be16(f);
65     info->xsize = (unsigned short) get_be16(f);
66     info->ysize = (unsigned short) get_be16(f);
67     info->zsize = (unsigned short) get_be16(f);
68
69 #ifdef DEBUG
70     printf("sgi header fields:\n");
71     printf("  magic: %d\n", info->magic);
72     printf("    rle: %d\n", info->rle);
73     printf("    bpc: %d\n", info->bytes_per_channel);
74     printf("    dim: %d\n", info->dimension);
75     printf("  xsize: %d\n", info->xsize);
76     printf("  ysize: %d\n", info->ysize);
77     printf("  zsize: %d\n", info->zsize);
78 #endif
79
80     return;
81 }
82
83
84 /* read an uncompressed sgi image */
85 static int read_uncompressed_sgi(const SGIInfo *si, 
86         AVPicture *pict, ByteIOContext *f)
87 {
88     int x, y, z, chan_offset, ret = 0;
89     uint8_t *dest_row;
90
91     /* skip header */ 
92     url_fseek(f, SGI_HEADER_SIZE, SEEK_SET);
93
94     pict->linesize[0] = si->xsize;
95
96     for (z = 0; z < si->zsize; z++) {
97
98 #ifndef WORDS_BIGENDIAN
99         /* rgba -> bgra for rgba32 on little endian cpus */
100         if (si->zsize == 4 && z != 3) 
101             chan_offset = 2 - z;
102         else
103 #endif
104             chan_offset = z;
105             
106         for (y = si->ysize - 1; y >= 0; y--) {
107             dest_row = pict->data[0] + (y * si->xsize * si->zsize);
108
109             for (x = 0; x < si->xsize; x++) {
110                 dest_row[chan_offset] = get_byte(f); 
111                 dest_row += si->zsize;
112             }
113         }
114     }
115
116     return ret;
117 }
118
119
120 /* expand an rle row into a channel */
121 static int expand_rle_row(ByteIOContext *f, unsigned char *optr,
122         int chan_offset, int pixelstride)
123 {
124     unsigned char pixel, count;
125     int length = 0;
126  
127 #ifndef WORDS_BIGENDIAN
128     /* rgba -> bgra for rgba32 on little endian cpus */
129     if (pixelstride == 4 && chan_offset != 3) {
130        chan_offset = 2 - chan_offset;
131     }
132 #endif
133         
134     optr += chan_offset;
135
136     while (1) {
137         pixel = get_byte(f);
138
139         if (!(count = (pixel & 0x7f))) {
140             return length;
141         }
142         if (pixel & 0x80) {
143             while (count--) {
144                 *optr = get_byte(f);
145                 length++;
146                 optr += pixelstride;
147             }
148         } else {
149             pixel = get_byte(f);
150
151             while (count--) {
152                 *optr = pixel;
153                 length++;
154                 optr += pixelstride;
155             }
156         }
157     }
158 }
159
160
161 /* read a run length encoded sgi image */
162 static int read_rle_sgi(const SGIInfo *sgi_info, 
163         AVPicture *pict, ByteIOContext *f)
164 {
165     uint8_t *dest_row;
166     unsigned long *start_table;
167     int y, z, xsize, ysize, zsize, tablen; 
168     long start_offset;
169     int ret = 0;
170
171     xsize = sgi_info->xsize;
172     ysize = sgi_info->ysize;
173     zsize = sgi_info->zsize;
174
175     /* skip header */ 
176     url_fseek(f, SGI_HEADER_SIZE, SEEK_SET);
177
178     /* size of rle offset and length tables */
179     tablen = ysize * zsize * sizeof(long);
180
181     start_table = (unsigned long *)av_malloc(tablen);
182
183     if (!get_buffer(f, (uint8_t *)start_table, tablen)) {
184         ret = AVERROR_IO;
185         goto fail;
186     }
187
188     /* skip run length table */ 
189     url_fseek(f, tablen, SEEK_CUR);
190
191     for (z = 0; z < zsize; z++) {
192         for (y = 0; y < ysize; y++) {
193             dest_row = pict->data[0] + (ysize - 1 - y) * (xsize * zsize);
194
195             start_offset = BE_32(&start_table[y + z * ysize]);
196
197             /* don't seek if already at the next rle start offset */
198             if (url_ftell(f) != start_offset) {
199                 url_fseek(f, start_offset, SEEK_SET);
200             }
201
202             if (expand_rle_row(f, dest_row, z, zsize) != xsize) {
203               ret =  AVERROR_INVALIDDATA;
204               goto fail;
205             }
206         }
207     }
208
209 fail:
210     av_free(start_table);
211
212     return ret;
213 }
214
215
216 static int sgi_read(ByteIOContext *f, 
217         int (*alloc_cb)(void *opaque, AVImageInfo *info), void *opaque)
218 {
219     SGIInfo sgi_info, *s = &sgi_info;
220     AVImageInfo info1, *info = &info1;
221     int ret;
222
223     read_sgi_header(f, s);
224
225     if (s->bytes_per_channel != 1) {
226         return AVERROR_INVALIDDATA;
227     }
228
229     /* check for supported image dimensions */
230     if (s->dimension != 2 && s->dimension != 3) {
231         return AVERROR_INVALIDDATA;
232     }
233
234     if (s->zsize == SGI_GRAYSCALE) {
235         info->pix_fmt = PIX_FMT_GRAY8;
236     } else if (s->zsize == SGI_RGB) {
237         info->pix_fmt = PIX_FMT_RGB24;
238     } else if (s->zsize == SGI_RGBA) {
239         info->pix_fmt = PIX_FMT_RGBA32;
240     } else {
241         return AVERROR_INVALIDDATA;
242     }
243
244     info->width = s->xsize;
245     info->height = s->ysize;
246
247     ret = alloc_cb(opaque, info);
248     if (ret)
249         return ret;
250
251     if (s->rle) {
252         return read_rle_sgi(s, &info->pict, f);
253     } else {
254         return read_uncompressed_sgi(s, &info->pict, f);
255     }
256
257     return 0; /* not reached */
258 }
259
260 #ifdef CONFIG_ENCODERS
261 static void write_sgi_header(ByteIOContext *f, const SGIInfo *info)
262 {
263     int i;
264
265     put_be16(f, SGI_MAGIC);
266     put_byte(f, info->rle);
267     put_byte(f, info->bytes_per_channel); 
268     put_be16(f, info->dimension);
269     put_be16(f, info->xsize);
270     put_be16(f, info->ysize);
271     put_be16(f, info->zsize);
272
273     /* The rest are constant in this implementation */
274     put_be32(f, 0L); /* pixmin */ 
275     put_be32(f, 255L); /* pixmax */ 
276     put_be32(f, 0L); /* dummy */ 
277
278     /* name */
279     for (i = 0; i < 80; i++) {
280         put_byte(f, 0);
281     }
282
283     put_be32(f, 0L); /* colormap */ 
284
285     /* The rest of the 512 byte header is unused. */
286     for (i = 0; i < 404; i++) {
287         put_byte(f, 0);
288     }
289 }
290
291
292 static int rle_row(ByteIOContext *f, char *row, int stride, int rowsize)
293 {
294     int length, count, i, x;
295     char *start, repeat = 0;
296
297     for (x = rowsize, length = 0; x > 0;) {
298         start = row;
299         row += (2 * stride);
300         x -= 2;
301
302         while (x > 0 && (row[-2 * stride] != row[-1 * stride] || 
303                     row[-1 * stride] != row[0])) {
304             row += stride;
305             x--;
306         };
307
308         row -= (2 * stride);
309         x += 2;
310
311         count = (row - start) / stride;
312         while (count > 0) {
313             i = count > 126 ? 126 : count;
314             count -= i;
315
316             put_byte(f, 0x80 | i); 
317             length++;
318
319             while (i > 0) {
320                 put_byte(f, *start);
321                 start += stride;
322                 i--;
323                 length++;
324             };
325         };
326
327         if (x <= 0) {
328             break;
329         }
330
331         start = row;
332         repeat = row[0];
333
334         row += stride;
335         x--;
336
337         while (x > 0 && *row == repeat) {
338             row += stride;
339             x--;
340         };
341
342         count = (row - start) / stride;
343         while (count > 0) {
344             i = count > 126 ? 126 : count;
345             count -= i;
346
347             put_byte(f, i);
348             length++;
349
350             put_byte(f, repeat); 
351             length++;
352         };
353     };
354
355     length++;
356
357     put_byte(f, 0); 
358     return (length);
359 }
360
361
362 static int sgi_write(ByteIOContext *pb, AVImageInfo *info)
363 {
364     SGIInfo sgi_info, *si = &sgi_info;
365     long *offsettab, *lengthtab;
366     int i, y, z;
367     int tablesize, chan_offset;
368     uint8_t *srcrow;
369
370     si->xsize = info->width;
371     si->ysize = info->height;
372     si->rle = 1;
373     si->bytes_per_channel = 1;
374     
375     switch(info->pix_fmt) {
376         case PIX_FMT_GRAY8:
377             si->dimension = SGI_SINGLE_CHAN;
378             si->zsize = SGI_GRAYSCALE;
379             break;
380         case PIX_FMT_RGB24:
381             si->dimension = SGI_MULTI_CHAN;
382             si->zsize = SGI_RGB;
383             break;
384          case PIX_FMT_RGBA32:
385             si->dimension = SGI_MULTI_CHAN;
386             si->zsize = SGI_RGBA;
387             break;
388         default:
389             return AVERROR_INVALIDDATA;
390     }
391
392     write_sgi_header(pb, si); 
393
394     tablesize = si->zsize * si->ysize * sizeof(long);
395     
396     /* skip rle offset and length tables, write them at the end. */
397     url_fseek(pb, tablesize * 2, SEEK_CUR);
398     put_flush_packet(pb);
399     
400     lengthtab = av_malloc(tablesize);
401     offsettab = av_malloc(tablesize);
402
403     for (z = 0; z < si->zsize; z++) {
404
405 #ifndef WORDS_BIGENDIAN
406         /* rgba -> bgra for rgba32 on little endian cpus */
407         if (si->zsize == 4 && z != 3) 
408             chan_offset = 2 - z;
409         else
410 #endif
411             chan_offset = z;
412         
413         srcrow = info->pict.data[0] + chan_offset;
414         
415         for (y = si->ysize -1; y >= 0; y--) {
416             offsettab[(z * si->ysize) + y] = url_ftell(pb);
417             lengthtab[(z * si->ysize) + y] = rle_row(pb, srcrow,
418                     si->zsize, si->xsize);
419             srcrow += info->pict.linesize[0]; 
420         }
421     }
422
423     url_fseek(pb, 512, SEEK_SET);
424     
425     /* write offset table */
426     for (i = 0; i < (si->ysize * si->zsize); i++) {
427         put_be32(pb, offsettab[i]);
428     }
429  
430     /* write length table */
431     for (i = 0; i < (si->ysize * si->zsize); i++) {
432         put_be32(pb, lengthtab[i]);
433     }
434
435     put_flush_packet(pb);
436     
437     av_free(lengthtab);
438     av_free(offsettab);
439
440     return 0;
441 }
442 #endif // CONFIG_ENCODERS
443
444 AVImageFormat sgi_image_format = {
445     "sgi",
446     "sgi,rgb,rgba,bw",
447     sgi_probe,
448     sgi_read,
449     (1 << PIX_FMT_GRAY8) | (1 << PIX_FMT_RGB24) | (1 << PIX_FMT_RGBA32), 
450 #ifdef CONFIG_ENCODERS
451     sgi_write,
452 #else
453     NULL,
454 #endif // CONFIG_ENCODERS
455 };