Coverage Report

Created: 2025-11-16 07:20

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
2.76k
#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
3.40k
{
70
3.40k
    size_t colors_seen = 0;
71
72
875k
    for (size_t i = 0; i < AVPALETTE_COUNT; i++) {
73
872k
        int seen = 0;
74
26.1M
        for (size_t c = 0; c < colors_seen; c++) {
75
25.9M
            if (src[i] == dst[c]) {
76
638k
                seen = 1;
77
638k
                break;
78
638k
            }
79
25.9M
        }
80
872k
        if (!seen) {
81
234k
            dst[colors_seen] = src[i];
82
234k
            map[i] = colors_seen;
83
234k
            colors_seen++;
84
234k
        }
85
872k
    }
86
87
3.40k
    *palette_count = colors_seen;
88
3.40k
}
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
3.40k
{
94
1.19M
    for (int i = 0; i < h; i++)
95
9.19M
        for (int j = 0; j < w; j++)
96
8.00M
            dst[i * dst_linesize + j] = map[src[i * src_linesize + j]];
97
3.40k
}
98
99
static int is_image_translucent(AVCodecContext *avctx,
100
                                const uint8_t *buf, const int linesize)
101
14.3k
{
102
14.3k
    GIFContext *s = avctx->priv_data;
103
14.3k
    int trans = s->transparent_index;
104
105
14.3k
    if (trans < 0)
106
11.2k
        return 0;
107
108
11.9k
    for (int y = 0; y < avctx->height; y++) {
109
137k
        for (int x = 0; x < avctx->width; x++) {
110
129k
            if (buf[x] == trans) {
111
1.06k
                return 1;
112
1.06k
            }
113
129k
        }
114
8.84k
        buf += linesize;
115
8.84k
    }
116
117
2.04k
    return 0;
118
3.10k
}
119
120
static int get_palette_transparency_index(const uint32_t *palette)
121
5.57k
{
122
5.57k
    int transparent_color_index = -1;
123
5.57k
    unsigned i, smallest_alpha = 0xff;
124
125
5.57k
    if (!palette)
126
1.85k
        return -1;
127
128
954k
    for (i = 0; i < AVPALETTE_COUNT; i++) {
129
950k
        const uint32_t v = palette[i];
130
950k
        if (v >> 24 < smallest_alpha) {
131
7.77k
            smallest_alpha = v >> 24;
132
7.77k
            transparent_color_index = i;
133
7.77k
        }
134
950k
    }
135
3.71k
    return smallest_alpha < 128 ? transparent_color_index : -1;
136
5.57k
}
137
138
static int pick_palette_entry(const uint8_t *buf, int linesize, int w, int h)
139
9.96k
{
140
9.96k
    int histogram[AVPALETTE_COUNT] = {0};
141
9.96k
    int x, y, i;
142
143
234k
    for (y = 0; y < h; y++) {
144
9.50M
        for (x = 0; x < w; x++)
145
9.27M
            histogram[buf[x]]++;
146
224k
        buf += linesize;
147
224k
    }
148
358k
    for (i = 0; i < FF_ARRAY_ELEMS(histogram); i++)
149
357k
        if (!histogram[i])
150
8.70k
            return i;
151
1.26k
    return -1;
152
9.96k
}
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.06k
{
159
1.06k
    GIFContext *s = avctx->priv_data;
160
1.06k
    int trans = s->transparent_index;
161
162
    /* Crop image */
163
1.06k
    if ((s->flags & GF_OFFSETTING) && trans >= 0) {
164
1.06k
        const int w = avctx->width;
165
1.06k
        const int h = avctx->height;
166
1.06k
        int x_end = w - 1,
167
1.06k
            y_end = h - 1;
168
169
        // crop top
170
19.7k
        while (*y_start < y_end) {
171
19.3k
            int is_trans = 1;
172
53.6k
            for (int i = 0; i < w; i++) {
173
34.9k
                if (buf[linesize * *y_start + i] != trans) {
174
627
                    is_trans = 0;
175
627
                    break;
176
627
                }
177
34.9k
            }
178
179
19.3k
            if (!is_trans)
180
627
                break;
181
18.7k
            (*y_start)++;
182
18.7k
        }
183
184
        // crop bottom
185
1.13M
        while (y_end > *y_start) {
186
1.13M
            int is_trans = 1;
187
7.77M
            for (int i = 0; i < w; i++) {
188
6.63M
                if (buf[linesize * y_end + i] != trans) {
189
597
                    is_trans = 0;
190
597
                    break;
191
597
                }
192
6.63M
            }
193
1.13M
            if (!is_trans)
194
597
                break;
195
1.13M
            y_end--;
196
1.13M
        }
197
198
        // crop left
199
580k
        while (*x_start < x_end) {
200
579k
            int is_trans = 1;
201
582k
            for (int i = *y_start; i < y_end; i++) {
202
2.71k
                if (buf[linesize * i + *x_start] != trans) {
203
567
                    is_trans = 0;
204
567
                    break;
205
567
                }
206
2.71k
            }
207
579k
            if (!is_trans)
208
567
                break;
209
579k
            (*x_start)++;
210
579k
        }
211
212
        // crop right
213
2.56k
        while (x_end > *x_start) {
214
2.06k
            int is_trans = 1;
215
5.41k
            for (int i = *y_start; i < y_end; i++) {
216
3.91k
                if (buf[linesize * i + x_end] != trans) {
217
562
                    is_trans = 0;
218
562
                    break;
219
562
                }
220
3.91k
            }
221
2.06k
            if (!is_trans)
222
562
                break;
223
1.50k
            x_end--;
224
1.50k
        }
225
226
1.06k
        *height = y_end + 1 - *y_start;
227
1.06k
        *width  = x_end + 1 - *x_start;
228
1.06k
        av_log(avctx, AV_LOG_DEBUG,"%dx%d image at pos (%d;%d) [area:%dx%d]\n",
229
1.06k
               *width, *height, *x_start, *y_start, avctx->width, avctx->height);
230
1.06k
    }
231
1.06k
}
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
13.2k
{
238
13.2k
    GIFContext *s = avctx->priv_data;
239
240
    /* Crop image */
241
13.2k
    if ((s->flags & GF_OFFSETTING) && s->last_frame && !palette) {
242
10.1k
        const uint8_t *ref = s->last_frame->data[0];
243
10.1k
        const int ref_linesize = s->last_frame->linesize[0];
244
10.1k
        int x_end = avctx->width  - 1,
245
10.1k
            y_end = avctx->height - 1;
246
247
        /* skip common lines */
248
30.2k
        while (*y_start < y_end) {
249
28.1k
            if (memcmp(ref + *y_start*ref_linesize, buf + *y_start*linesize, *width))
250
8.10k
                break;
251
20.0k
            (*y_start)++;
252
20.0k
        }
253
21.4k
        while (y_end > *y_start) {
254
18.8k
            if (memcmp(ref + y_end*ref_linesize, buf + y_end*linesize, *width))
255
7.57k
                break;
256
11.2k
            y_end--;
257
11.2k
        }
258
10.1k
        *height = y_end + 1 - *y_start;
259
260
        /* skip common columns */
261
18.3k
        while (*x_start < x_end) {
262
15.6k
            int same_column = 1;
263
38.7k
            for (int y = *y_start; y <= y_end; y++) {
264
30.6k
                if (ref[y*ref_linesize + *x_start] != buf[y*linesize + *x_start]) {
265
7.52k
                    same_column = 0;
266
7.52k
                    break;
267
7.52k
                }
268
30.6k
            }
269
15.6k
            if (!same_column)
270
7.52k
                break;
271
8.14k
            (*x_start)++;
272
8.14k
        }
273
13.2k
        while (x_end > *x_start) {
274
10.4k
            int same_column = 1;
275
27.4k
            for (int y = *y_start; y <= y_end; y++) {
276
24.3k
                if (ref[y*ref_linesize + x_end] != buf[y*linesize + x_end]) {
277
7.36k
                    same_column = 0;
278
7.36k
                    break;
279
7.36k
                }
280
24.3k
            }
281
10.4k
            if (!same_column)
282
7.36k
                break;
283
3.04k
            x_end--;
284
3.04k
        }
285
10.1k
        *width = x_end + 1 - *x_start;
286
287
10.1k
        av_log(avctx, AV_LOG_DEBUG,"%dx%d image at pos (%d;%d) [area:%dx%d]\n",
288
10.1k
               *width, *height, *x_start, *y_start, avctx->width, avctx->height);
289
10.1k
    }
290
13.2k
}
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
14.3k
{
298
14.3k
    GIFContext *s = avctx->priv_data;
299
14.3k
    int disposal, len = 0, height = avctx->height, width = avctx->width, x, y;
300
14.3k
    int x_start = 0, y_start = 0, trans = s->transparent_index;
301
14.3k
    int bcid = -1, honor_transparency = (s->flags & GF_TRANSDIFF) && s->last_frame && !palette;
302
14.3k
    const uint8_t *ptr;
303
14.3k
    uint32_t shrunk_palette[AVPALETTE_COUNT];
304
14.3k
    uint8_t map[AVPALETTE_COUNT] = { 0 };
305
14.3k
    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
14.3k
    memset(shrunk_palette, 0xff, AVPALETTE_SIZE);
314
315
14.3k
    if (!s->image && is_image_translucent(avctx, buf, linesize)) {
316
1.06k
        gif_crop_translucent(avctx, buf, linesize, &width, &height, &x_start, &y_start);
317
1.06k
        honor_transparency = 0;
318
1.06k
        disposal = GCE_DISPOSAL_BACKGROUND;
319
13.2k
    } else {
320
13.2k
        gif_crop_opaque(avctx, palette, buf, linesize, &width, &height, &x_start, &y_start);
321
13.2k
        disposal = GCE_DISPOSAL_INPLACE;
322
13.2k
    }
323
324
14.3k
    if (s->image || !avctx->frame_num) { /* GIF header */
325
909
        const uint32_t *global_palette = palette ? palette : s->palette;
326
909
        const AVRational sar = avctx->sample_aspect_ratio;
327
909
        int64_t aspect = 0;
328
329
909
        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
909
        bytestream_put_buffer(bytestream, gif89a_sig, sizeof(gif89a_sig));
336
909
        bytestream_put_le16(bytestream, avctx->width);
337
909
        bytestream_put_le16(bytestream, avctx->height);
338
339
909
        bcid = get_palette_transparency_index(global_palette);
340
341
909
        bytestream_put_byte(bytestream, ((uint8_t) s->use_global_palette << 7) | 0x70 | (s->use_global_palette ? 7 : 0)); /* flags: global clut, 256 entries */
342
909
        bytestream_put_byte(bytestream, bcid < 0 ? DEFAULT_TRANSPARENCY_INDEX : bcid); /* background color index */
343
909
        bytestream_put_byte(bytestream, aspect);
344
909
        if (s->use_global_palette) {
345
233k
            for (int i = 0; i < 256; i++) {
346
232k
                const uint32_t v = global_palette[i] & 0xffffff;
347
232k
                bytestream_put_be24(bytestream, v);
348
232k
            }
349
909
        }
350
909
    }
351
352
14.3k
    if (honor_transparency && trans < 0) {
353
9.96k
        trans = pick_palette_entry(buf + y_start*linesize + x_start,
354
9.96k
                                   linesize, width, height);
355
9.96k
        if (trans < 0) // TODO, patch welcome
356
1.26k
            av_log(avctx, AV_LOG_DEBUG, "No available color, can not use transparency\n");
357
9.96k
    }
358
359
14.3k
    if (trans < 0)
360
2.50k
        honor_transparency = 0;
361
362
14.3k
    if (palette || !s->use_global_palette) {
363
3.40k
        const uint32_t *pal = palette ? palette : s->palette;
364
3.40k
        shrink_palette(pal, map, shrunk_palette, &shrunk_palette_count);
365
3.40k
    }
366
367
14.3k
    bcid = honor_transparency || disposal == GCE_DISPOSAL_BACKGROUND ? trans : get_palette_transparency_index(palette);
368
369
    /* graphic control extension */
370
14.3k
    bytestream_put_byte(bytestream, GIF_EXTENSION_INTRODUCER);
371
14.3k
    bytestream_put_byte(bytestream, GIF_GCE_EXT_LABEL);
372
14.3k
    bytestream_put_byte(bytestream, 0x04); /* block size */
373
14.3k
    bytestream_put_byte(bytestream, disposal<<2 | (bcid >= 0));
374
14.3k
    bytestream_put_le16(bytestream, 5); // default delay
375
14.3k
    bytestream_put_byte(bytestream, bcid < 0 ? DEFAULT_TRANSPARENCY_INDEX : (shrunk_palette_count ? map[bcid] : bcid));
376
14.3k
    bytestream_put_byte(bytestream, 0x00);
377
378
    /* image block */
379
14.3k
    bytestream_put_byte(bytestream, GIF_IMAGE_SEPARATOR);
380
14.3k
    bytestream_put_le16(bytestream, x_start);
381
14.3k
    bytestream_put_le16(bytestream, y_start);
382
14.3k
    bytestream_put_le16(bytestream, width);
383
14.3k
    bytestream_put_le16(bytestream, height);
384
385
14.3k
    if (palette || !s->use_global_palette) {
386
3.40k
        unsigned pow2_count = av_log2(shrunk_palette_count - 1);
387
3.40k
        unsigned i;
388
389
3.40k
        bytestream_put_byte(bytestream, 1<<7 | pow2_count); /* flags */
390
277k
        for (i = 0; i < 1 << (pow2_count + 1); i++) {
391
274k
            const uint32_t v = shrunk_palette[i];
392
274k
            bytestream_put_be24(bytestream, v);
393
274k
        }
394
10.9k
    } else {
395
10.9k
        bytestream_put_byte(bytestream, 0x00); /* flags */
396
10.9k
    }
397
398
14.3k
    bytestream_put_byte(bytestream, 0x08);
399
400
14.3k
    ff_lzw_encode_init(s->lzw, s->buf, s->buf_size,
401
14.3k
                       12, FF_LZW_GIF, 1);
402
403
14.3k
    if (shrunk_palette_count) {
404
3.40k
        if (!s->shrunk_buf) {
405
317
            s->shrunk_buf = av_malloc(avctx->height * linesize);
406
317
            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
317
        }
411
3.40k
        remap_frame_to_palette(buf, linesize, s->shrunk_buf, linesize, avctx->width, avctx->height, map);
412
3.40k
        ptr = s->shrunk_buf + y_start*linesize + x_start;
413
10.9k
    } else {
414
10.9k
        ptr = buf + y_start*linesize + x_start;
415
10.9k
    }
416
14.3k
    if (honor_transparency) {
417
8.90k
        const int ref_linesize = s->last_frame->linesize[0];
418
8.90k
        const uint8_t *ref = s->last_frame->data[0] + y_start*ref_linesize + x_start;
419
420
161k
        for (y = 0; y < height; y++) {
421
152k
            memcpy(s->tmpl, ptr, width);
422
3.72M
            for (x = 0; x < width; x++)
423
3.57M
                if (ref[x] == ptr[x])
424
1.00M
                    s->tmpl[x] = trans;
425
152k
            len += ff_lzw_encode(s->lzw, s->tmpl, width);
426
152k
            ptr += linesize;
427
152k
            ref += ref_linesize;
428
152k
        }
429
8.90k
    } else {
430
1.31M
        for (y = 0; y < height; y++) {
431
1.31M
            len += ff_lzw_encode(s->lzw, ptr, width);
432
1.31M
            ptr += linesize;
433
1.31M
        }
434
5.40k
    }
435
14.3k
    len += ff_lzw_encode_flush(s->lzw);
436
437
14.3k
    ptr = s->buf;
438
76.0k
    while (len > 0) {
439
61.7k
        int size = FFMIN(255, len);
440
61.7k
        bytestream_put_byte(bytestream, size);
441
61.7k
        if (end - *bytestream < size)
442
0
            return -1;
443
61.7k
        bytestream_put_buffer(bytestream, ptr, size);
444
61.7k
        ptr += size;
445
61.7k
        len -= size;
446
61.7k
    }
447
14.3k
    bytestream_put_byte(bytestream, 0x00); /* end of image block */
448
14.3k
    return 0;
449
14.3k
}
450
451
static av_cold int gif_encode_init(AVCodecContext *avctx)
452
909
{
453
909
    GIFContext *s = avctx->priv_data;
454
455
909
    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
909
    s->transparent_index = -1;
461
462
909
    s->lzw = av_mallocz(ff_lzw_encode_state_size);
463
909
    s->buf_size = avctx->width*avctx->height*2 + 1000;
464
909
    s->buf = av_malloc(s->buf_size);
465
909
    s->tmpl = av_malloc(avctx->width);
466
909
    if (!s->tmpl || !s->buf || !s->lzw)
467
0
        return AVERROR(ENOMEM);
468
469
909
    if (avpriv_set_systematic_pal2(s->palette, avctx->pix_fmt) < 0)
470
317
        av_assert0(avctx->pix_fmt == AV_PIX_FMT_PAL8);
471
472
909
    return 0;
473
909
}
474
475
static int gif_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
476
                            const AVFrame *pict, int *got_packet)
