#include "libavutil/avassert.h"
#include "libavutil/bprint.h"
+#include "libavutil/crc.h"
#include "libavutil/imgutils.h"
+#include "libavutil/intreadwrite.h"
#include "libavutil/stereo3d.h"
#include "libavutil/mastering_display_metadata.h"
AVCodecContext *avctx;
GetByteContext gb;
- ThreadFrame previous_picture;
ThreadFrame last_picture;
ThreadFrame picture;
}
/* NOTE: 'dst' can be equal to 'last' */
-static void png_filter_row(PNGDSPContext *dsp, uint8_t *dst, int filter_type,
- uint8_t *src, uint8_t *last, int size, int bpp)
+void ff_png_filter_row(PNGDSPContext *dsp, uint8_t *dst, int filter_type,
+ uint8_t *src, uint8_t *last, int size, int bpp)
{
int i, p, r, g, b, a;
YUV2RGB(rgb8, uint8_t)
YUV2RGB(rgb16, uint16_t)
+static int percent_missing(PNGDecContext *s)
+{
+ if (s->interlace_type) {
+ return 100 - 100 * s->pass / (NB_PASSES - 1);
+ } else {
+ return 100 - 100 * s->y / s->cur_h;
+ }
+}
+
/* process exactly one decompressed row */
static void png_handle_row(PNGDecContext *s)
{
else
last_row = ptr - s->image_linesize;
- png_filter_row(&s->dsp, ptr, s->crow_buf[0], s->crow_buf + 1,
- last_row, s->row_size, s->bpp);
+ ff_png_filter_row(&s->dsp, ptr, s->crow_buf[0], s->crow_buf + 1,
+ last_row, s->row_size, s->bpp);
/* loco lags by 1 row so that it doesn't interfere with top prediction */
if (s->filter_type == PNG_FILTER_TYPE_LOCO && s->y > 0) {
if (s->bit_depth == 16) {
* wait for the next one */
if (got_line)
break;
- png_filter_row(&s->dsp, s->tmp_row, s->crow_buf[0], s->crow_buf + 1,
- s->last_row, s->pass_row_size, s->bpp);
+ ff_png_filter_row(&s->dsp, s->tmp_row, s->crow_buf[0], s->crow_buf + 1,
+ s->last_row, s->pass_row_size, s->bpp);
FFSWAP(uint8_t *, s->last_row, s->tmp_row);
FFSWAP(unsigned int, s->last_row_size, s->tmp_row_size);
got_line = 1;
{
int ret;
s->zstream.avail_in = FFMIN(length, bytestream2_get_bytes_left(&s->gb));
- s->zstream.next_in = (unsigned char *)s->gb.buffer;
+ s->zstream.next_in = s->gb.buffer;
bytestream2_skip(&s->gb, length);
/* decode one line if possible */
s->zstream.next_out = s->crow_buf;
}
if (ret == Z_STREAM_END && s->zstream.avail_in > 0) {
- av_log(NULL, AV_LOG_WARNING,
+ av_log(s->avctx, AV_LOG_WARNING,
"%d undecompressed bytes left in buffer\n", s->zstream.avail_in);
return 0;
}
zstream.opaque = NULL;
if (inflateInit(&zstream) != Z_OK)
return AVERROR_EXTERNAL;
- zstream.next_in = (unsigned char *)data;
+ zstream.next_in = data;
zstream.avail_in = data_end - data;
av_bprint_init(bp, 0, AV_BPRINT_SIZE_UNLIMITED);
s->bpp += byte_depth;
}
+ ff_thread_release_buffer(avctx, &s->picture);
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;
- }
+
p->pict_type = AV_PICTURE_TYPE_I;
p->key_frame = 1;
p->interlaced_frame = !!s->interlace_type;
return AVERROR_INVALIDDATA;
}
+ if (s->pic_state & PNG_IDAT) {
+ av_log(avctx, AV_LOG_ERROR, "fctl after IDAT\n");
+ return AVERROR_INVALIDDATA;
+ }
+
s->last_w = s->cur_w;
s->last_h = s->cur_h;
s->last_x_offset = s->x_offset;
return AVERROR_INVALIDDATA;
}
- if ((sequence_number == 0 || !s->previous_picture.f->data[0]) &&
+ if ((sequence_number == 0 || !s->last_picture.f->data[0]) &&
dispose_op == APNG_DISPOSE_OP_PREVIOUS) {
// No previous frame to revert to for the first frame
// Spec says to just treat it as a APNG_DISPOSE_OP_BACKGROUND
if (!buffer)
return AVERROR(ENOMEM);
+ ff_thread_await_progress(&s->last_picture, INT_MAX, 0);
- // Do the disposal operation specified by the last frame on the frame
- 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);
+ // need to reset a rectangle to background:
+ // create a new writable copy
+ if (s->last_dispose_op == APNG_DISPOSE_OP_BACKGROUND) {
+ int ret = av_frame_make_writable(s->last_picture.f);
+ if (ret < 0)
+ return ret;
- 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);
- memcpy(buffer, s->previous_picture.f->data[0], s->image_linesize * s->height);
+ for (y = s->last_y_offset; y < s->last_y_offset + s->last_h; y++) {
+ memset(s->last_picture.f->data[0] + s->image_linesize * y +
+ s->bpp * s->last_x_offset, 0, s->bpp * s->last_w);
+ }
}
+ memcpy(buffer, s->last_picture.f->data[0], s->image_linesize * s->height);
+
// Perform blending
if (s->blend_op == APNG_BLEND_OP_SOURCE) {
for (y = s->y_offset; y < s->y_offset + s->cur_h; ++y) {
static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s,
AVFrame *p, AVPacket *avpkt)
{
+ const AVCRC *crc_tab = av_crc_get_table(AV_CRC_32_IEEE_LE);
AVDictionary **metadatap = NULL;
uint32_t tag, length;
int decode_next_dat = 0;
ret = AVERROR_INVALIDDATA;
goto fail;
}
+ if (avctx->err_recognition & (AV_EF_CRCCHECK | AV_EF_IGNORE_ERR)) {
+ uint32_t crc_sig = AV_RB32(s->gb.buffer + length + 4);
+ uint32_t crc_cal = ~av_crc(crc_tab, UINT32_MAX, s->gb.buffer, length + 4);
+ if (crc_sig ^ crc_cal) {
+ av_log(avctx, AV_LOG_ERROR, "CRC mismatch in chunk");
+ if (avctx->err_recognition & AV_EF_EXPLODE) {
+ av_log(avctx, AV_LOG_ERROR, ", quitting\n");
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ av_log(avctx, AV_LOG_ERROR, ", skipping\n");
+ bytestream2_skip(&s->gb, 4); /* tag */
+ goto skip_tag;
+ }
+ }
tag = bytestream2_get_le32(&s->gb);
if (avctx->debug & FF_DEBUG_STARTCODE)
av_log(avctx, AV_LOG_DEBUG, "png: tag=%s length=%u\n",
case MKTAG('f', 'd', 'A', 'T'):
if (!CONFIG_APNG_DECODER || avctx->codec_id != AV_CODEC_ID_APNG)
goto skip_tag;
- if (!decode_next_dat) {
+ if (!decode_next_dat || length < 4) {
ret = AVERROR_INVALIDDATA;
goto fail;
}
case MKTAG('s', 'T', 'E', 'R'): {
int mode = bytestream2_get_byte(&s->gb);
AVStereo3D *stereo3d = av_stereo3d_create_side_data(p);
- if (!stereo3d)
+ if (!stereo3d) {
+ ret = AVERROR(ENOMEM);
goto fail;
+ }
if (mode == 0 || mode == 1) {
stereo3d->type = AV_STEREO3D_SIDEBYSIDE;
break;
}
case MKTAG('i', 'C', 'C', 'P'): {
- if (decode_iccp_chunk(s, length, p) < 0)
+ if ((ret = decode_iccp_chunk(s, length, p)) < 0)
goto fail;
break;
}
return 0;
}
+ if (percent_missing(s) > avctx->discard_damaged_percentage)
+ return AVERROR_INVALIDDATA;
+
if (s->bits_per_pixel <= 4)
handle_small_bpp(s, p);
for (y = 0; y < s->height; ++y) {
uint8_t *row = &s->image_buf[s->image_linesize * y];
- /* since we're updating in-place, we have to go from right to left */
- for (x = s->width; x > 0; --x) {
- uint8_t *pixel = &row[s->bpp * (x - 1)];
- memmove(pixel, &row[raw_bpp * (x - 1)], raw_bpp);
+ if (s->bpp == 2 && byte_depth == 1) {
+ uint8_t *pixel = &row[2 * s->width - 1];
+ uint8_t *rowp = &row[1 * s->width - 1];
+ int tcolor = s->transparent_color_be[0];
+ for (x = s->width; x > 0; --x) {
+ *pixel-- = *rowp == tcolor ? 0 : 0xff;
+ *pixel-- = *rowp--;
+ }
+ } else if (s->bpp == 4 && byte_depth == 1) {
+ uint8_t *pixel = &row[4 * s->width - 1];
+ uint8_t *rowp = &row[3 * s->width - 1];
+ int tcolor = AV_RL24(s->transparent_color_be);
+ for (x = s->width; x > 0; --x) {
+ *pixel-- = AV_RL24(rowp-2) == tcolor ? 0 : 0xff;
+ *pixel-- = *rowp--;
+ *pixel-- = *rowp--;
+ *pixel-- = *rowp--;
+ }
+ } else {
+ /* since we're updating in-place, we have to go from right to left */
+ for (x = s->width; x > 0; --x) {
+ uint8_t *pixel = &row[s->bpp * (x - 1)];
+ memmove(pixel, &row[raw_bpp * (x - 1)], raw_bpp);
- if (!memcmp(pixel, s->transparent_color_be, raw_bpp)) {
- memset(&pixel[raw_bpp], 0, byte_depth);
- } else {
- memset(&pixel[raw_bpp], 0xff, byte_depth);
+ if (!memcmp(pixel, s->transparent_color_be, raw_bpp)) {
+ memset(&pixel[raw_bpp], 0, byte_depth);
+ } else {
+ memset(&pixel[raw_bpp], 0xff, byte_depth);
+ }
}
}
}
}
}
ff_thread_report_progress(&s->picture, INT_MAX, 0);
- ff_thread_report_progress(&s->previous_picture, INT_MAX, 0);
return 0;
fail:
ff_thread_report_progress(&s->picture, INT_MAX, 0);
- ff_thread_report_progress(&s->previous_picture, INT_MAX, 0);
return ret;
}
PNGDecContext *const s = avctx->priv_data;
const uint8_t *buf = avpkt->data;
int buf_size = avpkt->size;
- AVFrame *p;
+ AVFrame *p = s->picture.f;
int64_t sig;
int ret;
- ff_thread_release_buffer(avctx, &s->last_picture);
- FFSWAP(ThreadFrame, s->picture, s->last_picture);
- p = s->picture.f;
-
bytestream2_init(&s->gb, buf, buf_size);
/* check signature */
if ((ret = av_frame_ref(data, s->picture.f)) < 0)
goto the_end;
+ if (!(avctx->active_thread_type & FF_THREAD_FRAME)) {
+ ff_thread_release_buffer(avctx, &s->last_picture);
+ FFSWAP(ThreadFrame, s->picture, s->last_picture);
+ }
+
*got_frame = 1;
ret = bytestream2_tell(&s->gb);
{
PNGDecContext *const s = avctx->priv_data;
int ret;
- AVFrame *p;
-
- ff_thread_release_buffer(avctx, &s->last_picture);
- FFSWAP(ThreadFrame, s->picture, s->last_picture);
- p = s->picture.f;
+ AVFrame *p = s->picture.f;
if (!(s->hdr_state & PNG_IHDR)) {
if (!avctx->extradata_size)
if ((ret = av_frame_ref(data, s->picture.f)) < 0)
goto end;
+ if (!(avctx->active_thread_type & FF_THREAD_FRAME)) {
+ if (s->dispose_op == APNG_DISPOSE_OP_PREVIOUS) {
+ ff_thread_release_buffer(avctx, &s->picture);
+ } else if (s->dispose_op == APNG_DISPOSE_OP_NONE) {
+ ff_thread_release_buffer(avctx, &s->last_picture);
+ FFSWAP(ThreadFrame, s->picture, s->last_picture);
+ }
+ }
+
*got_frame = 1;
ret = bytestream2_tell(&s->gb);
{
PNGDecContext *psrc = src->priv_data;
PNGDecContext *pdst = dst->priv_data;
+ ThreadFrame *src_frame = NULL;
int ret;
if (dst == src)
return 0;
- ff_thread_release_buffer(dst, &pdst->picture);
- if (psrc->picture.f->data[0] &&
- (ret = ff_thread_ref_frame(&pdst->picture, &psrc->picture)) < 0)
- return ret;
if (CONFIG_APNG_DECODER && dst->codec_id == AV_CODEC_ID_APNG) {
+
pdst->width = psrc->width;
pdst->height = psrc->height;
pdst->bit_depth = psrc->bit_depth;
memcpy(pdst->palette, psrc->palette, sizeof(pdst->palette));
pdst->hdr_state |= psrc->hdr_state;
+ }
- ff_thread_release_buffer(dst, &pdst->last_picture);
- if (psrc->last_picture.f->data[0] &&
- (ret = ff_thread_ref_frame(&pdst->last_picture, &psrc->last_picture)) < 0)
- return ret;
+ src_frame = psrc->dispose_op == APNG_DISPOSE_OP_NONE ?
+ &psrc->picture : &psrc->last_picture;
- 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)
+ ff_thread_release_buffer(dst, &pdst->last_picture);
+ if (src_frame && src_frame->f->data[0]) {
+ ret = ff_thread_ref_frame(&pdst->last_picture, src_frame);
+ if (ret < 0)
return ret;
}
avctx->color_range = AVCOL_RANGE_JPEG;
s->avctx = avctx;
- s->previous_picture.f = av_frame_alloc();
s->last_picture.f = av_frame_alloc();
s->picture.f = av_frame_alloc();
- if (!s->previous_picture.f || !s->last_picture.f || !s->picture.f) {
- av_frame_free(&s->previous_picture.f);
+ if (!s->last_picture.f || !s->picture.f) {
av_frame_free(&s->last_picture.f);
av_frame_free(&s->picture.f);
return AVERROR(ENOMEM);
}
- if (!avctx->internal->is_copy) {
- avctx->internal->allocate_progress = 1;
- ff_pngdsp_init(&s->dsp);
- }
+ ff_pngdsp_init(&s->dsp);
return 0;
}
{
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);
av_frame_free(&s->last_picture.f);
ff_thread_release_buffer(avctx, &s->picture);
.init = png_dec_init,
.close = png_dec_end,
.decode = decode_frame_apng,
- .init_thread_copy = ONLY_IF_THREADS_ENABLED(png_dec_init),
.update_thread_context = ONLY_IF_THREADS_ENABLED(update_thread_context),
.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS /*| AV_CODEC_CAP_DRAW_HORIZ_BAND*/,
- .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
+ .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE |
+ FF_CODEC_CAP_ALLOCATE_PROGRESS,
};
#endif
.init = png_dec_init,
.close = png_dec_end,
.decode = decode_frame_png,
- .init_thread_copy = ONLY_IF_THREADS_ENABLED(png_dec_init),
.update_thread_context = ONLY_IF_THREADS_ENABLED(update_thread_context),
.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS /*| AV_CODEC_CAP_DRAW_HORIZ_BAND*/,
- .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM | FF_CODEC_CAP_INIT_THREADSAFE,
+ .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM | FF_CODEC_CAP_INIT_THREADSAFE |
+ FF_CODEC_CAP_ALLOCATE_PROGRESS,
};
#endif