Commit 51d4bca5 authored by Donny Yang's avatar Donny Yang Committed by Paul B Mahol

avcodec/pngdec: fully support the tRNS chunk

Signed-off-by: 's avatarDonny Yang <work@kota.moe>
parent d5911e69
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
//#define DEBUG //#define DEBUG
#include "libavutil/avassert.h"
#include "libavutil/bprint.h" #include "libavutil/bprint.h"
#include "libavutil/imgutils.h" #include "libavutil/imgutils.h"
#include "avcodec.h" #include "avcodec.h"
...@@ -59,6 +60,7 @@ typedef struct PNGDecContext { ...@@ -59,6 +60,7 @@ typedef struct PNGDecContext {
int bits_per_pixel; int bits_per_pixel;
int bpp; int bpp;
int has_trns; int has_trns;
uint8_t transparent_color_be[6];
uint8_t *image_buf; uint8_t *image_buf;
int image_linesize; int image_linesize;
...@@ -590,6 +592,7 @@ static int decode_idat_chunk(AVCodecContext *avctx, PNGDecContext *s, ...@@ -590,6 +592,7 @@ static int decode_idat_chunk(AVCodecContext *avctx, PNGDecContext *s,
uint32_t length, AVFrame *p) uint32_t length, AVFrame *p)
{ {
int ret; int ret;
size_t byte_depth = s->bit_depth > 8 ? 2 : 1;
if (!(s->state & PNG_IHDR)) { if (!(s->state & PNG_IHDR)) {
av_log(avctx, AV_LOG_ERROR, "IDAT without IHDR\n"); av_log(avctx, AV_LOG_ERROR, "IDAT without IHDR\n");
...@@ -641,6 +644,31 @@ static int decode_idat_chunk(AVCodecContext *avctx, PNGDecContext *s, ...@@ -641,6 +644,31 @@ static int decode_idat_chunk(AVCodecContext *avctx, PNGDecContext *s,
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
} }
if (s->has_trns && s->color_type != PNG_COLOR_TYPE_PALETTE) {
switch (avctx->pix_fmt) {
case AV_PIX_FMT_RGB24:
avctx->pix_fmt = AV_PIX_FMT_RGBA;
break;
case AV_PIX_FMT_RGB48BE:
avctx->pix_fmt = AV_PIX_FMT_RGBA64BE;
break;
case AV_PIX_FMT_GRAY8:
avctx->pix_fmt = AV_PIX_FMT_YA8;
break;
case AV_PIX_FMT_GRAY16BE:
avctx->pix_fmt = AV_PIX_FMT_YA16BE;
break;
default:
av_assert0(0);
}
s->bpp += byte_depth;
}
if ((ret = ff_thread_get_buffer(avctx, &s->picture, AV_GET_BUFFER_FLAG_REF)) < 0) if ((ret = ff_thread_get_buffer(avctx, &s->picture, AV_GET_BUFFER_FLAG_REF)) < 0)
return ret; return ret;
if (avctx->codec_id == AV_CODEC_ID_APNG && s->last_dispose_op != APNG_DISPOSE_OP_PREVIOUS) { if (avctx->codec_id == AV_CODEC_ID_APNG && s->last_dispose_op != APNG_DISPOSE_OP_PREVIOUS) {
...@@ -691,9 +719,21 @@ static int decode_idat_chunk(AVCodecContext *avctx, PNGDecContext *s, ...@@ -691,9 +719,21 @@ static int decode_idat_chunk(AVCodecContext *avctx, PNGDecContext *s,
s->zstream.avail_out = s->crow_size; s->zstream.avail_out = s->crow_size;
s->zstream.next_out = s->crow_buf; s->zstream.next_out = s->crow_buf;
} }
s->state |= PNG_IDAT; s->state |= PNG_IDAT;
if ((ret = png_decode_idat(s, length)) < 0)
/* set image to non-transparent bpp while decompressing */
if (s->has_trns && s->color_type != PNG_COLOR_TYPE_PALETTE)
s->bpp -= byte_depth;
ret = png_decode_idat(s, length);
if (s->has_trns && s->color_type != PNG_COLOR_TYPE_PALETTE)
s->bpp += byte_depth;
if (ret < 0)
return ret; return ret;
bytestream2_skip(&s->gb, 4); /* crc */ bytestream2_skip(&s->gb, 4); /* crc */
return 0; return 0;
...@@ -727,17 +767,33 @@ static int decode_trns_chunk(AVCodecContext *avctx, PNGDecContext *s, ...@@ -727,17 +767,33 @@ static int decode_trns_chunk(AVCodecContext *avctx, PNGDecContext *s,
{ {
int v, i; int v, i;
/* read the transparency. XXX: Only palette mode supported */ if (s->color_type == PNG_COLOR_TYPE_PALETTE) {
if (s->color_type != PNG_COLOR_TYPE_PALETTE || if (length > 256 || !(s->state & PNG_PLTE))
length > 256 ||
!(s->state & PNG_PLTE))
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
for (i = 0; i < length; i++) { for (i = 0; i < length; i++) {
v = bytestream2_get_byte(&s->gb); v = bytestream2_get_byte(&s->gb);
s->palette[i] = (s->palette[i] & 0x00ffffff) | (v << 24); s->palette[i] = (s->palette[i] & 0x00ffffff) | (v << 24);
} }
bytestream2_skip(&s->gb, 4); /* crc */ } else if (s->color_type == PNG_COLOR_TYPE_GRAY || s->color_type == PNG_COLOR_TYPE_RGB) {
if ((s->color_type == PNG_COLOR_TYPE_GRAY && length != 2) ||
(s->color_type == PNG_COLOR_TYPE_RGB && length != 6))
return AVERROR_INVALIDDATA;
for (i = 0; i < length / 2; i++) {
/* only use the least significant bits */
v = bytestream2_get_be16(&s->gb) & ((1 << s->bit_depth) - 1);
if (s->bit_depth > 8)
AV_WB16(&s->transparent_color_be[2 * i], v);
else
s->transparent_color_be[i] = v;
}
} else {
return AVERROR_INVALIDDATA;
}
bytestream2_skip(&s->gb, 4); /* crc */
s->has_trns = 1; s->has_trns = 1;
return 0; return 0;
...@@ -1122,6 +1178,29 @@ exit_loop: ...@@ -1122,6 +1178,29 @@ exit_loop:
if (s->bits_per_pixel <= 4) if (s->bits_per_pixel <= 4)
handle_small_bpp(s, p); handle_small_bpp(s, p);
/* apply transparency if needed */
if (s->has_trns && s->color_type != PNG_COLOR_TYPE_PALETTE) {
size_t byte_depth = s->bit_depth > 8 ? 2 : 1;
size_t raw_bpp = s->bpp - byte_depth;
unsigned x, y;
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 (!memcmp(pixel, s->transparent_color_be, raw_bpp)) {
memset(&pixel[raw_bpp], 0, byte_depth);
} else {
memset(&pixel[raw_bpp], 0xff, byte_depth);
}
}
}
}
/* 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]) { if (s->last_picture.f->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")
...@@ -1285,6 +1364,7 @@ static int update_thread_context(AVCodecContext *dst, const AVCodecContext *src) ...@@ -1285,6 +1364,7 @@ static int update_thread_context(AVCodecContext *dst, const AVCodecContext *src)
pdst->x_offset = psrc->x_offset; pdst->x_offset = psrc->x_offset;
pdst->y_offset = psrc->y_offset; pdst->y_offset = psrc->y_offset;
pdst->has_trns = psrc->has_trns; pdst->has_trns = psrc->has_trns;
memcpy(pdst->transparent_color_be, psrc->transparent_color_be, sizeof(pdst->transparent_color_be));
pdst->dispose_op = psrc->dispose_op; pdst->dispose_op = psrc->dispose_op;
......
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