+}
+
+static int decode_fctl_chunk(AVCodecContext *avctx, PNGDecContext *s,
+ uint32_t length)
+{
+ uint32_t sequence_number;
+
+ if (length != 26)
+ return AVERROR_INVALIDDATA;
+
+ sequence_number = bytestream2_get_be32(&s->gb);
+ s->cur_w = bytestream2_get_be32(&s->gb);
+ s->cur_h = bytestream2_get_be32(&s->gb);
+ s->x_offset = bytestream2_get_be32(&s->gb);
+ s->y_offset = bytestream2_get_be32(&s->gb);
+ bytestream2_skip(&s->gb, 4); /* delay_num (2), delay_den (2) */
+ s->dispose_op = bytestream2_get_byte(&s->gb);
+ s->blend_op = bytestream2_get_byte(&s->gb);
+ bytestream2_skip(&s->gb, 4); /* crc */
+
+ if (sequence_number == 0 &&
+ (s->cur_w != s->width ||
+ s->cur_h != s->height ||
+ s->x_offset != 0 ||
+ s->y_offset != 0) ||
+ s->cur_w <= 0 || s->cur_h <= 0 ||
+ s->x_offset < 0 || s->y_offset < 0 ||
+ s->cur_w > s->width - s->x_offset|| s->cur_h > s->height - s->y_offset)
+ return AVERROR_INVALIDDATA;
+
+ /* always (re)start with a clean frame */
+ if (sequence_number == 0) {
+ s->dispose_op = APNG_DISPOSE_OP_BACKGROUND;
+ s->frame_id = 0;
+ } else {
+ s->frame_id++;
+ if (s->frame_id == 1 && s->dispose_op == APNG_DISPOSE_OP_PREVIOUS)
+ /* previous for the second frame is the first frame */
+ s->dispose_op = APNG_DISPOSE_OP_NONE;
+ }
+
+ return 0;
+}
+
+static void handle_p_frame_png(PNGDecContext *s, AVFrame *p)
+{
+ int i, j;
+ uint8_t *pd = p->data[0];
+ uint8_t *pd_last = s->last_picture.f->data[0];
+ int ls = FFMIN(av_image_get_linesize(p->format, s->width, 0), s->width * s->bpp);
+
+ ff_thread_await_progress(&s->last_picture, INT_MAX, 0);
+ for (j = 0; j < s->height; j++) {
+ for (i = 0; i < ls; i++)
+ pd[i] += pd_last[i];
+ pd += s->image_linesize;
+ pd_last += s->image_linesize;
+ }
+}
+
+// divide by 255 and round to nearest
+// apply a fast variant: (X+127)/255 = ((X+127)*257+257)>>16 = ((X+128)*257)>>16
+#define FAST_DIV255(x) ((((x) + 128) * 257) >> 16)
+
+static int handle_p_frame_apng(AVCodecContext *avctx, PNGDecContext *s,
+ AVFrame *p)
+{
+ int i, j;
+ uint8_t *pd = p->data[0];
+ uint8_t *pd_last = s->last_picture.f->data[0];
+ uint8_t *pd_last_region = s->dispose_op == APNG_DISPOSE_OP_PREVIOUS ?
+ s->previous_picture.f->data[0] : s->last_picture.f->data[0];
+ int ls = FFMIN(av_image_get_linesize(p->format, s->width, 0), s->width * s->bpp);
+
+ if (s->blend_op == APNG_BLEND_OP_OVER &&
+ avctx->pix_fmt != AV_PIX_FMT_RGBA && avctx->pix_fmt != AV_PIX_FMT_ARGB) {
+ avpriv_request_sample(avctx, "Blending with pixel format %s",
+ av_get_pix_fmt_name(avctx->pix_fmt));
+ return AVERROR_PATCHWELCOME;
+ }
+
+ ff_thread_await_progress(&s->last_picture, INT_MAX, 0);
+ if (s->dispose_op == APNG_DISPOSE_OP_PREVIOUS)
+ ff_thread_await_progress(&s->previous_picture, INT_MAX, 0);
+
+ for (j = 0; j < s->y_offset; j++) {
+ for (i = 0; i < ls; i++)
+ pd[i] = pd_last[i];
+ pd += s->image_linesize;
+ pd_last += s->image_linesize;
+ }
+
+ if (s->dispose_op != APNG_DISPOSE_OP_BACKGROUND && s->blend_op == APNG_BLEND_OP_OVER) {
+ uint8_t ri, gi, bi, ai;
+
+ pd_last_region += s->y_offset * s->image_linesize;
+ if (avctx->pix_fmt == AV_PIX_FMT_RGBA) {
+ ri = 0;
+ gi = 1;
+ bi = 2;
+ ai = 3;
+ } else {
+ ri = 3;
+ gi = 2;
+ bi = 1;
+ ai = 0;
+ }
+
+ for (j = s->y_offset; j < s->y_offset + s->cur_h; j++) {
+ for (i = 0; i < s->x_offset * s->bpp; i++)
+ pd[i] = pd_last[i];
+ for (; i < (s->x_offset + s->cur_w) * s->bpp; i += s->bpp) {
+ uint8_t alpha = pd[i+ai];
+
+ /* output = alpha * foreground + (1-alpha) * background */
+ switch (alpha) {
+ case 0:
+ pd[i+ri] = pd_last_region[i+ri];
+ pd[i+gi] = pd_last_region[i+gi];
+ pd[i+bi] = pd_last_region[i+bi];
+ pd[i+ai] = 0xff;
+ break;
+ case 255:
+ break;
+ default:
+ pd[i+ri] = FAST_DIV255(alpha * pd[i+ri] + (255 - alpha) * pd_last_region[i+ri]);
+ pd[i+gi] = FAST_DIV255(alpha * pd[i+gi] + (255 - alpha) * pd_last_region[i+gi]);
+ pd[i+bi] = FAST_DIV255(alpha * pd[i+bi] + (255 - alpha) * pd_last_region[i+bi]);
+ pd[i+ai] = 0xff;
+ break;
+ }
+ }
+ for (; i < ls; i++)
+ pd[i] = pd_last[i];
+ pd += s->image_linesize;
+ pd_last += s->image_linesize;
+ pd_last_region += s->image_linesize;
+ }
+ } else {
+ for (j = s->y_offset; j < s->y_offset + s->cur_h; j++) {
+ for (i = 0; i < s->x_offset * s->bpp; i++)
+ pd[i] = pd_last[i];
+ for (i = (s->x_offset + s->cur_w) * s->bpp; i < ls; i++)
+ pd[i] = pd_last[i];
+ pd += s->image_linesize;
+ pd_last += s->image_linesize;
+ }
+ }
+
+ for (j = s->y_offset + s->cur_h; j < s->height; j++) {
+ for (i = 0; i < ls; i++)
+ pd[i] = pd_last[i];
+ pd += s->image_linesize;
+ pd_last += s->image_linesize;
+ }
+
+ return 0;
+}
+
+static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s,
+ AVFrame *p, AVPacket *avpkt)
+{
+ AVDictionary *metadata = NULL;
+ uint32_t tag, length;
+ int decode_next_dat = 0;
+ int ret = AVERROR_INVALIDDATA;
+ AVFrame *ref;
+
+ for (;;) {
+ length = bytestream2_get_bytes_left(&s->gb);
+ if (length <= 0) {
+ if (CONFIG_APNG_DECODER && avctx->codec_id == AV_CODEC_ID_APNG && length == 0) {
+ if (!(s->state & PNG_IDAT))
+ return 0;
+ else
+ goto exit_loop;
+ }
+ av_log(avctx, AV_LOG_ERROR, "%d bytes left\n", length);
+ if ( s->state & PNG_ALLIMAGE
+ && avctx->strict_std_compliance <= FF_COMPLIANCE_NORMAL)
+ goto exit_loop;
+ goto fail;
+ }
+
+ length = bytestream2_get_be32(&s->gb);
+ if (length > 0x7fffffff || length > bytestream2_get_bytes_left(&s->gb)) {
+ av_log(avctx, AV_LOG_ERROR, "chunk too big\n");
+ goto fail;
+ }
+ tag = bytestream2_get_le32(&s->gb);
+ if (avctx->debug & FF_DEBUG_STARTCODE)
+ av_log(avctx, AV_LOG_DEBUG, "png: tag=%c%c%c%c length=%u\n",
+ (tag & 0xff),
+ ((tag >> 8) & 0xff),
+ ((tag >> 16) & 0xff),
+ ((tag >> 24) & 0xff), length);
+ switch (tag) {
+ case MKTAG('I', 'H', 'D', 'R'):
+ if (decode_ihdr_chunk(avctx, s, length) < 0)
+ goto fail;
+ break;
+ case MKTAG('p', 'H', 'Y', 's'):
+ if (decode_phys_chunk(avctx, s) < 0)
+ goto fail;
+ break;
+ case MKTAG('f', 'c', 'T', 'L'):
+ if (!CONFIG_APNG_DECODER || avctx->codec_id != AV_CODEC_ID_APNG)
+ goto skip_tag;
+ if ((ret = decode_fctl_chunk(avctx, s, length)) < 0)
+ goto fail;
+ decode_next_dat = 1;
+ break;
+ case MKTAG('f', 'd', 'A', 'T'):
+ if (!CONFIG_APNG_DECODER || avctx->codec_id != AV_CODEC_ID_APNG)
+ goto skip_tag;
+ if (!decode_next_dat)
+ goto fail;
+ bytestream2_get_be32(&s->gb);
+ length -= 4;
+ /* fallthrough */
+ case MKTAG('I', 'D', 'A', 'T'):
+ if (CONFIG_APNG_DECODER && avctx->codec_id == AV_CODEC_ID_APNG && !decode_next_dat)
+ goto skip_tag;
+ if (decode_idat_chunk(avctx, s, length, p) < 0)
+ goto fail;
+ break;
+ case MKTAG('P', 'L', 'T', 'E'):
+ if (decode_plte_chunk(avctx, s, length) < 0)
+ goto skip_tag;
+ break;
+ case MKTAG('t', 'R', 'N', 'S'):
+ if (decode_trns_chunk(avctx, s, length) < 0)
+ goto skip_tag;
+ break;
+ case MKTAG('t', 'E', 'X', 't'):
+ if (decode_text_chunk(s, length, 0, &metadata) < 0)
+ av_log(avctx, AV_LOG_WARNING, "Broken tEXt chunk\n");
+ bytestream2_skip(&s->gb, length + 4);
+ break;
+ case MKTAG('z', 'T', 'X', 't'):
+ if (decode_text_chunk(s, length, 1, &metadata) < 0)
+ av_log(avctx, AV_LOG_WARNING, "Broken zTXt chunk\n");
+ bytestream2_skip(&s->gb, length + 4);
+ break;
+ case MKTAG('I', 'E', 'N', 'D'):
+ if (!(s->state & PNG_ALLIMAGE))
+ av_log(avctx, AV_LOG_ERROR, "IEND without all image\n");
+ if (!(s->state & (PNG_ALLIMAGE|PNG_IDAT))) {
+ goto fail;
+ }
+ bytestream2_skip(&s->gb, 4); /* crc */
+ goto exit_loop;
+ default:
+ /* skip tag */
+skip_tag:
+ bytestream2_skip(&s->gb, length + 4);
+ break;
+ }
+ }
+exit_loop:
+
+ if (s->bits_per_pixel <= 4)
+ handle_small_bpp(s, p);