xref: /third_party/ffmpeg/libavcodec/sgienc.c (revision cabdff1a)
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