static int apng_encode_frame(AVCodecContext *avctx, const AVFrame *pict, APNGFctlChunk *best_fctl_chunk, APNGFctlChunk *best_last_fctl_chunk) { PNGEncContext *s = avctx->priv_data; int ret; unsigned int y; AVFrame* diffFrame; uint8_t bpp = (s->bits_per_pixel + 7) >> 3; uint8_t *original_bytestream, *original_bytestream_end; uint8_t *temp_bytestream = 0, *temp_bytestream_end; uint32_t best_sequence_number; uint8_t *best_bytestream; size_t best_bytestream_size = SIZE_MAX; APNGFctlChunk last_fctl_chunk = *best_last_fctl_chunk; APNGFctlChunk fctl_chunk = *best_fctl_chunk; if (avctx->frame_number == 0) { best_fctl_chunk->width = pict->width; best_fctl_chunk->height = pict->height; best_fctl_chunk->x_offset = 0; best_fctl_chunk->y_offset = 0; best_fctl_chunk->blend_op = APNG_BLEND_OP_SOURCE; return encode_frame(avctx, pict); } diffFrame = av_frame_alloc(); if (!diffFrame) return AVERROR(ENOMEM); diffFrame->format = pict->format; diffFrame->width = pict->width; diffFrame->height = pict->height; if ((ret = av_frame_get_buffer(diffFrame, 32)) < 0) goto fail; original_bytestream = s->bytestream; original_bytestream_end = s->bytestream_end; temp_bytestream = av_malloc(original_bytestream_end - original_bytestream); temp_bytestream_end = temp_bytestream + (original_bytestream_end - original_bytestream); if (!temp_bytestream) { ret = AVERROR(ENOMEM); goto fail; } for (last_fctl_chunk.dispose_op = 0; last_fctl_chunk.dispose_op < 3; ++last_fctl_chunk.dispose_op) { // 0: APNG_DISPOSE_OP_NONE // 1: APNG_DISPOSE_OP_BACKGROUND // 2: APNG_DISPOSE_OP_PREVIOUS for (fctl_chunk.blend_op = 0; fctl_chunk.blend_op < 2; ++fctl_chunk.blend_op) { // 0: APNG_BLEND_OP_SOURCE // 1: APNG_BLEND_OP_OVER uint32_t original_sequence_number = s->sequence_number, sequence_number; uint8_t *bytestream_start = s->bytestream; size_t bytestream_size; // Do disposal if (last_fctl_chunk.dispose_op != APNG_DISPOSE_OP_PREVIOUS) { diffFrame->width = pict->width; diffFrame->height = pict->height; av_frame_copy(diffFrame, s->last_frame); if (last_fctl_chunk.dispose_op == APNG_DISPOSE_OP_BACKGROUND) { for (y = last_fctl_chunk.y_offset; y < last_fctl_chunk.y_offset + last_fctl_chunk.height; ++y) { size_t row_start = diffFrame->linesize[0] * y + bpp * last_fctl_chunk.x_offset; memset(diffFrame->data[0] + row_start, 0, bpp * last_fctl_chunk.width); } } } else { if (!s->prev_frame) continue; diffFrame->width = pict->width; diffFrame->height = pict->height; av_frame_copy(diffFrame, s->prev_frame); } // Do inverse blending if (apng_do_inverse_blend(diffFrame, pict, &fctl_chunk, bpp) < 0) continue; // Do encoding ret = encode_frame(avctx, diffFrame); sequence_number = s->sequence_number; s->sequence_number = original_sequence_number; bytestream_size = s->bytestream - bytestream_start; s->bytestream = bytestream_start; if (ret < 0) goto fail; if (bytestream_size < best_bytestream_size) { *best_fctl_chunk = fctl_chunk; *best_last_fctl_chunk = last_fctl_chunk; best_sequence_number = sequence_number; best_bytestream = s->bytestream; best_bytestream_size = bytestream_size; if (best_bytestream == original_bytestream) { s->bytestream = temp_bytestream; s->bytestream_end = temp_bytestream_end; } else { s->bytestream = original_bytestream; s->bytestream_end = original_bytestream_end; } } } } s->sequence_number = best_sequence_number; s->bytestream = original_bytestream + best_bytestream_size; s->bytestream_end = original_bytestream_end; if (best_bytestream != original_bytestream) memcpy(original_bytestream, best_bytestream, best_bytestream_size); ret = 0; fail: av_freep(&temp_bytestream); av_frame_free(&diffFrame); return ret; }