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