Coverage Report

Created: 2026-05-16 07:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavcodec/gif.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2000 Fabrice Bellard
3
 * Copyright (c) 2002 Francois Revol
4
 * Copyright (c) 2006 Baptiste Coudurier
5
 * Copyright (c) 2018 Bjorn Roche
6
 * Copyright (c) 2018 Paul B Mahol
7
 *
8
 * first version by Francois Revol <revol@free.fr>
9
 *
10
 * This file is part of FFmpeg.
11
 *
12
 * FFmpeg is free software; you can redistribute it and/or
13
 * modify it under the terms of the GNU Lesser General Public
14
 * License as published by the Free Software Foundation; either
15
 * version 2.1 of the License, or (at your option) any later version.
16
 *
17
 * FFmpeg is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20
 * Lesser General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Lesser General Public
23
 * License along with FFmpeg; if not, write to the Free Software
24
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25
 */
26
27
/**
28
 * @file
29
 * GIF encoder
30
 * @see http://www.w3.org/Graphics/GIF/spec-gif89a.txt
31
 */
32
33
#include "libavutil/attributes.h"
34
#include "libavutil/imgutils_internal.h"
35
#include "libavutil/mem.h"
36
#include "libavutil/opt.h"
37
#include "avcodec.h"
38
#include "bytestream.h"
39
#include "codec_internal.h"
40
#include "encode.h"
41
#include "lzw.h"
42
#include "gif.h"
43
44
1.95k
#define DEFAULT_TRANSPARENCY_INDEX 0x1f
45
46
typedef struct GIFContext {
47
    const AVClass *class;
48
    LZWState *lzw;
49
    uint8_t *buf;
50
    uint8_t *shrunk_buf;
51
    int buf_size;
52
    AVFrame *last_frame;
53
    int flags;
54
    int image;
55
    int use_global_palette;
56
    uint32_t palette[AVPALETTE_COUNT];  ///< local reference palette for !pal8
57
    int palette_loaded;
58
    int transparent_index;
59
    uint8_t *tmpl;                      ///< temporary line buffer
60
} GIFContext;
61
62
enum {
63
    GF_OFFSETTING = 1<<0,
64
    GF_TRANSDIFF  = 1<<1,
65
};
66
67
static void shrink_palette(const uint32_t *src, uint8_t *map,
68
                           uint32_t *dst, size_t *palette_count)
69
4.25k
{
70
4.25k
    size_t colors_seen = 0;
71
72
1.09M
    for (size_t i = 0; i < AVPALETTE_COUNT; i++) {
73
1.08M
        int seen = 0;
74
26.0M
        for (size_t c = 0; c < colors_seen; c++) {
75
25.8M
            if (src[i] == dst[c]) {
76
854k
                seen = 1;
77
854k
                break;
78
854k
            }
79
25.8M
        }
80
1.08M
        if (!seen) {
81
233k
            dst[colors_seen] = src[i];
82
233k
            map[i] = colors_seen;
83
233k
            colors_seen++;
84
233k
        }
85
1.08M
    }
86
87
4.25k
    *palette_count = colors_seen;
88
4.25k
}
89
90
static void remap_frame_to_palette(const uint8_t *src, int src_linesize,
91
                                   uint8_t *dst, int dst_linesize,
92
                                   int w, int h, uint8_t *map)
93
4.25k
{
94
31.4k
    for (int i = 0; i < h; i++)
95
504k
        for (int j = 0; j < w; j++)
96
477k
            dst[i * dst_linesize + j] = map[src[i * src_linesize + j]];
97
4.25k
}
98
99
static int is_image_translucent(AVCodecContext *avctx,
100
                                const uint8_t *buf, const int linesize)
