1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * SGI image decoder 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/imgutils.h" 23cabdff1aSopenharmony_ci#include "avcodec.h" 24cabdff1aSopenharmony_ci#include "bytestream.h" 25cabdff1aSopenharmony_ci#include "codec_internal.h" 26cabdff1aSopenharmony_ci#include "internal.h" 27cabdff1aSopenharmony_ci#include "sgi.h" 28cabdff1aSopenharmony_ci 29cabdff1aSopenharmony_citypedef struct SgiState { 30cabdff1aSopenharmony_ci AVCodecContext *avctx; 31cabdff1aSopenharmony_ci unsigned int width; 32cabdff1aSopenharmony_ci unsigned int height; 33cabdff1aSopenharmony_ci unsigned int depth; 34cabdff1aSopenharmony_ci unsigned int bytes_per_channel; 35cabdff1aSopenharmony_ci int linesize; 36cabdff1aSopenharmony_ci GetByteContext g; 37cabdff1aSopenharmony_ci} SgiState; 38cabdff1aSopenharmony_ci 39cabdff1aSopenharmony_ci/** 40cabdff1aSopenharmony_ci * Expand an RLE row into a channel. 41cabdff1aSopenharmony_ci * @param s the current image state 42cabdff1aSopenharmony_ci * @param out_buf Points to one line after the output buffer. 43cabdff1aSopenharmony_ci * @param len length of out_buf in bytes 44cabdff1aSopenharmony_ci * @param pixelstride pixel stride of input buffer 45cabdff1aSopenharmony_ci * @return size of output in bytes, else return error code. 46cabdff1aSopenharmony_ci */ 47cabdff1aSopenharmony_cistatic int expand_rle_row8(SgiState *s, uint8_t *out_buf, 48cabdff1aSopenharmony_ci int len, int pixelstride) 49cabdff1aSopenharmony_ci{ 50cabdff1aSopenharmony_ci unsigned char pixel, count; 51cabdff1aSopenharmony_ci unsigned char *orig = out_buf; 52cabdff1aSopenharmony_ci uint8_t *out_end = out_buf + len; 53cabdff1aSopenharmony_ci 54cabdff1aSopenharmony_ci while (out_buf < out_end) { 55cabdff1aSopenharmony_ci if (bytestream2_get_bytes_left(&s->g) < 1) 56cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 57cabdff1aSopenharmony_ci pixel = bytestream2_get_byteu(&s->g); 58cabdff1aSopenharmony_ci if (!(count = (pixel & 0x7f))) { 59cabdff1aSopenharmony_ci break; 60cabdff1aSopenharmony_ci } 61cabdff1aSopenharmony_ci 62cabdff1aSopenharmony_ci /* Check for buffer overflow. */ 63cabdff1aSopenharmony_ci if (out_end - out_buf <= pixelstride * (count - 1)) { 64cabdff1aSopenharmony_ci av_log(s->avctx, AV_LOG_ERROR, "Invalid pixel count.\n"); 65cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 66cabdff1aSopenharmony_ci } 67cabdff1aSopenharmony_ci 68cabdff1aSopenharmony_ci if (pixel & 0x80) { 69cabdff1aSopenharmony_ci while (count--) { 70cabdff1aSopenharmony_ci *out_buf = bytestream2_get_byte(&s->g); 71cabdff1aSopenharmony_ci out_buf += pixelstride; 72cabdff1aSopenharmony_ci } 73cabdff1aSopenharmony_ci } else { 74cabdff1aSopenharmony_ci pixel = bytestream2_get_byte(&s->g); 75cabdff1aSopenharmony_ci 76cabdff1aSopenharmony_ci while (count--) { 77cabdff1aSopenharmony_ci *out_buf = pixel; 78cabdff1aSopenharmony_ci out_buf += pixelstride; 79cabdff1aSopenharmony_ci } 80cabdff1aSopenharmony_ci } 81cabdff1aSopenharmony_ci } 82cabdff1aSopenharmony_ci return (out_buf - orig) / pixelstride; 83cabdff1aSopenharmony_ci} 84cabdff1aSopenharmony_ci 85cabdff1aSopenharmony_cistatic int expand_rle_row16(SgiState *s, uint16_t *out_buf, 86cabdff1aSopenharmony_ci int len, int pixelstride) 87cabdff1aSopenharmony_ci{ 88cabdff1aSopenharmony_ci unsigned short pixel; 89cabdff1aSopenharmony_ci unsigned char count; 90cabdff1aSopenharmony_ci unsigned short *orig = out_buf; 91cabdff1aSopenharmony_ci uint16_t *out_end = out_buf + len; 92cabdff1aSopenharmony_ci 93cabdff1aSopenharmony_ci while (out_buf < out_end) { 94cabdff1aSopenharmony_ci if (bytestream2_get_bytes_left(&s->g) < 2) 95cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 96cabdff1aSopenharmony_ci pixel = bytestream2_get_be16u(&s->g); 97cabdff1aSopenharmony_ci if (!(count = (pixel & 0x7f))) 98cabdff1aSopenharmony_ci break; 99cabdff1aSopenharmony_ci 100cabdff1aSopenharmony_ci /* Check for buffer overflow. */ 101cabdff1aSopenharmony_ci if (out_end - out_buf <= pixelstride * (count - 1)) { 102cabdff1aSopenharmony_ci av_log(s->avctx, AV_LOG_ERROR, "Invalid pixel count.\n"); 103cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 104cabdff1aSopenharmony_ci } 105cabdff1aSopenharmony_ci 106cabdff1aSopenharmony_ci if (pixel & 0x80) { 107cabdff1aSopenharmony_ci while (count--) { 108cabdff1aSopenharmony_ci pixel = bytestream2_get_ne16(&s->g); 109cabdff1aSopenharmony_ci AV_WN16A(out_buf, pixel); 110cabdff1aSopenharmony_ci out_buf += pixelstride; 111cabdff1aSopenharmony_ci } 112cabdff1aSopenharmony_ci } else { 113cabdff1aSopenharmony_ci pixel = bytestream2_get_ne16(&s->g); 114cabdff1aSopenharmony_ci 115cabdff1aSopenharmony_ci while (count--) { 116cabdff1aSopenharmony_ci AV_WN16A(out_buf, pixel); 117cabdff1aSopenharmony_ci out_buf += pixelstride; 118cabdff1aSopenharmony_ci } 119cabdff1aSopenharmony_ci } 120cabdff1aSopenharmony_ci } 121cabdff1aSopenharmony_ci return (out_buf - orig) / pixelstride; 122cabdff1aSopenharmony_ci} 123cabdff1aSopenharmony_ci 124cabdff1aSopenharmony_ci 125cabdff1aSopenharmony_ci/** 126cabdff1aSopenharmony_ci * Read a run length encoded SGI image. 127cabdff1aSopenharmony_ci * @param out_buf output buffer 128cabdff1aSopenharmony_ci * @param s the current image state 129cabdff1aSopenharmony_ci * @return 0 if no error, else return error code. 130cabdff1aSopenharmony_ci */ 131cabdff1aSopenharmony_cistatic int read_rle_sgi(uint8_t *out_buf, SgiState *s) 132cabdff1aSopenharmony_ci{ 133cabdff1aSopenharmony_ci uint8_t *dest_row; 134cabdff1aSopenharmony_ci unsigned int len = s->height * s->depth * 4; 135cabdff1aSopenharmony_ci GetByteContext g_table = s->g; 136cabdff1aSopenharmony_ci unsigned int y, z; 137cabdff1aSopenharmony_ci unsigned int start_offset; 138cabdff1aSopenharmony_ci int linesize, ret; 139cabdff1aSopenharmony_ci 140cabdff1aSopenharmony_ci /* size of RLE offset and length tables */ 141cabdff1aSopenharmony_ci if (len * 2 > bytestream2_get_bytes_left(&s->g)) { 142cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 143cabdff1aSopenharmony_ci } 144cabdff1aSopenharmony_ci 145cabdff1aSopenharmony_ci for (z = 0; z < s->depth; z++) { 146cabdff1aSopenharmony_ci dest_row = out_buf; 147cabdff1aSopenharmony_ci for (y = 0; y < s->height; y++) { 148cabdff1aSopenharmony_ci linesize = s->width * s->depth; 149cabdff1aSopenharmony_ci dest_row -= s->linesize; 150cabdff1aSopenharmony_ci start_offset = bytestream2_get_be32(&g_table); 151cabdff1aSopenharmony_ci bytestream2_seek(&s->g, start_offset, SEEK_SET); 152cabdff1aSopenharmony_ci if (s->bytes_per_channel == 1) 153cabdff1aSopenharmony_ci ret = expand_rle_row8(s, dest_row + z, linesize, s->depth); 154cabdff1aSopenharmony_ci else 155cabdff1aSopenharmony_ci ret = expand_rle_row16(s, (uint16_t *)dest_row + z, linesize, s->depth); 156cabdff1aSopenharmony_ci if (ret != s->width) 157cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 158cabdff1aSopenharmony_ci } 159cabdff1aSopenharmony_ci } 160cabdff1aSopenharmony_ci return 0; 161cabdff1aSopenharmony_ci} 162cabdff1aSopenharmony_ci 163cabdff1aSopenharmony_ci/** 164cabdff1aSopenharmony_ci * Read an uncompressed SGI image. 165cabdff1aSopenharmony_ci * @param out_buf output buffer 166cabdff1aSopenharmony_ci * @param s the current image state 167cabdff1aSopenharmony_ci * @return 0 if read success, else return error code. 168cabdff1aSopenharmony_ci */ 169cabdff1aSopenharmony_cistatic int read_uncompressed_sgi(unsigned char *out_buf, SgiState *s) 170cabdff1aSopenharmony_ci{ 171cabdff1aSopenharmony_ci int x, y, z; 172cabdff1aSopenharmony_ci unsigned int offset = s->height * s->width * s->bytes_per_channel; 173cabdff1aSopenharmony_ci GetByteContext gp[4]; 174cabdff1aSopenharmony_ci uint8_t *out_end; 175cabdff1aSopenharmony_ci 176cabdff1aSopenharmony_ci /* Test buffer size. */ 177cabdff1aSopenharmony_ci if (offset * s->depth > bytestream2_get_bytes_left(&s->g)) 178cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 179cabdff1aSopenharmony_ci 180cabdff1aSopenharmony_ci /* Create a reader for each plane */ 181cabdff1aSopenharmony_ci for (z = 0; z < s->depth; z++) { 182cabdff1aSopenharmony_ci gp[z] = s->g; 183cabdff1aSopenharmony_ci bytestream2_skip(&gp[z], z * offset); 184cabdff1aSopenharmony_ci } 185cabdff1aSopenharmony_ci 186cabdff1aSopenharmony_ci for (y = s->height - 1; y >= 0; y--) { 187cabdff1aSopenharmony_ci out_end = out_buf + (y * s->linesize); 188cabdff1aSopenharmony_ci if (s->bytes_per_channel == 1) { 189cabdff1aSopenharmony_ci for (x = s->width; x > 0; x--) 190cabdff1aSopenharmony_ci for (z = 0; z < s->depth; z++) 191cabdff1aSopenharmony_ci *out_end++ = bytestream2_get_byteu(&gp[z]); 192cabdff1aSopenharmony_ci } else { 193cabdff1aSopenharmony_ci uint16_t *out16 = (uint16_t *)out_end; 194cabdff1aSopenharmony_ci for (x = s->width; x > 0; x--) 195cabdff1aSopenharmony_ci for (z = 0; z < s->depth; z++) 196cabdff1aSopenharmony_ci *out16++ = bytestream2_get_ne16u(&gp[z]); 197cabdff1aSopenharmony_ci } 198cabdff1aSopenharmony_ci } 199cabdff1aSopenharmony_ci return 0; 200cabdff1aSopenharmony_ci} 201cabdff1aSopenharmony_ci 202cabdff1aSopenharmony_cistatic int decode_frame(AVCodecContext *avctx, AVFrame *p, 203cabdff1aSopenharmony_ci int *got_frame, AVPacket *avpkt) 204cabdff1aSopenharmony_ci{ 205cabdff1aSopenharmony_ci SgiState *s = avctx->priv_data; 206cabdff1aSopenharmony_ci unsigned int dimension, rle; 207cabdff1aSopenharmony_ci int ret = 0; 208cabdff1aSopenharmony_ci uint8_t *out_buf, *out_end; 209cabdff1aSopenharmony_ci 210cabdff1aSopenharmony_ci bytestream2_init(&s->g, avpkt->data, avpkt->size); 211cabdff1aSopenharmony_ci if (bytestream2_get_bytes_left(&s->g) < SGI_HEADER_SIZE) { 212cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "buf_size too small (%d)\n", avpkt->size); 213cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 214cabdff1aSopenharmony_ci } 215cabdff1aSopenharmony_ci 216cabdff1aSopenharmony_ci /* Test for SGI magic. */ 217cabdff1aSopenharmony_ci if (bytestream2_get_be16u(&s->g) != SGI_MAGIC) { 218cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "bad magic number\n"); 219cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 220cabdff1aSopenharmony_ci } 221cabdff1aSopenharmony_ci 222cabdff1aSopenharmony_ci rle = bytestream2_get_byteu(&s->g); 223cabdff1aSopenharmony_ci s->bytes_per_channel = bytestream2_get_byteu(&s->g); 224cabdff1aSopenharmony_ci dimension = bytestream2_get_be16u(&s->g); 225cabdff1aSopenharmony_ci s->width = bytestream2_get_be16u(&s->g); 226cabdff1aSopenharmony_ci s->height = bytestream2_get_be16u(&s->g); 227cabdff1aSopenharmony_ci s->depth = bytestream2_get_be16u(&s->g); 228cabdff1aSopenharmony_ci 229cabdff1aSopenharmony_ci if (s->bytes_per_channel != 1 && s->bytes_per_channel != 2) { 230cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "wrong channel number\n"); 231cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 232cabdff1aSopenharmony_ci } 233cabdff1aSopenharmony_ci 234cabdff1aSopenharmony_ci /* Check for supported image dimensions. */ 235cabdff1aSopenharmony_ci if (dimension != 2 && dimension != 3) { 236cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "wrong dimension number\n"); 237cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 238cabdff1aSopenharmony_ci } 239cabdff1aSopenharmony_ci 240cabdff1aSopenharmony_ci if (s->depth == SGI_GRAYSCALE) { 241cabdff1aSopenharmony_ci avctx->pix_fmt = s->bytes_per_channel == 2 ? AV_PIX_FMT_GRAY16BE : AV_PIX_FMT_GRAY8; 242cabdff1aSopenharmony_ci } else if (s->depth == SGI_RGB) { 243cabdff1aSopenharmony_ci avctx->pix_fmt = s->bytes_per_channel == 2 ? AV_PIX_FMT_RGB48BE : AV_PIX_FMT_RGB24; 244cabdff1aSopenharmony_ci } else if (s->depth == SGI_RGBA) { 245cabdff1aSopenharmony_ci avctx->pix_fmt = s->bytes_per_channel == 2 ? AV_PIX_FMT_RGBA64BE : AV_PIX_FMT_RGBA; 246cabdff1aSopenharmony_ci } else { 247cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "wrong picture format\n"); 248cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 249cabdff1aSopenharmony_ci } 250cabdff1aSopenharmony_ci 251cabdff1aSopenharmony_ci ret = ff_set_dimensions(avctx, s->width, s->height); 252cabdff1aSopenharmony_ci if (ret < 0) 253cabdff1aSopenharmony_ci return ret; 254cabdff1aSopenharmony_ci 255cabdff1aSopenharmony_ci if ((ret = ff_get_buffer(avctx, p, 0)) < 0) 256cabdff1aSopenharmony_ci return ret; 257cabdff1aSopenharmony_ci 258cabdff1aSopenharmony_ci p->pict_type = AV_PICTURE_TYPE_I; 259cabdff1aSopenharmony_ci p->key_frame = 1; 260cabdff1aSopenharmony_ci out_buf = p->data[0]; 261cabdff1aSopenharmony_ci 262cabdff1aSopenharmony_ci out_end = out_buf + p->linesize[0] * s->height; 263cabdff1aSopenharmony_ci 264cabdff1aSopenharmony_ci s->linesize = p->linesize[0]; 265cabdff1aSopenharmony_ci 266cabdff1aSopenharmony_ci /* Skip header. */ 267cabdff1aSopenharmony_ci bytestream2_seek(&s->g, SGI_HEADER_SIZE, SEEK_SET); 268cabdff1aSopenharmony_ci if (rle) { 269cabdff1aSopenharmony_ci ret = read_rle_sgi(out_end, s); 270cabdff1aSopenharmony_ci } else { 271cabdff1aSopenharmony_ci ret = read_uncompressed_sgi(out_buf, s); 272cabdff1aSopenharmony_ci } 273cabdff1aSopenharmony_ci if (ret) 274cabdff1aSopenharmony_ci return ret; 275cabdff1aSopenharmony_ci 276cabdff1aSopenharmony_ci *got_frame = 1; 277cabdff1aSopenharmony_ci return avpkt->size; 278cabdff1aSopenharmony_ci} 279cabdff1aSopenharmony_ci 280cabdff1aSopenharmony_cistatic av_cold int sgi_decode_init(AVCodecContext *avctx) 281cabdff1aSopenharmony_ci{ 282cabdff1aSopenharmony_ci SgiState *s = avctx->priv_data; 283cabdff1aSopenharmony_ci 284cabdff1aSopenharmony_ci s->avctx = avctx; 285cabdff1aSopenharmony_ci 286cabdff1aSopenharmony_ci return 0; 287cabdff1aSopenharmony_ci} 288cabdff1aSopenharmony_ci 289cabdff1aSopenharmony_ciconst FFCodec ff_sgi_decoder = { 290cabdff1aSopenharmony_ci .p.name = "sgi", 291cabdff1aSopenharmony_ci .p.long_name = NULL_IF_CONFIG_SMALL("SGI image"), 292cabdff1aSopenharmony_ci .p.type = AVMEDIA_TYPE_VIDEO, 293cabdff1aSopenharmony_ci .p.id = AV_CODEC_ID_SGI, 294cabdff1aSopenharmony_ci .priv_data_size = sizeof(SgiState), 295cabdff1aSopenharmony_ci FF_CODEC_DECODE_CB(decode_frame), 296cabdff1aSopenharmony_ci .init = sgi_decode_init, 297cabdff1aSopenharmony_ci .p.capabilities = AV_CODEC_CAP_DR1, 298cabdff1aSopenharmony_ci .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, 299cabdff1aSopenharmony_ci}; 300