1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * SGI image encoder 3cabdff1aSopenharmony_ci * Todd Kirby <doubleshot@pacbell.net> 4cabdff1aSopenharmony_ci * 5cabdff1aSopenharmony_ci * This file is part of FFmpeg. 6cabdff1aSopenharmony_ci * 7cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or 8cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public 9cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either 10cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version. 11cabdff1aSopenharmony_ci * 12cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful, 13cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 14cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15cabdff1aSopenharmony_ci * Lesser General Public License for more details. 16cabdff1aSopenharmony_ci * 17cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public 18cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software 19cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20cabdff1aSopenharmony_ci */ 21cabdff1aSopenharmony_ci 22cabdff1aSopenharmony_ci#include "libavutil/opt.h" 23cabdff1aSopenharmony_ci 24cabdff1aSopenharmony_ci#include "avcodec.h" 25cabdff1aSopenharmony_ci#include "bytestream.h" 26cabdff1aSopenharmony_ci#include "codec_internal.h" 27cabdff1aSopenharmony_ci#include "encode.h" 28cabdff1aSopenharmony_ci#include "sgi.h" 29cabdff1aSopenharmony_ci#include "rle.h" 30cabdff1aSopenharmony_ci 31cabdff1aSopenharmony_ci#define SGI_SINGLE_CHAN 2 32cabdff1aSopenharmony_ci#define SGI_MULTI_CHAN 3 33cabdff1aSopenharmony_ci 34cabdff1aSopenharmony_citypedef struct SgiContext { 35cabdff1aSopenharmony_ci AVClass *class; 36cabdff1aSopenharmony_ci 37cabdff1aSopenharmony_ci int rle; 38cabdff1aSopenharmony_ci} SgiContext; 39cabdff1aSopenharmony_ci 40cabdff1aSopenharmony_cistatic av_cold int encode_init(AVCodecContext *avctx) 41cabdff1aSopenharmony_ci{ 42cabdff1aSopenharmony_ci if (avctx->width > 65535 || avctx->height > 65535) { 43cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "Unsupported resolution %dx%d. " 44cabdff1aSopenharmony_ci "SGI does not support resolutions above 65535x65535\n", 45cabdff1aSopenharmony_ci avctx->width, avctx->height); 46cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 47cabdff1aSopenharmony_ci } 48cabdff1aSopenharmony_ci 49cabdff1aSopenharmony_ci return 0; 50cabdff1aSopenharmony_ci} 51cabdff1aSopenharmony_ci 52cabdff1aSopenharmony_cistatic int sgi_rle_encode(PutByteContext *pbc, const uint8_t *src, 53cabdff1aSopenharmony_ci int w, int bpp) 54cabdff1aSopenharmony_ci{ 55cabdff1aSopenharmony_ci int val, count, x, start = bytestream2_tell_p(pbc); 56cabdff1aSopenharmony_ci void (*bytestream2_put)(PutByteContext *, unsigned int); 57cabdff1aSopenharmony_ci 58cabdff1aSopenharmony_ci if (bpp == 1) 59cabdff1aSopenharmony_ci bytestream2_put = bytestream2_put_byte; 60cabdff1aSopenharmony_ci else 61cabdff1aSopenharmony_ci bytestream2_put = bytestream2_put_be16; 62cabdff1aSopenharmony_ci 63cabdff1aSopenharmony_ci for (x = 0; x < w; x += count) { 64cabdff1aSopenharmony_ci /* see if we can encode the next set of pixels with RLE */ 65cabdff1aSopenharmony_ci count = ff_rle_count_pixels(src, w - x, bpp, 1); 66cabdff1aSopenharmony_ci if (count > 1) { 67cabdff1aSopenharmony_ci if (bytestream2_get_bytes_left_p(pbc) < bpp * 2) 68cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 69cabdff1aSopenharmony_ci 70cabdff1aSopenharmony_ci val = bpp == 1 ? *src : AV_RB16(src); 71cabdff1aSopenharmony_ci bytestream2_put(pbc, count); 72cabdff1aSopenharmony_ci bytestream2_put(pbc, val); 73cabdff1aSopenharmony_ci } else { 74cabdff1aSopenharmony_ci int i; 75cabdff1aSopenharmony_ci /* fall back on uncompressed */ 76cabdff1aSopenharmony_ci count = ff_rle_count_pixels(src, w - x, bpp, 0); 77cabdff1aSopenharmony_ci if (bytestream2_get_bytes_left_p(pbc) < bpp * (count + 1)) 78cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 79cabdff1aSopenharmony_ci 80cabdff1aSopenharmony_ci bytestream2_put(pbc, count + 0x80); 81cabdff1aSopenharmony_ci for (i = 0; i < count; i++) { 82cabdff1aSopenharmony_ci val = bpp == 1 ? src[i] : AV_RB16(src + i * bpp); 83cabdff1aSopenharmony_ci bytestream2_put(pbc, val); 84cabdff1aSopenharmony_ci } 85cabdff1aSopenharmony_ci } 86cabdff1aSopenharmony_ci 87cabdff1aSopenharmony_ci src += count * bpp; 88cabdff1aSopenharmony_ci } 89cabdff1aSopenharmony_ci 90cabdff1aSopenharmony_ci return bytestream2_tell_p(pbc) - start; 91cabdff1aSopenharmony_ci} 92cabdff1aSopenharmony_ci 93cabdff1aSopenharmony_cistatic int encode_frame(AVCodecContext *avctx, AVPacket *pkt, 94cabdff1aSopenharmony_ci const AVFrame *frame, int *got_packet) 95cabdff1aSopenharmony_ci{ 96cabdff1aSopenharmony_ci SgiContext *s = avctx->priv_data; 97cabdff1aSopenharmony_ci const AVFrame * const p = frame; 98cabdff1aSopenharmony_ci PutByteContext pbc; 99cabdff1aSopenharmony_ci uint8_t *in_buf, *encode_buf; 100cabdff1aSopenharmony_ci int x, y, z, length, tablesize, ret, i; 101cabdff1aSopenharmony_ci unsigned int width, height, depth, dimension; 102cabdff1aSopenharmony_ci unsigned int bytes_per_channel, pixmax, put_be; 103cabdff1aSopenharmony_ci 104cabdff1aSopenharmony_ci width = avctx->width; 105cabdff1aSopenharmony_ci height = avctx->height; 106cabdff1aSopenharmony_ci bytes_per_channel = 1; 107cabdff1aSopenharmony_ci pixmax = 0xFF; 108cabdff1aSopenharmony_ci put_be = HAVE_BIGENDIAN; 109cabdff1aSopenharmony_ci 110cabdff1aSopenharmony_ci switch (avctx->pix_fmt) { 111cabdff1aSopenharmony_ci case AV_PIX_FMT_GRAY8: 112cabdff1aSopenharmony_ci dimension = SGI_SINGLE_CHAN; 113cabdff1aSopenharmony_ci depth = SGI_GRAYSCALE; 114cabdff1aSopenharmony_ci break; 115cabdff1aSopenharmony_ci case AV_PIX_FMT_RGB24: 116cabdff1aSopenharmony_ci dimension = SGI_MULTI_CHAN; 117cabdff1aSopenharmony_ci depth = SGI_RGB; 118cabdff1aSopenharmony_ci break; 119cabdff1aSopenharmony_ci case AV_PIX_FMT_RGBA: 120cabdff1aSopenharmony_ci dimension = SGI_MULTI_CHAN; 121cabdff1aSopenharmony_ci depth = SGI_RGBA; 122cabdff1aSopenharmony_ci break; 123cabdff1aSopenharmony_ci case AV_PIX_FMT_GRAY16LE: 124cabdff1aSopenharmony_ci put_be = !HAVE_BIGENDIAN; 125cabdff1aSopenharmony_ci case AV_PIX_FMT_GRAY16BE: 126cabdff1aSopenharmony_ci bytes_per_channel = 2; 127cabdff1aSopenharmony_ci pixmax = 0xFFFF; 128cabdff1aSopenharmony_ci dimension = SGI_SINGLE_CHAN; 129cabdff1aSopenharmony_ci depth = SGI_GRAYSCALE; 130cabdff1aSopenharmony_ci break; 131cabdff1aSopenharmony_ci case AV_PIX_FMT_RGB48LE: 132cabdff1aSopenharmony_ci put_be = !HAVE_BIGENDIAN; 133cabdff1aSopenharmony_ci case AV_PIX_FMT_RGB48BE: 134cabdff1aSopenharmony_ci bytes_per_channel = 2; 135cabdff1aSopenharmony_ci pixmax = 0xFFFF; 136cabdff1aSopenharmony_ci dimension = SGI_MULTI_CHAN; 137cabdff1aSopenharmony_ci depth = SGI_RGB; 138cabdff1aSopenharmony_ci break; 139cabdff1aSopenharmony_ci case AV_PIX_FMT_RGBA64LE: 140cabdff1aSopenharmony_ci put_be = !HAVE_BIGENDIAN; 141cabdff1aSopenharmony_ci case AV_PIX_FMT_RGBA64BE: 142cabdff1aSopenharmony_ci bytes_per_channel = 2; 143cabdff1aSopenharmony_ci pixmax = 0xFFFF; 144cabdff1aSopenharmony_ci dimension = SGI_MULTI_CHAN; 145cabdff1aSopenharmony_ci depth = SGI_RGBA; 146cabdff1aSopenharmony_ci break; 147cabdff1aSopenharmony_ci default: 148cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 149cabdff1aSopenharmony_ci } 150cabdff1aSopenharmony_ci 151cabdff1aSopenharmony_ci tablesize = depth * height * 4; 152cabdff1aSopenharmony_ci length = SGI_HEADER_SIZE; 153cabdff1aSopenharmony_ci if (!s->rle) 154cabdff1aSopenharmony_ci length += depth * height * width; 155cabdff1aSopenharmony_ci else // assume sgi_rle_encode() produces at most 2x size of input 156cabdff1aSopenharmony_ci length += tablesize * 2 + depth * height * (2 * width + 1); 157cabdff1aSopenharmony_ci 158cabdff1aSopenharmony_ci if ((ret = ff_alloc_packet(avctx, pkt, bytes_per_channel * length)) < 0) 159cabdff1aSopenharmony_ci return ret; 160cabdff1aSopenharmony_ci 161cabdff1aSopenharmony_ci bytestream2_init_writer(&pbc, pkt->data, pkt->size); 162cabdff1aSopenharmony_ci 163cabdff1aSopenharmony_ci /* Encode header. */ 164cabdff1aSopenharmony_ci bytestream2_put_be16(&pbc, SGI_MAGIC); 165cabdff1aSopenharmony_ci bytestream2_put_byte(&pbc, s->rle); /* RLE 1 - VERBATIM 0 */ 166cabdff1aSopenharmony_ci bytestream2_put_byte(&pbc, bytes_per_channel); 167cabdff1aSopenharmony_ci bytestream2_put_be16(&pbc, dimension); 168cabdff1aSopenharmony_ci bytestream2_put_be16(&pbc, width); 169cabdff1aSopenharmony_ci bytestream2_put_be16(&pbc, height); 170cabdff1aSopenharmony_ci bytestream2_put_be16(&pbc, depth); 171cabdff1aSopenharmony_ci 172cabdff1aSopenharmony_ci bytestream2_put_be32(&pbc, 0L); /* pixmin */ 173cabdff1aSopenharmony_ci bytestream2_put_be32(&pbc, pixmax); 174cabdff1aSopenharmony_ci bytestream2_put_be32(&pbc, 0L); /* dummy */ 175cabdff1aSopenharmony_ci 176cabdff1aSopenharmony_ci /* name */ 177cabdff1aSopenharmony_ci for (i = 0; i < 80; i++) 178cabdff1aSopenharmony_ci bytestream2_put_byte(&pbc, 0L); 179cabdff1aSopenharmony_ci 180cabdff1aSopenharmony_ci /* colormap */ 181cabdff1aSopenharmony_ci bytestream2_put_be32(&pbc, 0L); 182cabdff1aSopenharmony_ci 183cabdff1aSopenharmony_ci /* The rest of the 512 byte header is unused. */ 184cabdff1aSopenharmony_ci for (i = 0; i < 404; i++) 185cabdff1aSopenharmony_ci bytestream2_put_byte(&pbc, 0L); 186cabdff1aSopenharmony_ci 187cabdff1aSopenharmony_ci if (s->rle) { 188cabdff1aSopenharmony_ci PutByteContext taboff_pcb, tablen_pcb; 189cabdff1aSopenharmony_ci 190cabdff1aSopenharmony_ci /* Skip RLE offset table. */ 191cabdff1aSopenharmony_ci bytestream2_init_writer(&taboff_pcb, pbc.buffer, tablesize); 192cabdff1aSopenharmony_ci bytestream2_skip_p(&pbc, tablesize); 193cabdff1aSopenharmony_ci 194cabdff1aSopenharmony_ci /* Skip RLE length table. */ 195cabdff1aSopenharmony_ci bytestream2_init_writer(&tablen_pcb, pbc.buffer, tablesize); 196cabdff1aSopenharmony_ci bytestream2_skip_p(&pbc, tablesize); 197cabdff1aSopenharmony_ci 198cabdff1aSopenharmony_ci /* Make an intermediate consecutive buffer. */ 199cabdff1aSopenharmony_ci if (!(encode_buf = av_malloc(width * bytes_per_channel))) 200cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 201cabdff1aSopenharmony_ci 202cabdff1aSopenharmony_ci for (z = 0; z < depth; z++) { 203cabdff1aSopenharmony_ci in_buf = p->data[0] + p->linesize[0] * (height - 1) + z * bytes_per_channel; 204cabdff1aSopenharmony_ci 205cabdff1aSopenharmony_ci for (y = 0; y < height; y++) { 206cabdff1aSopenharmony_ci bytestream2_put_be32(&taboff_pcb, bytestream2_tell_p(&pbc)); 207cabdff1aSopenharmony_ci 208cabdff1aSopenharmony_ci for (x = 0; x < width * bytes_per_channel; x += bytes_per_channel) 209cabdff1aSopenharmony_ci if (bytes_per_channel == 1) { 210cabdff1aSopenharmony_ci encode_buf[x] = in_buf[depth * x]; 211cabdff1aSopenharmony_ci } else if (HAVE_BIGENDIAN ^ put_be) { 212cabdff1aSopenharmony_ci encode_buf[x + 1] = in_buf[depth * x]; 213cabdff1aSopenharmony_ci encode_buf[x] = in_buf[depth * x + 1]; 214cabdff1aSopenharmony_ci } else { 215cabdff1aSopenharmony_ci encode_buf[x] = in_buf[depth * x]; 216cabdff1aSopenharmony_ci encode_buf[x + 1] = in_buf[depth * x + 1]; 217cabdff1aSopenharmony_ci } 218cabdff1aSopenharmony_ci 219cabdff1aSopenharmony_ci length = sgi_rle_encode(&pbc, encode_buf, width, 220cabdff1aSopenharmony_ci bytes_per_channel); 221cabdff1aSopenharmony_ci if (length < 1) { 222cabdff1aSopenharmony_ci av_free(encode_buf); 223cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 224cabdff1aSopenharmony_ci } 225cabdff1aSopenharmony_ci 226cabdff1aSopenharmony_ci bytestream2_put_be32(&tablen_pcb, length); 227cabdff1aSopenharmony_ci in_buf -= p->linesize[0]; 228cabdff1aSopenharmony_ci } 229cabdff1aSopenharmony_ci } 230cabdff1aSopenharmony_ci 231cabdff1aSopenharmony_ci av_free(encode_buf); 232cabdff1aSopenharmony_ci } else { 233cabdff1aSopenharmony_ci for (z = 0; z < depth; z++) { 234cabdff1aSopenharmony_ci in_buf = p->data[0] + p->linesize[0] * (height - 1) + z * bytes_per_channel; 235cabdff1aSopenharmony_ci 236cabdff1aSopenharmony_ci for (y = 0; y < height; y++) { 237cabdff1aSopenharmony_ci for (x = 0; x < width * depth; x += depth) 238cabdff1aSopenharmony_ci if (bytes_per_channel == 1) 239cabdff1aSopenharmony_ci bytestream2_put_byte(&pbc, in_buf[x]); 240cabdff1aSopenharmony_ci else 241cabdff1aSopenharmony_ci if (put_be) 242cabdff1aSopenharmony_ci bytestream2_put_be16(&pbc, ((uint16_t *)in_buf)[x]); 243cabdff1aSopenharmony_ci else 244cabdff1aSopenharmony_ci bytestream2_put_le16(&pbc, ((uint16_t *)in_buf)[x]); 245cabdff1aSopenharmony_ci 246cabdff1aSopenharmony_ci in_buf -= p->linesize[0]; 247cabdff1aSopenharmony_ci } 248cabdff1aSopenharmony_ci } 249cabdff1aSopenharmony_ci } 250cabdff1aSopenharmony_ci 251cabdff1aSopenharmony_ci /* total length */ 252cabdff1aSopenharmony_ci pkt->size = bytestream2_tell_p(&pbc); 253cabdff1aSopenharmony_ci *got_packet = 1; 254cabdff1aSopenharmony_ci 255cabdff1aSopenharmony_ci return 0; 256cabdff1aSopenharmony_ci} 257cabdff1aSopenharmony_ci 258cabdff1aSopenharmony_ci#define OFFSET(x) offsetof(SgiContext, x) 259cabdff1aSopenharmony_ci#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM 260cabdff1aSopenharmony_cistatic const AVOption options[] = { 261cabdff1aSopenharmony_ci { "rle", "Use run-length compression", OFFSET(rle), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, VE }, 262cabdff1aSopenharmony_ci 263cabdff1aSopenharmony_ci { NULL }, 264cabdff1aSopenharmony_ci}; 265cabdff1aSopenharmony_ci 266cabdff1aSopenharmony_cistatic const AVClass sgi_class = { 267cabdff1aSopenharmony_ci .class_name = "sgi", 268cabdff1aSopenharmony_ci .item_name = av_default_item_name, 269cabdff1aSopenharmony_ci .option = options, 270cabdff1aSopenharmony_ci .version = LIBAVUTIL_VERSION_INT, 271cabdff1aSopenharmony_ci}; 272cabdff1aSopenharmony_ci 273cabdff1aSopenharmony_ciconst FFCodec ff_sgi_encoder = { 274cabdff1aSopenharmony_ci .p.name = "sgi", 275cabdff1aSopenharmony_ci .p.long_name = NULL_IF_CONFIG_SMALL("SGI image"), 276cabdff1aSopenharmony_ci .p.type = AVMEDIA_TYPE_VIDEO, 277cabdff1aSopenharmony_ci .p.id = AV_CODEC_ID_SGI, 278cabdff1aSopenharmony_ci .priv_data_size = sizeof(SgiContext), 279cabdff1aSopenharmony_ci .p.priv_class = &sgi_class, 280cabdff1aSopenharmony_ci .init = encode_init, 281cabdff1aSopenharmony_ci FF_CODEC_ENCODE_CB(encode_frame), 282cabdff1aSopenharmony_ci .p.pix_fmts = (const enum AVPixelFormat[]) { 283cabdff1aSopenharmony_ci AV_PIX_FMT_RGB24, AV_PIX_FMT_RGBA, 284cabdff1aSopenharmony_ci AV_PIX_FMT_RGB48LE, AV_PIX_FMT_RGB48BE, 285cabdff1aSopenharmony_ci AV_PIX_FMT_RGBA64LE, AV_PIX_FMT_RGBA64BE, 286cabdff1aSopenharmony_ci AV_PIX_FMT_GRAY16LE, AV_PIX_FMT_GRAY16BE, AV_PIX_FMT_GRAY8, 287cabdff1aSopenharmony_ci AV_PIX_FMT_NONE 288cabdff1aSopenharmony_ci }, 289cabdff1aSopenharmony_ci .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, 290cabdff1aSopenharmony_ci}; 291