/src/ffmpeg/libavcodec/pngenc.c
Line | Count | Source |
1 | | /* |
2 | | * PNG image format |
3 | | * Copyright (c) 2003 Fabrice Bellard |
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 "avcodec.h" |
23 | | #include "codec_internal.h" |
24 | | #include "encode.h" |
25 | | #include "exif_internal.h" |
26 | | #include "bytestream.h" |
27 | | #include "lossless_videoencdsp.h" |
28 | | #include "png.h" |
29 | | #include "apng.h" |
30 | | #include "zlib_wrapper.h" |
31 | | |
32 | | #include "libavutil/avassert.h" |
33 | | #include "libavutil/buffer.h" |
34 | | #include "libavutil/crc.h" |
35 | | #include "libavutil/csp.h" |
36 | | #include "libavutil/libm.h" |
37 | | #include "libavutil/mastering_display_metadata.h" |
38 | | #include "libavutil/mem.h" |
39 | | #include "libavutil/opt.h" |
40 | | #include "libavutil/pixdesc.h" |
41 | | #include "libavutil/rational.h" |
42 | | #include "libavutil/stereo3d.h" |
43 | | #include <zlib.h> |
44 | | |
45 | 337k | #define IOBUF_SIZE 4096 |
46 | | |
47 | | typedef struct APNGFctlChunk { |
48 | | uint32_t sequence_number; |
49 | | uint32_t width, height; |
50 | | uint32_t x_offset, y_offset; |
51 | | uint16_t delay_num, delay_den; |
52 | | uint8_t dispose_op, blend_op; |
53 | | } APNGFctlChunk; |
54 | | |
55 | | typedef struct PNGEncContext { |
56 | | AVClass *class; |
57 | | LLVidEncDSPContext llvidencdsp; |
58 | | |
59 | | uint8_t *bytestream; |
60 | | uint8_t *bytestream_start; |
61 | | uint8_t *bytestream_end; |
62 | | |
63 | | int filter_type; |
64 | | |
65 | | FFZStream zstream; |
66 | | uint8_t buf[IOBUF_SIZE]; |
67 | | int dpi; ///< Physical pixel density, in dots per inch, if set |
68 | | int dpm; ///< Physical pixel density, in dots per meter, if set |
69 | | |
70 | | int is_progressive; |
71 | | int bit_depth; |
72 | | int color_type; |
73 | | int bits_per_pixel; |
74 | | |
75 | | // APNG |
76 | | uint32_t palette_checksum; // Used to ensure a single unique palette |
77 | | uint32_t sequence_number; |
78 | | int extra_data_updated; |
79 | | uint8_t *extra_data; |
80 | | int extra_data_size; |
81 | | |
82 | | AVFrame *prev_frame; |
83 | | AVFrame *last_frame; |
84 | | APNGFctlChunk last_frame_fctl; |
85 | | uint8_t *last_frame_packet; |
86 | | size_t last_frame_packet_size; |
87 | | } PNGEncContext; |
88 | | |
89 | | static void png_get_interlaced_row(uint8_t *dst, int row_size, |
90 | | int bits_per_pixel, int pass, |
91 | | const uint8_t *src, int width) |
92 | 0 | { |
93 | 0 | int x, mask, dst_x, j, b, bpp; |
94 | 0 | uint8_t *d; |
95 | 0 | const uint8_t *s; |
96 | 0 | static const int masks[] = {0x80, 0x08, 0x88, 0x22, 0xaa, 0x55, 0xff}; |
97 | |
|
98 | 0 | mask = masks[pass]; |
99 | 0 | switch (bits_per_pixel) { |
100 | 0 | case 1: |
101 | 0 | memset(dst, 0, row_size); |
102 | 0 | dst_x = 0; |
103 | 0 | for (x = 0; x < width; x++) { |
104 | 0 | j = (x & 7); |
105 | 0 | if ((mask << j) & 0x80) { |
106 | 0 | b = (src[x >> 3] >> (7 - j)) & 1; |
107 | 0 | dst[dst_x >> 3] |= b << (7 - (dst_x & 7)); |
108 | 0 | dst_x++; |
109 | 0 | } |
110 | 0 | } |
111 | 0 | break; |
112 | 0 | default: |
113 | 0 | bpp = bits_per_pixel >> 3; |
114 | 0 | d = dst; |
115 | 0 | s = src; |
116 | 0 | for (x = 0; x < width; x++) { |
117 | 0 | j = x & 7; |
118 | 0 | if ((mask << j) & 0x80) { |
119 | 0 | memcpy(d, s, bpp); |
120 | 0 | d += bpp; |
121 | 0 | } |
122 | 0 | s += bpp; |
123 | 0 | } |
124 | 0 | break; |
125 | 0 | } |
126 | 0 | } |
127 | | |
128 | | static void sub_png_paeth_prediction(uint8_t *dst, const uint8_t *src, const uint8_t *top, |
129 | | int w, int bpp) |
130 | 6.90M | { |
131 | 6.90M | int i; |
132 | 990M | for (i = 0; i < w; i++) { |
133 | 983M | int a, b, c, p, pa, pb, pc; |
134 | | |
135 | 983M | a = src[i - bpp]; |
136 | 983M | b = top[i]; |
137 | 983M | c = top[i - bpp]; |
138 | | |
139 | 983M | p = b - c; |
140 | 983M | pc = a - c; |
141 | | |
142 | 983M | pa = abs(p); |
143 | 983M | pb = abs(pc); |
144 | 983M | pc = abs(p + pc); |
145 | | |
146 | 983M | if (pa <= pb && pa <= pc) |
147 | 949M | p = a; |
148 | 34.4M | else if (pb <= pc) |
149 | 26.3M | p = b; |
150 | 8.12M | else |
151 | 8.12M | p = c; |
152 | 983M | dst[i] = src[i] - p; |
153 | 983M | } |
154 | 6.90M | } |
155 | | |
156 | | static void sub_left_prediction(PNGEncContext *c, uint8_t *dst, const uint8_t *src, int bpp, int size) |
157 | 66.7k | { |
158 | 66.7k | const uint8_t *src1 = src + bpp; |
159 | 66.7k | const uint8_t *src2 = src; |
160 | 66.7k | int x, unaligned_w; |
161 | | |
162 | 66.7k | memcpy(dst, src, bpp); |
163 | 66.7k | dst += bpp; |
164 | 66.7k | size -= bpp; |
165 | 66.7k | unaligned_w = FFMIN(32 - bpp, size); |
166 | 835k | for (x = 0; x < unaligned_w; x++) |
167 | 768k | *dst++ = *src1++ - *src2++; |
168 | 66.7k | size -= unaligned_w; |
169 | 66.7k | c->llvidencdsp.diff_bytes(dst, src1, src2, size); |
170 | 66.7k | } |
171 | | |
172 | | static void png_filter_row(PNGEncContext *c, uint8_t *dst, int filter_type, |
173 | | const uint8_t *src, const uint8_t *top, int size, int bpp) |
174 | 7.35M | { |
175 | 7.35M | int i; |
176 | | |
177 | 7.35M | switch (filter_type) { |
178 | 386k | case PNG_FILTER_VALUE_NONE: |
179 | 386k | memcpy(dst, src, size); |
180 | 386k | break; |
181 | 66.7k | case PNG_FILTER_VALUE_SUB: |
182 | 66.7k | sub_left_prediction(c, dst, src, bpp, size); |
183 | 66.7k | break; |
184 | 0 | case PNG_FILTER_VALUE_UP: |
185 | 0 | c->llvidencdsp.diff_bytes(dst, src, top, size); |
186 | 0 | break; |
187 | 0 | case PNG_FILTER_VALUE_AVG: |
188 | 0 | for (i = 0; i < bpp; i++) |
189 | 0 | dst[i] = src[i] - (top[i] >> 1); |
190 | 0 | for (; i < size; i++) |
191 | 0 | dst[i] = src[i] - ((src[i - bpp] + top[i]) >> 1); |
192 | 0 | break; |
193 | 6.90M | case PNG_FILTER_VALUE_PAETH: |
194 | 41.4M | for (i = 0; i < bpp; i++) |
195 | 34.5M | dst[i] = src[i] - top[i]; |
196 | 6.90M | sub_png_paeth_prediction(dst + i, src + i, top + i, size - i, bpp); |
197 | 6.90M | break; |
198 | 0 | default: |
199 | 0 | av_unreachable("PNG_FILTER_VALUE_MIXED can't happen here and all others are covered"); |
200 | 7.35M | } |
201 | 7.35M | } |
202 | | |
203 | | static uint8_t *png_choose_filter(PNGEncContext *s, uint8_t *dst, |
204 | | const uint8_t *src, const uint8_t *top, int size, int bpp) |
205 | 7.35M | { |
206 | 7.35M | int pred = s->filter_type; |
207 | 7.35M | av_assert0(bpp || !pred); |
208 | 7.35M | if (!top && pred) |
209 | 66.7k | pred = PNG_FILTER_VALUE_SUB; |
210 | 7.35M | if (pred == PNG_FILTER_VALUE_MIXED) { |
211 | 0 | int i; |
212 | 0 | int cost, bcost = INT_MAX; |
213 | 0 | uint8_t *buf1 = dst, *buf2 = dst + size + 16; |
214 | 0 | for (pred = 0; pred < 5; pred++) { |
215 | 0 | png_filter_row(s, buf1 + 1, pred, src, top, size, bpp); |
216 | 0 | buf1[0] = pred; |
217 | 0 | cost = 0; |
218 | 0 | for (i = 0; i <= size; i++) |
219 | 0 | cost += abs((int8_t) buf1[i]); |
220 | 0 | if (cost < bcost) { |
221 | 0 | bcost = cost; |
222 | 0 | FFSWAP(uint8_t *, buf1, buf2); |
223 | 0 | } |
224 | 0 | } |
225 | 0 | return buf2; |
226 | 7.35M | } else { |
227 | 7.35M | png_filter_row(s, dst + 1, pred, src, top, size, bpp); |
228 | 7.35M | dst[0] = pred; |
229 | 7.35M | return dst; |
230 | 7.35M | } |
231 | 7.35M | } |
232 | | |
233 | | static void png_write_chunk(uint8_t **f, uint32_t tag, |
234 | | const uint8_t *buf, int length) |
235 | 108k | { |
236 | 108k | const AVCRC *crc_table = av_crc_get_table(AV_CRC_32_IEEE_LE); |
237 | 108k | uint32_t crc = ~0U; |
238 | 108k | uint8_t tagbuf[4]; |
239 | | |
240 | 108k | bytestream_put_be32(f, length); |
241 | 108k | AV_WL32(tagbuf, tag); |
242 | 108k | crc = av_crc(crc_table, crc, tagbuf, 4); |
243 | 108k | bytestream_put_be32(f, av_bswap32(tag)); |
244 | 108k | if (length > 0) { |
245 | 90.1k | crc = av_crc(crc_table, crc, buf, length); |
246 | 90.1k | if (*f != buf) |
247 | 90.1k | memcpy(*f, buf, length); |
248 | 90.1k | *f += length; |
249 | 90.1k | } |
250 | 108k | bytestream_put_be32(f, ~crc); |
251 | 108k | } |
252 | | |
253 | | static void png_write_image_data(AVCodecContext *avctx, |
254 | | const uint8_t *buf, int length) |
255 | 89.4k | { |
256 | 89.4k | PNGEncContext *s = avctx->priv_data; |
257 | 89.4k | const AVCRC *crc_table = av_crc_get_table(AV_CRC_32_IEEE_LE); |
258 | 89.4k | uint32_t crc = ~0U; |
259 | | |
260 | 89.4k | if (avctx->codec_id == AV_CODEC_ID_PNG || avctx->frame_num == 0) { |
261 | 30.0k | png_write_chunk(&s->bytestream, MKTAG('I', 'D', 'A', 'T'), buf, length); |
262 | 30.0k | return; |
263 | 30.0k | } |
264 | | |
265 | 59.4k | bytestream_put_be32(&s->bytestream, length + 4); |
266 | | |
267 | 59.4k | bytestream_put_be32(&s->bytestream, MKBETAG('f', 'd', 'A', 'T')); |
268 | 59.4k | bytestream_put_be32(&s->bytestream, s->sequence_number); |
269 | 59.4k | crc = av_crc(crc_table, crc, s->bytestream - 8, 8); |
270 | | |
271 | 59.4k | crc = av_crc(crc_table, crc, buf, length); |
272 | 59.4k | memcpy(s->bytestream, buf, length); |
273 | 59.4k | s->bytestream += length; |
274 | | |
275 | 59.4k | bytestream_put_be32(&s->bytestream, ~crc); |
276 | | |
277 | 59.4k | ++s->sequence_number; |
278 | 59.4k | } |
279 | | |
280 | | /* XXX: do filtering */ |
281 | | static int png_write_row(AVCodecContext *avctx, const uint8_t *data, int size) |
282 | 7.35M | { |
283 | 7.35M | PNGEncContext *s = avctx->priv_data; |
284 | 7.35M | z_stream *const zstream = &s->zstream.zstream; |
285 | 7.35M | int ret; |
286 | | |
287 | 7.35M | zstream->avail_in = size; |
288 | 7.35M | zstream->next_in = data; |
289 | 14.7M | while (zstream->avail_in > 0) { |
290 | 7.36M | ret = deflate(zstream, Z_NO_FLUSH); |
291 | 7.36M | if (ret != Z_OK) |
292 | 0 | return -1; |
293 | 7.36M | if (zstream->avail_out == 0) { |
294 | 11.2k | if (s->bytestream_end - s->bytestream > IOBUF_SIZE + 100) |
295 | 11.2k | png_write_image_data(avctx, s->buf, IOBUF_SIZE); |
296 | 11.2k | zstream->avail_out = IOBUF_SIZE; |
297 | 11.2k | zstream->next_out = s->buf; |
298 | 11.2k | } |
299 | 7.36M | } |
300 | 7.35M | return 0; |
301 | 7.35M | } |
302 | | |
303 | | #define PNG_LRINT(d, divisor) lrint((d) * (divisor)) |
304 | | #define PNG_Q2D(q, divisor) PNG_LRINT(av_q2d(q), (divisor)) |
305 | 0 | #define AV_WB32_PNG_D(buf, q) AV_WB32(buf, PNG_Q2D(q, 100000)) |
306 | | static int png_get_chrm(enum AVColorPrimaries prim, uint8_t *buf) |
307 | 19.5k | { |
308 | 19.5k | const AVColorPrimariesDesc *desc = av_csp_primaries_desc_from_id(prim); |
309 | 19.5k | if (!desc) |
310 | 19.5k | return 0; |
311 | | |
312 | 0 | AV_WB32_PNG_D(buf, desc->wp.x); |
313 | 0 | AV_WB32_PNG_D(buf + 4, desc->wp.y); |
314 | 0 | AV_WB32_PNG_D(buf + 8, desc->prim.r.x); |
315 | 0 | AV_WB32_PNG_D(buf + 12, desc->prim.r.y); |
316 | 0 | AV_WB32_PNG_D(buf + 16, desc->prim.g.x); |
317 | 0 | AV_WB32_PNG_D(buf + 20, desc->prim.g.y); |
318 | 0 | AV_WB32_PNG_D(buf + 24, desc->prim.b.x); |
319 | 0 | AV_WB32_PNG_D(buf + 28, desc->prim.b.y); |
320 | |
|
321 | 0 | return 1; |
322 | 19.5k | } |
323 | | |
324 | | static int png_get_gama(enum AVColorTransferCharacteristic trc, uint8_t *buf) |
325 | 19.5k | { |
326 | 19.5k | double gamma = av_csp_approximate_eotf_gamma(trc); |
327 | 19.5k | if (gamma <= 1e-6) |
328 | 19.5k | return 0; |
329 | | |
330 | 0 | AV_WB32(buf, PNG_LRINT(1.0 / gamma, 100000)); |
331 | 0 | return 1; |
332 | 19.5k | } |
333 | | |
334 | | static int png_write_iccp(PNGEncContext *s, const AVFrameSideData *sd) |
335 | 19.5k | { |
336 | 19.5k | z_stream *const zstream = &s->zstream.zstream; |
337 | 19.5k | const AVDictionaryEntry *entry; |
338 | 19.5k | const char *name; |
339 | 19.5k | uint8_t *start, *buf; |
340 | 19.5k | int ret; |
341 | | |
342 | 19.5k | if (!sd || !sd->size) |
343 | 19.5k | return 0; |
344 | 0 | zstream->next_in = sd->data; |
345 | 0 | zstream->avail_in = sd->size; |
346 | | |
347 | | /* write the chunk contents first */ |
348 | 0 | start = s->bytestream + 8; /* make room for iCCP tag + length */ |
349 | 0 | buf = start; |
350 | | |
351 | | /* profile description */ |
352 | 0 | entry = av_dict_get(sd->metadata, "name", NULL, 0); |
353 | 0 | name = (entry && entry->value[0]) ? entry->value : "icc"; |
354 | 0 | for (int i = 0;; i++) { |
355 | 0 | char c = (i == 79) ? 0 : name[i]; |
356 | 0 | bytestream_put_byte(&buf, c); |
357 | 0 | if (!c) |
358 | 0 | break; |
359 | 0 | } |
360 | | |
361 | | /* compression method and profile data */ |
362 | 0 | bytestream_put_byte(&buf, 0); |
363 | 0 | zstream->next_out = buf; |
364 | 0 | zstream->avail_out = s->bytestream_end - buf; |
365 | 0 | ret = deflate(zstream, Z_FINISH); |
366 | 0 | deflateReset(zstream); |
367 | 0 | if (ret != Z_STREAM_END) |
368 | 0 | return AVERROR_EXTERNAL; |
369 | | |
370 | | /* rewind to the start and write the chunk header/crc */ |
371 | 0 | png_write_chunk(&s->bytestream, MKTAG('i', 'C', 'C', 'P'), start, |
372 | 0 | zstream->next_out - start); |
373 | 0 | return 0; |
374 | 0 | } |
375 | | |
376 | | static int encode_headers(AVCodecContext *avctx, const AVFrame *pict) |
377 | 19.5k | { |
378 | 19.5k | AVFrameSideData *side_data; |
379 | 19.5k | PNGEncContext *s = avctx->priv_data; |
380 | 19.5k | AVBufferRef *exif_data = NULL; |
381 | 19.5k | int ret; |
382 | | |
383 | | /* write png header */ |
384 | 19.5k | AV_WB32(s->buf, avctx->width); |
385 | 19.5k | AV_WB32(s->buf + 4, avctx->height); |
386 | 19.5k | s->buf[8] = s->bit_depth; |
387 | 19.5k | s->buf[9] = s->color_type; |
388 | 19.5k | s->buf[10] = 0; /* compression type */ |
389 | 19.5k | s->buf[11] = 0; /* filter type */ |
390 | 19.5k | s->buf[12] = s->is_progressive; /* interlace type */ |
391 | 19.5k | png_write_chunk(&s->bytestream, MKTAG('I', 'H', 'D', 'R'), s->buf, 13); |
392 | | |
393 | | /* write physical information */ |
394 | 19.5k | if (s->dpm) { |
395 | 0 | AV_WB32(s->buf, s->dpm); |
396 | 0 | AV_WB32(s->buf + 4, s->dpm); |
397 | 0 | s->buf[8] = 1; /* unit specifier is meter */ |
398 | 19.5k | } else { |
399 | 19.5k | AV_WB32(s->buf, avctx->sample_aspect_ratio.num); |
400 | 19.5k | AV_WB32(s->buf + 4, avctx->sample_aspect_ratio.den); |
401 | 19.5k | s->buf[8] = 0; /* unit specifier is unknown */ |
402 | 19.5k | } |
403 | 19.5k | png_write_chunk(&s->bytestream, MKTAG('p', 'H', 'Y', 's'), s->buf, 9); |
404 | | |
405 | | /* write stereoscopic information */ |
406 | 19.5k | side_data = av_frame_get_side_data(pict, AV_FRAME_DATA_STEREO3D); |
407 | 19.5k | if (side_data) { |
408 | 0 | AVStereo3D *stereo3d = (AVStereo3D *)side_data->data; |
409 | 0 | switch (stereo3d->type) { |
410 | 0 | case AV_STEREO3D_SIDEBYSIDE: |
411 | 0 | s->buf[0] = ((stereo3d->flags & AV_STEREO3D_FLAG_INVERT) == 0) ? 1 : 0; |
412 | 0 | png_write_chunk(&s->bytestream, MKTAG('s', 'T', 'E', 'R'), s->buf, 1); |
413 | 0 | break; |
414 | 0 | case AV_STEREO3D_2D: |
415 | 0 | break; |
416 | 0 | default: |
417 | 0 | av_log(avctx, AV_LOG_WARNING, "Only side-by-side stereo3d flag can be defined within sTER chunk\n"); |
418 | 0 | break; |
419 | 0 | } |
420 | 0 | } |
421 | | |
422 | 19.5k | ret = ff_exif_get_buffer(avctx, pict, &exif_data, AV_EXIF_TIFF_HEADER); |
423 | 19.5k | if (exif_data) { |
424 | | // png_write_chunk accepts an int, not a size_t, so we have to check overflow |
425 | 0 | if (exif_data->size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE) |
426 | | // that's a very big exif chunk, probably a bug |
427 | 0 | av_log(avctx, AV_LOG_ERROR, "extremely large EXIF buffer detected, not writing\n"); |
428 | 0 | else |
429 | 0 | png_write_chunk(&s->bytestream, MKTAG('e','X','I','f'), exif_data->data, exif_data->size); |
430 | 0 | av_buffer_unref(&exif_data); |
431 | 19.5k | } else if (ret < 0) { |
432 | 0 | av_log(avctx, AV_LOG_WARNING, "unable to attach EXIF metadata: %s\n", av_err2str(ret)); |
433 | 0 | } |
434 | | |
435 | 19.5k | side_data = av_frame_get_side_data(pict, AV_FRAME_DATA_ICC_PROFILE); |
436 | 19.5k | if ((ret = png_write_iccp(s, side_data))) |
437 | 0 | return ret; |
438 | | |
439 | | /* write colorspace information */ |
440 | 19.5k | if (pict->color_primaries == AVCOL_PRI_BT709 && |
441 | 0 | pict->color_trc == AVCOL_TRC_IEC61966_2_1) { |
442 | 0 | s->buf[0] = 1; /* rendering intent, relative colorimetric by default */ |
443 | 0 | png_write_chunk(&s->bytestream, MKTAG('s', 'R', 'G', 'B'), s->buf, 1); |
444 | 19.5k | } else if (pict->color_trc != AVCOL_TRC_UNSPECIFIED && !side_data) { |
445 | | /* |
446 | | * Avoid writing cICP if the transfer is unknown. Known primaries |
447 | | * with unknown transfer can be handled by cHRM. |
448 | | * |
449 | | * We also avoid writing cICP if an ICC Profile is present, because |
450 | | * the standard requires that cICP overrides iCCP. |
451 | | * |
452 | | * These values match H.273 so no translation is needed. |
453 | | */ |
454 | 0 | s->buf[0] = pict->color_primaries; |
455 | 0 | s->buf[1] = pict->color_trc; |
456 | 0 | s->buf[2] = 0; /* colorspace = RGB */ |
457 | 0 | s->buf[3] = pict->color_range == AVCOL_RANGE_MPEG ? 0 : 1; |
458 | 0 | png_write_chunk(&s->bytestream, MKTAG('c', 'I', 'C', 'P'), s->buf, 4); |
459 | 0 | } |
460 | | |
461 | 19.5k | side_data = av_frame_get_side_data(pict, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); |
462 | 19.5k | if (side_data) { |
463 | 0 | AVContentLightMetadata *clli = (AVContentLightMetadata *) side_data->data; |
464 | 0 | AV_WB32(s->buf, clli->MaxCLL * 10000); |
465 | 0 | AV_WB32(s->buf + 4, clli->MaxFALL * 10000); |
466 | 0 | png_write_chunk(&s->bytestream, MKTAG('c', 'L', 'L', 'I'), s->buf, 8); |
467 | 0 | } |
468 | | |
469 | 19.5k | side_data = av_frame_get_side_data(pict, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); |
470 | 19.5k | if (side_data) { |
471 | 0 | AVMasteringDisplayMetadata *mdcv = (AVMasteringDisplayMetadata *) side_data->data; |
472 | 0 | if (mdcv->has_luminance && mdcv->has_primaries) { |
473 | 0 | for (int i = 0; i < 3; i++) { |
474 | 0 | AV_WB16(s->buf + 2*i, PNG_Q2D(mdcv->display_primaries[i][0], 50000)); |
475 | 0 | AV_WB16(s->buf + 2*i + 2, PNG_Q2D(mdcv->display_primaries[i][1], 50000)); |
476 | 0 | } |
477 | 0 | AV_WB16(s->buf + 12, PNG_Q2D(mdcv->white_point[0], 50000)); |
478 | 0 | AV_WB16(s->buf + 14, PNG_Q2D(mdcv->white_point[1], 50000)); |
479 | 0 | AV_WB32(s->buf + 16, PNG_Q2D(mdcv->max_luminance, 10000)); |
480 | 0 | AV_WB32(s->buf + 20, PNG_Q2D(mdcv->min_luminance, 10000)); |
481 | 0 | png_write_chunk(&s->bytestream, MKTAG('m', 'D', 'C', 'V'), s->buf, 24); |
482 | 0 | } |
483 | 0 | } |
484 | | |
485 | 19.5k | if (png_get_chrm(pict->color_primaries, s->buf)) |
486 | 0 | png_write_chunk(&s->bytestream, MKTAG('c', 'H', 'R', 'M'), s->buf, 32); |
487 | 19.5k | if (png_get_gama(pict->color_trc, s->buf)) |
488 | 0 | png_write_chunk(&s->bytestream, MKTAG('g', 'A', 'M', 'A'), s->buf, 4); |
489 | | |
490 | 19.5k | if (avctx->bits_per_raw_sample > 0 && |
491 | 0 | avctx->bits_per_raw_sample < (s->color_type & PNG_COLOR_MASK_PALETTE ? 8 : s->bit_depth)) { |
492 | 0 | int len = s->color_type & PNG_COLOR_MASK_PALETTE ? 3 : ff_png_get_nb_channels(s->color_type); |
493 | 0 | memset(s->buf, avctx->bits_per_raw_sample, len); |
494 | 0 | png_write_chunk(&s->bytestream, MKTAG('s', 'B', 'I', 'T'), s->buf, len); |
495 | 0 | } |
496 | | |
497 | | /* put the palette if needed, must be after colorspace information */ |
498 | 19.5k | if (s->color_type == PNG_COLOR_TYPE_PALETTE) { |
499 | 2.73k | int has_alpha, alpha, i; |
500 | 2.73k | unsigned int v; |
501 | 2.73k | uint32_t *palette; |
502 | 2.73k | uint8_t *ptr, *alpha_ptr; |
503 | | |
504 | 2.73k | palette = (uint32_t *)pict->data[1]; |
505 | 2.73k | ptr = s->buf; |
506 | 2.73k | alpha_ptr = s->buf + 256 * 3; |
507 | 2.73k | has_alpha = 0; |
508 | 702k | for (i = 0; i < 256; i++) { |
509 | 699k | v = palette[i]; |
510 | 699k | alpha = v >> 24; |
511 | 699k | if (alpha != 0xff) |
512 | 597k | has_alpha = 1; |
513 | 699k | *alpha_ptr++ = alpha; |
514 | 699k | bytestream_put_be24(&ptr, v); |
515 | 699k | } |
516 | 2.73k | png_write_chunk(&s->bytestream, |
517 | 2.73k | MKTAG('P', 'L', 'T', 'E'), s->buf, 256 * 3); |
518 | 2.73k | if (has_alpha) { |
519 | 2.51k | png_write_chunk(&s->bytestream, |
520 | 2.51k | MKTAG('t', 'R', 'N', 'S'), s->buf + 256 * 3, 256); |
521 | 2.51k | } |
522 | 2.73k | } |
523 | | |
524 | 19.5k | return 0; |
525 | 19.5k | } |
526 | | |
527 | | static int encode_frame(AVCodecContext *avctx, const AVFrame *pict) |
528 | 73.5k | { |
529 | 73.5k | PNGEncContext *s = avctx->priv_data; |
530 | 73.5k | z_stream *const zstream = &s->zstream.zstream; |
531 | 73.5k | const AVFrame *const p = pict; |
532 | 73.5k | int y, len, ret; |
533 | 73.5k | int row_size, pass_row_size; |
534 | 73.5k | uint8_t *crow_buf, *crow; |
535 | 73.5k | uint8_t *crow_base = NULL; |
536 | 73.5k | uint8_t *progressive_buf = NULL; |
537 | 73.5k | uint8_t *top_buf = NULL; |
538 | | |
539 | 73.5k | row_size = (pict->width * s->bits_per_pixel + 7) >> 3; |
540 | | |
541 | 73.5k | crow_base = av_malloc((row_size + 32) << (s->filter_type == PNG_FILTER_VALUE_MIXED)); |
542 | 73.5k | if (!crow_base) { |
543 | 0 | ret = AVERROR(ENOMEM); |
544 | 0 | goto the_end; |
545 | 0 | } |
546 | | // pixel data should be aligned, but there's a control byte before it |
547 | 73.5k | crow_buf = crow_base + 15; |
548 | 73.5k | if (s->is_progressive) { |
549 | 0 | progressive_buf = av_malloc(row_size + 1); |
550 | 0 | top_buf = av_malloc(row_size + 1); |
551 | 0 | if (!progressive_buf || !top_buf) { |
552 | 0 | ret = AVERROR(ENOMEM); |
553 | 0 | goto the_end; |
554 | 0 | } |
555 | 0 | } |
556 | | |
557 | | /* put each row */ |
558 | 73.5k | zstream->avail_out = IOBUF_SIZE; |
559 | 73.5k | zstream->next_out = s->buf; |
560 | 73.5k | if (s->is_progressive) { |
561 | 0 | int pass; |
562 | |
|
563 | 0 | for (pass = 0; pass < NB_PASSES; pass++) { |
564 | | /* NOTE: a pass is completely omitted if no pixels would be |
565 | | * output */ |
566 | 0 | pass_row_size = ff_png_pass_row_size(pass, s->bits_per_pixel, pict->width); |
567 | 0 | if (pass_row_size > 0) { |
568 | 0 | uint8_t *top = NULL; |
569 | 0 | for (y = 0; y < pict->height; y++) |
570 | 0 | if ((ff_png_pass_ymask[pass] << (y & 7)) & 0x80) { |
571 | 0 | const uint8_t *ptr = p->data[0] + y * p->linesize[0]; |
572 | 0 | FFSWAP(uint8_t *, progressive_buf, top_buf); |
573 | 0 | png_get_interlaced_row(progressive_buf, pass_row_size, |
574 | 0 | s->bits_per_pixel, pass, |
575 | 0 | ptr, pict->width); |
576 | 0 | crow = png_choose_filter(s, crow_buf, progressive_buf, |
577 | 0 | top, pass_row_size, s->bits_per_pixel >> 3); |
578 | 0 | png_write_row(avctx, crow, pass_row_size + 1); |
579 | 0 | top = progressive_buf; |
580 | 0 | } |
581 | 0 | } |
582 | 0 | } |
583 | 73.5k | } else { |
584 | 73.5k | const uint8_t *top = NULL; |
585 | 7.42M | for (y = 0; y < pict->height; y++) { |
586 | 7.35M | const uint8_t *ptr = p->data[0] + y * p->linesize[0]; |
587 | 7.35M | crow = png_choose_filter(s, crow_buf, ptr, top, |
588 | 7.35M | row_size, s->bits_per_pixel >> 3); |
589 | 7.35M | png_write_row(avctx, crow, row_size + 1); |
590 | 7.35M | top = ptr; |
591 | 7.35M | } |
592 | 73.5k | } |
593 | | /* compress last bytes */ |
594 | 78.2k | for (;;) { |
595 | 78.2k | ret = deflate(zstream, Z_FINISH); |
596 | 78.2k | if (ret == Z_OK || ret == Z_STREAM_END) { |
597 | 78.2k | len = IOBUF_SIZE - zstream->avail_out; |
598 | 78.2k | if (len > 0 && s->bytestream_end - s->bytestream > len + 100) { |
599 | 78.2k | png_write_image_data(avctx, s->buf, len); |
600 | 78.2k | } |
601 | 78.2k | zstream->avail_out = IOBUF_SIZE; |
602 | 78.2k | zstream->next_out = s->buf; |
603 | 78.2k | if (ret == Z_STREAM_END) |
604 | 73.5k | break; |
605 | 78.2k | } else { |
606 | 0 | ret = -1; |
607 | 0 | goto the_end; |
608 | 0 | } |
609 | 78.2k | } |
610 | | |
611 | 73.5k | ret = 0; |
612 | | |
613 | 73.5k | the_end: |
614 | 73.5k | av_freep(&crow_base); |
615 | 73.5k | av_freep(&progressive_buf); |
616 | 73.5k | av_freep(&top_buf); |
617 | 73.5k | deflateReset(zstream); |
618 | 73.5k | return ret; |
619 | 73.5k | } |
620 | | |
621 | | static int add_icc_profile_size(AVCodecContext *avctx, const AVFrame *pict, |
622 | | uint64_t *max_packet_size) |
623 | 19.5k | { |
624 | 19.5k | PNGEncContext *s = avctx->priv_data; |
625 | 19.5k | const AVFrameSideData *sd; |
626 | 19.5k | const int hdr_size = 128; |
627 | 19.5k | uint64_t new_pkt_size; |
628 | 19.5k | uLong bound; |
629 | | |
630 | 19.5k | if (!pict) |
631 | 0 | return 0; |
632 | 19.5k | sd = av_frame_get_side_data(pict, AV_FRAME_DATA_ICC_PROFILE); |
633 | 19.5k | if (!sd || !sd->size) |
634 | 19.5k | return 0; |
635 | 0 | if (sd->size != (uLong) sd->size) |
636 | 0 | return AVERROR_INVALIDDATA; |
637 | | |
638 | 0 | bound = deflateBound(&s->zstream.zstream, sd->size); |
639 | 0 | if (bound > INT32_MAX - hdr_size) |
640 | 0 | return AVERROR_INVALIDDATA; |
641 | | |
642 | 0 | new_pkt_size = *max_packet_size + bound + hdr_size; |
643 | 0 | if (new_pkt_size < *max_packet_size) |
644 | 0 | return AVERROR_INVALIDDATA; |
645 | 0 | *max_packet_size = new_pkt_size; |
646 | 0 | return 0; |
647 | 0 | } |
648 | | |
649 | | static int add_exif_profile_size(AVCodecContext *avctx, const AVFrame *pict, |
650 | | uint64_t *max_packet_size) |
651 | 19.5k | { |
652 | 19.5k | const AVFrameSideData *sd; |
653 | 19.5k | uint64_t new_pkt_size; |
654 | | /* includes orientation tag */ |
655 | 19.5k | const int base_exif_size = 92; |
656 | 19.5k | uint64_t estimated_exif_size; |
657 | | |
658 | 19.5k | sd = av_frame_get_side_data(pict, AV_FRAME_DATA_EXIF); |
659 | 19.5k | estimated_exif_size = sd ? sd->size : 0; |
660 | 19.5k | sd = av_frame_get_side_data(pict, AV_FRAME_DATA_DISPLAYMATRIX); |
661 | 19.5k | if (sd) |
662 | 0 | estimated_exif_size += base_exif_size; |
663 | | |
664 | 19.5k | if (!estimated_exif_size) |
665 | 19.5k | return 0; |
666 | | |
667 | | /* 12 is the png chunk header size */ |
668 | 0 | new_pkt_size = *max_packet_size + estimated_exif_size + 12; |
669 | 0 | if (new_pkt_size < *max_packet_size) |
670 | 0 | return AVERROR_INVALIDDATA; |
671 | | |
672 | 0 | *max_packet_size = new_pkt_size; |
673 | |
|
674 | 0 | return 0; |
675 | 0 | } |
676 | | |
677 | | static int encode_png(AVCodecContext *avctx, AVPacket *pkt, |
678 | | const AVFrame *pict, int *got_packet) |
679 | 17.9k | { |
680 | 17.9k | PNGEncContext *s = avctx->priv_data; |
681 | 17.9k | int ret; |
682 | 17.9k | int enc_row_size; |
683 | 17.9k | uint64_t max_packet_size; |
684 | | |
685 | 17.9k | enc_row_size = deflateBound(&s->zstream.zstream, |
686 | 17.9k | (avctx->width * s->bits_per_pixel + 7) >> 3); |
687 | 17.9k | max_packet_size = |
688 | 17.9k | FF_INPUT_BUFFER_MIN_SIZE + // headers |
689 | 17.9k | avctx->height * ( |
690 | 17.9k | enc_row_size + |
691 | 17.9k | 12 * (((int64_t)enc_row_size + IOBUF_SIZE - 1) / IOBUF_SIZE) // IDAT * ceil(enc_row_size / IOBUF_SIZE) |
692 | 17.9k | ); |
693 | 17.9k | if ((ret = add_icc_profile_size(avctx, pict, &max_packet_size))) |
694 | 0 | return ret; |
695 | 17.9k | ret = add_exif_profile_size(avctx, pict, &max_packet_size); |
696 | 17.9k | if (ret < 0) |
697 | 0 | return ret; |
698 | | |
699 | 17.9k | ret = ff_alloc_packet(avctx, pkt, max_packet_size); |
700 | 17.9k | if (ret < 0) |
701 | 0 | return ret; |
702 | | |
703 | 17.9k | s->bytestream_start = |
704 | 17.9k | s->bytestream = pkt->data; |
705 | 17.9k | s->bytestream_end = pkt->data + pkt->size; |
706 | | |
707 | 17.9k | AV_WB64(s->bytestream, PNGSIG); |
708 | 17.9k | s->bytestream += 8; |
709 | | |
710 | 17.9k | ret = encode_headers(avctx, pict); |
711 | 17.9k | if (ret < 0) |
712 | 0 | return ret; |
713 | | |
714 | 17.9k | ret = encode_frame(avctx, pict); |
715 | 17.9k | if (ret < 0) |
716 | 0 | return ret; |
717 | | |
718 | 17.9k | png_write_chunk(&s->bytestream, MKTAG('I', 'E', 'N', 'D'), NULL, 0); |
719 | | |
720 | 17.9k | pkt->size = s->bytestream - s->bytestream_start; |
721 | 17.9k | pkt->flags |= AV_PKT_FLAG_KEY; |
722 | 17.9k | *got_packet = 1; |
723 | | |
724 | 17.9k | return 0; |
725 | 17.9k | } |
726 | | |
727 | | static int apng_do_inverse_blend(AVFrame *output, const AVFrame *input, |
728 | | APNGFctlChunk *fctl_chunk, uint8_t bpp) |
729 | 79.1k | { |
730 | | // output: background, input: foreground |
731 | | // output the image such that when blended with the background, will produce the foreground |
732 | | |
733 | 79.1k | unsigned int x, y; |
734 | 79.1k | unsigned int leftmost_x = input->width; |
735 | 79.1k | unsigned int rightmost_x = 0; |
736 | 79.1k | unsigned int topmost_y = input->height; |
737 | 79.1k | unsigned int bottommost_y = 0; |
738 | 79.1k | const uint8_t *input_data = input->data[0]; |
739 | 79.1k | uint8_t *output_data = output->data[0]; |
740 | 79.1k | ptrdiff_t input_linesize = input->linesize[0]; |
741 | 79.1k | ptrdiff_t output_linesize = output->linesize[0]; |
742 | | |
743 | | // Find bounding box of changes |
744 | 1.60M | for (y = 0; y < input->height; ++y) { |
745 | 46.5M | for (x = 0; x < input->width; ++x) { |
746 | 45.0M | if (!memcmp(input_data + bpp * x, output_data + bpp * x, bpp)) |
747 | 17.7M | continue; |
748 | | |
749 | 27.2M | if (x < leftmost_x) |
750 | 71.2k | leftmost_x = x; |
751 | 27.2M | if (x >= rightmost_x) |
752 | 779k | rightmost_x = x + 1; |
753 | 27.2M | if (y < topmost_y) |
754 | 59.4k | topmost_y = y; |
755 | 27.2M | if (y >= bottommost_y) |
756 | 999k | bottommost_y = y + 1; |
757 | 27.2M | } |
758 | | |
759 | 1.52M | input_data += input_linesize; |
760 | 1.52M | output_data += output_linesize; |
761 | 1.52M | } |
762 | | |
763 | 79.1k | if (leftmost_x == input->width && rightmost_x == 0) { |
764 | | // Empty frame |
765 | | // APNG does not support empty frames, so we make it a 1x1 frame |
766 | 19.6k | leftmost_x = topmost_y = 0; |
767 | 19.6k | rightmost_x = bottommost_y = 1; |
768 | 19.6k | } |
769 | | |
770 | | // Do actual inverse blending |
771 | 79.1k | if (fctl_chunk->blend_op == APNG_BLEND_OP_SOURCE) { |
772 | 39.5k | output_data = output->data[0]; |
773 | 644k | for (y = topmost_y; y < bottommost_y; ++y) { |
774 | 604k | memcpy(output_data, |
775 | 604k | input->data[0] + input_linesize * y + bpp * leftmost_x, |
776 | 604k | bpp * (rightmost_x - leftmost_x)); |
777 | 604k | output_data += output_linesize; |
778 | 604k | } |
779 | 39.5k | } else { // APNG_BLEND_OP_OVER |
780 | 39.5k | size_t transparent_palette_index; |
781 | 39.5k | uint32_t *palette; |
782 | | |
783 | 39.5k | switch (input->format) { |
784 | 8.30k | case AV_PIX_FMT_RGBA64BE: |
785 | 11.5k | case AV_PIX_FMT_YA16BE: |
786 | 15.5k | case AV_PIX_FMT_RGBA: |
787 | 27.7k | case AV_PIX_FMT_GRAY8A: |
788 | 27.7k | break; |
789 | | |
790 | 7.27k | case AV_PIX_FMT_PAL8: |
791 | 7.27k | palette = (uint32_t*)input->data[1]; |
792 | 1.80M | for (transparent_palette_index = 0; transparent_palette_index < 256; ++transparent_palette_index) |
793 | 1.79M | if (palette[transparent_palette_index] >> 24 == 0) |
794 | 280 | break; |
795 | 7.27k | break; |
796 | | |
797 | 4.51k | default: |
798 | | // No alpha, so blending not possible |
799 | 4.51k | return -1; |
800 | 39.5k | } |
801 | | |
802 | 217k | for (y = topmost_y; y < bottommost_y; ++y) { |
803 | 203k | const uint8_t *foreground = input->data[0] + input_linesize * y + bpp * leftmost_x; |
804 | 203k | uint8_t *background = output->data[0] + output_linesize * y + bpp * leftmost_x; |
805 | 203k | output_data = output->data[0] + output_linesize * (y - topmost_y); |
806 | 6.43M | for (x = leftmost_x; x < rightmost_x; ++x, foreground += bpp, background += bpp, output_data += bpp) { |
807 | 6.24M | if (!memcmp(foreground, background, bpp)) { |
808 | 3.26M | if (input->format == AV_PIX_FMT_PAL8) { |
809 | 2.50M | if (transparent_palette_index == 256) { |
810 | | // Need fully transparent colour, but none exists |
811 | 4.51k | return -1; |
812 | 4.51k | } |
813 | | |
814 | 2.50M | *output_data = transparent_palette_index; |
815 | 2.50M | } else { |
816 | 761k | memset(output_data, 0, bpp); |
817 | 761k | } |
818 | 3.26M | continue; |
819 | 3.26M | } |
820 | | |
821 | | // Check for special alpha values, since full inverse |
822 | | // alpha-on-alpha blending is rarely possible, and when |
823 | | // possible, doesn't compress much better than |
824 | | // APNG_BLEND_OP_SOURCE blending |
825 | 2.98M | switch (input->format) { |
826 | 536k | case AV_PIX_FMT_RGBA64BE: |
827 | 536k | if (((uint16_t*)foreground)[3] == 0xffff || |
828 | 527k | ((uint16_t*)background)[3] == 0) |
829 | 533k | break; |
830 | 3.60k | return -1; |
831 | | |
832 | 416k | case AV_PIX_FMT_YA16BE: |
833 | 416k | if (((uint16_t*)foreground)[1] == 0xffff || |
834 | 414k | ((uint16_t*)background)[1] == 0) |
835 | 414k | break; |
836 | 1.69k | return -1; |
837 | | |
838 | 193k | case AV_PIX_FMT_RGBA: |
839 | 193k | if (foreground[3] == 0xff || background[3] == 0) |
840 | 190k | break; |
841 | 2.36k | return -1; |
842 | | |
843 | 1.20M | case AV_PIX_FMT_GRAY8A: |
844 | 1.20M | if (foreground[1] == 0xff || background[1] == 0) |
845 | 1.19M | break; |
846 | 5.91k | return -1; |
847 | | |
848 | 633k | case AV_PIX_FMT_PAL8: |
849 | 633k | if (palette[*foreground] >> 24 == 0xff || |
850 | 633k | palette[*background] >> 24 == 0) |
851 | 631k | break; |
852 | 2.49k | return -1; |
853 | | |
854 | 0 | default: |
855 | 0 | av_unreachable("Pixfmt has been checked before"); |
856 | 2.98M | } |
857 | | |
858 | 2.96M | memmove(output_data, foreground, bpp); |
859 | 2.96M | } |
860 | 203k | } |
861 | 35.0k | } |
862 | | |
863 | 54.0k | output->width = rightmost_x - leftmost_x; |
864 | 54.0k | output->height = bottommost_y - topmost_y; |
865 | 54.0k | fctl_chunk->width = output->width; |
866 | 54.0k | fctl_chunk->height = output->height; |
867 | 54.0k | fctl_chunk->x_offset = leftmost_x; |
868 | 54.0k | fctl_chunk->y_offset = topmost_y; |
869 | | |
870 | 54.0k | return 0; |
871 | 79.1k | } |
872 | | |
873 | | static int apng_encode_frame(AVCodecContext *avctx, const AVFrame *pict, |
874 | | APNGFctlChunk *best_fctl_chunk, APNGFctlChunk *best_last_fctl_chunk) |
875 | 15.8k | { |
876 | 15.8k | PNGEncContext *s = avctx->priv_data; |
877 | 15.8k | int ret; |
878 | 15.8k | unsigned int y; |
879 | 15.8k | AVFrame* diffFrame; |
880 | 15.8k | uint8_t bpp = (s->bits_per_pixel + 7) >> 3; |
881 | 15.8k | uint8_t *original_bytestream, *original_bytestream_end; |
882 | 15.8k | uint8_t *temp_bytestream = 0, *temp_bytestream_end; |
883 | 15.8k | uint32_t best_sequence_number; |
884 | 15.8k | uint8_t *best_bytestream; |
885 | 15.8k | size_t best_bytestream_size = SIZE_MAX; |
886 | 15.8k | APNGFctlChunk last_fctl_chunk = *best_last_fctl_chunk; |
887 | 15.8k | APNGFctlChunk fctl_chunk = *best_fctl_chunk; |
888 | 15.8k | const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pict->format); |
889 | | |
890 | 15.8k | if (avctx->frame_num == 0) { |
891 | 1.56k | best_fctl_chunk->width = pict->width; |
892 | 1.56k | best_fctl_chunk->height = pict->height; |
893 | 1.56k | best_fctl_chunk->x_offset = 0; |
894 | 1.56k | best_fctl_chunk->y_offset = 0; |
895 | 1.56k | best_fctl_chunk->blend_op = APNG_BLEND_OP_SOURCE; |
896 | 1.56k | return encode_frame(avctx, pict); |
897 | 1.56k | } |
898 | | |
899 | 14.2k | diffFrame = av_frame_alloc(); |
900 | 14.2k | if (!diffFrame) |
901 | 0 | return AVERROR(ENOMEM); |
902 | | |
903 | 14.2k | diffFrame->format = pict->format; |
904 | 14.2k | diffFrame->width = pict->width; |
905 | 14.2k | diffFrame->height = pict->height; |
906 | 14.2k | if ((ret = av_frame_get_buffer(diffFrame, 0)) < 0) |
907 | 0 | goto fail; |
908 | | |
909 | 14.2k | original_bytestream = s->bytestream; |
910 | 14.2k | original_bytestream_end = s->bytestream_end; |
911 | | |
912 | 14.2k | temp_bytestream = av_malloc(original_bytestream_end - original_bytestream); |
913 | 14.2k | if (!temp_bytestream) { |
914 | 0 | ret = AVERROR(ENOMEM); |
915 | 0 | goto fail; |
916 | 0 | } |
917 | 14.2k | temp_bytestream_end = temp_bytestream + (original_bytestream_end - original_bytestream); |
918 | | |
919 | 57.0k | for (last_fctl_chunk.dispose_op = 0; last_fctl_chunk.dispose_op < 3; ++last_fctl_chunk.dispose_op) { |
920 | | // 0: APNG_DISPOSE_OP_NONE |
921 | | // 1: APNG_DISPOSE_OP_BACKGROUND |
922 | | // 2: APNG_DISPOSE_OP_PREVIOUS |
923 | 42.7k | if (last_fctl_chunk.dispose_op == APNG_DISPOSE_OP_BACKGROUND) { |
924 | 14.2k | if (!(desc->flags & AV_PIX_FMT_FLAG_ALPHA)) |
925 | 2.31k | continue; |
926 | 14.2k | } |
927 | 121k | for (fctl_chunk.blend_op = 0; fctl_chunk.blend_op < 2; ++fctl_chunk.blend_op) { |
928 | | // 0: APNG_BLEND_OP_SOURCE |
929 | | // 1: APNG_BLEND_OP_OVER |
930 | | |
931 | 80.9k | uint32_t original_sequence_number = s->sequence_number, sequence_number; |
932 | 80.9k | uint8_t *bytestream_start = s->bytestream; |
933 | 80.9k | size_t bytestream_size; |
934 | | |
935 | | // Do disposal |
936 | 80.9k | if (last_fctl_chunk.dispose_op != APNG_DISPOSE_OP_PREVIOUS) { |
937 | 52.3k | diffFrame->width = pict->width; |
938 | 52.3k | diffFrame->height = pict->height; |
939 | 52.3k | ret = av_frame_copy(diffFrame, s->last_frame); |
940 | 52.3k | if (ret < 0) |
941 | 0 | goto fail; |
942 | | |
943 | 52.3k | if (last_fctl_chunk.dispose_op == APNG_DISPOSE_OP_BACKGROUND) { |
944 | 428k | for (y = last_fctl_chunk.y_offset; y < last_fctl_chunk.y_offset + last_fctl_chunk.height; ++y) { |
945 | 404k | size_t row_start = diffFrame->linesize[0] * y + bpp * last_fctl_chunk.x_offset; |
946 | 404k | memset(diffFrame->data[0] + row_start, 0, bpp * last_fctl_chunk.width); |
947 | 404k | } |
948 | 23.8k | } |
949 | 52.3k | } else { |
950 | 28.5k | if (!s->prev_frame) |
951 | 1.75k | continue; |
952 | | |
953 | 26.7k | diffFrame->width = pict->width; |
954 | 26.7k | diffFrame->height = pict->height; |
955 | 26.7k | ret = av_frame_copy(diffFrame, s->prev_frame); |
956 | 26.7k | if (ret < 0) |
957 | 0 | goto fail; |
958 | 26.7k | } |
959 | | |
960 | | // Do inverse blending |
961 | 79.1k | if (apng_do_inverse_blend(diffFrame, pict, &fctl_chunk, bpp) < 0) |
962 | 25.1k | continue; |
963 | | |
964 | | // Do encoding |
965 | 54.0k | ret = encode_frame(avctx, diffFrame); |
966 | 54.0k | sequence_number = s->sequence_number; |
967 | 54.0k | s->sequence_number = original_sequence_number; |
968 | 54.0k | bytestream_size = s->bytestream - bytestream_start; |
969 | 54.0k | s->bytestream = bytestream_start; |
970 | 54.0k | if (ret < 0) |
971 | 0 | goto fail; |
972 | | |
973 | 54.0k | if (bytestream_size < best_bytestream_size) { |
974 | 16.7k | *best_fctl_chunk = fctl_chunk; |
975 | 16.7k | *best_last_fctl_chunk = last_fctl_chunk; |
976 | | |
977 | 16.7k | best_sequence_number = sequence_number; |
978 | 16.7k | best_bytestream = s->bytestream; |
979 | 16.7k | best_bytestream_size = bytestream_size; |
980 | | |
981 | 16.7k | if (best_bytestream == original_bytestream) { |
982 | 14.3k | s->bytestream = temp_bytestream; |
983 | 14.3k | s->bytestream_end = temp_bytestream_end; |
984 | 14.3k | } else { |
985 | 2.39k | s->bytestream = original_bytestream; |
986 | 2.39k | s->bytestream_end = original_bytestream_end; |
987 | 2.39k | } |
988 | 16.7k | } |
989 | 54.0k | } |
990 | 40.4k | } |
991 | | |
992 | 14.2k | s->sequence_number = best_sequence_number; |
993 | 14.2k | s->bytestream = original_bytestream + best_bytestream_size; |
994 | 14.2k | s->bytestream_end = original_bytestream_end; |
995 | 14.2k | if (best_bytestream != original_bytestream) |
996 | 2.34k | memcpy(original_bytestream, best_bytestream, best_bytestream_size); |
997 | | |
998 | 14.2k | ret = 0; |
999 | | |
1000 | 14.2k | fail: |
1001 | 14.2k | av_freep(&temp_bytestream); |
1002 | 14.2k | av_frame_free(&diffFrame); |
1003 | 14.2k | return ret; |
1004 | 14.2k | } |
1005 | | |
1006 | | static int encode_apng(AVCodecContext *avctx, AVPacket *pkt, |
1007 | | const AVFrame *pict, int *got_packet) |
1008 | 19.0k | { |
1009 | 19.0k | PNGEncContext *s = avctx->priv_data; |
1010 | 19.0k | int ret; |
1011 | 19.0k | int enc_row_size; |
1012 | 19.0k | uint64_t max_packet_size; |
1013 | 19.0k | APNGFctlChunk fctl_chunk = {0}; |
1014 | | |
1015 | 19.0k | if (pict && s->color_type == PNG_COLOR_TYPE_PALETTE) { |
1016 | 2.83k | uint32_t checksum = ~av_crc(av_crc_get_table(AV_CRC_32_IEEE_LE), ~0U, pict->data[1], 256 * sizeof(uint32_t)); |
1017 | | |
1018 | 2.83k | if (avctx->frame_num == 0) { |
1019 | 215 | s->palette_checksum = checksum; |
1020 | 2.61k | } else if (checksum != s->palette_checksum) { |
1021 | 154 | av_log(avctx, AV_LOG_ERROR, |
1022 | 154 | "Input contains more than one unique palette. APNG does not support multiple palettes.\n"); |
1023 | 154 | return -1; |
1024 | 154 | } |
1025 | 2.83k | } |
1026 | | |
1027 | 18.9k | enc_row_size = deflateBound(&s->zstream.zstream, |
1028 | 18.9k | (avctx->width * s->bits_per_pixel + 7) >> 3); |
1029 | 18.9k | max_packet_size = |
1030 | 18.9k | FF_INPUT_BUFFER_MIN_SIZE + // headers |
1031 | 18.9k | avctx->height * ( |
1032 | 18.9k | enc_row_size + |
1033 | 18.9k | (4 + 12) * (((int64_t)enc_row_size + IOBUF_SIZE - 1) / IOBUF_SIZE) // fdAT * ceil(enc_row_size / IOBUF_SIZE) |
1034 | 18.9k | ); |
1035 | 18.9k | if (max_packet_size > INT_MAX) |
1036 | 0 | return AVERROR(ENOMEM); |
1037 | | |
1038 | 18.9k | if (avctx->frame_num == 0) { |
1039 | 1.56k | if (!pict) |
1040 | 0 | return AVERROR(EINVAL); |
1041 | 1.56k | uint64_t extradata_size = FF_INPUT_BUFFER_MIN_SIZE; |
1042 | 1.56k | ret = add_icc_profile_size(avctx, pict, &extradata_size); |
1043 | 1.56k | if (ret < 0) |
1044 | 0 | return ret; |
1045 | 1.56k | ret = add_exif_profile_size(avctx, pict, &extradata_size); |
1046 | 1.56k | if (ret < 0) |
1047 | 0 | return ret; |
1048 | | /* the compiler will optimize this out if UINT64_MAX == SIZE_MAX */ |
1049 | 1.56k | if (extradata_size > SIZE_MAX) |
1050 | 0 | return AVERROR(ENOMEM); |
1051 | 1.56k | s->bytestream = s->extra_data = av_malloc(extradata_size); |
1052 | 1.56k | if (!s->extra_data) |
1053 | 0 | return AVERROR(ENOMEM); |
1054 | | |
1055 | 1.56k | ret = encode_headers(avctx, pict); |
1056 | 1.56k | if (ret < 0) |
1057 | 0 | return ret; |
1058 | | |
1059 | 1.56k | s->extra_data_size = s->bytestream - s->extra_data; |
1060 | | |
1061 | 1.56k | s->last_frame_packet = av_malloc(max_packet_size); |
1062 | 1.56k | if (!s->last_frame_packet) |
1063 | 0 | return AVERROR(ENOMEM); |
1064 | 17.3k | } else if (s->last_frame) { |
1065 | 15.8k | ret = ff_get_encode_buffer(avctx, pkt, s->last_frame_packet_size, 0); |
1066 | 15.8k | if (ret < 0) |
1067 | 0 | return ret; |
1068 | | |
1069 | 15.8k | memcpy(pkt->data, s->last_frame_packet, s->last_frame_packet_size); |
1070 | 15.8k | pkt->pts = s->last_frame->pts; |
1071 | 15.8k | pkt->duration = s->last_frame->duration; |
1072 | | |
1073 | 15.8k | ret = ff_encode_reordered_opaque(avctx, pkt, s->last_frame); |
1074 | 15.8k | if (ret < 0) |
1075 | 0 | return ret; |
1076 | 15.8k | } |
1077 | | |
1078 | 18.9k | if (pict) { |
1079 | 15.8k | s->bytestream_start = |
1080 | 15.8k | s->bytestream = s->last_frame_packet; |
1081 | 15.8k | s->bytestream_end = s->bytestream + max_packet_size; |
1082 | | |
1083 | | // We're encoding the frame first, so we have to do a bit of shuffling around |
1084 | | // to have the image data write to the correct place in the buffer |
1085 | 15.8k | fctl_chunk.sequence_number = s->sequence_number; |
1086 | 15.8k | ++s->sequence_number; |
1087 | 15.8k | s->bytestream += APNG_FCTL_CHUNK_SIZE + 12; |
1088 | | |
1089 | 15.8k | ret = apng_encode_frame(avctx, pict, &fctl_chunk, &s->last_frame_fctl); |
1090 | 15.8k | if (ret < 0) |
1091 | 0 | return ret; |
1092 | | |
1093 | 15.8k | fctl_chunk.delay_num = 0; // delay filled in during muxing |
1094 | 15.8k | fctl_chunk.delay_den = 0; |
1095 | 15.8k | } else { |
1096 | 3.12k | s->last_frame_fctl.dispose_op = APNG_DISPOSE_OP_NONE; |
1097 | 3.12k | } |
1098 | | |
1099 | 18.9k | if (s->last_frame) { |
1100 | 15.8k | uint8_t* last_fctl_chunk_start = pkt->data; |
1101 | 15.8k | uint8_t buf[APNG_FCTL_CHUNK_SIZE]; |
1102 | 15.8k | if (!s->extra_data_updated) { |
1103 | 1.56k | uint8_t *side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, s->extra_data_size); |
1104 | 1.56k | if (!side_data) |
1105 | 0 | return AVERROR(ENOMEM); |
1106 | 1.56k | memcpy(side_data, s->extra_data, s->extra_data_size); |
1107 | 1.56k | s->extra_data_updated = 1; |
1108 | 1.56k | } |
1109 | | |
1110 | 15.8k | AV_WB32(buf + 0, s->last_frame_fctl.sequence_number); |
1111 | 15.8k | AV_WB32(buf + 4, s->last_frame_fctl.width); |
1112 | 15.8k | AV_WB32(buf + 8, s->last_frame_fctl.height); |
1113 | 15.8k | AV_WB32(buf + 12, s->last_frame_fctl.x_offset); |
1114 | 15.8k | AV_WB32(buf + 16, s->last_frame_fctl.y_offset); |
1115 | 15.8k | AV_WB16(buf + 20, s->last_frame_fctl.delay_num); |
1116 | 15.8k | AV_WB16(buf + 22, s->last_frame_fctl.delay_den); |
1117 | 15.8k | buf[24] = s->last_frame_fctl.dispose_op; |
1118 | 15.8k | buf[25] = s->last_frame_fctl.blend_op; |
1119 | 15.8k | png_write_chunk(&last_fctl_chunk_start, MKTAG('f', 'c', 'T', 'L'), buf, sizeof(buf)); |
1120 | | |
1121 | 15.8k | *got_packet = 1; |
1122 | 15.8k | } |
1123 | | |
1124 | 18.9k | if (pict) { |
1125 | 15.8k | if (!s->last_frame) { |
1126 | 1.56k | s->last_frame = av_frame_alloc(); |
1127 | 1.56k | if (!s->last_frame) |
1128 | 0 | return AVERROR(ENOMEM); |
1129 | 14.2k | } else if (s->last_frame_fctl.dispose_op != APNG_DISPOSE_OP_PREVIOUS) { |
1130 | 13.4k | if (!s->prev_frame) { |
1131 | 879 | s->prev_frame = av_frame_alloc(); |
1132 | 879 | if (!s->prev_frame) |
1133 | 0 | return AVERROR(ENOMEM); |
1134 | | |
1135 | 879 | s->prev_frame->format = pict->format; |
1136 | 879 | s->prev_frame->width = pict->width; |
1137 | 879 | s->prev_frame->height = pict->height; |
1138 | 879 | if ((ret = av_frame_get_buffer(s->prev_frame, 0)) < 0) |
1139 | 0 | return ret; |
1140 | 879 | } |
1141 | | |
1142 | | // Do disposal, but not blending |
1143 | 13.4k | av_frame_copy(s->prev_frame, s->last_frame); |
1144 | 13.4k | if (s->last_frame_fctl.dispose_op == APNG_DISPOSE_OP_BACKGROUND) { |
1145 | 1.61k | uint32_t y; |
1146 | 1.61k | uint8_t bpp = (s->bits_per_pixel + 7) >> 3; |
1147 | 105k | for (y = s->last_frame_fctl.y_offset; y < s->last_frame_fctl.y_offset + s->last_frame_fctl.height; ++y) { |
1148 | 104k | size_t row_start = s->prev_frame->linesize[0] * y + bpp * s->last_frame_fctl.x_offset; |
1149 | 104k | memset(s->prev_frame->data[0] + row_start, 0, bpp * s->last_frame_fctl.width); |
1150 | 104k | } |
1151 | 1.61k | } |
1152 | 13.4k | } |
1153 | | |
1154 | 15.8k | ret = av_frame_replace(s->last_frame, pict); |
1155 | 15.8k | if (ret < 0) |
1156 | 0 | return ret; |
1157 | | |
1158 | 15.8k | s->last_frame_fctl = fctl_chunk; |
1159 | 15.8k | s->last_frame_packet_size = s->bytestream - s->bytestream_start; |
1160 | 15.8k | } else { |
1161 | 3.12k | av_frame_free(&s->last_frame); |
1162 | 3.12k | } |
1163 | | |
1164 | 18.9k | return 0; |
1165 | 18.9k | } |
1166 | | |
1167 | | static av_cold int png_enc_init(AVCodecContext *avctx) |
1168 | 2.68k | { |
1169 | 2.68k | PNGEncContext *s = avctx->priv_data; |
1170 | 2.68k | int compression_level; |
1171 | | |
1172 | 2.68k | switch (avctx->pix_fmt) { |
1173 | 210 | case AV_PIX_FMT_RGBA: |
1174 | 210 | avctx->bits_per_coded_sample = 32; |
1175 | 210 | break; |
1176 | 218 | case AV_PIX_FMT_RGB24: |
1177 | 218 | avctx->bits_per_coded_sample = 24; |
1178 | 218 | break; |
1179 | 278 | case AV_PIX_FMT_GRAY8: |
1180 | 278 | avctx->bits_per_coded_sample = 0x28; |
1181 | 278 | break; |
1182 | 213 | case AV_PIX_FMT_MONOBLACK: |
1183 | 213 | avctx->bits_per_coded_sample = 1; |
1184 | 213 | break; |
1185 | 291 | case AV_PIX_FMT_PAL8: |
1186 | 291 | avctx->bits_per_coded_sample = 8; |
1187 | 2.68k | } |
1188 | | |
1189 | 2.68k | ff_llvidencdsp_init(&s->llvidencdsp); |
1190 | | |
1191 | 2.68k | if (avctx->pix_fmt == AV_PIX_FMT_MONOBLACK) |
1192 | 213 | s->filter_type = PNG_FILTER_VALUE_NONE; |
1193 | | |
1194 | 2.68k | if (s->dpi && s->dpm) { |
1195 | 0 | av_log(avctx, AV_LOG_ERROR, "Only one of 'dpi' or 'dpm' options should be set\n"); |
1196 | 0 | return AVERROR(EINVAL); |
1197 | 2.68k | } else if (s->dpi) { |
1198 | 0 | s->dpm = s->dpi * 10000 / 254; |
1199 | 0 | } |
1200 | | |
1201 | 2.68k | s->is_progressive = !!(avctx->flags & AV_CODEC_FLAG_INTERLACED_DCT); |
1202 | 2.68k | switch (avctx->pix_fmt) { |
1203 | 662 | case AV_PIX_FMT_RGBA64BE: |
1204 | 662 | s->bit_depth = 16; |
1205 | 662 | s->color_type = PNG_COLOR_TYPE_RGB_ALPHA; |
1206 | 662 | break; |
1207 | 290 | case AV_PIX_FMT_RGB48BE: |
1208 | 290 | s->bit_depth = 16; |
1209 | 290 | s->color_type = PNG_COLOR_TYPE_RGB; |
1210 | 290 | break; |
1211 | 210 | case AV_PIX_FMT_RGBA: |
1212 | 210 | s->bit_depth = 8; |
1213 | 210 | s->color_type = PNG_COLOR_TYPE_RGB_ALPHA; |
1214 | 210 | break; |
1215 | 218 | case AV_PIX_FMT_RGB24: |
1216 | 218 | s->bit_depth = 8; |
1217 | 218 | s->color_type = PNG_COLOR_TYPE_RGB; |
1218 | 218 | break; |
1219 | 65 | case AV_PIX_FMT_GRAY16BE: |
1220 | 65 | s->bit_depth = 16; |
1221 | 65 | s->color_type = PNG_COLOR_TYPE_GRAY; |
1222 | 65 | break; |
1223 | 278 | case AV_PIX_FMT_GRAY8: |
1224 | 278 | s->bit_depth = 8; |
1225 | 278 | s->color_type = PNG_COLOR_TYPE_GRAY; |
1226 | 278 | break; |
1227 | 240 | case AV_PIX_FMT_GRAY8A: |
1228 | 240 | s->bit_depth = 8; |
1229 | 240 | s->color_type = PNG_COLOR_TYPE_GRAY_ALPHA; |
1230 | 240 | break; |
1231 | 219 | case AV_PIX_FMT_YA16BE: |
1232 | 219 | s->bit_depth = 16; |
1233 | 219 | s->color_type = PNG_COLOR_TYPE_GRAY_ALPHA; |
1234 | 219 | break; |
1235 | 213 | case AV_PIX_FMT_MONOBLACK: |
1236 | 213 | s->bit_depth = 1; |
1237 | 213 | s->color_type = PNG_COLOR_TYPE_GRAY; |
1238 | 213 | break; |
1239 | 291 | case AV_PIX_FMT_PAL8: |
1240 | 291 | s->bit_depth = 8; |
1241 | 291 | s->color_type = PNG_COLOR_TYPE_PALETTE; |
1242 | 291 | break; |
1243 | 0 | default: |
1244 | 0 | av_unreachable("Already checked via CODEC_PIXFMTS"); |
1245 | 2.68k | } |
1246 | 2.68k | s->bits_per_pixel = ff_png_get_nb_channels(s->color_type) * s->bit_depth; |
1247 | | |
1248 | 2.68k | compression_level = avctx->compression_level == FF_COMPRESSION_DEFAULT |
1249 | 2.68k | ? Z_DEFAULT_COMPRESSION |
1250 | 2.68k | : av_clip(avctx->compression_level, 0, 9); |
1251 | 2.68k | return ff_deflate_init(&s->zstream, compression_level, avctx); |
1252 | 2.68k | } |
1253 | | |
1254 | | static av_cold int png_enc_close(AVCodecContext *avctx) |
1255 | 2.68k | { |
1256 | 2.68k | PNGEncContext *s = avctx->priv_data; |
1257 | | |
1258 | 2.68k | ff_deflate_end(&s->zstream); |
1259 | 2.68k | av_frame_free(&s->last_frame); |
1260 | 2.68k | av_frame_free(&s->prev_frame); |
1261 | 2.68k | av_freep(&s->last_frame_packet); |
1262 | 2.68k | av_freep(&s->extra_data); |
1263 | 2.68k | s->extra_data_size = 0; |
1264 | 2.68k | return 0; |
1265 | 2.68k | } |
1266 | | |
1267 | | #define OFFSET(x) offsetof(PNGEncContext, x) |
1268 | | #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM |
1269 | | static const AVOption options[] = { |
1270 | | {"dpi", "Set image resolution (in dots per inch)", OFFSET(dpi), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 0x10000, VE}, |
1271 | | {"dpm", "Set image resolution (in dots per meter)", OFFSET(dpm), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 0x10000, VE}, |
1272 | | { "pred", "Prediction method", OFFSET(filter_type), AV_OPT_TYPE_INT, { .i64 = PNG_FILTER_VALUE_PAETH }, PNG_FILTER_VALUE_NONE, PNG_FILTER_VALUE_MIXED, VE, .unit = "pred" }, |
1273 | | { "none", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PNG_FILTER_VALUE_NONE }, INT_MIN, INT_MAX, VE, .unit = "pred" }, |
1274 | | { "sub", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PNG_FILTER_VALUE_SUB }, INT_MIN, INT_MAX, VE, .unit = "pred" }, |
1275 | | { "up", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PNG_FILTER_VALUE_UP }, INT_MIN, INT_MAX, VE, .unit = "pred" }, |
1276 | | { "avg", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PNG_FILTER_VALUE_AVG }, INT_MIN, INT_MAX, VE, .unit = "pred" }, |
1277 | | { "paeth", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PNG_FILTER_VALUE_PAETH }, INT_MIN, INT_MAX, VE, .unit = "pred" }, |
1278 | | { "mixed", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PNG_FILTER_VALUE_MIXED }, INT_MIN, INT_MAX, VE, .unit = "pred" }, |
1279 | | { NULL}, |
1280 | | }; |
1281 | | |
1282 | | static const AVClass pngenc_class = { |
1283 | | .class_name = "(A)PNG encoder", |
1284 | | .item_name = av_default_item_name, |
1285 | | .option = options, |
1286 | | .version = LIBAVUTIL_VERSION_INT, |
1287 | | }; |
1288 | | |
1289 | | const FFCodec ff_png_encoder = { |
1290 | | .p.name = "png", |
1291 | | CODEC_LONG_NAME("PNG (Portable Network Graphics) image"), |
1292 | | .p.type = AVMEDIA_TYPE_VIDEO, |
1293 | | .p.id = AV_CODEC_ID_PNG, |
1294 | | .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | |
1295 | | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, |
1296 | | .priv_data_size = sizeof(PNGEncContext), |
1297 | | .init = png_enc_init, |
1298 | | .close = png_enc_close, |
1299 | | FF_CODEC_ENCODE_CB(encode_png), |
1300 | | CODEC_PIXFMTS(AV_PIX_FMT_RGB24, AV_PIX_FMT_RGBA, |
1301 | | AV_PIX_FMT_RGB48BE, AV_PIX_FMT_RGBA64BE, |
1302 | | AV_PIX_FMT_PAL8, |
1303 | | AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY8A, |
1304 | | AV_PIX_FMT_GRAY16BE, AV_PIX_FMT_YA16BE, |
1305 | | AV_PIX_FMT_MONOBLACK), |
1306 | | .alpha_modes = AVALPHA_MODE_STRAIGHT, |
1307 | | .p.priv_class = &pngenc_class, |
1308 | | .caps_internal = FF_CODEC_CAP_ICC_PROFILES, |
1309 | | }; |
1310 | | |
1311 | | const FFCodec ff_apng_encoder = { |
1312 | | .p.name = "apng", |
1313 | | CODEC_LONG_NAME("APNG (Animated Portable Network Graphics) image"), |
1314 | | .p.type = AVMEDIA_TYPE_VIDEO, |
1315 | | .p.id = AV_CODEC_ID_APNG, |
1316 | | .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | |
1317 | | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, |
1318 | | .priv_data_size = sizeof(PNGEncContext), |
1319 | | .init = png_enc_init, |
1320 | | .close = png_enc_close, |
1321 | | FF_CODEC_ENCODE_CB(encode_apng), |
1322 | | CODEC_PIXFMTS(AV_PIX_FMT_RGB24, AV_PIX_FMT_RGBA, |
1323 | | AV_PIX_FMT_RGB48BE, AV_PIX_FMT_RGBA64BE, |
1324 | | AV_PIX_FMT_PAL8, |
1325 | | AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY8A, |
1326 | | AV_PIX_FMT_GRAY16BE, AV_PIX_FMT_YA16BE), |
1327 | | .alpha_modes = AVALPHA_MODE_STRAIGHT, |
1328 | | .p.priv_class = &pngenc_class, |
1329 | | .caps_internal = FF_CODEC_CAP_ICC_PROFILES, |
1330 | | }; |