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