changeset 382:37a29b5200d8 libavformat

added SGI image format, encoding and decoding, courtesy of Todd Kirby <doubleshot@pacbell.net>
author melanson
date Sat, 13 Mar 2004 17:30:37 +0000
parents 7be99ba54f0f
children 1674ed5ca2f0
files Makefile allformats.c avformat.h sgi.c
diffstat 4 files changed, 478 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Fri Mar 12 02:26:01 2004 +0000
+++ b/Makefile	Sat Mar 13 17:30:37 2004 +0000
@@ -34,7 +34,7 @@
 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 
--- a/allformats.c	Fri Mar 12 02:26:01 2004 +0000
+++ b/allformats.c	Sat Mar 13 17:30:37 2004 +0000
@@ -112,6 +112,7 @@
 #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 */
--- a/avformat.h	Fri Mar 12 02:26:01 2004 +0000
+++ b/avformat.h	Sat Mar 13 17:30:37 2004 +0000
@@ -366,6 +366,7 @@
 #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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sgi.c	Sat Mar 13 17:30:37 2004 +0000
@@ -0,0 +1,475 @@
+/*
+ * SGI image format
+ * Todd Kirby <doubleshot@pacbell.net>
+ *
+ * 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
+};