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