Coverage Report

Created: 2026-05-16 07:49

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