mirror of https://github.com/FFmpeg/FFmpeg.git
<doubleshot@pacbell.net> Originally committed as revision 2876 to svn://svn.ffmpeg.org/ffmpeg/trunkpull/126/head
parent
38088adf63
commit
6a91ec51fd
6 changed files with 480 additions and 1 deletions
@ -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
|
||||
}; |
Loading…
Reference in new issue