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