yading@10: /* yading@10: * SGI image encoder yading@10: * Todd Kirby yading@10: * yading@10: * This file is part of FFmpeg. yading@10: * yading@10: * FFmpeg is free software; you can redistribute it and/or yading@10: * modify it under the terms of the GNU Lesser General Public yading@10: * License as published by the Free Software Foundation; either yading@10: * version 2.1 of the License, or (at your option) any later version. yading@10: * yading@10: * FFmpeg is distributed in the hope that it will be useful, yading@10: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@10: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@10: * Lesser General Public License for more details. yading@10: * yading@10: * You should have received a copy of the GNU Lesser General Public yading@10: * License along with FFmpeg; if not, write to the Free Software yading@10: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@10: */ yading@10: yading@10: #include "avcodec.h" yading@10: #include "bytestream.h" yading@10: #include "internal.h" yading@10: #include "sgi.h" yading@10: #include "rle.h" yading@10: yading@10: #define SGI_SINGLE_CHAN 2 yading@10: #define SGI_MULTI_CHAN 3 yading@10: yading@10: typedef struct SgiContext { yading@10: AVFrame picture; yading@10: } SgiContext; yading@10: yading@10: static av_cold int encode_init(AVCodecContext *avctx) yading@10: { yading@10: SgiContext *s = avctx->priv_data; yading@10: yading@10: if (avctx->width > 65535 || avctx->height > 65535) { yading@10: av_log(avctx, AV_LOG_ERROR, "SGI does not support resolutions above 65535x65535\n"); yading@10: return -1; yading@10: } yading@10: yading@10: avcodec_get_frame_defaults(&s->picture); yading@10: avctx->coded_frame = &s->picture; yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, yading@10: const AVFrame *frame, int *got_packet) yading@10: { yading@10: SgiContext *s = avctx->priv_data; yading@10: AVFrame * const p = &s->picture; yading@10: uint8_t *offsettab, *lengthtab, *in_buf, *encode_buf, *buf; yading@10: int x, y, z, length, tablesize, ret; yading@10: unsigned int width, height, depth, dimension, bytes_per_channel, pixmax, put_be; yading@10: unsigned char *end_buf; yading@10: yading@10: *p = *frame; yading@10: p->pict_type = AV_PICTURE_TYPE_I; yading@10: p->key_frame = 1; yading@10: yading@10: width = avctx->width; yading@10: height = avctx->height; yading@10: bytes_per_channel = 1; yading@10: pixmax = 0xFF; yading@10: put_be = HAVE_BIGENDIAN; yading@10: yading@10: switch (avctx->pix_fmt) { yading@10: case AV_PIX_FMT_GRAY8: yading@10: dimension = SGI_SINGLE_CHAN; yading@10: depth = SGI_GRAYSCALE; yading@10: break; yading@10: case AV_PIX_FMT_RGB24: yading@10: dimension = SGI_MULTI_CHAN; yading@10: depth = SGI_RGB; yading@10: break; yading@10: case AV_PIX_FMT_RGBA: yading@10: dimension = SGI_MULTI_CHAN; yading@10: depth = SGI_RGBA; yading@10: break; yading@10: case AV_PIX_FMT_GRAY16LE: yading@10: put_be = !HAVE_BIGENDIAN; yading@10: case AV_PIX_FMT_GRAY16BE: yading@10: avctx->coder_type = FF_CODER_TYPE_RAW; yading@10: bytes_per_channel = 2; yading@10: pixmax = 0xFFFF; yading@10: dimension = SGI_SINGLE_CHAN; yading@10: depth = SGI_GRAYSCALE; yading@10: break; yading@10: case AV_PIX_FMT_RGB48LE: yading@10: put_be = !HAVE_BIGENDIAN; yading@10: case AV_PIX_FMT_RGB48BE: yading@10: avctx->coder_type = FF_CODER_TYPE_RAW; yading@10: bytes_per_channel = 2; yading@10: pixmax = 0xFFFF; yading@10: dimension = SGI_MULTI_CHAN; yading@10: depth = SGI_RGB; yading@10: break; yading@10: case AV_PIX_FMT_RGBA64LE: yading@10: put_be = !HAVE_BIGENDIAN; yading@10: case AV_PIX_FMT_RGBA64BE: yading@10: avctx->coder_type = FF_CODER_TYPE_RAW; yading@10: bytes_per_channel = 2; yading@10: pixmax = 0xFFFF; yading@10: dimension = SGI_MULTI_CHAN; yading@10: depth = SGI_RGBA; yading@10: break; yading@10: default: yading@10: return AVERROR_INVALIDDATA; yading@10: } yading@10: yading@10: tablesize = depth * height * 4; yading@10: length = SGI_HEADER_SIZE; yading@10: if (avctx->coder_type == FF_CODER_TYPE_RAW) yading@10: length += depth * height * width; yading@10: else // assume ff_rl_encode() produces at most 2x size of input yading@10: length += tablesize * 2 + depth * height * (2 * width + 1); yading@10: yading@10: if ((ret = ff_alloc_packet2(avctx, pkt, bytes_per_channel * length)) < 0) yading@10: return ret; yading@10: buf = pkt->data; yading@10: end_buf = pkt->data + pkt->size; yading@10: yading@10: /* Encode header. */ yading@10: bytestream_put_be16(&buf, SGI_MAGIC); yading@10: bytestream_put_byte(&buf, avctx->coder_type != FF_CODER_TYPE_RAW); /* RLE 1 - VERBATIM 0*/ yading@10: bytestream_put_byte(&buf, bytes_per_channel); yading@10: bytestream_put_be16(&buf, dimension); yading@10: bytestream_put_be16(&buf, width); yading@10: bytestream_put_be16(&buf, height); yading@10: bytestream_put_be16(&buf, depth); yading@10: yading@10: bytestream_put_be32(&buf, 0L); /* pixmin */ yading@10: bytestream_put_be32(&buf, pixmax); yading@10: bytestream_put_be32(&buf, 0L); /* dummy */ yading@10: yading@10: /* name */ yading@10: memset(buf, 0, SGI_HEADER_SIZE); yading@10: buf += 80; yading@10: yading@10: /* colormap */ yading@10: bytestream_put_be32(&buf, 0L); yading@10: yading@10: /* The rest of the 512 byte header is unused. */ yading@10: buf += 404; yading@10: offsettab = buf; yading@10: yading@10: if (avctx->coder_type != FF_CODER_TYPE_RAW) { yading@10: /* Skip RLE offset table. */ yading@10: buf += tablesize; yading@10: lengthtab = buf; yading@10: yading@10: /* Skip RLE length table. */ yading@10: buf += tablesize; yading@10: yading@10: /* Make an intermediate consecutive buffer. */ yading@10: if (!(encode_buf = av_malloc(width))) yading@10: return -1; yading@10: yading@10: for (z = 0; z < depth; z++) { yading@10: in_buf = p->data[0] + p->linesize[0] * (height - 1) + z; yading@10: yading@10: for (y = 0; y < height; y++) { yading@10: bytestream_put_be32(&offsettab, buf - pkt->data); yading@10: yading@10: for (x = 0; x < width; x++) yading@10: encode_buf[x] = in_buf[depth * x]; yading@10: yading@10: if ((length = ff_rle_encode(buf, end_buf - buf - 1, encode_buf, 1, width, 0, 0, 0x80, 0)) < 1) { yading@10: av_free(encode_buf); yading@10: return -1; yading@10: } yading@10: yading@10: buf += length; yading@10: bytestream_put_byte(&buf, 0); yading@10: bytestream_put_be32(&lengthtab, length + 1); yading@10: in_buf -= p->linesize[0]; yading@10: } yading@10: } yading@10: yading@10: av_free(encode_buf); yading@10: } else { yading@10: for (z = 0; z < depth; z++) { yading@10: in_buf = p->data[0] + p->linesize[0] * (height - 1) + z * bytes_per_channel; yading@10: yading@10: for (y = 0; y < height; y++) { yading@10: for (x = 0; x < width * depth; x += depth) yading@10: if (bytes_per_channel == 1) { yading@10: bytestream_put_byte(&buf, in_buf[x]); yading@10: } else { yading@10: if (put_be) { yading@10: bytestream_put_be16(&buf, ((uint16_t *)in_buf)[x]); yading@10: } else { yading@10: bytestream_put_le16(&buf, ((uint16_t *)in_buf)[x]); yading@10: } yading@10: } yading@10: yading@10: in_buf -= p->linesize[0]; yading@10: } yading@10: } yading@10: } yading@10: yading@10: /* total length */ yading@10: pkt->size = buf - pkt->data; yading@10: pkt->flags |= AV_PKT_FLAG_KEY; yading@10: *got_packet = 1; yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: AVCodec ff_sgi_encoder = { yading@10: .name = "sgi", yading@10: .type = AVMEDIA_TYPE_VIDEO, yading@10: .id = AV_CODEC_ID_SGI, yading@10: .priv_data_size = sizeof(SgiContext), yading@10: .init = encode_init, yading@10: .encode2 = encode_frame, yading@10: .pix_fmts = (const enum AVPixelFormat[]){ yading@10: AV_PIX_FMT_RGB24, AV_PIX_FMT_RGBA, yading@10: AV_PIX_FMT_RGB48LE, AV_PIX_FMT_RGB48BE, yading@10: AV_PIX_FMT_RGBA64LE, AV_PIX_FMT_RGBA64BE, yading@10: AV_PIX_FMT_GRAY16LE, AV_PIX_FMT_GRAY16BE, yading@10: AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE yading@10: }, yading@10: .long_name = NULL_IF_CONFIG_SMALL("SGI image"), yading@10: };