101
16.4k
{
102
16.4k
    GIFContext *s = avctx->priv_data;
103
16.4k
    int trans = s->transparent_index;
104
105
16.4k
    if (trans < 0)
106
12.1k
        return 0;
107
108
15.9k
    for (int y = 0; y < avctx->height; y++) {
109
154k
        for (int x = 0; x < avctx->width; x++) {
110
143k
            if (buf[x] == trans) {
111
1.45k
                return 1;
112
1.45k
            }
113
143k
        }
114
11.6k
        buf += linesize;
115
11.6k
    }
116
117
2.81k
    return 0;
118
4.26k
}
119
120
static int get_palette_transparency_index(const uint32_t *palette)
121
5.38k
{
122
5.38k
    int transparent_color_index = -1;
123
5.38k
    unsigned i, smallest_alpha = 0xff;
124
125
5.38k
    if (!palette)
126
1.00k
        return -1;
127
128
1.12M
    for (i = 0; i < AVPALETTE_COUNT; i++) {
129
1.12M
        const uint32_t v = palette[i];
130
1.12M
        if (v >> 24 < smallest_alpha) {
131
8.46k
            smallest_alpha = v >> 24;
132
8.46k
            transparent_color_index = i;
133
8.46k
        }
134
1.12M
    }
135
4.37k
    return smallest_alpha < 128 ? transparent_color_index : -1;
136
5.38k
}
137
138
static int pick_palette_entry(const uint8_t *buf, int linesize, int w, int h)
139
10.9k
{
140
10.9k
    int histogram[AVPALETTE_COUNT] = {0};
141
10.9k
    int x, y, i;
142
143
287k
    for (y = 0; y < h; y++) {
144
5.73M
        for (x = 0; x < w; x++)
145
5.45M
            histogram[buf[x]]++;
146
276k
        buf += linesize;
147
276k
    }
148
99.0k
    for (i = 0; i < FF_ARRAY_ELEMS(histogram); i++)
149
98.8k
        if (!histogram[i])
150
10.7k
            return i;
151
265
    return -1;
152
10.9k
}
153
154
static void gif_crop_translucent(AVCodecContext *avctx,
155
                                 const uint8_t *buf, const int linesize,
156
                                 int *width, int *height,
157
                                 int *x_start, int *y_start)
158
1.45k
{
159
1.45k
    GIFContext *s = avctx->priv_data;
160
1.45k
    int trans = s->transparent_index;
161
162
    /* Crop image */
163
1.45k
    if ((s->flags & GF_OFFSETTING) && trans >= 0) {
164
1.45k
        const int w = avctx->width;
165
1.45k
        const int h = avctx->height;
166
1.45k
        int x_end = w - 1,
167
1.45k
            y_end = h - 1;
168
169
        // crop top
170
4.11k
        while (*y_start < y_end) {
171
3.58k
            int is_trans = 1;
172
39.8k
            for (int i = 0; i < w; i++) {
173
37.1k
                if (buf[linesize * *y_start + i] != trans) {
174
921
                    is_trans = 0;
175
921
                    break;
176
921
                }
177
37.1k
            }
178
179
3.58k
            if (!is_trans)
180
921
                break;
181
2.66k
            (*y_start)++;
182
2.66k
        }
183
184
        // crop bottom
185
1.38M
        while (y_end > *y_start) {
186
1.38M
            int is_trans = 1;
187
8.54M
            for (int i = 0; i < w; i++) {
188
7.16M
                if (buf[linesize * y_end + i] != trans) {
189
886
                    is_trans = 0;
190
886
                    break;
191
886
                }
192
7.16M
            }
193
1.38M
            if (!is_trans)
194
886
                break;
195
1.38M
            y_end--;
196
1.38M
        }
197
198
        // crop left
199
575k
        while (*x_start < x_end) {
200
574k
            int is_trans = 1;
201
584k
            for (int i = *y_start; i < y_end; i++) {
202
10.1k
                if (buf[linesize * i + *x_start] != trans) {
203
840
                    is_trans = 0;
204
840
                    break;
205
840
                }
206
10.1k
            }
207
574k
            if (!is_trans)
208
840
                break;
209
574k
            (*x_start)++;
210
574k
        }
211
212
        // crop right
213
3.29k
        while (x_end > *x_start) {
214
2.67k
            int is_trans = 1;
215
12.1k
            for (int i = *y_start; i < y_end; i++) {
216
10.2k
                if (buf[linesize * i + x_end] != trans) {
217
832
                    is_trans = 0;
218
832
                    break;
219
832
                }
220
10.2k
            }
221
2.67k
            if (!is_trans)
222
832
                break;
223
1.83k
            x_end--;
224
1.83k
        }
225
226
1.45k
        *height = y_end + 1 - *y_start;
227
1.45k
        *width  = x_end + 1 - *x_start;
228
1.45k
        av_log(avctx, AV_LOG_DEBUG,"%dx%d image at pos (%d;%d) [area:%dx%d]\n",
229
1.45k
               *width, *height, *x_start, *y_start, avctx->width, avctx->height);
230
1.45k
    }
231
1.45k
}
232
233
static void gif_crop_opaque(AVCodecContext *avctx,
234
                            const uint32_t *palette,
235
                            const uint8_t *buf, const int linesize,
236
                            int *width, int *height, int *x_start, int *y_start)
