1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * Targa (.tga) image encoder 3cabdff1aSopenharmony_ci * Copyright (c) 2007 Bobby Bingham 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 <string.h> 23cabdff1aSopenharmony_ci 24cabdff1aSopenharmony_ci#include "libavutil/imgutils.h" 25cabdff1aSopenharmony_ci#include "libavutil/internal.h" 26cabdff1aSopenharmony_ci#include "libavutil/intreadwrite.h" 27cabdff1aSopenharmony_ci#include "libavutil/opt.h" 28cabdff1aSopenharmony_ci#include "libavutil/pixdesc.h" 29cabdff1aSopenharmony_ci#include "avcodec.h" 30cabdff1aSopenharmony_ci#include "codec_internal.h" 31cabdff1aSopenharmony_ci#include "encode.h" 32cabdff1aSopenharmony_ci#include "rle.h" 33cabdff1aSopenharmony_ci#include "targa.h" 34cabdff1aSopenharmony_ci 35cabdff1aSopenharmony_citypedef struct TargaContext { 36cabdff1aSopenharmony_ci AVClass *class; 37cabdff1aSopenharmony_ci 38cabdff1aSopenharmony_ci int rle; 39cabdff1aSopenharmony_ci} TargaContext; 40cabdff1aSopenharmony_ci 41cabdff1aSopenharmony_ci/** 42cabdff1aSopenharmony_ci * RLE compress the image, with maximum size of out_size 43cabdff1aSopenharmony_ci * @param outbuf Output buffer 44cabdff1aSopenharmony_ci * @param out_size Maximum output size 45cabdff1aSopenharmony_ci * @param pic Image to compress 46cabdff1aSopenharmony_ci * @param bpp Bytes per pixel 47cabdff1aSopenharmony_ci * @param w Image width 48cabdff1aSopenharmony_ci * @param h Image height 49cabdff1aSopenharmony_ci * @return Size of output in bytes, or -1 if larger than out_size 50cabdff1aSopenharmony_ci */ 51cabdff1aSopenharmony_cistatic int targa_encode_rle(uint8_t *outbuf, int out_size, const AVFrame *pic, 52cabdff1aSopenharmony_ci int bpp, int w, int h) 53cabdff1aSopenharmony_ci{ 54cabdff1aSopenharmony_ci int y,ret; 55cabdff1aSopenharmony_ci uint8_t *out; 56cabdff1aSopenharmony_ci 57cabdff1aSopenharmony_ci out = outbuf; 58cabdff1aSopenharmony_ci 59cabdff1aSopenharmony_ci for(y = 0; y < h; y ++) { 60cabdff1aSopenharmony_ci ret = ff_rle_encode(out, out_size, pic->data[0] + pic->linesize[0] * y, bpp, w, 0x7f, 0, -1, 0); 61cabdff1aSopenharmony_ci if(ret == -1){ 62cabdff1aSopenharmony_ci return -1; 63cabdff1aSopenharmony_ci } 64cabdff1aSopenharmony_ci out+= ret; 65cabdff1aSopenharmony_ci out_size -= ret; 66cabdff1aSopenharmony_ci } 67cabdff1aSopenharmony_ci 68cabdff1aSopenharmony_ci return out - outbuf; 69cabdff1aSopenharmony_ci} 70cabdff1aSopenharmony_ci 71cabdff1aSopenharmony_cistatic int targa_encode_normal(uint8_t *outbuf, const AVFrame *pic, int bpp, int w, int h) 72cabdff1aSopenharmony_ci{ 73cabdff1aSopenharmony_ci int i, n = bpp * w; 74cabdff1aSopenharmony_ci uint8_t *out = outbuf; 75cabdff1aSopenharmony_ci uint8_t *ptr = pic->data[0]; 76cabdff1aSopenharmony_ci 77cabdff1aSopenharmony_ci for(i=0; i < h; i++) { 78cabdff1aSopenharmony_ci memcpy(out, ptr, n); 79cabdff1aSopenharmony_ci out += n; 80cabdff1aSopenharmony_ci ptr += pic->linesize[0]; 81cabdff1aSopenharmony_ci } 82cabdff1aSopenharmony_ci 83cabdff1aSopenharmony_ci return out - outbuf; 84cabdff1aSopenharmony_ci} 85cabdff1aSopenharmony_ci 86cabdff1aSopenharmony_cistatic int targa_encode_frame(AVCodecContext *avctx, AVPacket *pkt, 87cabdff1aSopenharmony_ci const AVFrame *p, int *got_packet) 88cabdff1aSopenharmony_ci{ 89cabdff1aSopenharmony_ci TargaContext *s = avctx->priv_data; 90cabdff1aSopenharmony_ci int bpp, picsize, datasize = -1, ret, i; 91cabdff1aSopenharmony_ci uint8_t *out; 92cabdff1aSopenharmony_ci 93cabdff1aSopenharmony_ci picsize = av_image_get_buffer_size(avctx->pix_fmt, 94cabdff1aSopenharmony_ci avctx->width, avctx->height, 1); 95cabdff1aSopenharmony_ci if ((ret = ff_alloc_packet(avctx, pkt, picsize + 45)) < 0) 96cabdff1aSopenharmony_ci return ret; 97cabdff1aSopenharmony_ci 98cabdff1aSopenharmony_ci /* zero out the header and only set applicable fields */ 99cabdff1aSopenharmony_ci memset(pkt->data, 0, 12); 100cabdff1aSopenharmony_ci AV_WL16(pkt->data+12, avctx->width); 101cabdff1aSopenharmony_ci AV_WL16(pkt->data+14, avctx->height); 102cabdff1aSopenharmony_ci /* image descriptor byte: origin is always top-left, bits 0-3 specify alpha */ 103cabdff1aSopenharmony_ci pkt->data[17] = 0x20 | (avctx->pix_fmt == AV_PIX_FMT_BGRA ? 8 : 0); 104cabdff1aSopenharmony_ci 105cabdff1aSopenharmony_ci out = pkt->data + 18; /* skip past the header we write */ 106cabdff1aSopenharmony_ci 107cabdff1aSopenharmony_ci avctx->bits_per_coded_sample = av_get_bits_per_pixel(av_pix_fmt_desc_get(avctx->pix_fmt)); 108cabdff1aSopenharmony_ci switch(avctx->pix_fmt) { 109cabdff1aSopenharmony_ci case AV_PIX_FMT_PAL8: { 110cabdff1aSopenharmony_ci int pal_bpp = 24; /* Only write 32bit palette if there is transparency information */ 111cabdff1aSopenharmony_ci for (i = 0; i < 256; i++) 112cabdff1aSopenharmony_ci if (AV_RN32(p->data[1] + 4 * i) >> 24 != 0xFF) { 113cabdff1aSopenharmony_ci pal_bpp = 32; 114cabdff1aSopenharmony_ci break; 115cabdff1aSopenharmony_ci } 116cabdff1aSopenharmony_ci pkt->data[1] = 1; /* palette present */ 117cabdff1aSopenharmony_ci pkt->data[2] = TGA_PAL; /* uncompressed palettised image */ 118cabdff1aSopenharmony_ci pkt->data[6] = 1; /* palette contains 256 entries */ 119cabdff1aSopenharmony_ci pkt->data[7] = pal_bpp; /* palette contains pal_bpp bit entries */ 120cabdff1aSopenharmony_ci pkt->data[16] = 8; /* bpp */ 121cabdff1aSopenharmony_ci for (i = 0; i < 256; i++) 122cabdff1aSopenharmony_ci if (pal_bpp == 32) { 123cabdff1aSopenharmony_ci AV_WL32(pkt->data + 18 + 4 * i, *(uint32_t *)(p->data[1] + i * 4)); 124cabdff1aSopenharmony_ci } else { 125cabdff1aSopenharmony_ci AV_WL24(pkt->data + 18 + 3 * i, *(uint32_t *)(p->data[1] + i * 4)); 126cabdff1aSopenharmony_ci } 127cabdff1aSopenharmony_ci out += 32 * pal_bpp; /* skip past the palette we just output */ 128cabdff1aSopenharmony_ci break; 129cabdff1aSopenharmony_ci } 130cabdff1aSopenharmony_ci case AV_PIX_FMT_GRAY8: 131cabdff1aSopenharmony_ci pkt->data[2] = TGA_BW; /* uncompressed grayscale image */ 132cabdff1aSopenharmony_ci avctx->bits_per_coded_sample = 0x28; 133cabdff1aSopenharmony_ci pkt->data[16] = 8; /* bpp */ 134cabdff1aSopenharmony_ci break; 135cabdff1aSopenharmony_ci case AV_PIX_FMT_RGB555LE: 136cabdff1aSopenharmony_ci pkt->data[2] = TGA_RGB; /* uncompressed true-color image */ 137cabdff1aSopenharmony_ci avctx->bits_per_coded_sample = 138cabdff1aSopenharmony_ci pkt->data[16] = 16; /* bpp */ 139cabdff1aSopenharmony_ci break; 140cabdff1aSopenharmony_ci case AV_PIX_FMT_BGR24: 141cabdff1aSopenharmony_ci pkt->data[2] = TGA_RGB; /* uncompressed true-color image */ 142cabdff1aSopenharmony_ci pkt->data[16] = 24; /* bpp */ 143cabdff1aSopenharmony_ci break; 144cabdff1aSopenharmony_ci case AV_PIX_FMT_BGRA: 145cabdff1aSopenharmony_ci pkt->data[2] = TGA_RGB; /* uncompressed true-color image */ 146cabdff1aSopenharmony_ci pkt->data[16] = 32; /* bpp */ 147cabdff1aSopenharmony_ci break; 148cabdff1aSopenharmony_ci default: 149cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "Pixel format '%s' not supported.\n", 150cabdff1aSopenharmony_ci av_get_pix_fmt_name(avctx->pix_fmt)); 151cabdff1aSopenharmony_ci return AVERROR(EINVAL); 152cabdff1aSopenharmony_ci } 153cabdff1aSopenharmony_ci bpp = pkt->data[16] >> 3; 154cabdff1aSopenharmony_ci 155cabdff1aSopenharmony_ci 156cabdff1aSopenharmony_ci /* try RLE compression */ 157cabdff1aSopenharmony_ci if (s->rle) 158cabdff1aSopenharmony_ci datasize = targa_encode_rle(out, picsize, p, bpp, avctx->width, avctx->height); 159cabdff1aSopenharmony_ci 160cabdff1aSopenharmony_ci /* if that worked well, mark the picture as RLE compressed */ 161cabdff1aSopenharmony_ci if(datasize >= 0) 162cabdff1aSopenharmony_ci pkt->data[2] |= TGA_RLE; 163cabdff1aSopenharmony_ci 164cabdff1aSopenharmony_ci /* if RLE didn't make it smaller, go back to no compression */ 165cabdff1aSopenharmony_ci else datasize = targa_encode_normal(out, p, bpp, avctx->width, avctx->height); 166cabdff1aSopenharmony_ci 167cabdff1aSopenharmony_ci out += datasize; 168cabdff1aSopenharmony_ci 169cabdff1aSopenharmony_ci /* The standard recommends including this section, even if we don't use 170cabdff1aSopenharmony_ci * any of the features it affords. TODO: take advantage of the pixel 171cabdff1aSopenharmony_ci * aspect ratio and encoder ID fields available? */ 172cabdff1aSopenharmony_ci memcpy(out, "\0\0\0\0\0\0\0\0TRUEVISION-XFILE.", 26); 173cabdff1aSopenharmony_ci 174cabdff1aSopenharmony_ci pkt->size = out + 26 - pkt->data; 175cabdff1aSopenharmony_ci *got_packet = 1; 176cabdff1aSopenharmony_ci 177cabdff1aSopenharmony_ci return 0; 178cabdff1aSopenharmony_ci} 179cabdff1aSopenharmony_ci 180cabdff1aSopenharmony_cistatic av_cold int targa_encode_init(AVCodecContext *avctx) 181cabdff1aSopenharmony_ci{ 182cabdff1aSopenharmony_ci if (avctx->width > 0xffff || avctx->height > 0xffff) { 183cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "image dimensions too large\n"); 184cabdff1aSopenharmony_ci return AVERROR(EINVAL); 185cabdff1aSopenharmony_ci } 186cabdff1aSopenharmony_ci 187cabdff1aSopenharmony_ci return 0; 188cabdff1aSopenharmony_ci} 189cabdff1aSopenharmony_ci 190cabdff1aSopenharmony_ci#define OFFSET(x) offsetof(TargaContext, x) 191cabdff1aSopenharmony_ci#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM 192cabdff1aSopenharmony_cistatic const AVOption options[] = { 193cabdff1aSopenharmony_ci { "rle", "Use run-length compression", OFFSET(rle), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, VE }, 194cabdff1aSopenharmony_ci 195cabdff1aSopenharmony_ci { NULL }, 196cabdff1aSopenharmony_ci}; 197cabdff1aSopenharmony_ci 198cabdff1aSopenharmony_cistatic const AVClass targa_class = { 199cabdff1aSopenharmony_ci .class_name = "targa", 200cabdff1aSopenharmony_ci .item_name = av_default_item_name, 201cabdff1aSopenharmony_ci .option = options, 202cabdff1aSopenharmony_ci .version = LIBAVUTIL_VERSION_INT, 203cabdff1aSopenharmony_ci}; 204cabdff1aSopenharmony_ci 205cabdff1aSopenharmony_ciconst FFCodec ff_targa_encoder = { 206cabdff1aSopenharmony_ci .p.name = "targa", 207cabdff1aSopenharmony_ci .p.long_name = NULL_IF_CONFIG_SMALL("Truevision Targa image"), 208cabdff1aSopenharmony_ci .p.type = AVMEDIA_TYPE_VIDEO, 209cabdff1aSopenharmony_ci .p.id = AV_CODEC_ID_TARGA, 210cabdff1aSopenharmony_ci .priv_data_size = sizeof(TargaContext), 211cabdff1aSopenharmony_ci .p.priv_class = &targa_class, 212cabdff1aSopenharmony_ci .init = targa_encode_init, 213cabdff1aSopenharmony_ci FF_CODEC_ENCODE_CB(targa_encode_frame), 214cabdff1aSopenharmony_ci .p.pix_fmts = (const enum AVPixelFormat[]){ 215cabdff1aSopenharmony_ci AV_PIX_FMT_BGR24, AV_PIX_FMT_BGRA, AV_PIX_FMT_RGB555LE, AV_PIX_FMT_GRAY8, AV_PIX_FMT_PAL8, 216cabdff1aSopenharmony_ci AV_PIX_FMT_NONE 217cabdff1aSopenharmony_ci }, 218cabdff1aSopenharmony_ci .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, 219cabdff1aSopenharmony_ci}; 220