3 * Copyright (c) 2003 Fabrice Bellard
5 * This file is part of FFmpeg.
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.
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.
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
23 #include "codec_internal.h"
25 #include "bytestream.h"
26 #include "lossless_videoencdsp.h"
29 #include "zlib_wrapper.h"
31 #include "libavutil/avassert.h"
32 #include "libavutil/crc.h"
33 #include "libavutil/csp.h"
34 #include "libavutil/libm.h"
35 #include "libavutil/mastering_display_metadata.h"
36 #include "libavutil/mem.h"
37 #include "libavutil/opt.h"
38 #include "libavutil/rational.h"
39 #include "libavutil/stereo3d.h"
43 #define IOBUF_SIZE 4096
45 typedef struct APNGFctlChunk
{
46 uint32_t sequence_number
;
47 uint32_t width
, height
;
48 uint32_t x_offset
, y_offset
;
49 uint16_t delay_num
, delay_den
;
50 uint8_t dispose_op
, blend_op
;
53 typedef struct PNGEncContext
{
55 LLVidEncDSPContext llvidencdsp
;
58 uint8_t *bytestream_start
;
59 uint8_t *bytestream_end
;
64 uint8_t buf
[IOBUF_SIZE
];
65 int dpi
; ///< Physical pixel density, in dots per inch, if set
66 int dpm
; ///< Physical pixel density, in dots per meter, if set
74 uint32_t palette_checksum
; // Used to ensure a single unique palette
75 uint32_t sequence_number
;
76 int extra_data_updated
;
82 APNGFctlChunk last_frame_fctl
;
83 uint8_t *last_frame_packet
;
84 size_t last_frame_packet_size
;
87 static void png_get_interlaced_row(uint8_t *dst
, int row_size
,
88 int bits_per_pixel
, int pass
,
89 const uint8_t *src
, int width
)
91 int x
, mask
, dst_x
, j
, b
, bpp
;
94 static const int masks
[] = {0x80, 0x08, 0x88, 0x22, 0xaa, 0x55, 0xff};
97 switch (bits_per_pixel
) {
99 memset(dst
, 0, row_size
);
101 for (x
= 0; x
< width
; x
++) {
103 if ((mask
<< j
) & 0x80) {
104 b
= (src
[x
>> 3] >> (7 - j
)) & 1;
105 dst
[dst_x
>> 3] |= b
<< (7 - (dst_x
& 7));
111 bpp
= bits_per_pixel
>> 3;
114 for (x
= 0; x
< width
; x
++) {
116 if ((mask
<< j
) & 0x80) {
126 static void sub_png_paeth_prediction(uint8_t *dst
, const uint8_t *src
, const uint8_t *top
,
130 for (i
= 0; i
< w
; i
++) {
131 int a
, b
, c
, p
, pa
, pb
, pc
;
144 if (pa
<= pb
&& pa
<= pc
)
154 static void sub_left_prediction(PNGEncContext
*c
, uint8_t *dst
, const uint8_t *src
, int bpp
, int size
)
156 const uint8_t *src1
= src
+ bpp
;
157 const uint8_t *src2
= src
;
160 memcpy(dst
, src
, bpp
);
163 unaligned_w
= FFMIN(32 - bpp
, size
);
164 for (x
= 0; x
< unaligned_w
; x
++)
165 *dst
++ = *src1
++ - *src2
++;
167 c
->llvidencdsp
.diff_bytes(dst
, src1
, src2
, size
);
170 static void png_filter_row(PNGEncContext
*c
, uint8_t *dst
, int filter_type
,
171 const uint8_t *src
, const uint8_t *top
, int size
, int bpp
)
175 switch (filter_type
) {
176 case PNG_FILTER_VALUE_NONE
:
177 memcpy(dst
, src
, size
);
179 case PNG_FILTER_VALUE_SUB
:
180 sub_left_prediction(c
, dst
, src
, bpp
, size
);
182 case PNG_FILTER_VALUE_UP
:
183 c
->llvidencdsp
.diff_bytes(dst
, src
, top
, size
);
185 case PNG_FILTER_VALUE_AVG
:
186 for (i
= 0; i
< bpp
; i
++)
187 dst
[i
] = src
[i
] - (top
[i
] >> 1);
188 for (; i
< size
; i
++)
189 dst
[i
] = src
[i
] - ((src
[i
- bpp
] + top
[i
]) >> 1);
191 case PNG_FILTER_VALUE_PAETH
:
192 for (i
= 0; i
< bpp
; i
++)
193 dst
[i
] = src
[i
] - top
[i
];
194 sub_png_paeth_prediction(dst
+ i
, src
+ i
, top
+ i
, size
- i
, bpp
);
199 static uint8_t *png_choose_filter(PNGEncContext
*s
, uint8_t *dst
,
200 const uint8_t *src
, const uint8_t *top
, int size
, int bpp
)
202 int pred
= s
->filter_type
;
203 av_assert0(bpp
|| !pred
);
205 pred
= PNG_FILTER_VALUE_SUB
;
206 if (pred
== PNG_FILTER_VALUE_MIXED
) {
208 int cost
, bcost
= INT_MAX
;
209 uint8_t *buf1
= dst
, *buf2
= dst
+ size
+ 16;
210 for (pred
= 0; pred
< 5; pred
++) {
211 png_filter_row(s
, buf1
+ 1, pred
, src
, top
, size
, bpp
);
214 for (i
= 0; i
<= size
; i
++)
215 cost
+= abs((int8_t) buf1
[i
]);
218 FFSWAP(uint8_t *, buf1
, buf2
);
223 png_filter_row(s
, dst
+ 1, pred
, src
, top
, size
, bpp
);
229 static void png_write_chunk(uint8_t **f
, uint32_t tag
,
230 const uint8_t *buf
, int length
)
232 const AVCRC
*crc_table
= av_crc_get_table(AV_CRC_32_IEEE_LE
);
236 bytestream_put_be32(f
, length
);
237 AV_WL32(tagbuf
, tag
);
238 crc
= av_crc(crc_table
, crc
, tagbuf
, 4);
239 bytestream_put_be32(f
, av_bswap32(tag
));
241 crc
= av_crc(crc_table
, crc
, buf
, length
);
243 memcpy(*f
, buf
, length
);
246 bytestream_put_be32(f
, ~crc
);
249 static void png_write_image_data(AVCodecContext
*avctx
,
250 const uint8_t *buf
, int length
)
252 PNGEncContext
*s
= avctx
->priv_data
;
253 const AVCRC
*crc_table
= av_crc_get_table(AV_CRC_32_IEEE_LE
);
256 if (avctx
->codec_id
== AV_CODEC_ID_PNG
|| avctx
->frame_num
== 0) {
257 png_write_chunk(&s
->bytestream
, MKTAG('I', 'D', 'A', 'T'), buf
, length
);
261 bytestream_put_be32(&s
->bytestream
, length
+ 4);
263 bytestream_put_be32(&s
->bytestream
, MKBETAG('f', 'd', 'A', 'T'));
264 bytestream_put_be32(&s
->bytestream
, s
->sequence_number
);
265 crc
= av_crc(crc_table
, crc
, s
->bytestream
- 8, 8);
267 crc
= av_crc(crc_table
, crc
, buf
, length
);
268 memcpy(s
->bytestream
, buf
, length
);
269 s
->bytestream
+= length
;
271 bytestream_put_be32(&s
->bytestream
, ~crc
);
273 ++s
->sequence_number
;
276 /* XXX: do filtering */
277 static int png_write_row(AVCodecContext
*avctx
, const uint8_t *data
, int size
)
279 PNGEncContext
*s
= avctx
->priv_data
;
280 z_stream
*const zstream
= &s
->zstream
.zstream
;
283 zstream
->avail_in
= size
;
284 zstream
->next_in
= data
;
285 while (zstream
->avail_in
> 0) {
286 ret
= deflate(zstream
, Z_NO_FLUSH
);
289 if (zstream
->avail_out
== 0) {
290 if (s
->bytestream_end
- s
->bytestream
> IOBUF_SIZE
+ 100)
291 png_write_image_data(avctx
, s
->buf
, IOBUF_SIZE
);
292 zstream
->avail_out
= IOBUF_SIZE
;
293 zstream
->next_out
= s
->buf
;
299 #define PNG_LRINT(d, divisor) lrint((d) * (divisor))
300 #define PNG_Q2D(q, divisor) PNG_LRINT(av_q2d(q), (divisor))
301 #define AV_WB32_PNG_D(buf, q) AV_WB32(buf, PNG_Q2D(q, 100000))
302 static int png_get_chrm(enum AVColorPrimaries prim
, uint8_t *buf
)
304 const AVColorPrimariesDesc
*desc
= av_csp_primaries_desc_from_id(prim
);
308 AV_WB32_PNG_D(buf
, desc
->wp
.x
);
309 AV_WB32_PNG_D(buf
+ 4, desc
->wp
.y
);
310 AV_WB32_PNG_D(buf
+ 8, desc
->prim
.r
.x
);
311 AV_WB32_PNG_D(buf
+ 12, desc
->prim
.r
.y
);
312 AV_WB32_PNG_D(buf
+ 16, desc
->prim
.g
.x
);
313 AV_WB32_PNG_D(buf
+ 20, desc
->prim
.g
.y
);
314 AV_WB32_PNG_D(buf
+ 24, desc
->prim
.b
.x
);
315 AV_WB32_PNG_D(buf
+ 28, desc
->prim
.b
.y
);
320 static int png_get_gama(enum AVColorTransferCharacteristic trc
, uint8_t *buf
)
322 double gamma
= av_csp_approximate_trc_gamma(trc
);
326 AV_WB32(buf
, PNG_LRINT(1.0 / gamma
, 100000));
330 static int png_write_iccp(PNGEncContext
*s
, const AVFrameSideData
*sd
)
332 z_stream
*const zstream
= &s
->zstream
.zstream
;
333 const AVDictionaryEntry
*entry
;
335 uint8_t *start
, *buf
;
338 if (!sd
|| !sd
->size
)
340 zstream
->next_in
= sd
->data
;
341 zstream
->avail_in
= sd
->size
;
343 /* write the chunk contents first */
344 start
= s
->bytestream
+ 8; /* make room for iCCP tag + length */
347 /* profile description */
348 entry
= av_dict_get(sd
->metadata
, "name", NULL
, 0);
349 name
= (entry
&& entry
->value
[0]) ? entry
->value
: "icc";
350 for (int i
= 0;; i
++) {
351 char c
= (i
== 79) ? 0 : name
[i
];
352 bytestream_put_byte(&buf
, c
);
357 /* compression method and profile data */
358 bytestream_put_byte(&buf
, 0);
359 zstream
->next_out
= buf
;
360 zstream
->avail_out
= s
->bytestream_end
- buf
;
361 ret
= deflate(zstream
, Z_FINISH
);
362 deflateReset(zstream
);
363 if (ret
!= Z_STREAM_END
)
364 return AVERROR_EXTERNAL
;
366 /* rewind to the start and write the chunk header/crc */
367 png_write_chunk(&s
->bytestream
, MKTAG('i', 'C', 'C', 'P'), start
,
368 zstream
->next_out
- start
);
372 static int encode_headers(AVCodecContext
*avctx
, const AVFrame
*pict
)
374 AVFrameSideData
*side_data
;
375 PNGEncContext
*s
= avctx
->priv_data
;
378 /* write png header */
379 AV_WB32(s
->buf
, avctx
->width
);
380 AV_WB32(s
->buf
+ 4, avctx
->height
);
381 s
->buf
[8] = s
->bit_depth
;
382 s
->buf
[9] = s
->color_type
;
383 s
->buf
[10] = 0; /* compression type */
384 s
->buf
[11] = 0; /* filter type */
385 s
->buf
[12] = s
->is_progressive
; /* interlace type */
386 png_write_chunk(&s
->bytestream
, MKTAG('I', 'H', 'D', 'R'), s
->buf
, 13);
388 /* write physical information */
390 AV_WB32(s
->buf
, s
->dpm
);
391 AV_WB32(s
->buf
+ 4, s
->dpm
);
392 s
->buf
[8] = 1; /* unit specifier is meter */
394 AV_WB32(s
->buf
, avctx
->sample_aspect_ratio
.num
);
395 AV_WB32(s
->buf
+ 4, avctx
->sample_aspect_ratio
.den
);
396 s
->buf
[8] = 0; /* unit specifier is unknown */
398 png_write_chunk(&s
->bytestream
, MKTAG('p', 'H', 'Y', 's'), s
->buf
, 9);
400 /* write stereoscopic information */
401 side_data
= av_frame_get_side_data(pict
, AV_FRAME_DATA_STEREO3D
);
403 AVStereo3D
*stereo3d
= (AVStereo3D
*)side_data
->data
;
404 switch (stereo3d
->type
) {
405 case AV_STEREO3D_SIDEBYSIDE
:
406 s
->buf
[0] = ((stereo3d
->flags
& AV_STEREO3D_FLAG_INVERT
) == 0) ? 1 : 0;
407 png_write_chunk(&s
->bytestream
, MKTAG('s', 'T', 'E', 'R'), s
->buf
, 1);
412 av_log(avctx
, AV_LOG_WARNING
, "Only side-by-side stereo3d flag can be defined within sTER chunk\n");
417 side_data
= av_frame_get_side_data(pict
, AV_FRAME_DATA_ICC_PROFILE
);
418 if ((ret
= png_write_iccp(s
, side_data
)))
421 /* write colorspace information */
422 if (pict
->color_primaries
== AVCOL_PRI_BT709
&&
423 pict
->color_trc
== AVCOL_TRC_IEC61966_2_1
) {
424 s
->buf
[0] = 1; /* rendering intent, relative colorimetric by default */
425 png_write_chunk(&s
->bytestream
, MKTAG('s', 'R', 'G', 'B'), s
->buf
, 1);
426 } else if (pict
->color_trc
!= AVCOL_TRC_UNSPECIFIED
&& !side_data
) {
428 * Avoid writing cICP if the transfer is unknown. Known primaries
429 * with unknown transfer can be handled by cHRM.
431 * We also avoid writing cICP if an ICC Profile is present, because
432 * the standard requires that cICP overrides iCCP.
434 * These values match H.273 so no translation is needed.
436 s
->buf
[0] = pict
->color_primaries
;
437 s
->buf
[1] = pict
->color_trc
;
438 s
->buf
[2] = 0; /* colorspace = RGB */
439 s
->buf
[3] = pict
->color_range
== AVCOL_RANGE_MPEG
? 0 : 1;
440 png_write_chunk(&s
->bytestream
, MKTAG('c', 'I', 'C', 'P'), s
->buf
, 4);
443 side_data
= av_frame_get_side_data(pict
, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL
);
445 AVContentLightMetadata
*clli
= (AVContentLightMetadata
*) side_data
->data
;
446 AV_WB32(s
->buf
, clli
->MaxCLL
* 10000);
447 AV_WB32(s
->buf
+ 4, clli
->MaxFALL
* 10000);
448 png_write_chunk(&s
->bytestream
, MKTAG('c', 'L', 'L', 'I'), s
->buf
, 8);
451 side_data
= av_frame_get_side_data(pict
, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA
);
453 AVMasteringDisplayMetadata
*mdcv
= (AVMasteringDisplayMetadata
*) side_data
->data
;
454 if (mdcv
->has_luminance
&& mdcv
->has_primaries
) {
455 for (int i
= 0; i
< 3; i
++) {
456 AV_WB16(s
->buf
+ 2*i
, PNG_Q2D(mdcv
->display_primaries
[i
][0], 50000));
457 AV_WB16(s
->buf
+ 2*i
+ 2, PNG_Q2D(mdcv
->display_primaries
[i
][1], 50000));
459 AV_WB16(s
->buf
+ 12, PNG_Q2D(mdcv
->white_point
[0], 50000));
460 AV_WB16(s
->buf
+ 14, PNG_Q2D(mdcv
->white_point
[1], 50000));
461 AV_WB32(s
->buf
+ 16, PNG_Q2D(mdcv
->max_luminance
, 10000));
462 AV_WB32(s
->buf
+ 20, PNG_Q2D(mdcv
->min_luminance
, 10000));
463 png_write_chunk(&s
->bytestream
, MKTAG('m', 'D', 'C', 'V'), s
->buf
, 24);
467 if (png_get_chrm(pict
->color_primaries
, s
->buf
))
468 png_write_chunk(&s
->bytestream
, MKTAG('c', 'H', 'R', 'M'), s
->buf
, 32);
469 if (png_get_gama(pict
->color_trc
, s
->buf
))
470 png_write_chunk(&s
->bytestream
, MKTAG('g', 'A', 'M', 'A'), s
->buf
, 4);
472 if (avctx
->bits_per_raw_sample
> 0 &&
473 avctx
->bits_per_raw_sample
< (s
->color_type
& PNG_COLOR_MASK_PALETTE
? 8 : s
->bit_depth
)) {
474 int len
= s
->color_type
& PNG_COLOR_MASK_PALETTE
? 3 : ff_png_get_nb_channels(s
->color_type
);
475 memset(s
->buf
, avctx
->bits_per_raw_sample
, len
);
476 png_write_chunk(&s
->bytestream
, MKTAG('s', 'B', 'I', 'T'), s
->buf
, len
);
479 /* put the palette if needed, must be after colorspace information */
480 if (s
->color_type
== PNG_COLOR_TYPE_PALETTE
) {
481 int has_alpha
, alpha
, i
;
484 uint8_t *ptr
, *alpha_ptr
;
486 palette
= (uint32_t *)pict
->data
[1];
488 alpha_ptr
= s
->buf
+ 256 * 3;
490 for (i
= 0; i
< 256; i
++) {
495 *alpha_ptr
++ = alpha
;
496 bytestream_put_be24(&ptr
, v
);
498 png_write_chunk(&s
->bytestream
,
499 MKTAG('P', 'L', 'T', 'E'), s
->buf
, 256 * 3);
501 png_write_chunk(&s
->bytestream
,
502 MKTAG('t', 'R', 'N', 'S'), s
->buf
+ 256 * 3, 256);
509 static int encode_frame(AVCodecContext
*avctx
, const AVFrame
*pict
)
511 PNGEncContext
*s
= avctx
->priv_data
;
512 z_stream
*const zstream
= &s
->zstream
.zstream
;
513 const AVFrame
*const p
= pict
;
515 int row_size
, pass_row_size
;
516 uint8_t *crow_buf
, *crow
;
517 uint8_t *crow_base
= NULL
;
518 uint8_t *progressive_buf
= NULL
;
519 uint8_t *top_buf
= NULL
;
521 row_size
= (pict
->width
* s
->bits_per_pixel
+ 7) >> 3;
523 crow_base
= av_malloc((row_size
+ 32) << (s
->filter_type
== PNG_FILTER_VALUE_MIXED
));
525 ret
= AVERROR(ENOMEM
);
528 // pixel data should be aligned, but there's a control byte before it
529 crow_buf
= crow_base
+ 15;
530 if (s
->is_progressive
) {
531 progressive_buf
= av_malloc(row_size
+ 1);
532 top_buf
= av_malloc(row_size
+ 1);
533 if (!progressive_buf
|| !top_buf
) {
534 ret
= AVERROR(ENOMEM
);
540 zstream
->avail_out
= IOBUF_SIZE
;
541 zstream
->next_out
= s
->buf
;
542 if (s
->is_progressive
) {
545 for (pass
= 0; pass
< NB_PASSES
; pass
++) {
546 /* NOTE: a pass is completely omitted if no pixels would be
548 pass_row_size
= ff_png_pass_row_size(pass
, s
->bits_per_pixel
, pict
->width
);
549 if (pass_row_size
> 0) {
551 for (y
= 0; y
< pict
->height
; y
++)
552 if ((ff_png_pass_ymask
[pass
] << (y
& 7)) & 0x80) {
553 const uint8_t *ptr
= p
->data
[0] + y
* p
->linesize
[0];
554 FFSWAP(uint8_t *, progressive_buf
, top_buf
);
555 png_get_interlaced_row(progressive_buf
, pass_row_size
,
556 s
->bits_per_pixel
, pass
,
558 crow
= png_choose_filter(s
, crow_buf
, progressive_buf
,
559 top
, pass_row_size
, s
->bits_per_pixel
>> 3);
560 png_write_row(avctx
, crow
, pass_row_size
+ 1);
561 top
= progressive_buf
;
566 const uint8_t *top
= NULL
;
567 for (y
= 0; y
< pict
->height
; y
++) {
568 const uint8_t *ptr
= p
->data
[0] + y
* p
->linesize
[0];
569 crow
= png_choose_filter(s
, crow_buf
, ptr
, top
,
570 row_size
, s
->bits_per_pixel
>> 3);
571 png_write_row(avctx
, crow
, row_size
+ 1);
575 /* compress last bytes */
577 ret
= deflate(zstream
, Z_FINISH
);
578 if (ret
== Z_OK
|| ret
== Z_STREAM_END
) {
579 len
= IOBUF_SIZE
- zstream
->avail_out
;
580 if (len
> 0 && s
->bytestream_end
- s
->bytestream
> len
+ 100) {
581 png_write_image_data(avctx
, s
->buf
, len
);
583 zstream
->avail_out
= IOBUF_SIZE
;
584 zstream
->next_out
= s
->buf
;
585 if (ret
== Z_STREAM_END
)
596 av_freep(&crow_base
);
597 av_freep(&progressive_buf
);
599 deflateReset(zstream
);
603 static int add_icc_profile_size(AVCodecContext
*avctx
, const AVFrame
*pict
,
604 uint64_t *max_packet_size
)
606 PNGEncContext
*s
= avctx
->priv_data
;
607 const AVFrameSideData
*sd
;
608 const int hdr_size
= 128;
609 uint64_t new_pkt_size
;
614 sd
= av_frame_get_side_data(pict
, AV_FRAME_DATA_ICC_PROFILE
);
615 if (!sd
|| !sd
->size
)
617 if (sd
->size
!= (uLong
) sd
->size
)
618 return AVERROR_INVALIDDATA
;
620 bound
= deflateBound(&s
->zstream
.zstream
, sd
->size
);
621 if (bound
> INT32_MAX
- hdr_size
)
622 return AVERROR_INVALIDDATA
;
624 new_pkt_size
= *max_packet_size
+ bound
+ hdr_size
;
625 if (new_pkt_size
< *max_packet_size
)
626 return AVERROR_INVALIDDATA
;
627 *max_packet_size
= new_pkt_size
;
631 static int encode_png(AVCodecContext
*avctx
, AVPacket
*pkt
,
632 const AVFrame
*pict
, int *got_packet
)
634 PNGEncContext
*s
= avctx
->priv_data
;
637 uint64_t max_packet_size
;
639 enc_row_size
= deflateBound(&s
->zstream
.zstream
,
640 (avctx
->width
* s
->bits_per_pixel
+ 7) >> 3);
642 FF_INPUT_BUFFER_MIN_SIZE
+ // headers
645 12 * (((int64_t)enc_row_size
+ IOBUF_SIZE
- 1) / IOBUF_SIZE
) // IDAT * ceil(enc_row_size / IOBUF_SIZE)
647 if ((ret
= add_icc_profile_size(avctx
, pict
, &max_packet_size
)))
649 ret
= ff_alloc_packet(avctx
, pkt
, max_packet_size
);
653 s
->bytestream_start
=
654 s
->bytestream
= pkt
->data
;
655 s
->bytestream_end
= pkt
->data
+ pkt
->size
;
657 AV_WB64(s
->bytestream
, PNGSIG
);
660 ret
= encode_headers(avctx
, pict
);
664 ret
= encode_frame(avctx
, pict
);
668 png_write_chunk(&s
->bytestream
, MKTAG('I', 'E', 'N', 'D'), NULL
, 0);
670 pkt
->size
= s
->bytestream
- s
->bytestream_start
;
671 pkt
->flags
|= AV_PKT_FLAG_KEY
;
677 static int apng_do_inverse_blend(AVFrame
*output
, const AVFrame
*input
,
678 APNGFctlChunk
*fctl_chunk
, uint8_t bpp
)
680 // output: background, input: foreground
681 // output the image such that when blended with the background, will produce the foreground
684 unsigned int leftmost_x
= input
->width
;
685 unsigned int rightmost_x
= 0;
686 unsigned int topmost_y
= input
->height
;
687 unsigned int bottommost_y
= 0;
688 const uint8_t *input_data
= input
->data
[0];
689 uint8_t *output_data
= output
->data
[0];
690 ptrdiff_t input_linesize
= input
->linesize
[0];
691 ptrdiff_t output_linesize
= output
->linesize
[0];
693 // Find bounding box of changes
694 for (y
= 0; y
< input
->height
; ++y
) {
695 for (x
= 0; x
< input
->width
; ++x
) {
696 if (!memcmp(input_data
+ bpp
* x
, output_data
+ bpp
* x
, bpp
))
701 if (x
>= rightmost_x
)
705 if (y
>= bottommost_y
)
706 bottommost_y
= y
+ 1;
709 input_data
+= input_linesize
;
710 output_data
+= output_linesize
;
713 if (leftmost_x
== input
->width
&& rightmost_x
== 0) {
715 // APNG does not support empty frames, so we make it a 1x1 frame
716 leftmost_x
= topmost_y
= 0;
717 rightmost_x
= bottommost_y
= 1;
720 // Do actual inverse blending
721 if (fctl_chunk
->blend_op
== APNG_BLEND_OP_SOURCE
) {
722 output_data
= output
->data
[0];
723 for (y
= topmost_y
; y
< bottommost_y
; ++y
) {
725 input
->data
[0] + input_linesize
* y
+ bpp
* leftmost_x
,
726 bpp
* (rightmost_x
- leftmost_x
));
727 output_data
+= output_linesize
;
729 } else { // APNG_BLEND_OP_OVER
730 size_t transparent_palette_index
;
733 switch (input
->format
) {
734 case AV_PIX_FMT_RGBA64BE
:
735 case AV_PIX_FMT_YA16BE
:
736 case AV_PIX_FMT_RGBA
:
737 case AV_PIX_FMT_GRAY8A
:
740 case AV_PIX_FMT_PAL8
:
741 palette
= (uint32_t*)input
->data
[1];
742 for (transparent_palette_index
= 0; transparent_palette_index
< 256; ++transparent_palette_index
)
743 if (palette
[transparent_palette_index
] >> 24 == 0)
748 // No alpha, so blending not possible
752 for (y
= topmost_y
; y
< bottommost_y
; ++y
) {
753 const uint8_t *foreground
= input
->data
[0] + input_linesize
* y
+ bpp
* leftmost_x
;
754 uint8_t *background
= output
->data
[0] + output_linesize
* y
+ bpp
* leftmost_x
;
755 output_data
= output
->data
[0] + output_linesize
* (y
- topmost_y
);
756 for (x
= leftmost_x
; x
< rightmost_x
; ++x
, foreground
+= bpp
, background
+= bpp
, output_data
+= bpp
) {
757 if (!memcmp(foreground
, background
, bpp
)) {
758 if (input
->format
== AV_PIX_FMT_PAL8
) {
759 if (transparent_palette_index
== 256) {
760 // Need fully transparent colour, but none exists
764 *output_data
= transparent_palette_index
;
766 memset(output_data
, 0, bpp
);
771 // Check for special alpha values, since full inverse
772 // alpha-on-alpha blending is rarely possible, and when
773 // possible, doesn't compress much better than
774 // APNG_BLEND_OP_SOURCE blending
775 switch (input
->format
) {
776 case AV_PIX_FMT_RGBA64BE
:
777 if (((uint16_t*)foreground
)[3] == 0xffff ||
778 ((uint16_t*)background
)[3] == 0)
782 case AV_PIX_FMT_YA16BE
:
783 if (((uint16_t*)foreground
)[1] == 0xffff ||
784 ((uint16_t*)background
)[1] == 0)
788 case AV_PIX_FMT_RGBA
:
789 if (foreground
[3] == 0xff || background
[3] == 0)
793 case AV_PIX_FMT_GRAY8A
:
794 if (foreground
[1] == 0xff || background
[1] == 0)
798 case AV_PIX_FMT_PAL8
:
799 if (palette
[*foreground
] >> 24 == 0xff ||
800 palette
[*background
] >> 24 == 0)
805 memmove(output_data
, foreground
, bpp
);
810 output
->width
= rightmost_x
- leftmost_x
;
811 output
->height
= bottommost_y
- topmost_y
;
812 fctl_chunk
->width
= output
->width
;
813 fctl_chunk
->height
= output
->height
;
814 fctl_chunk
->x_offset
= leftmost_x
;
815 fctl_chunk
->y_offset
= topmost_y
;
820 static int apng_encode_frame(AVCodecContext
*avctx
, const AVFrame
*pict
,
821 APNGFctlChunk
*best_fctl_chunk
, APNGFctlChunk
*best_last_fctl_chunk
)
823 PNGEncContext
*s
= avctx
->priv_data
;
827 uint8_t bpp
= (s
->bits_per_pixel
+ 7) >> 3;
828 uint8_t *original_bytestream
, *original_bytestream_end
;
829 uint8_t *temp_bytestream
= 0, *temp_bytestream_end
;
830 uint32_t best_sequence_number
;
831 uint8_t *best_bytestream
;
832 size_t best_bytestream_size
= SIZE_MAX
;
833 APNGFctlChunk last_fctl_chunk
= *best_last_fctl_chunk
;
834 APNGFctlChunk fctl_chunk
= *best_fctl_chunk
;
836 if (avctx
->frame_num
== 0) {
837 best_fctl_chunk
->width
= pict
->width
;
838 best_fctl_chunk
->height
= pict
->height
;
839 best_fctl_chunk
->x_offset
= 0;
840 best_fctl_chunk
->y_offset
= 0;
841 best_fctl_chunk
->blend_op
= APNG_BLEND_OP_SOURCE
;
842 return encode_frame(avctx
, pict
);
845 diffFrame
= av_frame_alloc();
847 return AVERROR(ENOMEM
);
849 diffFrame
->format
= pict
->format
;
850 diffFrame
->width
= pict
->width
;
851 diffFrame
->height
= pict
->height
;
852 if ((ret
= av_frame_get_buffer(diffFrame
, 0)) < 0)
855 original_bytestream
= s
->bytestream
;
856 original_bytestream_end
= s
->bytestream_end
;
858 temp_bytestream
= av_malloc(original_bytestream_end
- original_bytestream
);
859 if (!temp_bytestream
) {
860 ret
= AVERROR(ENOMEM
);
863 temp_bytestream_end
= temp_bytestream
+ (original_bytestream_end
- original_bytestream
);
865 for (last_fctl_chunk
.dispose_op
= 0; last_fctl_chunk
.dispose_op
< 3; ++last_fctl_chunk
.dispose_op
) {
866 // 0: APNG_DISPOSE_OP_NONE
867 // 1: APNG_DISPOSE_OP_BACKGROUND
868 // 2: APNG_DISPOSE_OP_PREVIOUS
870 for (fctl_chunk
.blend_op
= 0; fctl_chunk
.blend_op
< 2; ++fctl_chunk
.blend_op
) {
871 // 0: APNG_BLEND_OP_SOURCE
872 // 1: APNG_BLEND_OP_OVER
874 uint32_t original_sequence_number
= s
->sequence_number
, sequence_number
;
875 uint8_t *bytestream_start
= s
->bytestream
;
876 size_t bytestream_size
;
879 if (last_fctl_chunk
.dispose_op
!= APNG_DISPOSE_OP_PREVIOUS
) {
880 diffFrame
->width
= pict
->width
;
881 diffFrame
->height
= pict
->height
;
882 ret
= av_frame_copy(diffFrame
, s
->last_frame
);
886 if (last_fctl_chunk
.dispose_op
== APNG_DISPOSE_OP_BACKGROUND
) {
887 for (y
= last_fctl_chunk
.y_offset
; y
< last_fctl_chunk
.y_offset
+ last_fctl_chunk
.height
; ++y
) {
888 size_t row_start
= diffFrame
->linesize
[0] * y
+ bpp
* last_fctl_chunk
.x_offset
;
889 memset(diffFrame
->data
[0] + row_start
, 0, bpp
* last_fctl_chunk
.width
);
896 diffFrame
->width
= pict
->width
;
897 diffFrame
->height
= pict
->height
;
898 ret
= av_frame_copy(diffFrame
, s
->prev_frame
);
903 // Do inverse blending
904 if (apng_do_inverse_blend(diffFrame
, pict
, &fctl_chunk
, bpp
) < 0)
908 ret
= encode_frame(avctx
, diffFrame
);
909 sequence_number
= s
->sequence_number
;
910 s
->sequence_number
= original_sequence_number
;
911 bytestream_size
= s
->bytestream
- bytestream_start
;
912 s
->bytestream
= bytestream_start
;
916 if (bytestream_size
< best_bytestream_size
) {
917 *best_fctl_chunk
= fctl_chunk
;
918 *best_last_fctl_chunk
= last_fctl_chunk
;
920 best_sequence_number
= sequence_number
;
921 best_bytestream
= s
->bytestream
;
922 best_bytestream_size
= bytestream_size
;
924 if (best_bytestream
== original_bytestream
) {
925 s
->bytestream
= temp_bytestream
;
926 s
->bytestream_end
= temp_bytestream_end
;
928 s
->bytestream
= original_bytestream
;
929 s
->bytestream_end
= original_bytestream_end
;
935 s
->sequence_number
= best_sequence_number
;
936 s
->bytestream
= original_bytestream
+ best_bytestream_size
;
937 s
->bytestream_end
= original_bytestream_end
;
938 if (best_bytestream
!= original_bytestream
)
939 memcpy(original_bytestream
, best_bytestream
, best_bytestream_size
);
944 av_freep(&temp_bytestream
);
945 av_frame_free(&diffFrame
);
949 static int encode_apng(AVCodecContext
*avctx
, AVPacket
*pkt
,
950 const AVFrame
*pict
, int *got_packet
)
952 PNGEncContext
*s
= avctx
->priv_data
;
955 uint64_t max_packet_size
;
956 APNGFctlChunk fctl_chunk
= {0};
958 if (pict
&& s
->color_type
== PNG_COLOR_TYPE_PALETTE
) {
959 uint32_t checksum
= ~av_crc(av_crc_get_table(AV_CRC_32_IEEE_LE
), ~0U, pict
->data
[1], 256 * sizeof(uint32_t));
961 if (avctx
->frame_num
== 0) {
962 s
->palette_checksum
= checksum
;
963 } else if (checksum
!= s
->palette_checksum
) {
964 av_log(avctx
, AV_LOG_ERROR
,
965 "Input contains more than one unique palette. APNG does not support multiple palettes.\n");
970 enc_row_size
= deflateBound(&s
->zstream
.zstream
,
971 (avctx
->width
* s
->bits_per_pixel
+ 7) >> 3);
973 FF_INPUT_BUFFER_MIN_SIZE
+ // headers
976 (4 + 12) * (((int64_t)enc_row_size
+ IOBUF_SIZE
- 1) / IOBUF_SIZE
) // fdAT * ceil(enc_row_size / IOBUF_SIZE)
978 if ((ret
= add_icc_profile_size(avctx
, pict
, &max_packet_size
)))
980 if (max_packet_size
> INT_MAX
)
981 return AVERROR(ENOMEM
);
983 if (avctx
->frame_num
== 0) {
985 return AVERROR(EINVAL
);
987 s
->bytestream
= s
->extra_data
= av_malloc(FF_INPUT_BUFFER_MIN_SIZE
);
989 return AVERROR(ENOMEM
);
991 ret
= encode_headers(avctx
, pict
);
995 s
->extra_data_size
= s
->bytestream
- s
->extra_data
;
997 s
->last_frame_packet
= av_malloc(max_packet_size
);
998 if (!s
->last_frame_packet
)
999 return AVERROR(ENOMEM
);
1000 } else if (s
->last_frame
) {
1001 ret
= ff_get_encode_buffer(avctx
, pkt
, s
->last_frame_packet_size
, 0);
1005 memcpy(pkt
->data
, s
->last_frame_packet
, s
->last_frame_packet_size
);
1006 pkt
->pts
= s
->last_frame
->pts
;
1007 pkt
->duration
= s
->last_frame
->duration
;
1009 ret
= ff_encode_reordered_opaque(avctx
, pkt
, s
->last_frame
);
1015 s
->bytestream_start
=
1016 s
->bytestream
= s
->last_frame_packet
;
1017 s
->bytestream_end
= s
->bytestream
+ max_packet_size
;
1019 // We're encoding the frame first, so we have to do a bit of shuffling around
1020 // to have the image data write to the correct place in the buffer
1021 fctl_chunk
.sequence_number
= s
->sequence_number
;
1022 ++s
->sequence_number
;
1023 s
->bytestream
+= APNG_FCTL_CHUNK_SIZE
+ 12;
1025 ret
= apng_encode_frame(avctx
, pict
, &fctl_chunk
, &s
->last_frame_fctl
);
1029 fctl_chunk
.delay_num
= 0; // delay filled in during muxing
1030 fctl_chunk
.delay_den
= 0;
1032 s
->last_frame_fctl
.dispose_op
= APNG_DISPOSE_OP_NONE
;
1035 if (s
->last_frame
) {
1036 uint8_t* last_fctl_chunk_start
= pkt
->data
;
1037 uint8_t buf
[APNG_FCTL_CHUNK_SIZE
];
1038 if (!s
->extra_data_updated
) {
1039 uint8_t *side_data
= av_packet_new_side_data(pkt
, AV_PKT_DATA_NEW_EXTRADATA
, s
->extra_data_size
);
1041 return AVERROR(ENOMEM
);
1042 memcpy(side_data
, s
->extra_data
, s
->extra_data_size
);
1043 s
->extra_data_updated
= 1;
1046 AV_WB32(buf
+ 0, s
->last_frame_fctl
.sequence_number
);
1047 AV_WB32(buf
+ 4, s
->last_frame_fctl
.width
);
1048 AV_WB32(buf
+ 8, s
->last_frame_fctl
.height
);
1049 AV_WB32(buf
+ 12, s
->last_frame_fctl
.x_offset
);
1050 AV_WB32(buf
+ 16, s
->last_frame_fctl
.y_offset
);
1051 AV_WB16(buf
+ 20, s
->last_frame_fctl
.delay_num
);
1052 AV_WB16(buf
+ 22, s
->last_frame_fctl
.delay_den
);
1053 buf
[24] = s
->last_frame_fctl
.dispose_op
;
1054 buf
[25] = s
->last_frame_fctl
.blend_op
;
1055 png_write_chunk(&last_fctl_chunk_start
, MKTAG('f', 'c', 'T', 'L'), buf
, sizeof(buf
));
1061 if (!s
->last_frame
) {
1062 s
->last_frame
= av_frame_alloc();
1064 return AVERROR(ENOMEM
);
1065 } else if (s
->last_frame_fctl
.dispose_op
!= APNG_DISPOSE_OP_PREVIOUS
) {
1066 if (!s
->prev_frame
) {
1067 s
->prev_frame
= av_frame_alloc();
1069 return AVERROR(ENOMEM
);
1071 s
->prev_frame
->format
= pict
->format
;
1072 s
->prev_frame
->width
= pict
->width
;
1073 s
->prev_frame
->height
= pict
->height
;
1074 if ((ret
= av_frame_get_buffer(s
->prev_frame
, 0)) < 0)
1078 // Do disposal, but not blending
1079 av_frame_copy(s
->prev_frame
, s
->last_frame
);
1080 if (s
->last_frame_fctl
.dispose_op
== APNG_DISPOSE_OP_BACKGROUND
) {
1082 uint8_t bpp
= (s
->bits_per_pixel
+ 7) >> 3;
1083 for (y
= s
->last_frame_fctl
.y_offset
; y
< s
->last_frame_fctl
.y_offset
+ s
->last_frame_fctl
.height
; ++y
) {
1084 size_t row_start
= s
->prev_frame
->linesize
[0] * y
+ bpp
* s
->last_frame_fctl
.x_offset
;
1085 memset(s
->prev_frame
->data
[0] + row_start
, 0, bpp
* s
->last_frame_fctl
.width
);
1090 ret
= av_frame_replace(s
->last_frame
, pict
);
1094 s
->last_frame_fctl
= fctl_chunk
;
1095 s
->last_frame_packet_size
= s
->bytestream
- s
->bytestream_start
;
1097 av_frame_free(&s
->last_frame
);
1103 static av_cold
int png_enc_init(AVCodecContext
*avctx
)
1105 PNGEncContext
*s
= avctx
->priv_data
;
1106 int compression_level
;
1108 switch (avctx
->pix_fmt
) {
1109 case AV_PIX_FMT_RGBA
:
1110 avctx
->bits_per_coded_sample
= 32;
1112 case AV_PIX_FMT_RGB24
:
1113 avctx
->bits_per_coded_sample
= 24;
1115 case AV_PIX_FMT_GRAY8
:
1116 avctx
->bits_per_coded_sample
= 0x28;
1118 case AV_PIX_FMT_MONOBLACK
:
1119 avctx
->bits_per_coded_sample
= 1;
1121 case AV_PIX_FMT_PAL8
:
1122 avctx
->bits_per_coded_sample
= 8;
1125 ff_llvidencdsp_init(&s
->llvidencdsp
);
1127 if (avctx
->pix_fmt
== AV_PIX_FMT_MONOBLACK
)
1128 s
->filter_type
= PNG_FILTER_VALUE_NONE
;
1130 if (s
->dpi
&& s
->dpm
) {
1131 av_log(avctx
, AV_LOG_ERROR
, "Only one of 'dpi' or 'dpm' options should be set\n");
1132 return AVERROR(EINVAL
);
1133 } else if (s
->dpi
) {
1134 s
->dpm
= s
->dpi
* 10000 / 254;
1137 s
->is_progressive
= !!(avctx
->flags
& AV_CODEC_FLAG_INTERLACED_DCT
);
1138 switch (avctx
->pix_fmt
) {
1139 case AV_PIX_FMT_RGBA64BE
:
1141 s
->color_type
= PNG_COLOR_TYPE_RGB_ALPHA
;
1143 case AV_PIX_FMT_RGB48BE
:
1145 s
->color_type
= PNG_COLOR_TYPE_RGB
;
1147 case AV_PIX_FMT_RGBA
:
1149 s
->color_type
= PNG_COLOR_TYPE_RGB_ALPHA
;
1151 case AV_PIX_FMT_RGB24
:
1153 s
->color_type
= PNG_COLOR_TYPE_RGB
;
1155 case AV_PIX_FMT_GRAY16BE
:
1157 s
->color_type
= PNG_COLOR_TYPE_GRAY
;
1159 case AV_PIX_FMT_GRAY8
:
1161 s
->color_type
= PNG_COLOR_TYPE_GRAY
;
1163 case AV_PIX_FMT_GRAY8A
:
1165 s
->color_type
= PNG_COLOR_TYPE_GRAY_ALPHA
;
1167 case AV_PIX_FMT_YA16BE
:
1169 s
->color_type
= PNG_COLOR_TYPE_GRAY_ALPHA
;
1171 case AV_PIX_FMT_MONOBLACK
:
1173 s
->color_type
= PNG_COLOR_TYPE_GRAY
;
1175 case AV_PIX_FMT_PAL8
:
1177 s
->color_type
= PNG_COLOR_TYPE_PALETTE
;
1182 s
->bits_per_pixel
= ff_png_get_nb_channels(s
->color_type
) * s
->bit_depth
;
1184 compression_level
= avctx
->compression_level
== FF_COMPRESSION_DEFAULT
1185 ? Z_DEFAULT_COMPRESSION
1186 : av_clip(avctx
->compression_level
, 0, 9);
1187 return ff_deflate_init(&s
->zstream
, compression_level
, avctx
);
1190 static av_cold
int png_enc_close(AVCodecContext
*avctx
)
1192 PNGEncContext
*s
= avctx
->priv_data
;
1194 ff_deflate_end(&s
->zstream
);
1195 av_frame_free(&s
->last_frame
);
1196 av_frame_free(&s
->prev_frame
);
1197 av_freep(&s
->last_frame_packet
);
1198 av_freep(&s
->extra_data
);
1199 s
->extra_data_size
= 0;
1203 #define OFFSET(x) offsetof(PNGEncContext, x)
1204 #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
1205 static const AVOption options
[] = {
1206 {"dpi", "Set image resolution (in dots per inch)", OFFSET(dpi
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, 0x10000, VE
},
1207 {"dpm", "Set image resolution (in dots per meter)", OFFSET(dpm
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, 0x10000, VE
},
1208 { "pred", "Prediction method", OFFSET(filter_type
), AV_OPT_TYPE_INT
, { .i64
= PNG_FILTER_VALUE_NONE
}, PNG_FILTER_VALUE_NONE
, PNG_FILTER_VALUE_MIXED
, VE
, .unit
= "pred" },
1209 { "none", NULL
, 0, AV_OPT_TYPE_CONST
, { .i64
= PNG_FILTER_VALUE_NONE
}, INT_MIN
, INT_MAX
, VE
, .unit
= "pred" },
1210 { "sub", NULL
, 0, AV_OPT_TYPE_CONST
, { .i64
= PNG_FILTER_VALUE_SUB
}, INT_MIN
, INT_MAX
, VE
, .unit
= "pred" },
1211 { "up", NULL
, 0, AV_OPT_TYPE_CONST
, { .i64
= PNG_FILTER_VALUE_UP
}, INT_MIN
, INT_MAX
, VE
, .unit
= "pred" },
1212 { "avg", NULL
, 0, AV_OPT_TYPE_CONST
, { .i64
= PNG_FILTER_VALUE_AVG
}, INT_MIN
, INT_MAX
, VE
, .unit
= "pred" },
1213 { "paeth", NULL
, 0, AV_OPT_TYPE_CONST
, { .i64
= PNG_FILTER_VALUE_PAETH
}, INT_MIN
, INT_MAX
, VE
, .unit
= "pred" },
1214 { "mixed", NULL
, 0, AV_OPT_TYPE_CONST
, { .i64
= PNG_FILTER_VALUE_MIXED
}, INT_MIN
, INT_MAX
, VE
, .unit
= "pred" },
1218 static const AVClass pngenc_class
= {
1219 .class_name
= "(A)PNG encoder",
1220 .item_name
= av_default_item_name
,
1222 .version
= LIBAVUTIL_VERSION_INT
,
1225 const FFCodec ff_png_encoder
= {
1227 CODEC_LONG_NAME("PNG (Portable Network Graphics) image"),
1228 .p
.type
= AVMEDIA_TYPE_VIDEO
,
1229 .p
.id
= AV_CODEC_ID_PNG
,
1230 .p
.capabilities
= AV_CODEC_CAP_DR1
| AV_CODEC_CAP_FRAME_THREADS
|
1231 AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE
,
1232 .priv_data_size
= sizeof(PNGEncContext
),
1233 .init
= png_enc_init
,
1234 .close
= png_enc_close
,
1235 FF_CODEC_ENCODE_CB(encode_png
),
1236 .p
.pix_fmts
= (const enum AVPixelFormat
[]) {
1237 AV_PIX_FMT_RGB24
, AV_PIX_FMT_RGBA
,
1238 AV_PIX_FMT_RGB48BE
, AV_PIX_FMT_RGBA64BE
,
1240 AV_PIX_FMT_GRAY8
, AV_PIX_FMT_GRAY8A
,
1241 AV_PIX_FMT_GRAY16BE
, AV_PIX_FMT_YA16BE
,
1242 AV_PIX_FMT_MONOBLACK
, AV_PIX_FMT_NONE
1244 .p
.priv_class
= &pngenc_class
,
1245 .caps_internal
= FF_CODEC_CAP_ICC_PROFILES
,
1248 const FFCodec ff_apng_encoder
= {
1250 CODEC_LONG_NAME("APNG (Animated Portable Network Graphics) image"),
1251 .p
.type
= AVMEDIA_TYPE_VIDEO
,
1252 .p
.id
= AV_CODEC_ID_APNG
,
1253 .p
.capabilities
= AV_CODEC_CAP_DR1
| AV_CODEC_CAP_DELAY
|
1254 AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE
,
1255 .priv_data_size
= sizeof(PNGEncContext
),
1256 .init
= png_enc_init
,
1257 .close
= png_enc_close
,
1258 FF_CODEC_ENCODE_CB(encode_apng
),
1259 .p
.pix_fmts
= (const enum AVPixelFormat
[]) {
1260 AV_PIX_FMT_RGB24
, AV_PIX_FMT_RGBA
,
1261 AV_PIX_FMT_RGB48BE
, AV_PIX_FMT_RGBA64BE
,
1263 AV_PIX_FMT_GRAY8
, AV_PIX_FMT_GRAY8A
,
1264 AV_PIX_FMT_GRAY16BE
, AV_PIX_FMT_YA16BE
,
1267 .p
.priv_class
= &pngenc_class
,
1268 .caps_internal
= FF_CODEC_CAP_ICC_PROFILES
,