237
14.9k
{
238
14.9k
    GIFContext *s = avctx->priv_data;
239
240
    /* Crop image */
241
14.9k
    if ((s->flags & GF_OFFSETTING) && s->last_frame && !palette) {
242
11.1k
        const uint8_t *ref = s->last_frame->data[0];
243
11.1k
        const int ref_linesize = s->last_frame->linesize[0];
244
11.1k
        int x_end = avctx->width  - 1,
245
11.1k
            y_end = avctx->height - 1;
246
247
        /* skip common lines */
248
39.8k
        while (*y_start < y_end) {
249
36.3k
            if (memcmp(ref + *y_start*ref_linesize, buf + *y_start*linesize, *width))
250
7.64k
                break;
251
28.6k
            (*y_start)++;
252
28.6k
        }
253
31.2k
        while (y_end > *y_start) {
254
27.0k
            if (memcmp(ref + y_end*ref_linesize, buf + y_end*linesize, *width))
255
6.93k
                break;
256
20.0k
            y_end--;
257
20.0k
        }
258
11.1k
        *height = y_end + 1 - *y_start;
259
260
        /* skip common columns */
261
29.1k
        while (*x_start < x_end) {
262
25.6k
            int same_column = 1;
263
123k
            for (int y = *y_start; y <= y_end; y++) {
264
105k
                if (ref[y*ref_linesize + *x_start] != buf[y*linesize + *x_start]) {
265
7.63k
                    same_column = 0;
266
7.63k
                    break;
267
7.63k
                }
268
105k
            }
269
25.6k
            if (!same_column)
270
7.63k
                break;
271
18.0k
            (*x_start)++;
272
18.0k
        }
273
19.3k
        while (x_end > *x_start) {
274
15.4k
            int same_column = 1;
275
100k
            for (int y = *y_start; y <= y_end; y++) {
276
91.8k
                if (ref[y*ref_linesize + x_end] != buf[y*linesize + x_end]) {
277
7.34k
                    same_column = 0;
278
7.34k
                    break;
279
7.34k
                }
280
91.8k
            }
281
15.4k
            if (!same_column)
282
7.34k
                break;
283
8.13k
            x_end--;
284
8.13k
        }
285
11.1k
        *width = x_end + 1 - *x_start;
286
287
11.1k
        av_log(avctx, AV_LOG_DEBUG,"%dx%d image at pos (%d;%d) [area:%dx%d]\n",
288
11.1k
               *width, *height, *x_start, *y_start, avctx->width, avctx->height);
289
11.1k
    }
290
14.9k
}
291
292
static int gif_image_write_image(AVCodecContext *avctx,
293
                                 uint8_t **bytestream, uint8_t *end,
294
                                 const uint32_t *palette,
295
                                 const uint8_t *buf, const int linesize,
296
                                 AVPacket *pkt)
