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