1/* 2 * SGI image encoder 3 * Todd Kirby <doubleshot@pacbell.net> 4 * 5 * This file is part of Libav. 6 * 7 * Libav 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 * Libav 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 Libav; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22#include "avcodec.h" 23#include "bytestream.h" 24#include "sgi.h" 25#include "rle.h" 26 27#define SGI_SINGLE_CHAN 2 28#define SGI_MULTI_CHAN 3 29 30typedef struct SgiContext { 31 AVFrame picture; 32} SgiContext; 33 34static av_cold int encode_init(AVCodecContext *avctx) 35{ 36 SgiContext *s = avctx->priv_data; 37 38 avcodec_get_frame_defaults(&s->picture); 39 avctx->coded_frame = &s->picture; 40 41 return 0; 42} 43 44static int encode_frame(AVCodecContext *avctx, unsigned char *buf, 45 int buf_size, void *data) 46{ 47 SgiContext *s = avctx->priv_data; 48 AVFrame * const p = &s->picture; 49 uint8_t *offsettab, *lengthtab, *in_buf, *encode_buf; 50 int x, y, z, length, tablesize; 51 unsigned int width, height, depth, dimension; 52 unsigned char *orig_buf = buf, *end_buf = buf + buf_size; 53 54 *p = *(AVFrame*)data; 55 p->pict_type = AV_PICTURE_TYPE_I; 56 p->key_frame = 1; 57 58 width = avctx->width; 59 height = avctx->height; 60 61 switch (avctx->pix_fmt) { 62 case PIX_FMT_GRAY8: 63 dimension = SGI_SINGLE_CHAN; 64 depth = SGI_GRAYSCALE; 65 break; 66 case PIX_FMT_RGB24: 67 dimension = SGI_MULTI_CHAN; 68 depth = SGI_RGB; 69 break; 70 case PIX_FMT_RGBA: 71 dimension = SGI_MULTI_CHAN; 72 depth = SGI_RGBA; 73 break; 74 default: 75 return AVERROR_INVALIDDATA; 76 } 77 78 tablesize = depth * height * 4; 79 length = tablesize * 2 + SGI_HEADER_SIZE; 80 81 if (buf_size < length) { 82 av_log(avctx, AV_LOG_ERROR, "buf_size too small(need %d, got %d)\n", length, buf_size); 83 return -1; 84 } 85 86 /* Encode header. */ 87 bytestream_put_be16(&buf, SGI_MAGIC); 88 bytestream_put_byte(&buf, avctx->coder_type != FF_CODER_TYPE_RAW); /* RLE 1 - VERBATIM 0*/ 89 bytestream_put_byte(&buf, 1); /* bytes_per_channel */ 90 bytestream_put_be16(&buf, dimension); 91 bytestream_put_be16(&buf, width); 92 bytestream_put_be16(&buf, height); 93 bytestream_put_be16(&buf, depth); 94 95 /* The rest are constant in this implementation. */ 96 bytestream_put_be32(&buf, 0L); /* pixmin */ 97 bytestream_put_be32(&buf, 255L); /* pixmax */ 98 bytestream_put_be32(&buf, 0L); /* dummy */ 99 100 /* name */ 101 memset(buf, 0, SGI_HEADER_SIZE); 102 buf += 80; 103 104 /* colormap */ 105 bytestream_put_be32(&buf, 0L); 106 107 /* The rest of the 512 byte header is unused. */ 108 buf += 404; 109 offsettab = buf; 110 111 if (avctx->coder_type != FF_CODER_TYPE_RAW) { 112 /* Skip RLE offset table. */ 113 buf += tablesize; 114 lengthtab = buf; 115 116 /* Skip RLE length table. */ 117 buf += tablesize; 118 119 /* Make an intermediate consecutive buffer. */ 120 if (!(encode_buf = av_malloc(width))) 121 return -1; 122 123 for (z = 0; z < depth; z++) { 124 in_buf = p->data[0] + p->linesize[0] * (height - 1) + z; 125 126 for (y = 0; y < height; y++) { 127 bytestream_put_be32(&offsettab, buf - orig_buf); 128 129 for (x = 0; x < width; x++) 130 encode_buf[x] = in_buf[depth * x]; 131 132 if ((length = ff_rle_encode(buf, end_buf - buf - 1, encode_buf, 1, width, 0, 0, 0x80, 0)) < 1) { 133 av_free(encode_buf); 134 return -1; 135 } 136 137 buf += length; 138 bytestream_put_byte(&buf, 0); 139 bytestream_put_be32(&lengthtab, length + 1); 140 in_buf -= p->linesize[0]; 141 } 142 } 143 144 av_free(encode_buf); 145 } else { 146 for (z = 0; z < depth; z++) { 147 in_buf = p->data[0] + p->linesize[0] * (height - 1) + z; 148 149 for (y = 0; y < height; y++) { 150 for (x = 0; x < width * depth; x += depth) 151 bytestream_put_byte(&buf, in_buf[x]); 152 153 in_buf -= p->linesize[0]; 154 } 155 } 156 } 157 158 /* total length */ 159 return buf - orig_buf; 160} 161 162AVCodec ff_sgi_encoder = { 163 .name = "sgi", 164 .type = AVMEDIA_TYPE_VIDEO, 165 .id = CODEC_ID_SGI, 166 .priv_data_size = sizeof(SgiContext), 167 .init = encode_init, 168 .encode = encode_frame, 169 .pix_fmts= (const enum PixelFormat[]){PIX_FMT_RGB24, PIX_FMT_RGBA, PIX_FMT_GRAY8, PIX_FMT_NONE}, 170 .long_name= NULL_IF_CONFIG_SMALL("SGI image"), 171}; 172