297
16.4k
{
298
16.4k
    GIFContext *s = avctx->priv_data;
299
16.4k
    int disposal, len = 0, height = avctx->height, width = avctx->width, x, y;
300
16.4k
    int x_start = 0, y_start = 0, trans = s->transparent_index;
301
16.4k
    int bcid = -1, honor_transparency = (s->flags & GF_TRANSDIFF) && s->last_frame && !palette;
302
16.4k
    const uint8_t *ptr;
303
16.4k
    uint32_t shrunk_palette[AVPALETTE_COUNT];
304
16.4k
    uint8_t map[AVPALETTE_COUNT] = { 0 };
305
16.4k
    size_t shrunk_palette_count = 0;
306
307
    /*
308
     * We memset to 0xff instead of 0x00 so that the transparency detection
309
     * doesn't pick anything after the palette entries as the transparency
310
     * index, and because GIF89a requires us to always write a power-of-2
311
     * number of palette entries.
312
     */
313
16.4k
    memset(shrunk_palette, 0xff, AVPALETTE_SIZE);
314
315
16.4k
    if (!s->image && is_image_translucent(avctx, buf, linesize)) {
316
1.45k
        gif_crop_translucent(avctx, buf, linesize, &width, &height, &x_start, &y_start);
317
1.45k
        honor_transparency = 0;
318
1.45k
        disposal = GCE_DISPOSAL_BACKGROUND;
319
14.9k
    } else {
320
14.9k
        gif_crop_opaque(avctx, palette, buf, linesize, &width, &height, &x_start, &y_start);
321
14.9k
        disposal = GCE_DISPOSAL_INPLACE;
322
14.9k
    }
323
324
16.4k
    if (s->image || !avctx->frame_num) { /* GIF header */
325
956
        const uint32_t *global_palette = palette ? palette : s->palette;
326
956
        const AVRational sar = avctx->sample_aspect_ratio;
327
956
        int64_t aspect = 0;
328
329
956
        if (sar.num > 0 && sar.den > 0) {
330
0
            aspect = sar.num * 64LL / sar.den - 15;
331
0
            if (aspect < 0 || aspect > 255)
332
0
                aspect = 0;
333
0
        }
334
335
956
        bytestream_put_buffer(bytestream, gif89a_sig, sizeof(gif89a_sig));
336
956
        bytestream_put_le16(bytestream, avctx->width);
337
956
        bytestream_put_le16(bytestream, avctx->height);
338
339
956
        bcid = get_palette_transparency_index(global_palette);
340
341
956
        bytestream_put_byte(bytestream, ((uint8_t) s->use_global_palette << 7) | 0x70 | (s->use_global_palette ? 7 : 0)); /* flags: global clut, 256 entries */
342
956
        bytestream_put_byte(bytestream, bcid < 0 ? DEFAULT_TRANSPARENCY_INDEX : bcid); /* background color index */
343
956
        bytestream_put_byte(bytestream, aspect);
344
956
        if (s->use_global_palette) {
345
245k
            for (int i = 0; i < 256; i++) {
346
244k
                const uint32_t v = global_palette[i] & 0xffffff;
347
244k
                bytestream_put_be24(bytestream, v);
348
244k
            }
349
956
        }
350
956
    }
351
352
16.4k
    if (honor_transparency && trans < 0) {
353
10.9k
        trans = pick_palette_entry(buf + y_start*linesize + x_start,
354
10.9k
                                   linesize, width, height);
355
10.9k
        if (trans < 0) // TODO, patch welcome
356
265
            av_log(avctx, AV_LOG_DEBUG, "No available color, can not use transparency\n");
357
10.9k
    }
358
359
16.4k
    if (trans < 0)
360
1.46k
        honor_transparency = 0;
361
362
16.4k
    if (palette || !s->use_global_palette) {
363
4.25k
        const uint32_t *pal = palette ? palette : s->palette;
364
4.25k
        shrink_palette(pal, map, shrunk_palette, &shrunk_palette_count);
365
4.25k
    }
366
367
16.4k
    bcid = honor_transparency || disposal == GCE_DISPOSAL_BACKGROUND ? trans : get_palette_transparency_index(palette);
368
369
    /* graphic control extension */
370
16.4k
    bytestream_put_byte(bytestream, GIF_EXTENSION_INTRODUCER);
371
16.4k
    bytestream_put_byte(bytestream, GIF_GCE_EXT_LABEL);
372
16.4k
    bytestream_put_byte(bytestream, 0x04); /* block size */
373
16.4k
    bytestream_put_byte(bytestream, disposal<<2 | (bcid >= 0));
374
16.4k
    bytestream_put_le16(bytestream, 5); // default delay
375
16.4k
    bytestream_put_byte(bytestream, bcid < 0 ? DEFAULT_TRANSPARENCY_INDEX : (shrunk_palette_count ? map[bcid] : bcid));
376
16.4k
    bytestream_put_byte(bytestream, 0x00);
377
378
    /* image block */
379
16.4k
    bytestream_put_byte(bytestream, GIF_IMAGE_SEPARATOR);
380
16.4k
    bytestream_put_le16(bytestream, x_start);
381
16.4k
    bytestream_put_le16(bytestream, y_start);
382
16.4k
    bytestream_put_le16(bytestream, width);
383
16.4k
    bytestream_put_le16(bytestream, height);
384
385
16.4k
    if (palette || !s->use_global_palette) {
386
4.25k
        unsigned pow2_count = av_log2(shrunk_palette_count - 1);
387
4.25k
        unsigned i;
388
389
4.25k
        bytestream_put_byte(bytestream, 1<<7 | pow2_count); /* flags */
390
274k
        for (i = 0; i < 1 << (pow2_count + 1); i++) {
391
270k
            const uint32_t v = shrunk_palette[i];
392
270k
            bytestream_put_be24(bytestream, v);
393
270k
        }
394
12.1k
    } else {
395
12.1k
        bytestream_put_byte(bytestream, 0x00); /* flags */
396
12.1k
    }
397
398
16.4k
    bytestream_put_byte(bytestream, 0x08);
399
400
16.4k
    ff_lzw_encode_init(s->lzw, s->buf, s->buf_size,
401
16.4k
                       12, FF_LZW_GIF, 1);
402
403
16.4k
    if (shrunk_palette_count) {
404
4.25k
        if (!s->shrunk_buf) {
405
210
            s->shrunk_buf = av_malloc(avctx->height * linesize);
406
210
            if (!s->shrunk_buf) {
407
0
                av_log(avctx, AV_LOG_ERROR, "Could not allocated remapped frame buffer.\n");
408
0
                return AVERROR(ENOMEM);
409
0
            }
410
210
        }
411
4.25k
        remap_frame_to_palette(buf, linesize, s->shrunk_buf, linesize, avctx->width, avctx->height, map);
412
4.25k
        ptr = s->shrunk_buf + y_start*linesize + x_start;
413
12.1k
    } else {
414
12.1k
        ptr = buf + y_start*linesize + x_start;
415
12.1k
    }
416
16.4k
    if (honor_transparency) {
417
10.9k
        const int ref_linesize = s->last_frame->linesize[0];
418
10.9k
        const uint8_t *ref = s->last_frame->data[0] + y_start*ref_linesize + x_start;
419
420
269k
        for (y = 0; y < height; y++) {
421
258k
            memcpy(s->tmpl, ptr, width);
422
4.28M
            for (x = 0; x < width; x++)
423
4.02M
                if (ref[x] == ptr[x])
424
1.55M
                    s->tmpl[x] = trans;
425
258k
            len += ff_lzw_encode(s->lzw, s->tmpl, width);
426
258k
            ptr += linesize;
427
258k
            ref += ref_linesize;
428
258k
        }
429
10.9k
    } else {
430
1.75M
        for (y = 0; y < height; y++) {
431
1.74M
            len += ff_lzw_encode(s->lzw, ptr, width);
432
1.74M
            ptr += linesize;
433
1.74M
        }
434
5.53k
    }
435
16.4k
    len += ff_lzw_encode_flush(s->lzw);
436
437
16.4k
    ptr = s->buf;
438
60.1k
    while (len > 0) {
439
43.6k
        int size = FFMIN(255, len);
440
43.6k
        bytestream_put_byte(bytestream, size);
441
43.6k
        if (end - *bytestream < size)
442
0
            return -1;
443
43.6k
        bytestream_put_buffer(bytestream, ptr, size);
444
43.6k
        ptr += size;
445
43.6k
        len -= size;
446
43.6k
    }
447
16.4k
    bytestream_put_byte(bytestream, 0x00); /* end of image block */
448
16.4k
    return 0;
449
16.4k
}
450
451
static av_cold int gif_encode_init(AVCodecContext *avctx)
452
956
{
453
956
    GIFContext *s = avctx->priv_data;
454
455
956
    if (avctx->width > 65535 || avctx->height > 65535) {
456
0
        av_log(avctx, AV_LOG_ERROR, "GIF does not support resolutions above 65535x65535\n");
457
0
        return AVERROR(EINVAL);
458
0
    }
459
460
956
    s->transparent_index = -1;
461
462
956
    s->lzw = av_mallocz(ff_lzw_encode_state_size);
463
956
    s->buf_size = avctx->width*avctx->height*2 + 1000;
464
956
    s->buf = av_malloc(s->buf_size);
465
956
    s->tmpl = av_malloc(avctx->width);
466
956
    if (!s->tmpl || !s->buf || !s->lzw)
467
0
        return AVERROR(ENOMEM);
468
469
956
    if (avpriv_set_systematic_pal2(s->palette, avctx->pix_fmt) < 0)
470
347
        av_assert0(avctx->pix_fmt == AV_PIX_FMT_PAL8);
471
472
956
    return 0;
473
956
}
474
475
static int gif_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
476
                            const AVFrame *pict, int *got_packet)