477
14.3k
{
478
14.3k
    GIFContext *s = avctx->priv_data;
479
14.3k
    uint8_t *outbuf_ptr, *end;
480
14.3k
    const uint32_t *palette = NULL;
481
14.3k
    int ret;
482
483
14.3k
    if ((ret = ff_alloc_packet(avctx, pkt, avctx->width*avctx->height*7/5 + FF_INPUT_BUFFER_MIN_SIZE)) < 0)
484
0
        return ret;
485
14.3k
    outbuf_ptr = pkt->data;
486
14.3k
    end        = pkt->data + pkt->size;
487
488
14.3k
    if (avctx->pix_fmt == AV_PIX_FMT_PAL8) {
489
3.75k
        palette = (uint32_t*)pict->data[1];
490
491
3.75k
        if (!s->palette_loaded) {
492
317
            memcpy(s->palette, palette, AVPALETTE_SIZE);
493
317
            s->transparent_index = get_palette_transparency_index(palette);
494
317
            s->palette_loaded = 1;
495
3.43k
        } else if (!memcmp(s->palette, palette, AVPALETTE_SIZE)) {
496
346
            palette = NULL;
497
346
        }
498
3.75k
    }
499
500
14.3k
    gif_image_write_image(avctx, &outbuf_ptr, end, palette,
501
14.3k
                          pict->data[0], pict->linesize[0], pkt);
502
14.3k
    if (!s->last_frame && !s->image) {
503
909
        s->last_frame = av_frame_alloc();
504
909
        if (!s->last_frame)
505
0
            return AVERROR(ENOMEM);
506
909
    }
507
508
14.3k
    if (!s->image) {
509
14.3k
        ret = av_frame_replace(s->last_frame, pict);
510
14.3k
        if (ret < 0)
511
0
            return ret;
512
14.3k
    }
513
514
14.3k
    pkt->size   = outbuf_ptr - pkt->data;
515
14.3k
    if (s->image || !avctx->frame_num)
516
909
        pkt->flags |= AV_PKT_FLAG_KEY;
517
14.3k
    *got_packet = 1;
518
519
14.3k
    return 0;
520
14.3k
}
521
522
static av_cold int gif_encode_close(AVCodecContext *avctx)
523
909
{
524
909
    GIFContext *s = avctx->priv_data;
525
526
909
    av_freep(&s->lzw);
527
909
    av_freep(&s->buf);
528
909
    av_freep(&s->shrunk_buf);
529
909
    s->buf_size = 0;
530
909
    av_frame_free(&s->last_frame);
531
909
    av_freep(&s->tmpl);
532
909
    return 0;
533
909
}
534
535
#define OFFSET(x) offsetof(GIFContext, x)
536
#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
537
static const AVOption gif_options[] = {
538
    { "gifflags", "set GIF flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = GF_OFFSETTING|GF_TRANSDIFF}, 0, INT_MAX, FLAGS, .unit = "flags" },
539
        { "offsetting", "enable picture offsetting", 0, AV_OPT_TYPE_CONST, {.i64=GF_OFFSETTING}, INT_MIN, INT_MAX, FLAGS, .unit = "flags" },
540
        { "transdiff", "enable transparency detection between frames", 0, AV_OPT_TYPE_CONST, {.i64=GF_TRANSDIFF}, INT_MIN, INT_MAX, FLAGS, .unit = "flags" },
541
    { "gifimage", "enable encoding only images per frame", OFFSET(image), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
542
    { "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 },
543
    { NULL }
544
};
545
546
static const AVClass gif_class = {
547
    .class_name = "GIF encoder",
548
    .item_name  = av_default_item_name,
549
    .option     = gif_options,
550
    .version    = LIBAVUTIL_VERSION_INT,
551
};
552
553
const FFCodec ff_gif_encoder = {
554
    .p.name         = "gif",
555
    CODEC_LONG_NAME("GIF (Graphics Interchange Format)"),
556
    .p.type         = AVMEDIA_TYPE_VIDEO,
557
    .p.id           = AV_CODEC_ID_GIF,
558
    .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
559
    .priv_data_size = sizeof(GIFContext),
560
    .init           = gif_encode_init,
561
    FF_CODEC_ENCODE_CB(gif_encode_frame),
562
    .close          = gif_encode_close,
563
    CODEC_PIXFMTS(AV_PIX_FMT_RGB8, AV_PIX_FMT_BGR8, AV_PIX_FMT_RGB4_BYTE,
564
                  AV_PIX_FMT_BGR4_BYTE, AV_PIX_FMT_GRAY8, AV_PIX_FMT_PAL8),
565
    .p.priv_class   = &gif_class,
566
    .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP,
567
};