Commit 08aec6f6 authored by Benoit Fouet's avatar Benoit Fouet Committed by Michael Niedermayer

libavcodec/pngdec: support 'previous' dispose operation for APNG.

Signed-off-by: 's avatarMichael Niedermayer <michaelni@gmx.at>
parent 49d9cbe5
...@@ -38,6 +38,7 @@ typedef struct PNGDecContext { ...@@ -38,6 +38,7 @@ typedef struct PNGDecContext {
AVCodecContext *avctx; AVCodecContext *avctx;
GetByteContext gb; GetByteContext gb;
ThreadFrame previous_picture;
ThreadFrame last_picture; ThreadFrame last_picture;
ThreadFrame picture; ThreadFrame picture;
...@@ -55,6 +56,7 @@ typedef struct PNGDecContext { ...@@ -55,6 +56,7 @@ typedef struct PNGDecContext {
int bits_per_pixel; int bits_per_pixel;
int bpp; int bpp;
int frame_id;
uint8_t *image_buf; uint8_t *image_buf;
int image_linesize; int image_linesize;
uint32_t palette[256]; uint32_t palette[256];
...@@ -827,13 +829,14 @@ static int decode_fctl_chunk(AVCodecContext *avctx, PNGDecContext *s, ...@@ -827,13 +829,14 @@ static int decode_fctl_chunk(AVCodecContext *avctx, PNGDecContext *s,
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
/* always (re)start with a clean frame */ /* always (re)start with a clean frame */
if (sequence_number == 0) if (sequence_number == 0) {
s->dispose_op = APNG_DISPOSE_OP_BACKGROUND; s->dispose_op = APNG_DISPOSE_OP_BACKGROUND;
s->frame_id = 0;
if (s->dispose_op == APNG_DISPOSE_OP_PREVIOUS) { } else {
av_log(avctx, AV_LOG_ERROR, s->frame_id++;
"Dispose operation 'previous' is not yet implemented, using 'none'.\n"); if (s->frame_id == 1 && s->dispose_op == APNG_DISPOSE_OP_PREVIOUS)
s->dispose_op = APNG_DISPOSE_OP_NONE; /* previous for the second frame is the first frame */
s->dispose_op = APNG_DISPOSE_OP_NONE;
} }
return 0; return 0;
...@@ -864,8 +867,9 @@ static int handle_p_frame_apng(AVCodecContext *avctx, PNGDecContext *s, ...@@ -864,8 +867,9 @@ static int handle_p_frame_apng(AVCodecContext *avctx, PNGDecContext *s,
{ {
int i, j; int i, j;
uint8_t *pd = p->data[0]; uint8_t *pd = p->data[0];
/* TODO make pd_last point to the one before for APNG_DISPOSE_OP_PREVIOUS */
uint8_t *pd_last = s->last_picture.f->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); int ls = FFMIN(av_image_get_linesize(p->format, s->width, 0), s->width * s->bpp);
if (s->blend_op == APNG_BLEND_OP_OVER && if (s->blend_op == APNG_BLEND_OP_OVER &&
...@@ -876,6 +880,9 @@ static int handle_p_frame_apng(AVCodecContext *avctx, PNGDecContext *s, ...@@ -876,6 +880,9 @@ static int handle_p_frame_apng(AVCodecContext *avctx, PNGDecContext *s,
} }
ff_thread_await_progress(&s->last_picture, INT_MAX, 0); 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 (j = 0; j < s->y_offset; j++) {
for (i = 0; i < ls; i++) for (i = 0; i < ls; i++)
pd[i] = pd_last[i]; pd[i] = pd_last[i];
...@@ -886,6 +893,7 @@ static int handle_p_frame_apng(AVCodecContext *avctx, PNGDecContext *s, ...@@ -886,6 +893,7 @@ static int handle_p_frame_apng(AVCodecContext *avctx, PNGDecContext *s,
if (s->dispose_op != APNG_DISPOSE_OP_BACKGROUND && s->blend_op == APNG_BLEND_OP_OVER) { if (s->dispose_op != APNG_DISPOSE_OP_BACKGROUND && s->blend_op == APNG_BLEND_OP_OVER) {
uint8_t ri, gi, bi, ai; uint8_t ri, gi, bi, ai;
pd_last_region += s->y_offset * s->image_linesize;
if (avctx->pix_fmt == AV_PIX_FMT_RGBA) { if (avctx->pix_fmt == AV_PIX_FMT_RGBA) {
ri = 0; ri = 0;
gi = 1; gi = 1;
...@@ -907,17 +915,17 @@ static int handle_p_frame_apng(AVCodecContext *avctx, PNGDecContext *s, ...@@ -907,17 +915,17 @@ static int handle_p_frame_apng(AVCodecContext *avctx, PNGDecContext *s,
/* output = alpha * foreground + (1-alpha) * background */ /* output = alpha * foreground + (1-alpha) * background */
switch (alpha) { switch (alpha) {
case 0: case 0:
pd[i+ri] = pd_last[i+ri]; pd[i+ri] = pd_last_region[i+ri];
pd[i+gi] = pd_last[i+gi]; pd[i+gi] = pd_last_region[i+gi];
pd[i+bi] = pd_last[i+bi]; pd[i+bi] = pd_last_region[i+bi];
pd[i+ai] = 0xff; pd[i+ai] = 0xff;
break; break;
case 255: case 255:
break; break;
default: default:
pd[i+ri] = FAST_DIV255(alpha * pd[i+ri] + (255 - alpha) * pd_last[i+ri]); 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[i+gi]); 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[i+bi]); pd[i+bi] = FAST_DIV255(alpha * pd[i+bi] + (255 - alpha) * pd_last_region[i+bi]);
pd[i+ai] = 0xff; pd[i+ai] = 0xff;
break; break;
} }
...@@ -926,6 +934,7 @@ static int handle_p_frame_apng(AVCodecContext *avctx, PNGDecContext *s, ...@@ -926,6 +934,7 @@ static int handle_p_frame_apng(AVCodecContext *avctx, PNGDecContext *s,
pd[i] = pd_last[i]; pd[i] = pd_last[i];
pd += s->image_linesize; pd += s->image_linesize;
pd_last += s->image_linesize; pd_last += s->image_linesize;
pd_last_region += s->image_linesize;
} }
} else { } else {
for (j = s->y_offset; j < s->y_offset + s->cur_h; j++) { for (j = s->y_offset; j < s->y_offset + s->cur_h; j++) {
...@@ -955,6 +964,7 @@ static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s, ...@@ -955,6 +964,7 @@ static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s,
uint32_t tag, length; uint32_t tag, length;
int decode_next_dat = 0; int decode_next_dat = 0;
int ret = AVERROR_INVALIDDATA; int ret = AVERROR_INVALIDDATA;
AVFrame *ref;
for (;;) { for (;;) {
length = bytestream2_get_bytes_left(&s->gb); length = bytestream2_get_bytes_left(&s->gb);
...@@ -1053,11 +1063,13 @@ exit_loop: ...@@ -1053,11 +1063,13 @@ exit_loop:
handle_small_bpp(s, p); handle_small_bpp(s, p);
/* handle p-frames only if a predecessor frame is available */ /* handle p-frames only if a predecessor frame is available */
if (s->last_picture.f->data[0]) { ref = s->dispose_op == APNG_DISPOSE_OP_PREVIOUS ?
s->previous_picture.f : s->last_picture.f;
if (ref->data[0]) {
if ( !(avpkt->flags & AV_PKT_FLAG_KEY) && avctx->codec_tag != AV_RL32("MPNG") if ( !(avpkt->flags & AV_PKT_FLAG_KEY) && avctx->codec_tag != AV_RL32("MPNG")
&& s->last_picture.f->width == p->width && ref->width == p->width
&& s->last_picture.f->height== p->height && ref->height== p->height
&& s->last_picture.f->format== p->format && ref->format== p->format
) { ) {
if (CONFIG_PNG_DECODER && avctx->codec_id != AV_CODEC_ID_APNG) if (CONFIG_PNG_DECODER && avctx->codec_id != AV_CODEC_ID_APNG)
handle_p_frame_png(s, p); handle_p_frame_png(s, p);
...@@ -1141,9 +1153,13 @@ static int decode_frame_apng(AVCodecContext *avctx, ...@@ -1141,9 +1153,13 @@ static int decode_frame_apng(AVCodecContext *avctx,
PNGDecContext *const s = avctx->priv_data; PNGDecContext *const s = avctx->priv_data;
int ret; int ret;
AVFrame *p; AVFrame *p;
ThreadFrame tmp;
ff_thread_release_buffer(avctx, &s->last_picture); ff_thread_release_buffer(avctx, &s->previous_picture);
FFSWAP(ThreadFrame, s->picture, s->last_picture); tmp = s->previous_picture;
s->previous_picture = s->last_picture;
s->last_picture = s->picture;
s->picture = tmp;
p = s->picture.f; p = s->picture.f;
if (!(s->state & PNG_IHDR)) { if (!(s->state & PNG_IHDR)) {
...@@ -1193,13 +1209,22 @@ static int update_thread_context(AVCodecContext *dst, const AVCodecContext *src) ...@@ -1193,13 +1209,22 @@ static int update_thread_context(AVCodecContext *dst, const AVCodecContext *src)
{ {
PNGDecContext *psrc = src->priv_data; PNGDecContext *psrc = src->priv_data;
PNGDecContext *pdst = dst->priv_data; PNGDecContext *pdst = dst->priv_data;
int ret;
if (dst == src) if (dst == src)
return 0; return 0;
pdst->frame_id = psrc->frame_id;
ff_thread_release_buffer(dst, &pdst->picture); ff_thread_release_buffer(dst, &pdst->picture);
if (psrc->picture.f->data[0]) if (psrc->picture.f->data[0] &&
return ff_thread_ref_frame(&pdst->picture, &psrc->picture); (ret = ff_thread_ref_frame(&pdst->picture, &psrc->picture)) < 0)
return ret;
if (CONFIG_APNG_DECODER && dst->codec_id == AV_CODEC_ID_APNG) {
ff_thread_release_buffer(dst, &pdst->last_picture);
if (psrc->last_picture.f->data[0])
return ff_thread_ref_frame(&pdst->last_picture, &psrc->last_picture);
}
return 0; return 0;
} }
...@@ -1209,9 +1234,10 @@ static av_cold int png_dec_init(AVCodecContext *avctx) ...@@ -1209,9 +1234,10 @@ static av_cold int png_dec_init(AVCodecContext *avctx)
PNGDecContext *s = avctx->priv_data; PNGDecContext *s = avctx->priv_data;
s->avctx = avctx; s->avctx = avctx;
s->previous_picture.f = av_frame_alloc();
s->last_picture.f = av_frame_alloc(); s->last_picture.f = av_frame_alloc();
s->picture.f = av_frame_alloc(); s->picture.f = av_frame_alloc();
if (!s->last_picture.f || !s->picture.f) if (!s->previous_picture.f || !s->last_picture.f || !s->picture.f)
return AVERROR(ENOMEM); return AVERROR(ENOMEM);
if (!avctx->internal->is_copy) { if (!avctx->internal->is_copy) {
...@@ -1226,6 +1252,8 @@ static av_cold int png_dec_end(AVCodecContext *avctx) ...@@ -1226,6 +1252,8 @@ static av_cold int png_dec_end(AVCodecContext *avctx)
{ {
PNGDecContext *s = avctx->priv_data; PNGDecContext *s = avctx->priv_data;
ff_thread_release_buffer(avctx, &s->previous_picture);
av_frame_free(&s->previous_picture.f);
ff_thread_release_buffer(avctx, &s->last_picture); ff_thread_release_buffer(avctx, &s->last_picture);
av_frame_free(&s->last_picture.f); av_frame_free(&s->last_picture.f);
ff_thread_release_buffer(avctx, &s->picture); ff_thread_release_buffer(avctx, &s->picture);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment