FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/gif.c
Date: 2023-09-24 13:02:57
Exec Total Coverage
Lines: 218 289 75.4%
Functions: 10 11 90.9%
Branches: 131 218 60.1%

Line Branch Exec 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/opt.h"
34 #include "avcodec.h"
35 #include "bytestream.h"
36 #include "codec_internal.h"
37 #include "encode.h"
38 #include "lzw.h"
39 #include "gif.h"
40
41 #define DEFAULT_TRANSPARENCY_INDEX 0x1f
42
43 typedef struct GIFContext {
44 const AVClass *class;
45 LZWState *lzw;
46 uint8_t *buf;
47 uint8_t *shrunk_buf;
48 int buf_size;
49 AVFrame *last_frame;
50 int flags;
51 int image;
52 int use_global_palette;
53 uint32_t palette[AVPALETTE_COUNT]; ///< local reference palette for !pal8
54 int palette_loaded;
55 int transparent_index;
56 uint8_t *tmpl; ///< temporary line buffer
57 } GIFContext;
58
59 enum {
60 GF_OFFSETTING = 1<<0,
61 GF_TRANSDIFF = 1<<1,
62 };
63
64 1 static void shrink_palette(const uint32_t *src, uint8_t *map,
65 uint32_t *dst, size_t *palette_count)
66 {
67 1 size_t colors_seen = 0;
68
69
2/2
✓ Branch 0 taken 256 times.
✓ Branch 1 taken 1 times.
257 for (size_t i = 0; i < AVPALETTE_COUNT; i++) {
70 256 int seen = 0;
71
2/2
✓ Branch 0 taken 32640 times.
✓ Branch 1 taken 256 times.
32896 for (size_t c = 0; c < colors_seen; c++) {
72
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32640 times.
32640 if (src[i] == dst[c]) {
73 seen = 1;
74 break;
75 }
76 }
77
1/2
✓ Branch 0 taken 256 times.
✗ Branch 1 not taken.
256 if (!seen) {
78 256 dst[colors_seen] = src[i];
79 256 map[i] = colors_seen;
80 256 colors_seen++;
81 }
82 }
83
84 1 *palette_count = colors_seen;
85 1 }
86
87 1 static void remap_frame_to_palette(const uint8_t *src, int src_linesize,
88 uint8_t *dst, int dst_linesize,
89 int w, int h, uint8_t *map)
90 {
91
2/2
✓ Branch 0 taken 217 times.
✓ Branch 1 taken 1 times.
218 for (int i = 0; i < h; i++)
92
2/2
✓ Branch 0 taken 47089 times.
✓ Branch 1 taken 217 times.
47306 for (int j = 0; j < w; j++)
93 47089 dst[i * dst_linesize + j] = map[src[i * src_linesize + j]];
94 1 }
95
96 1063 static int is_image_translucent(AVCodecContext *avctx,
97 const uint8_t *buf, const int linesize)
98 {
99 1063 GIFContext *s = avctx->priv_data;
100 1063 int trans = s->transparent_index;
101
102
1/2
✓ Branch 0 taken 1063 times.
✗ Branch 1 not taken.
1063 if (trans < 0)
103 1063 return 0;
104
105 for (int y = 0; y < avctx->height; y++) {
106 for (int x = 0; x < avctx->width; x++) {
107 if (buf[x] == trans) {
108 return 1;
109 }
110 }
111 buf += linesize;
112 }
113
114 return 0;
115 }
116
117 39 static int get_palette_transparency_index(const uint32_t *palette)
118 {
119 39 int transparent_color_index = -1;
120 39 unsigned i, smallest_alpha = 0xff;
121
122
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 9 times.
39 if (!palette)
123 30 return -1;
124
125
2/2
✓ Branch 0 taken 2304 times.
✓ Branch 1 taken 9 times.
2313 for (i = 0; i < AVPALETTE_COUNT; i++) {
126 2304 const uint32_t v = palette[i];
127
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2302 times.
2304 if (v >> 24 < smallest_alpha) {
128 2 smallest_alpha = v >> 24;
129 2 transparent_color_index = i;
130 }
131 }
132
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 7 times.
9 return smallest_alpha < 128 ? transparent_color_index : -1;
133 }
134
135 1056 static int pick_palette_entry(const uint8_t *buf, int linesize, int w, int h)
136 {
137 1056 int histogram[AVPALETTE_COUNT] = {0};
138 int x, y, i;
139
140
2/2
✓ Branch 0 taken 32487 times.
✓ Branch 1 taken 1056 times.
33543 for (y = 0; y < h; y++) {
141
2/2
✓ Branch 0 taken 3157775 times.
✓ Branch 1 taken 32487 times.
3190262 for (x = 0; x < w; x++)
142 3157775 histogram[buf[x]]++;
143 32487 buf += linesize;
144 }
145
2/2
✓ Branch 0 taken 7288 times.
✓ Branch 1 taken 24 times.
7312 for (i = 0; i < FF_ARRAY_ELEMS(histogram); i++)
146
2/2
✓ Branch 0 taken 1032 times.
✓ Branch 1 taken 6256 times.
7288 if (!histogram[i])
147 1032 return i;
148 24 return -1;
149 }
150
151 static void gif_crop_translucent(AVCodecContext *avctx,
152 const uint8_t *buf, const int linesize,
153 int *width, int *height,
154 int *x_start, int *y_start)
155 {
156 GIFContext *s = avctx->priv_data;
157 int trans = s->transparent_index;
158
159 /* Crop image */
160 if ((s->flags & GF_OFFSETTING) && trans >= 0) {
161 const int w = avctx->width;
162 const int h = avctx->height;
163 int x_end = w - 1,
164 y_end = h - 1;
165
166 // crop top
167 while (*y_start < y_end) {
168 int is_trans = 1;
169 for (int i = 0; i < w; i++) {
170 if (buf[linesize * *y_start + i] != trans) {
171 is_trans = 0;
172 break;
173 }
174 }
175
176 if (!is_trans)
177 break;
178 (*y_start)++;
179 }
180
181 // crop bottom
182 while (y_end > *y_start) {
183 int is_trans = 1;
184 for (int i = 0; i < w; i++) {
185 if (buf[linesize * y_end + i] != trans) {
186 is_trans = 0;
187 break;
188 }
189 }
190 if (!is_trans)
191 break;
192 y_end--;
193 }
194
195 // crop left
196 while (*x_start < x_end) {
197 int is_trans = 1;
198 for (int i = *y_start; i < y_end; i++) {
199 if (buf[linesize * i + *x_start] != trans) {
200 is_trans = 0;
201 break;
202 }
203 }
204 if (!is_trans)
205 break;
206 (*x_start)++;
207 }
208
209 // crop right
210 while (x_end > *x_start) {
211 int is_trans = 1;
212 for (int i = *y_start; i < y_end; i++) {
213 if (buf[linesize * i + x_end] != trans) {
214 is_trans = 0;
215 break;
216 }
217 }
218 if (!is_trans)
219 break;
220 x_end--;
221 }
222
223 *height = y_end + 1 - *y_start;
224 *width = x_end + 1 - *x_start;
225 av_log(avctx, AV_LOG_DEBUG,"%dx%d image at pos (%d;%d) [area:%dx%d]\n",
226 *width, *height, *x_start, *y_start, avctx->width, avctx->height);
227 }
228 }
229
230 1063 static void gif_crop_opaque(AVCodecContext *avctx,
231 const uint32_t *palette,
232 const uint8_t *buf, const int linesize,
233 int *width, int *height, int *x_start, int *y_start)
234 {
235 1063 GIFContext *s = avctx->priv_data;
236
237 /* Crop image */
238
4/6
✓ Branch 0 taken 1063 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1056 times.
✓ Branch 3 taken 7 times.
✓ Branch 4 taken 1056 times.
✗ Branch 5 not taken.
1063 if ((s->flags & GF_OFFSETTING) && s->last_frame && !palette) {
239 1056 const uint8_t *ref = s->last_frame->data[0];
240 1056 const int ref_linesize = s->last_frame->linesize[0];
241 1056 int x_end = avctx->width - 1,
242 1056 y_end = avctx->height - 1;
243
244 /* skip common lines */
245
1/2
✓ Branch 0 taken 100704 times.
✗ Branch 1 not taken.
100704 while (*y_start < y_end) {
246
2/2
✓ Branch 0 taken 1056 times.
✓ Branch 1 taken 99648 times.
100704 if (memcmp(ref + *y_start*ref_linesize, buf + *y_start*linesize, *width))
247 1056 break;
248 99648 (*y_start)++;
249 }
250
1/2
✓ Branch 0 taken 99777 times.
✗ Branch 1 not taken.
99777 while (y_end > *y_start) {
251
2/2
✓ Branch 0 taken 1056 times.
✓ Branch 1 taken 98721 times.
99777 if (memcmp(ref + y_end*ref_linesize, buf + y_end*linesize, *width))
252 1056 break;
253 98721 y_end--;
254 }
255 1056 *height = y_end + 1 - *y_start;
256
257 /* skip common columns */
258
1/2
✓ Branch 0 taken 96923 times.
✗ Branch 1 not taken.
96923 while (*x_start < x_end) {
259 96923 int same_column = 1;
260
2/2
✓ Branch 0 taken 2402634 times.
✓ Branch 1 taken 95867 times.
2498501 for (int y = *y_start; y <= y_end; y++) {
261
2/2
✓ Branch 0 taken 1056 times.
✓ Branch 1 taken 2401578 times.
2402634 if (ref[y*ref_linesize + *x_start] != buf[y*linesize + *x_start]) {
262 1056 same_column = 0;
263 1056 break;
264 }
265 }
266
2/2
✓ Branch 0 taken 1056 times.
✓ Branch 1 taken 95867 times.
96923 if (!same_column)
267 1056 break;
268 95867 (*x_start)++;
269 }
270
1/2
✓ Branch 0 taken 104460 times.
✗ Branch 1 not taken.
104460 while (x_end > *x_start) {
271 104460 int same_column = 1;
272
2/2
✓ Branch 0 taken 2442709 times.
✓ Branch 1 taken 103404 times.
2546113 for (int y = *y_start; y <= y_end; y++) {
273
2/2
✓ Branch 0 taken 1056 times.
✓ Branch 1 taken 2441653 times.
2442709 if (ref[y*ref_linesize + x_end] != buf[y*linesize + x_end]) {
274 1056 same_column = 0;
275 1056 break;
276 }
277 }
278
2/2
✓ Branch 0 taken 1056 times.
✓ Branch 1 taken 103404 times.
104460 if (!same_column)
279 1056 break;
280 103404 x_end--;
281 }
282 1056 *width = x_end + 1 - *x_start;
283
284 1056 av_log(avctx, AV_LOG_DEBUG,"%dx%d image at pos (%d;%d) [area:%dx%d]\n",
285 *width, *height, *x_start, *y_start, avctx->width, avctx->height);
286 }
287 1063 }
288
289 1063 static int gif_image_write_image(AVCodecContext *avctx,
290 uint8_t **bytestream, uint8_t *end,
291 const uint32_t *palette,
292 const uint8_t *buf, const int linesize,
293 AVPacket *pkt)
294 {
295 1063 GIFContext *s = avctx->priv_data;
296 1063 int disposal, len = 0, height = avctx->height, width = avctx->width, x, y;
297 1063 int x_start = 0, y_start = 0, trans = s->transparent_index;
298
4/6
✓ Branch 0 taken 1063 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1056 times.
✓ Branch 3 taken 7 times.
✓ Branch 4 taken 1056 times.
✗ Branch 5 not taken.
1063 int bcid = -1, honor_transparency = (s->flags & GF_TRANSDIFF) && s->last_frame && !palette;
299 const uint8_t *ptr;
300 uint32_t shrunk_palette[AVPALETTE_COUNT];
301 1063 uint8_t map[AVPALETTE_COUNT] = { 0 };
302 1063 size_t shrunk_palette_count = 0;
303
304 /*
305 * We memset to 0xff instead of 0x00 so that the transparency detection
306 * doesn't pick anything after the palette entries as the transparency
307 * index, and because GIF89a requires us to always write a power-of-2
308 * number of palette entries.
309 */
310 1063 memset(shrunk_palette, 0xff, AVPALETTE_SIZE);
311
312
2/4
✓ Branch 0 taken 1063 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1063 times.
1063 if (!s->image && is_image_translucent(avctx, buf, linesize)) {
313 gif_crop_translucent(avctx, buf, linesize, &width, &height, &x_start, &y_start);
314 honor_transparency = 0;
315 disposal = GCE_DISPOSAL_BACKGROUND;
316 } else {
317 1063 gif_crop_opaque(avctx, palette, buf, linesize, &width, &height, &x_start, &y_start);
318 1063 disposal = GCE_DISPOSAL_INPLACE;
319 }
320
321
3/4
✓ Branch 0 taken 1063 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 1056 times.
1063 if (s->image || !avctx->frame_num) { /* GIF header */
322
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
7 const uint32_t *global_palette = palette ? palette : s->palette;
323 7 const AVRational sar = avctx->sample_aspect_ratio;
324 7 int64_t aspect = 0;
325
326
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
7 if (sar.num > 0 && sar.den > 0) {
327 aspect = sar.num * 64LL / sar.den - 15;
328 if (aspect < 0 || aspect > 255)
329 aspect = 0;
330 }
331
332 7 bytestream_put_buffer(bytestream, gif89a_sig, sizeof(gif89a_sig));
333 7 bytestream_put_le16(bytestream, avctx->width);
334 7 bytestream_put_le16(bytestream, avctx->height);
335
336 7 bcid = get_palette_transparency_index(global_palette);
337
338
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 bytestream_put_byte(bytestream, ((uint8_t) s->use_global_palette << 7) | 0x70 | (s->use_global_palette ? 7 : 0)); /* flags: global clut, 256 entries */
339
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
7 bytestream_put_byte(bytestream, bcid < 0 ? DEFAULT_TRANSPARENCY_INDEX : bcid); /* background color index */
340 7 bytestream_put_byte(bytestream, aspect);
341
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 if (s->use_global_palette) {
342
2/2
✓ Branch 0 taken 1792 times.
✓ Branch 1 taken 7 times.
1799 for (int i = 0; i < 256; i++) {
343 1792 const uint32_t v = global_palette[i] & 0xffffff;
344 1792 bytestream_put_be24(bytestream, v);
345 }
346 }
347 }
348
349
3/4
✓ Branch 0 taken 1056 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 1056 times.
✗ Branch 3 not taken.
1063 if (honor_transparency && trans < 0) {
350 1056 trans = pick_palette_entry(buf + y_start*linesize + x_start,
351 linesize, width, height);
352
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 1032 times.
1056 if (trans < 0) // TODO, patch welcome
353 24 av_log(avctx, AV_LOG_DEBUG, "No available color, can not use transparency\n");
354 }
355
356
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 1032 times.
1063 if (trans < 0)
357 31 honor_transparency = 0;
358
359
3/4
✓ Branch 0 taken 1062 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1062 times.
1063 if (palette || !s->use_global_palette) {
360
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 const uint32_t *pal = palette ? palette : s->palette;
361 1 shrink_palette(pal, map, shrunk_palette, &shrunk_palette_count);
362 }
363
364
3/4
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 1032 times.
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
1063 bcid = honor_transparency || disposal == GCE_DISPOSAL_BACKGROUND ? trans : get_palette_transparency_index(palette);
365
366 /* graphic control extension */
367 1063 bytestream_put_byte(bytestream, GIF_EXTENSION_INTRODUCER);
368 1063 bytestream_put_byte(bytestream, GIF_GCE_EXT_LABEL);
369 1063 bytestream_put_byte(bytestream, 0x04); /* block size */
370 1063 bytestream_put_byte(bytestream, disposal<<2 | (bcid >= 0));
371 1063 bytestream_put_le16(bytestream, 5); // default delay
372
3/4
✓ Branch 0 taken 1032 times.
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1032 times.
1063 bytestream_put_byte(bytestream, bcid < 0 ? DEFAULT_TRANSPARENCY_INDEX : (shrunk_palette_count ? map[bcid] : bcid));
373 1063 bytestream_put_byte(bytestream, 0x00);
374
375 /* image block */
376 1063 bytestream_put_byte(bytestream, GIF_IMAGE_SEPARATOR);
377 1063 bytestream_put_le16(bytestream, x_start);
378 1063 bytestream_put_le16(bytestream, y_start);
379 1063 bytestream_put_le16(bytestream, width);
380 1063 bytestream_put_le16(bytestream, height);
381
382
3/4
✓ Branch 0 taken 1062 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1062 times.
1064 if (palette || !s->use_global_palette) {
383 1 unsigned pow2_count = av_log2(shrunk_palette_count - 1);
384 unsigned i;
385
386 1 bytestream_put_byte(bytestream, 1<<7 | pow2_count); /* flags */
387
2/2
✓ Branch 0 taken 256 times.
✓ Branch 1 taken 1 times.
257 for (i = 0; i < 1 << (pow2_count + 1); i++) {
388 256 const uint32_t v = shrunk_palette[i];
389 256 bytestream_put_be24(bytestream, v);
390 }
391 } else {
392 1062 bytestream_put_byte(bytestream, 0x00); /* flags */
393 }
394
395 1063 bytestream_put_byte(bytestream, 0x08);
396
397 1063 ff_lzw_encode_init(s->lzw, s->buf, s->buf_size,
398 12, FF_LZW_GIF, 1);
399
400
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1062 times.
1063 if (shrunk_palette_count) {
401
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (!s->shrunk_buf) {
402 1 s->shrunk_buf = av_malloc(avctx->height * linesize);
403
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!s->shrunk_buf) {
404 av_log(avctx, AV_LOG_ERROR, "Could not allocated remapped frame buffer.\n");
405 return AVERROR(ENOMEM);
406 }
407 }
408 1 remap_frame_to_palette(buf, linesize, s->shrunk_buf, linesize, avctx->width, avctx->height, map);
409 1 ptr = s->shrunk_buf + y_start*linesize + x_start;
410 } else {
411 1062 ptr = buf + y_start*linesize + x_start;
412 }
413
2/2
✓ Branch 0 taken 1032 times.
✓ Branch 1 taken 31 times.
1063 if (honor_transparency) {
414 1032 const int ref_linesize = s->last_frame->linesize[0];
415 1032 const uint8_t *ref = s->last_frame->data[0] + y_start*ref_linesize + x_start;
416
417
2/2
✓ Branch 0 taken 25575 times.
✓ Branch 1 taken 1032 times.
26607 for (y = 0; y < height; y++) {
418 25575 memcpy(s->tmpl, ptr, width);
419
2/2
✓ Branch 0 taken 724751 times.
✓ Branch 1 taken 25575 times.
750326 for (x = 0; x < width; x++)
420
2/2
✓ Branch 0 taken 367271 times.
✓ Branch 1 taken 357480 times.
724751 if (ref[x] == ptr[x])
421 367271 s->tmpl[x] = trans;
422 25575 len += ff_lzw_encode(s->lzw, s->tmpl, width);
423 25575 ptr += linesize;
424 25575 ref += ref_linesize;
425 }
426 } else {
427
2/2
✓ Branch 0 taken 8502 times.
✓ Branch 1 taken 31 times.
8533 for (y = 0; y < height; y++) {
428 8502 len += ff_lzw_encode(s->lzw, ptr, width);
429 8502 ptr += linesize;
430 }
431 }
432 1063 len += ff_lzw_encode_flush(s->lzw);
433
434 1063 ptr = s->buf;
435
2/2
✓ Branch 0 taken 9251 times.
✓ Branch 1 taken 1063 times.
10314 while (len > 0) {
436 9251 int size = FFMIN(255, len);
437 9251 bytestream_put_byte(bytestream, size);
438
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9251 times.
9251 if (end - *bytestream < size)
439 return -1;
440 9251 bytestream_put_buffer(bytestream, ptr, size);
441 9251 ptr += size;
442 9251 len -= size;
443 }
444 1063 bytestream_put_byte(bytestream, 0x00); /* end of image block */
445 1063 return 0;
446 }
447
448 7 static av_cold int gif_encode_init(AVCodecContext *avctx)
449 {
450 7 GIFContext *s = avctx->priv_data;
451
452
2/4
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
7 if (avctx->width > 65535 || avctx->height > 65535) {
453 av_log(avctx, AV_LOG_ERROR, "GIF does not support resolutions above 65535x65535\n");
454 return AVERROR(EINVAL);
455 }
456
457 7 s->transparent_index = -1;
458
459 7 s->lzw = av_mallocz(ff_lzw_encode_state_size);
460 7 s->buf_size = avctx->width*avctx->height*2 + 1000;
461 7 s->buf = av_malloc(s->buf_size);
462 7 s->tmpl = av_malloc(avctx->width);
463
3/6
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 7 times.
7 if (!s->tmpl || !s->buf || !s->lzw)
464 return AVERROR(ENOMEM);
465
466
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 6 times.
7 if (avpriv_set_systematic_pal2(s->palette, avctx->pix_fmt) < 0)
467
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 av_assert0(avctx->pix_fmt == AV_PIX_FMT_PAL8);
468
469 7 return 0;
470 }
471
472 1063 static int gif_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
473 const AVFrame *pict, int *got_packet)
474 {
475 1063 GIFContext *s = avctx->priv_data;
476 uint8_t *outbuf_ptr, *end;
477 1063 const uint32_t *palette = NULL;
478 int ret;
479
480
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1063 times.
1063 if ((ret = ff_alloc_packet(avctx, pkt, avctx->width*avctx->height*7/5 + AV_INPUT_BUFFER_MIN_SIZE)) < 0)
481 return ret;
482 1063 outbuf_ptr = pkt->data;
483 1063 end = pkt->data + pkt->size;
484
485
2/2
✓ Branch 0 taken 173 times.
✓ Branch 1 taken 890 times.
1063 if (avctx->pix_fmt == AV_PIX_FMT_PAL8) {
486 173 palette = (uint32_t*)pict->data[1];
487
488
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 172 times.
173 if (!s->palette_loaded) {
489 1 memcpy(s->palette, palette, AVPALETTE_SIZE);
490 1 s->transparent_index = get_palette_transparency_index(palette);
491 1 s->palette_loaded = 1;
492
1/2
✓ Branch 0 taken 172 times.
✗ Branch 1 not taken.
172 } else if (!memcmp(s->palette, palette, AVPALETTE_SIZE)) {
493 172 palette = NULL;
494 }
495 }
496
497 1063 gif_image_write_image(avctx, &outbuf_ptr, end, palette,
498 1063 pict->data[0], pict->linesize[0], pkt);
499
3/4
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1056 times.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
1063 if (!s->last_frame && !s->image) {
500 7 s->last_frame = av_frame_alloc();
501
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (!s->last_frame)
502 return AVERROR(ENOMEM);
503 }
504
505
1/2
✓ Branch 0 taken 1063 times.
✗ Branch 1 not taken.
1063 if (!s->image) {
506 1063 ret = av_frame_replace(s->last_frame, pict);
507
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1063 times.
1063 if (ret < 0)
508 return ret;
509 }
510
511 1063 pkt->size = outbuf_ptr - pkt->data;
512
3/4
✓ Branch 0 taken 1063 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 1056 times.
1063 if (s->image || !avctx->frame_num)
513 7 pkt->flags |= AV_PKT_FLAG_KEY;
514 1063 *got_packet = 1;
515
516 1063 return 0;
517 }
518
519 7 static int gif_encode_close(AVCodecContext *avctx)
520 {
521 7 GIFContext *s = avctx->priv_data;
522
523 7 av_freep(&s->lzw);
524 7 av_freep(&s->buf);
525 7 av_freep(&s->shrunk_buf);
526 7 s->buf_size = 0;
527 7 av_frame_free(&s->last_frame);
528 7 av_freep(&s->tmpl);
529 7 return 0;
530 }
531
532 #define OFFSET(x) offsetof(GIFContext, x)
533 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
534 static const AVOption gif_options[] = {
535 { "gifflags", "set GIF flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = GF_OFFSETTING|GF_TRANSDIFF}, 0, INT_MAX, FLAGS, "flags" },
536 { "offsetting", "enable picture offsetting", 0, AV_OPT_TYPE_CONST, {.i64=GF_OFFSETTING}, INT_MIN, INT_MAX, FLAGS, "flags" },
537 { "transdiff", "enable transparency detection between frames", 0, AV_OPT_TYPE_CONST, {.i64=GF_TRANSDIFF}, INT_MIN, INT_MAX, FLAGS, "flags" },
538 { "gifimage", "enable encoding only images per frame", OFFSET(image), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
539 { "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 },
540 { NULL }
541 };
542
543 static const AVClass gif_class = {
544 .class_name = "GIF encoder",
545 .item_name = av_default_item_name,
546 .option = gif_options,
547 .version = LIBAVUTIL_VERSION_INT,
548 };
549
550 const FFCodec ff_gif_encoder = {
551 .p.name = "gif",
552 CODEC_LONG_NAME("GIF (Graphics Interchange Format)"),
553 .p.type = AVMEDIA_TYPE_VIDEO,
554 .p.id = AV_CODEC_ID_GIF,
555 .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
556 .priv_data_size = sizeof(GIFContext),
557 .init = gif_encode_init,
558 FF_CODEC_ENCODE_CB(gif_encode_frame),
559 .close = gif_encode_close,
560 .p.pix_fmts = (const enum AVPixelFormat[]){
561 AV_PIX_FMT_RGB8, AV_PIX_FMT_BGR8, AV_PIX_FMT_RGB4_BYTE, AV_PIX_FMT_BGR4_BYTE,
562 AV_PIX_FMT_GRAY8, AV_PIX_FMT_PAL8, AV_PIX_FMT_NONE
563 },
564 .p.priv_class = &gif_class,
565 .caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
566 };
567