int channels;
int bits_per_pixel;
int bpp;
+ int has_trns;
uint8_t *image_buf;
int image_linesize;
return AVERROR_INVALIDDATA;
}
- s->width = bytestream2_get_be32(&s->gb);
- s->height = bytestream2_get_be32(&s->gb);
+ if (s->state & PNG_IHDR) {
+ av_log(avctx, AV_LOG_ERROR, "Multiple IHDR\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ s->width = s->cur_w = bytestream2_get_be32(&s->gb);
+ s->height = s->cur_h = bytestream2_get_be32(&s->gb);
if (av_image_check_size(s->width, s->height, 0, avctx)) {
- s->width = s->height = 0;
+ s->cur_w = s->cur_h = s->width = s->height = 0;
av_log(avctx, AV_LOG_ERROR, "Invalid image size\n");
return AVERROR_INVALIDDATA;
}
- if (s->cur_w == 0 && s->cur_h == 0) {
- // Only set cur_w/h if update_thread_context() has not set it
- s->cur_w = s->width;
- s->cur_h = s->height;
- }
s->bit_depth = bytestream2_get_byte(&s->gb);
s->color_type = bytestream2_get_byte(&s->gb);
s->compression_type = bytestream2_get_byte(&s->gb);
}
bytestream2_skip(&s->gb, 4); /* crc */
+ s->has_trns = 1;
+
return 0;
}
uint32_t length)
{
uint32_t sequence_number;
+ int cur_w, cur_h, x_offset, y_offset, dispose_op, blend_op;
if (length != 26)
return AVERROR_INVALIDDATA;
+ if (!(s->state & PNG_IHDR)) {
+ av_log(avctx, AV_LOG_ERROR, "fctl before IHDR\n");
+ return AVERROR_INVALIDDATA;
+ }
+
s->last_w = s->cur_w;
s->last_h = s->cur_h;
s->last_x_offset = s->x_offset;
s->last_dispose_op = s->dispose_op;
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);
+ cur_w = bytestream2_get_be32(&s->gb);
+ cur_h = bytestream2_get_be32(&s->gb);
+ x_offset = bytestream2_get_be32(&s->gb);
+ 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);
+ dispose_op = bytestream2_get_byte(&s->gb);
+ 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)
+ (cur_w != s->width ||
+ cur_h != s->height ||
+ x_offset != 0 ||
+ y_offset != 0) ||
+ cur_w <= 0 || cur_h <= 0 ||
+ x_offset < 0 || y_offset < 0 ||
+ cur_w > s->width - x_offset|| cur_h > s->height - y_offset)
return AVERROR_INVALIDDATA;
if (sequence_number == 0 && s->dispose_op == APNG_DISPOSE_OP_PREVIOUS) {
s->dispose_op = APNG_DISPOSE_OP_BACKGROUND;
}
+ if (s->dispose_op == APNG_BLEND_OP_OVER && !s->has_trns && (
+ avctx->pix_fmt == AV_PIX_FMT_RGB24 ||
+ avctx->pix_fmt == AV_PIX_FMT_RGB48BE ||
+ avctx->pix_fmt == AV_PIX_FMT_PAL8 ||
+ avctx->pix_fmt == AV_PIX_FMT_GRAY8 ||
+ avctx->pix_fmt == AV_PIX_FMT_GRAY16BE ||
+ avctx->pix_fmt == AV_PIX_FMT_MONOBLACK
+ )) {
+ // APNG_DISPOSE_OP_OVER is the same as APNG_DISPOSE_OP_SOURCE when there is no alpha channel
+ s->dispose_op = APNG_BLEND_OP_SOURCE;
+ }
+
+ s->cur_w = cur_w;
+ s->cur_h = cur_h;
+ s->x_offset = x_offset;
+ s->y_offset = y_offset;
+ s->dispose_op = dispose_op;
+ s->blend_op = blend_op;
+
return 0;
}
return AVERROR(ENOMEM);
if (s->blend_op == APNG_BLEND_OP_OVER &&
- avctx->pix_fmt != AV_PIX_FMT_RGBA) {
+ avctx->pix_fmt != AV_PIX_FMT_RGBA &&
+ avctx->pix_fmt != AV_PIX_FMT_GRAY8A &&
+ avctx->pix_fmt != AV_PIX_FMT_PAL8) {
avpriv_request_sample(avctx, "Blending with pixel format %s",
av_get_pix_fmt_name(avctx->pix_fmt));
return AVERROR_PATCHWELCOME;
foreground_alpha = foreground[3];
background_alpha = background[3];
break;
+
+ case AV_PIX_FMT_GRAY8A:
+ foreground_alpha = foreground[1];
+ background_alpha = background[1];
+ break;
+
+ case AV_PIX_FMT_PAL8:
+ foreground_alpha = s->palette[foreground[0]] >> 24;
+ background_alpha = s->palette[background[0]] >> 24;
+ break;
}
if (foreground_alpha == 0)
continue;
}
+ if (avctx->pix_fmt == AV_PIX_FMT_PAL8) {
+ // TODO: Alpha blending with PAL8 will likely need the entire image converted over to RGBA first
+ avpriv_request_sample(avctx, "Alpha blending palette samples");
+ background[0] = foreground[0];
+ continue;
+ }
+
output_alpha = foreground_alpha + FAST_DIV255((255 - foreground_alpha) * background_alpha);
for (b = 0; b < s->bpp - 1; ++b) {
(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;
+ pdst->color_type = psrc->color_type;
+ pdst->compression_type = psrc->compression_type;
+ pdst->interlace_type = psrc->interlace_type;
+ pdst->filter_type = psrc->filter_type;
pdst->cur_w = psrc->cur_w;
pdst->cur_h = psrc->cur_h;
pdst->x_offset = psrc->x_offset;
pdst->y_offset = psrc->y_offset;
+
pdst->dispose_op = psrc->dispose_op;
+ memcpy(pdst->palette, psrc->palette, sizeof(pdst->palette));
+
+ 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);