477
16.4k
{
478
16.4k
    GIFContext *s = avctx->priv_data;
479
16.4k
    uint8_t *outbuf_ptr, *end;
480
16.4k
    const uint32_t *palette = NULL;
481
16.4k
    int ret;
482
483
16.4k
    if ((ret = ff_alloc_packet(avctx, pkt, avctx->width*avctx->height*7/5 + FF_INPUT_BUFFER_MIN_SIZE)) < 0)
484
0
        return ret;
485
16.4k
    outbuf_ptr = pkt->data;
486
16.4k
    end        = pkt->data + pkt->size;
487
488
16.4k
    if (avctx->pix_fmt == AV_PIX_FMT_PAL8) {
489
4.85k
        palette = (uint32_t*)pict->data[1];
490
491
4.85k
        if (!s->palette_loaded) {
492
347
            memcpy(s->palette, palette, AVPALETTE_SIZE);
493
347
            s->transparent_index = get_palette_transparency_index(palette);
494
347
            s->palette_loaded = 1;
495
347
            if (s->use_global_palette)
496
347
                palette = NULL;
497
4.51k
        } else if (!memcmp(s->palette, palette, AVPALETTE_SIZE)) {
498
261
            palette = NULL;
499
261
        }
500
4.85k
    }
501
502
16.4k
    gif_image_write_image(avctx, &outbuf_ptr, end, palette,
503
16.4k
                          pict->data[0], pict->linesize[0], pkt);
504
16.4k
    if (!s->last_frame && !s->image) {
505
956
        s->last_frame = av_frame_alloc();
506
956
        if (!s->last_frame)
507
0
            return AVERROR(ENOMEM);
508
956
    }
509
510
16.4k
    if (!s->image) {
511
16.4k
        ret = av_frame_replace(s->last_frame, pict);
512
16.4k
        if (ret < 0)
513
0
            return ret;
514
16.4k
    }
515
516
16.4k
    pkt->size   = outbuf_ptr - pkt->data;
517
16.4k
    if (s->image || !avctx->frame_num)
518
956
        pkt->flags |= AV_PKT_FLAG_KEY;
519
16.4k
    *got_packet = 1;
520
521
16.4k
    return 0;
522
16.4k
}
523
524
static av_cold int gif_encode_close(AVCodecContext *avctx)
525
956
{
526
956
    GIFContext *s = avctx->priv_data;
527
528
956
    av_freep(&s->lzw);
529
956
    av_freep(&s->buf);
530
956
    av_freep(&s->shrunk_buf);
531
956
    s->buf_size = 0;
532
956
    av_frame_free(&s->last_frame);
533
956
    av_freep(&s->tmpl);
534
956
    return 0;
535
956
}
536
537
#define OFFSET(x) offsetof(GIFContext, x)
538
#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
539
static const AVOption gif_options[] = {
540
    { "gifflags", "set GIF flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = GF_OFFSETTING|GF_TRANSDIFF}, 0, INT_MAX, FLAGS, .unit = "flags" },
541
        { "offsetting", "enable picture offsetting", 0, AV_OPT_TYPE_CONST, {.i64=GF_OFFSETTING}, INT_MIN, INT_MAX, FLAGS, .unit = "flags" },
542
        { "transdiff", "enable transparency detection between frames", 0, AV_OPT_TYPE_CONST, {.i64=GF_TRANSDIFF}, INT_MIN, INT_MAX, FLAGS, .unit = "flags" },
543
    { "gifimage", "enable encoding only images per frame", OFFSET(image), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
544
    { "global_palette", "write a palette to the global gif header where feasible", OFFSET(use_global_palette), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS },
545
    { NULL }
546
};
547
548
static const AVClass gif_class = {
549
    .class_name = "GIF encoder",
550
    .item_name  = av_default_item_name,
551
    .option     = gif_options,
552
    .version    = LIBAVUTIL_VERSION_INT,
553
};
554
555
const FFCodec ff_gif_encoder = {
556
    .p.name         = "gif",
557
    CODEC_LONG_NAME("GIF (Graphics Interchange Format)"),
558
    .p.type         = AVMEDIA_TYPE_VIDEO,
559
    .p.id           = AV_CODEC_ID_GIF,
560
    .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
561
    .priv_data_size = sizeof(GIFContext),
562
    .init           = gif_encode_init,
563
    FF_CODEC_ENCODE_CB(gif_encode_frame),
564
    .close          = gif_encode_close,
565
    CODEC_PIXFMTS(AV_PIX_FMT_RGB8, AV_PIX_FMT_BGR8, AV_PIX_FMT_RGB4_BYTE,
566
                  AV_PIX_FMT_BGR4_BYTE, AV_PIX_FMT_GRAY8, AV_PIX_FMT_PAL8),
567
    .p.priv_class   = &gif_class,
568
    .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP,
569
};