From 6a91ec51fd08784bd836449a998def1dbe54772e Mon Sep 17 00:00:00 2001 From: Mike Melanson Date: Sat, 13 Mar 2004 17:30:37 +0000 Subject: [PATCH] added SGI image format, encoding and decoding, courtesy of Todd Kirby Originally committed as revision 2876 to svn://svn.ffmpeg.org/ffmpeg/trunk --- CREDITS | 1 + doc/ffmpeg-doc.texi | 1 + libavformat/Makefile | 2 +- libavformat/allformats.c | 1 + libavformat/avformat.h | 1 + libavformat/sgi.c | 475 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 480 insertions(+), 1 deletion(-) create mode 100644 libavformat/sgi.c diff --git a/CREDITS b/CREDITS index ce76154703..f0dd26a0d6 100644 --- a/CREDITS +++ b/CREDITS @@ -15,6 +15,7 @@ Wolfgang Hesseler Falk Hueffner Zdenek Kabelac Robin Kay +Todd Kirby Nick Kurshev Mike Melanson Michael Niedermayer diff --git a/doc/ffmpeg-doc.texi b/doc/ffmpeg-doc.texi index 849b93a23c..e205904d86 100644 --- a/doc/ffmpeg-doc.texi +++ b/doc/ffmpeg-doc.texi @@ -678,6 +678,7 @@ following image formats are supported: @item .Y.U.V @tab X @tab X @tab One raw file per component @item Animated GIF @tab X @tab X @tab Only uncompressed GIFs are generated @item PNG @tab X @tab X @tab 2 bit and 4 bit/pixel not supported yet +@item SGI @tab X @tab X @tab SGI RGB image format @end multitable @code{X} means that the encoding (resp. decoding) is supported. diff --git a/libavformat/Makefile b/libavformat/Makefile index 0b8161b047..23aecba137 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -34,7 +34,7 @@ OBJS+= amr.o endif # image formats -OBJS+= pnm.o yuv.o png.o jpeg.o gifdec.o +OBJS+= pnm.o yuv.o png.o jpeg.o gifdec.o sgi.o # file I/O OBJS+= avio.o aviobuf.o file.o OBJS+= framehook.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index b0b0e64372..9feaa987c3 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -112,6 +112,7 @@ void av_register_all(void) #endif av_register_image_format(&jpeg_image_format); av_register_image_format(&gif_image_format); + av_register_image_format(&sgi_image_format); #endif //CONFIG_ENCODERS /* file protocols */ diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 5d87ac7e22..5da4d9c494 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -366,6 +366,7 @@ extern AVImageFormat png_image_format; #endif extern AVImageFormat jpeg_image_format; extern AVImageFormat gif_image_format; +extern AVImageFormat sgi_image_format; /* XXX: use automatic init with either ELF sections or C file parser */ /* modules */ diff --git a/libavformat/sgi.c b/libavformat/sgi.c new file mode 100644 index 0000000000..11b6ab50f6 --- /dev/null +++ b/libavformat/sgi.c @@ -0,0 +1,475 @@ +/* + * SGI image format + * Todd Kirby + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "avformat.h" +#include "avio.h" + +/* #define DEBUG */ + +#define BE_16(x) ((((uint8_t*)(x))[0] << 8) | ((uint8_t*)(x))[1]) + +#define BE_32(x) ((((uint8_t*)(x))[0] << 24) | \ + (((uint8_t*)(x))[1] << 16) | \ + (((uint8_t*)(x))[2] << 8) | \ + ((uint8_t*)(x))[3]) + +/* sgi image file signature */ +#define SGI_MAGIC 474 + +#define SGI_HEADER_SIZE 512 + +#define SGI_GRAYSCALE 1 +#define SGI_RGB 3 +#define SGI_RGBA 4 + +#define SGI_SINGLE_CHAN 2 +#define SGI_MULTI_CHAN 3 + +typedef struct SGIInfo{ + short magic; + char rle; + char bytes_per_channel; + unsigned short dimension; + unsigned short xsize; + unsigned short ysize; + unsigned short zsize; +} SGIInfo; + + +static int sgi_probe(AVProbeData *pd) +{ + /* test for sgi magic */ + if (pd->buf_size >= 2 && BE_16(&pd->buf[0]) == SGI_MAGIC) { + return AVPROBE_SCORE_MAX; + } else { + return 0; + } +} + +/* read sgi header fields */ +static void read_sgi_header(ByteIOContext *f, SGIInfo *info) +{ + info->magic = (unsigned short) get_be16(f); + info->rle = get_byte(f); + info->bytes_per_channel = get_byte(f); + info->dimension = (unsigned short)get_be16(f); + info->xsize = (unsigned short) get_be16(f); + info->ysize = (unsigned short) get_be16(f); + info->zsize = (unsigned short) get_be16(f); + +#ifdef DEBUG + printf("sgi header fields:\n"); + printf(" magic: %d\n", info->magic); + printf(" rle: %d\n", info->rle); + printf(" bpc: %d\n", info->bytes_per_channel); + printf(" dim: %d\n", info->dimension); + printf(" xsize: %d\n", info->xsize); + printf(" ysize: %d\n", info->ysize); + printf(" zsize: %d\n", info->zsize); +#endif + + return; +} + + +/* read an uncompressed sgi image */ +static int read_uncompressed_sgi(const SGIInfo *si, + AVPicture *pict, ByteIOContext *f) +{ + int x, y, z, chan_offset, ret = 0; + uint8_t *dest_row, *tmp_row = NULL; + + tmp_row = av_malloc(si->xsize); + + /* skip header */ + url_fseek(f, SGI_HEADER_SIZE, SEEK_SET); + + pict->linesize[0] = si->xsize; + + for (z = 0; z < si->zsize; z++) { + +#ifndef WORDS_BIGENDIAN + /* rgba -> bgra for rgba32 on little endian cpus */ + if (si->zsize == 4 && z != 3) + chan_offset = 2 - z; + else +#endif + chan_offset = z; + + for (y = si->ysize - 1; y >= 0; y--) { + dest_row = pict->data[0] + (y * si->xsize * si->zsize); + + if (!get_buffer(f, tmp_row, si->xsize)) { + ret = -1; + goto cleanup; + } + for (x = 0; x < si->xsize; x++) { + dest_row[chan_offset] = tmp_row[x]; + dest_row += si->zsize; + } + } + } + +cleanup: + av_free(tmp_row); + return ret; +} + + +/* expand an rle row into a channel */ +static void expand_rle_row(unsigned char *optr, unsigned char *iptr, + int chan_offset, int pixelstride) +{ + unsigned char pixel, count; + +#ifndef WORDS_BIGENDIAN + /* rgba -> bgra for rgba32 on little endian cpus */ + if (pixelstride == 4 && chan_offset != 3) { + chan_offset = 2 - chan_offset; + } +#endif + + optr += chan_offset; + + while (1) { + pixel = *iptr++; + + if (!(count = (pixel & 0x7f))) { + return; + } + if (pixel & 0x80) { + while (count--) { + *optr = *iptr; + optr += pixelstride; + iptr++; + } + } else { + pixel = *iptr++; + + while (count--) { + *optr = pixel; + optr += pixelstride; + } + } + } +} + + +/* read a run length encoded sgi image */ +static int read_rle_sgi(const SGIInfo *sgi_info, + AVPicture *pict, ByteIOContext *f) +{ + uint8_t *dest_row, *rle_data = NULL; + unsigned long *start_table, *length_table; + int y, z, xsize, ysize, zsize, tablen; + long start_offset, run_length; + int ret = 0; + + xsize = sgi_info->xsize; + ysize = sgi_info->ysize; + zsize = sgi_info->zsize; + + rle_data = av_malloc(xsize); + + /* skip header */ + url_fseek(f, SGI_HEADER_SIZE, SEEK_SET); + + /* size of rle offset and length tables */ + tablen = ysize * zsize * sizeof(long); + + start_table = (unsigned long *)av_malloc(tablen); + length_table = (unsigned long *)av_malloc(tablen); + + if (!get_buffer(f, (uint8_t *)start_table, tablen)) { + ret = -1; + goto fail; + } + + if (!get_buffer(f, (uint8_t *)length_table, tablen)) { + ret = -1; + goto fail; + } + + for (z = 0; z < zsize; z++) { + for (y = 0; y < ysize; y++) { + dest_row = pict->data[0] + (ysize - 1 - y) * (xsize * zsize); + + start_offset = BE_32(&start_table[y + z * ysize]); + run_length = BE_32(&length_table[y + z * ysize]); + + /* don't seek if already in the correct spot */ + if (url_ftell(f) != start_offset) { + url_fseek(f, start_offset, SEEK_SET); + } + + get_buffer(f, rle_data, run_length); + + expand_rle_row(dest_row, rle_data, z, zsize); + } + } + +fail: + av_free(start_table); + av_free(length_table); + av_free(rle_data); + + return ret; +} + + +static int sgi_read(ByteIOContext *f, + int (*alloc_cb)(void *opaque, AVImageInfo *info), void *opaque) +{ + SGIInfo sgi_info, *s = &sgi_info; + AVImageInfo info1, *info = &info1; + int ret; + + read_sgi_header(f, s); + + if (s->bytes_per_channel != 1) { + return AVERROR_INVALIDDATA; + } + + /* check for supported image dimensions */ + if (s->dimension != 2 && s->dimension != 3) { + return AVERROR_INVALIDDATA; + } + + if (s->zsize == SGI_GRAYSCALE) { + info->pix_fmt = PIX_FMT_GRAY8; + } else if (s->zsize == SGI_RGB) { + info->pix_fmt = PIX_FMT_RGB24; + } else if (s->zsize == SGI_RGBA) { + info->pix_fmt = PIX_FMT_RGBA32; + } else { + return AVERROR_INVALIDDATA; + } + + info->width = s->xsize; + info->height = s->ysize; + + ret = alloc_cb(opaque, info); + if (ret) + return ret; + + if (s->rle) { + return read_rle_sgi(s, &info->pict, f); + } else { + return read_uncompressed_sgi(s, &info->pict, f); + } + + return 0; /* not reached */ +} + +#ifdef CONFIG_ENCODERS +static void write_sgi_header(ByteIOContext *f, const SGIInfo *info) +{ + int i; + + put_be16(f, SGI_MAGIC); + put_byte(f, info->rle); + put_byte(f, info->bytes_per_channel); + put_be16(f, info->dimension); + put_be16(f, info->xsize); + put_be16(f, info->ysize); + put_be16(f, info->zsize); + + /* The rest are constant in this implementation */ + put_be32(f, 0L); /* pixmin */ + put_be32(f, 255L); /* pixmax */ + put_be32(f, 0L); /* dummy */ + + /* name */ + for (i = 0; i < 80; i++) { + put_byte(f, 0); + } + + put_be32(f, 0L); /* colormap */ + + /* The rest of the 512 byte header is unused. */ + for (i = 0; i < 404; i++) { + put_byte(f, 0); + } +} + + +static int rle_row(ByteIOContext *f, char *row, int stride, int rowsize) +{ + int length, count, i, x; + char *start, repeat = 0; + + for (x = rowsize, length = 0; x > 0;) { + start = row; + row += (2 * stride); + x -= 2; + + while (x > 0 && (row[-2 * stride] != row[-1 * stride] || + row[-1 * stride] != row[0])) { + row += stride; + x--; + }; + + row -= (2 * stride); + x += 2; + + count = (row - start) / stride; + while (count > 0) { + i = count > 126 ? 126 : count; + count -= i; + + put_byte(f, 0x80 | i); + length++; + + while (i > 0) { + put_byte(f, *start); + start += stride; + i--; + length++; + }; + }; + + if (x <= 0) { + break; + } + + start = row; + repeat = row[0]; + + row += stride; + x--; + + while (x > 0 && *row == repeat) { + row += stride; + x--; + }; + + count = (row - start) / stride; + while (count > 0) { + i = count > 126 ? 126 : count; + count -= i; + + put_byte(f, i); + length++; + + put_byte(f, repeat); + length++; + }; + }; + + length++; + + put_byte(f, 0); + return (length); +} + + +static int sgi_write(ByteIOContext *pb, AVImageInfo *info) +{ + SGIInfo sgi_info, *si = &sgi_info; + long *offsettab, *lengthtab; + int i, y, z; + int tablesize, chan_offset; + uint8_t *srcrow; + + si->xsize = info->width; + si->ysize = info->height; + si->rle = 1; + si->bytes_per_channel = 1; + + switch(info->pix_fmt) { + case PIX_FMT_GRAY8: + si->dimension = SGI_SINGLE_CHAN; + si->zsize = SGI_GRAYSCALE; + break; + case PIX_FMT_RGB24: + si->dimension = SGI_MULTI_CHAN; + si->zsize = SGI_RGB; + break; + case PIX_FMT_RGBA32: + si->dimension = SGI_MULTI_CHAN; + si->zsize = SGI_RGBA; + break; + default: + return AVERROR_INVALIDDATA; + } + + write_sgi_header(pb, si); + + tablesize = si->zsize * si->ysize * sizeof(long); + + /* skip rle offset and length tables, write them at the end. */ + url_fseek(pb, tablesize * 2, SEEK_CUR); + put_flush_packet(pb); + + lengthtab = av_malloc(tablesize); + offsettab = av_malloc(tablesize); + + for (z = 0; z < si->zsize; z++) { + +#ifndef WORDS_BIGENDIAN + /* rgba -> bgra for rgba32 on little endian cpus */ + if (si->zsize == 4 && z != 3) + chan_offset = 2 - z; + else +#endif + chan_offset = z; + + srcrow = info->pict.data[0] + chan_offset; + + for (y = si->ysize -1; y >= 0; y--) { + offsettab[(z * si->ysize) + y] = url_ftell(pb); + lengthtab[(z * si->ysize) + y] = rle_row(pb, srcrow, + si->zsize, si->xsize); + srcrow += info->pict.linesize[0]; + } + } + + url_fseek(pb, 512, SEEK_SET); + + /* write offset table */ + for (i = 0; i < (si->ysize * si->zsize); i++) { + put_be32(pb, offsettab[i]); + } + + /* write length table */ + for (i = 0; i < (si->ysize * si->zsize); i++) { + put_be32(pb, lengthtab[i]); + } + + put_flush_packet(pb); + + av_free(lengthtab); + av_free(offsettab); + + return 0; +} +#endif // CONFIG_ENCODERS + +AVImageFormat sgi_image_format = { + "sgi", + "sgi,rgb,rgba,bw", + sgi_probe, + sgi_read, + (1 << PIX_FMT_GRAY8) | (1 << PIX_FMT_RGB24) | (1 << PIX_FMT_RGBA32), +#ifdef CONFIG_ENCODERS + sgi_write, +#else + NULL, +#endif // CONFIG_ENCODERS +};