1/* 2 * SGI image encoder 3 * Todd Kirby <doubleshot@pacbell.net> 4 * 5 * This file is part of FFmpeg. 6 * 7 * FFmpeg 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 * FFmpeg 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 FFmpeg; 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 "internal.h" 25#include "sgi.h" 26#include "rle.h" 27 28#define SGI_SINGLE_CHAN 2 29#define SGI_MULTI_CHAN 3 30 31static av_cold int encode_init(AVCodecContext *avctx) 32{ 33 if (avctx->width > 65535 || avctx->height > 65535) { 34 av_log(avctx, AV_LOG_ERROR, 35 "Unsupported resolution %dx%d.\n", avctx->width, avctx->height); 36 av_log(avctx, AV_LOG_ERROR, "SGI does not support resolutions above 65535x65535\n"); 37 return AVERROR_INVALIDDATA; 38 } 39 40 avctx->coded_frame = av_frame_alloc(); 41 if (!avctx->coded_frame) 42 return AVERROR(ENOMEM); 43 44 return 0; 45} 46 47static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, 48 const AVFrame *frame, int *got_packet) 49{ 50 const AVFrame * const p = frame; 51 uint8_t *offsettab, *lengthtab, *in_buf, *encode_buf, *buf; 52 int x, y, z, length, tablesize, ret; 53 unsigned int width, height, depth, dimension; 54 unsigned int bytes_per_channel, pixmax, put_be; 55 unsigned char *end_buf; 56 57 avctx->coded_frame->pict_type = AV_PICTURE_TYPE_I; 58 avctx->coded_frame->key_frame = 1; 59 60 width = avctx->width; 61 height = avctx->height; 62 bytes_per_channel = 1; 63 pixmax = 0xFF; 64 put_be = HAVE_BIGENDIAN; 65 66 switch (avctx->pix_fmt) { 67 case AV_PIX_FMT_GRAY8: 68 dimension = SGI_SINGLE_CHAN; 69 depth = SGI_GRAYSCALE; 70 break; 71 case AV_PIX_FMT_RGB24: 72 dimension = SGI_MULTI_CHAN; 73 depth = SGI_RGB; 74 break; 75 case AV_PIX_FMT_RGBA: 76 dimension = SGI_MULTI_CHAN; 77 depth = SGI_RGBA; 78 break; 79 case AV_PIX_FMT_GRAY16LE: 80 put_be = !HAVE_BIGENDIAN; 81 case AV_PIX_FMT_GRAY16BE: 82 avctx->coder_type = FF_CODER_TYPE_RAW; 83 bytes_per_channel = 2; 84 pixmax = 0xFFFF; 85 dimension = SGI_SINGLE_CHAN; 86 depth = SGI_GRAYSCALE; 87 break; 88 case AV_PIX_FMT_RGB48LE: 89 put_be = !HAVE_BIGENDIAN; 90 case AV_PIX_FMT_RGB48BE: 91 avctx->coder_type = FF_CODER_TYPE_RAW; 92 bytes_per_channel = 2; 93 pixmax = 0xFFFF; 94 dimension = SGI_MULTI_CHAN; 95 depth = SGI_RGB; 96 break; 97 case AV_PIX_FMT_RGBA64LE: 98 put_be = !HAVE_BIGENDIAN; 99 case AV_PIX_FMT_RGBA64BE: 100 avctx->coder_type = FF_CODER_TYPE_RAW; 101 bytes_per_channel = 2; 102 pixmax = 0xFFFF; 103 dimension = SGI_MULTI_CHAN; 104 depth = SGI_RGBA; 105 break; 106 default: 107 return AVERROR_INVALIDDATA; 108 } 109 110 tablesize = depth * height * 4; 111 length = SGI_HEADER_SIZE; 112 if (avctx->coder_type == FF_CODER_TYPE_RAW) 113 length += depth * height * width; 114 else // assume ff_rl_encode() produces at most 2x size of input 115 length += tablesize * 2 + depth * height * (2 * width + 1); 116 117 if ((ret = ff_alloc_packet2(avctx, pkt, bytes_per_channel * length)) < 0) 118 return ret; 119 buf = pkt->data; 120 end_buf = pkt->data + pkt->size; 121 122 /* Encode header. */ 123 bytestream_put_be16(&buf, SGI_MAGIC); 124 bytestream_put_byte(&buf, avctx->coder_type != FF_CODER_TYPE_RAW); /* RLE 1 - VERBATIM 0*/ 125 bytestream_put_byte(&buf, bytes_per_channel); 126 bytestream_put_be16(&buf, dimension); 127 bytestream_put_be16(&buf, width); 128 bytestream_put_be16(&buf, height); 129 bytestream_put_be16(&buf, depth); 130 131 bytestream_put_be32(&buf, 0L); /* pixmin */ 132 bytestream_put_be32(&buf, pixmax); 133 bytestream_put_be32(&buf, 0L); /* dummy */ 134 135 /* name */ 136 memset(buf, 0, SGI_HEADER_SIZE); 137 buf += 80; 138 139 /* colormap */ 140 bytestream_put_be32(&buf, 0L); 141 142 /* The rest of the 512 byte header is unused. */ 143 buf += 404; 144 offsettab = buf; 145 146 if (avctx->coder_type != FF_CODER_TYPE_RAW) { 147 /* Skip RLE offset table. */ 148 buf += tablesize; 149 lengthtab = buf; 150 151 /* Skip RLE length table. */ 152 buf += tablesize; 153 154 /* Make an intermediate consecutive buffer. */ 155 if (!(encode_buf = av_malloc(width))) 156 return -1; 157 158 for (z = 0; z < depth; z++) { 159 in_buf = p->data[0] + p->linesize[0] * (height - 1) + z; 160 161 for (y = 0; y < height; y++) { 162 bytestream_put_be32(&offsettab, buf - pkt->data); 163 164 for (x = 0; x < width; x++) 165 encode_buf[x] = in_buf[depth * x]; 166 167 if ((length = ff_rle_encode(buf, end_buf - buf - 1, encode_buf, 1, width, 0, 0, 0x80, 0)) < 1) { 168 av_free(encode_buf); 169 return -1; 170 } 171 172 buf += length; 173 bytestream_put_byte(&buf, 0); 174 bytestream_put_be32(&lengthtab, length + 1); 175 in_buf -= p->linesize[0]; 176 } 177 } 178 179 av_free(encode_buf); 180 } else { 181 for (z = 0; z < depth; z++) { 182 in_buf = p->data[0] + p->linesize[0] * (height - 1) + z * bytes_per_channel; 183 184 for (y = 0; y < height; y++) { 185 for (x = 0; x < width * depth; x += depth) 186 if (bytes_per_channel == 1) { 187 bytestream_put_byte(&buf, in_buf[x]); 188 } else { 189 if (put_be) { 190 bytestream_put_be16(&buf, ((uint16_t *)in_buf)[x]); 191 } else { 192 bytestream_put_le16(&buf, ((uint16_t *)in_buf)[x]); 193 } 194 } 195 196 in_buf -= p->linesize[0]; 197 } 198 } 199 } 200 201 /* total length */ 202 pkt->size = buf - pkt->data; 203 pkt->flags |= AV_PKT_FLAG_KEY; 204 *got_packet = 1; 205 206 return 0; 207} 208 209static av_cold int encode_close(AVCodecContext *avctx) 210{ 211 av_frame_free(&avctx->coded_frame); 212 return 0; 213} 214 215AVCodec ff_sgi_encoder = { 216 .name = "sgi", 217 .long_name = NULL_IF_CONFIG_SMALL("SGI image"), 218 .type = AVMEDIA_TYPE_VIDEO, 219 .id = AV_CODEC_ID_SGI, 220 .init = encode_init, 221 .encode2 = encode_frame, 222 .close = encode_close, 223 .pix_fmts = (const enum AVPixelFormat[]) { 224 AV_PIX_FMT_RGB24, AV_PIX_FMT_RGBA, 225 AV_PIX_FMT_RGB48LE, AV_PIX_FMT_RGB48BE, 226 AV_PIX_FMT_RGBA64LE, AV_PIX_FMT_RGBA64BE, 227 AV_PIX_FMT_GRAY16LE, AV_PIX_FMT_GRAY16BE, AV_PIX_FMT_GRAY8, 228 AV_PIX_FMT_NONE 229 }, 230}; 231