]> git.sesse.net Git - ffmpeg/blob - libavformat/sgi.c
COSMETICS: Remove all trailing whitespace.
[ffmpeg] / 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     if(info->zsize > 4096)
70         info->zsize= 0;
71
72 #ifdef DEBUG
73     printf("sgi header fields:\n");
74     printf("  magic: %d\n", info->magic);
75     printf("    rle: %d\n", info->rle);
76     printf("    bpc: %d\n", info->bytes_per_channel);
77     printf("    dim: %d\n", info->dimension);
78     printf("  xsize: %d\n", info->xsize);
79     printf("  ysize: %d\n", info->ysize);
80     printf("  zsize: %d\n", info->zsize);
81 #endif
82
83     return;
84 }
85
86
87 /* read an uncompressed sgi image */
88 static int read_uncompressed_sgi(const SGIInfo *si,
89         AVPicture *pict, ByteIOContext *f)
90 {
91     int x, y, z, chan_offset, ret = 0;
92     uint8_t *dest_row;
93
94     /* skip header */
95     url_fseek(f, SGI_HEADER_SIZE, SEEK_SET);
96
97     pict->linesize[0] = si->xsize;
98
99     for (z = 0; z < si->zsize; z++) {
100
101 #ifndef WORDS_BIGENDIAN
102         /* rgba -> bgra for rgba32 on little endian cpus */
103         if (si->zsize == 4 && z != 3)
104             chan_offset = 2 - z;
105         else
106 #endif
107             chan_offset = z;
108
109         for (y = si->ysize - 1; y >= 0; y--) {
110             dest_row = pict->data[0] + (y * si->xsize * si->zsize);
111
112             for (x = 0; x < si->xsize; x++) {
113                 dest_row[chan_offset] = get_byte(f);
114                 dest_row += si->zsize;
115             }
116         }
117     }
118
119     return ret;
120 }
121
122
123 /* expand an rle row into a channel */
124 static int expand_rle_row(ByteIOContext *f, unsigned char *optr,
125         int chan_offset, int pixelstride)
126 {
127     unsigned char pixel, count;
128     int length = 0;
129
130 #ifndef WORDS_BIGENDIAN
131     /* rgba -> bgra for rgba32 on little endian cpus */
132     if (pixelstride == 4 && chan_offset != 3) {
133        chan_offset = 2 - chan_offset;
134     }
135 #endif
136
137     optr += chan_offset;
138
139     while (1) {
140         pixel = get_byte(f);
141
142         if (!(count = (pixel & 0x7f))) {
143             return length;
144         }
145         if (pixel & 0x80) {
146             while (count--) {
147                 *optr = get_byte(f);
148                 length++;
149                 optr += pixelstride;
150             }
151         } else {
152             pixel = get_byte(f);
153
154             while (count--) {
155                 *optr = pixel;
156                 length++;
157                 optr += pixelstride;
158             }
159         }
160     }
161 }
162
163
164 /* read a run length encoded sgi image */
165 static int read_rle_sgi(const SGIInfo *sgi_info,
166         AVPicture *pict, ByteIOContext *f)
167 {
168     uint8_t *dest_row;
169     unsigned long *start_table;
170     int y, z, xsize, ysize, zsize, tablen;
171     long start_offset;
172     int ret = 0;
173
174     xsize = sgi_info->xsize;
175     ysize = sgi_info->ysize;
176     zsize = sgi_info->zsize;
177
178     /* skip header */
179     url_fseek(f, SGI_HEADER_SIZE, SEEK_SET);
180
181     /* size of rle offset and length tables */
182     tablen = ysize * zsize * sizeof(long);
183
184     start_table = (unsigned long *)av_malloc(tablen);
185
186     if (!get_buffer(f, (uint8_t *)start_table, tablen)) {
187         ret = AVERROR_IO;
188         goto fail;
189     }
190
191     /* skip run length table */
192     url_fseek(f, tablen, SEEK_CUR);
193
194     for (z = 0; z < zsize; z++) {
195         for (y = 0; y < ysize; y++) {
196             dest_row = pict->data[0] + (ysize - 1 - y) * (xsize * zsize);
197
198             start_offset = BE_32(&start_table[y + z * ysize]);
199
200             /* don't seek if already at the next rle start offset */
201             if (url_ftell(f) != start_offset) {
202                 url_fseek(f, start_offset, SEEK_SET);
203             }
204
205             if (expand_rle_row(f, dest_row, z, zsize) != xsize) {
206               ret =  AVERROR_INVALIDDATA;
207               goto fail;
208             }
209         }
210     }
211
212 fail:
213     av_free(start_table);
214
215     return ret;
216 }
217
218
219 static int sgi_read(ByteIOContext *f,
220         int (*alloc_cb)(void *opaque, AVImageInfo *info), void *opaque)
221 {
222     SGIInfo sgi_info, *s = &sgi_info;
223     AVImageInfo info1, *info = &info1;
224     int ret;
225
226     read_sgi_header(f, s);
227
228     if (s->bytes_per_channel != 1) {
229         return AVERROR_INVALIDDATA;
230     }
231
232     /* check for supported image dimensions */
233     if (s->dimension != 2 && s->dimension != 3) {
234         return AVERROR_INVALIDDATA;
235     }
236
237     if (s->zsize == SGI_GRAYSCALE) {
238         info->pix_fmt = PIX_FMT_GRAY8;
239     } else if (s->zsize == SGI_RGB) {
240         info->pix_fmt = PIX_FMT_RGB24;
241     } else if (s->zsize == SGI_RGBA) {
242         info->pix_fmt = PIX_FMT_RGBA32;
243     } else {
244         return AVERROR_INVALIDDATA;
245     }
246
247     info->width = s->xsize;
248     info->height = s->ysize;
249
250     ret = alloc_cb(opaque, info);
251     if (ret)
252         return ret;
253
254     if (s->rle) {
255         return read_rle_sgi(s, &info->pict, f);
256     } else {
257         return read_uncompressed_sgi(s, &info->pict, f);
258     }
259
260     return 0; /* not reached */
261 }
262
263 #ifdef CONFIG_MUXERS
264 static void write_sgi_header(ByteIOContext *f, const SGIInfo *info)
265 {
266     int i;
267
268     put_be16(f, SGI_MAGIC);
269     put_byte(f, info->rle);
270     put_byte(f, info->bytes_per_channel);
271     put_be16(f, info->dimension);
272     put_be16(f, info->xsize);
273     put_be16(f, info->ysize);
274     put_be16(f, info->zsize);
275
276     /* The rest are constant in this implementation */
277     put_be32(f, 0L); /* pixmin */
278     put_be32(f, 255L); /* pixmax */
279     put_be32(f, 0L); /* dummy */
280
281     /* name */
282     for (i = 0; i < 80; i++) {
283         put_byte(f, 0);
284     }
285
286     put_be32(f, 0L); /* colormap */
287
288     /* The rest of the 512 byte header is unused. */
289     for (i = 0; i < 404; i++) {
290         put_byte(f, 0);
291     }
292 }
293
294
295 static int rle_row(ByteIOContext *f, char *row, int stride, int rowsize)
296 {
297     int length, count, i, x;
298     char *start, repeat = 0;
299
300     for (x = rowsize, length = 0; x > 0;) {
301         start = row;
302         row += (2 * stride);
303         x -= 2;
304
305         while (x > 0 && (row[-2 * stride] != row[-1 * stride] ||
306                     row[-1 * stride] != row[0])) {
307             row += stride;
308             x--;
309         };
310
311         row -= (2 * stride);
312         x += 2;
313
314         count = (row - start) / stride;
315         while (count > 0) {
316             i = count > 126 ? 126 : count;
317             count -= i;
318
319             put_byte(f, 0x80 | i);
320             length++;
321
322             while (i > 0) {
323                 put_byte(f, *start);
324                 start += stride;
325                 i--;
326                 length++;
327             };
328         };
329
330         if (x <= 0) {
331             break;
332         }
333
334         start = row;
335         repeat = row[0];
336
337         row += stride;
338         x--;
339
340         while (x > 0 && *row == repeat) {
341             row += stride;
342             x--;
343         };
344
345         count = (row - start) / stride;
346         while (count > 0) {
347             i = count > 126 ? 126 : count;
348             count -= i;
349
350             put_byte(f, i);
351             length++;
352
353             put_byte(f, repeat);
354             length++;
355         };
356     };
357
358     length++;
359
360     put_byte(f, 0);
361     return (length);
362 }
363
364
365 static int sgi_write(ByteIOContext *pb, AVImageInfo *info)
366 {
367     SGIInfo sgi_info, *si = &sgi_info;
368     long *offsettab, *lengthtab;
369     int i, y, z;
370     int tablesize, chan_offset;
371     uint8_t *srcrow;
372
373     si->xsize = info->width;
374     si->ysize = info->height;
375     si->rle = 1;
376     si->bytes_per_channel = 1;
377
378     switch(info->pix_fmt) {
379         case PIX_FMT_GRAY8:
380             si->dimension = SGI_SINGLE_CHAN;
381             si->zsize = SGI_GRAYSCALE;
382             break;
383         case PIX_FMT_RGB24:
384             si->dimension = SGI_MULTI_CHAN;
385             si->zsize = SGI_RGB;
386             break;
387          case PIX_FMT_RGBA32:
388             si->dimension = SGI_MULTI_CHAN;
389             si->zsize = SGI_RGBA;
390             break;
391         default:
392             return AVERROR_INVALIDDATA;
393     }
394
395     write_sgi_header(pb, si);
396
397     tablesize = si->zsize * si->ysize * sizeof(long);
398
399     /* skip rle offset and length tables, write them at the end. */
400     url_fseek(pb, tablesize * 2, SEEK_CUR);
401     put_flush_packet(pb);
402
403     lengthtab = av_malloc(tablesize);
404     offsettab = av_malloc(tablesize);
405
406     for (z = 0; z < si->zsize; z++) {
407
408 #ifndef WORDS_BIGENDIAN
409         /* rgba -> bgra for rgba32 on little endian cpus */
410         if (si->zsize == 4 && z != 3)
411             chan_offset = 2 - z;
412         else
413 #endif
414             chan_offset = z;
415
416         srcrow = info->pict.data[0] + chan_offset;
417
418         for (y = si->ysize -1; y >= 0; y--) {
419             offsettab[(z * si->ysize) + y] = url_ftell(pb);
420             lengthtab[(z * si->ysize) + y] = rle_row(pb, srcrow,
421                     si->zsize, si->xsize);
422             srcrow += info->pict.linesize[0];
423         }
424     }
425
426     url_fseek(pb, 512, SEEK_SET);
427
428     /* write offset table */
429     for (i = 0; i < (si->ysize * si->zsize); i++) {
430         put_be32(pb, offsettab[i]);
431     }
432
433     /* write length table */
434     for (i = 0; i < (si->ysize * si->zsize); i++) {
435         put_be32(pb, lengthtab[i]);
436     }
437
438     put_flush_packet(pb);
439
440     av_free(lengthtab);
441     av_free(offsettab);
442
443     return 0;
444 }
445 #endif // CONFIG_MUXERS
446
447 AVImageFormat sgi_image_format = {
448     "sgi",
449     "sgi,rgb,rgba,bw",
450     sgi_probe,
451     sgi_read,
452     (1 << PIX_FMT_GRAY8) | (1 << PIX_FMT_RGB24) | (1 << PIX_FMT_RGBA32),
453 #ifdef CONFIG_MUXERS
454     sgi_write,
455 #else
456     NULL,
457 #endif // CONFIG_MUXERS
458 };