apng: Fix decoding images with the PREVIOUS dispose op
authorDonny Yang <work@kota.moe>
Sun, 19 Jul 2015 02:43:20 +0000 (02:43 +0000)
committerPaul B Mahol <onemda@gmail.com>
Wed, 22 Jul 2015 16:42:24 +0000 (16:42 +0000)
libavcodec/pngdec.c

index b8011fe..cb1cebb 100644 (file)
@@ -643,6 +643,11 @@ static int decode_idat_chunk(AVCodecContext *avctx, PNGDecContext *s,
 
         if ((ret = ff_thread_get_buffer(avctx, &s->picture, AV_GET_BUFFER_FLAG_REF)) < 0)
             return ret;
+        if (avctx->codec_id == AV_CODEC_ID_APNG && s->last_dispose_op != APNG_DISPOSE_OP_PREVIOUS) {
+            ff_thread_release_buffer(avctx, &s->previous_picture);
+            if ((ret = ff_thread_get_buffer(avctx, &s->previous_picture, AV_GET_BUFFER_FLAG_REF)) < 0)
+                return ret;
+        }
         ff_thread_finish_setup(avctx);
 
         p->pict_type        = AV_PICTURE_TYPE_I;
@@ -917,20 +922,20 @@ static int handle_p_frame_apng(AVCodecContext *avctx, PNGDecContext *s,
         return AVERROR_PATCHWELCOME;
     }
 
-    // Copy the previous frame to the buffer
-    ff_thread_await_progress(&s->last_picture, INT_MAX, 0);
-    memcpy(buffer, s->last_picture.f->data[0], s->image_linesize * s->height);
-
     // Do the disposal operation specified by the last frame on the frame
-    if (s->last_dispose_op == APNG_DISPOSE_OP_BACKGROUND) {
-        for (y = s->last_y_offset; y < s->last_y_offset + s->last_h; ++y)
-            memset(buffer + s->image_linesize * y + s->bpp * s->last_x_offset, 0, s->bpp * s->last_w);
-    } else if (s->last_dispose_op == APNG_DISPOSE_OP_PREVIOUS) {
+    if (s->last_dispose_op != APNG_DISPOSE_OP_PREVIOUS) {
+        ff_thread_await_progress(&s->last_picture, INT_MAX, 0);
+        memcpy(buffer, s->last_picture.f->data[0], s->image_linesize * s->height);
+
+        if (s->last_dispose_op == APNG_DISPOSE_OP_BACKGROUND)
+            for (y = s->last_y_offset; y < s->last_y_offset + s->last_h; ++y)
+                memset(buffer + s->image_linesize * y + s->bpp * s->last_x_offset, 0, s->bpp * s->last_w);
+
+        memcpy(s->previous_picture.f->data[0], buffer, s->image_linesize * s->height);
+        ff_thread_report_progress(&s->previous_picture, INT_MAX, 0);
+    } else {
         ff_thread_await_progress(&s->previous_picture, INT_MAX, 0);
-        for (y = s->last_y_offset; y < s->last_y_offset + s->last_h; ++y) {
-            size_t row_start = s->image_linesize * y + s->bpp * s->last_x_offset;
-            memcpy(buffer + row_start, s->previous_picture.f->data[0] + row_start, s->bpp * s->last_w);
-        }
+        memcpy(buffer, s->previous_picture.f->data[0], s->image_linesize * s->height);
     }
 
     // Perform blending
@@ -1206,13 +1211,9 @@ static int decode_frame_apng(AVCodecContext *avctx,
     PNGDecContext *const s = avctx->priv_data;
     int ret;
     AVFrame *p;
-    ThreadFrame tmp;
 
-    ff_thread_release_buffer(avctx, &s->previous_picture);
-    tmp = s->previous_picture;
-    s->previous_picture = s->last_picture;
-    s->last_picture = s->picture;
-    s->picture = tmp;
+    ff_thread_release_buffer(avctx, &s->last_picture);
+    FFSWAP(ThreadFrame, s->picture, s->last_picture);
     p = s->picture.f;
 
     if (!(s->state & PNG_IHDR)) {
@@ -1292,8 +1293,14 @@ static int update_thread_context(AVCodecContext *dst, const AVCodecContext *src)
         pdst->state |= psrc->state & (PNG_IHDR | PNG_PLTE);
 
         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);
+        if (psrc->last_picture.f->data[0] &&
+            (ret = ff_thread_ref_frame(&pdst->last_picture, &psrc->last_picture)) < 0)
+            return ret;
+
+        ff_thread_release_buffer(dst, &pdst->previous_picture);
+        if (psrc->previous_picture.f->data[0] &&
+            (ret = ff_thread_ref_frame(&pdst->previous_picture, &psrc->previous_picture)) < 0)
+            return ret;
     }
 
     return 0;