[FFmpeg-devel] [PATCH] png: Fully support the tRNS chunk

Paul B Mahol onemda at gmail.com
Sun Aug 30 16:58:45 CEST 2015


On 8/28/15, Donny Yang <work at kota.moe> wrote:
> Signed-off-by: Donny Yang <work at kota.moe>
> ---
>  libavcodec/pngdec.c | 98
> ++++++++++++++++++++++++++++++++++++++++++++++++-----
>  1 file changed, 89 insertions(+), 9 deletions(-)
>
> diff --git a/libavcodec/pngdec.c b/libavcodec/pngdec.c
> index 6e7eae0..1153d65 100644
> --- a/libavcodec/pngdec.c
> +++ b/libavcodec/pngdec.c
> @@ -21,6 +21,7 @@
>
>  //#define DEBUG
>
> +#include "libavutil/avassert.h"
>  #include "libavutil/bprint.h"
>  #include "libavutil/imgutils.h"
>  #include "avcodec.h"
> @@ -59,6 +60,7 @@ typedef struct PNGDecContext {
>      int bits_per_pixel;
>      int bpp;
>      int has_trns;
> +    uint8_t transparent_colour_be[6];
>
>      uint8_t *image_buf;
>      int image_linesize;
> @@ -590,6 +592,7 @@ static int decode_idat_chunk(AVCodecContext *avctx,
> PNGDecContext *s,
>                               uint32_t length, AVFrame *p)
>  {
>      int ret;
> +    size_t byte_depth = s->bit_depth > 8 ? 2 : 1;
>
>      if (!(s->state & PNG_IHDR)) {
>          av_log(avctx, AV_LOG_ERROR, "IDAT without IHDR\n");
> @@ -641,6 +644,31 @@ static int decode_idat_chunk(AVCodecContext *avctx,
> PNGDecContext *s,
>              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)
>              return ret;
>          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,
>          s->zstream.avail_out = s->crow_size;
>          s->zstream.next_out  = s->crow_buf;
>      }
> +
>      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;
> +
>      bytestream2_skip(&s->gb, 4); /* crc */
>
>      return 0;
> @@ -727,17 +767,33 @@ static int decode_trns_chunk(AVCodecContext *avctx,
> PNGDecContext *s,
>  {
>      int v, i;
>
> -    /* read the transparency. XXX: Only palette mode supported */
> -    if (s->color_type != PNG_COLOR_TYPE_PALETTE ||
> -            length > 256 ||
> -            !(s->state & PNG_PLTE))
> +    if (s->color_type == PNG_COLOR_TYPE_PALETTE) {
> +        if (length > 256 || !(s->state & PNG_PLTE))
> +            return AVERROR_INVALIDDATA;
> +
> +        for (i = 0; i < length; i++) {
> +            v = bytestream2_get_byte(&s->gb);
> +            s->palette[i] = (s->palette[i] & 0x00ffffff) | (v << 24);
> +        }
> +    } 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);

This can crash if length of trns chunk is > 6.

> +
> +            if (s->bit_depth > 8)
> +                AV_WB16(&s->transparent_colour_be[2 * i], v);
> +            else
> +                s->transparent_colour_be[i] = v;
> +        }
> +    } else {
>          return AVERROR_INVALIDDATA;
> -    for (i = 0; i < length; i++) {
> -        v = bytestream2_get_byte(&s->gb);
> -        s->palette[i] = (s->palette[i] & 0x00ffffff) | (v << 24);
>      }
> -    bytestream2_skip(&s->gb, 4);     /* crc */
>
> +    bytestream2_skip(&s->gb, 4); /* crc */
>      s->has_trns = 1;
>
>      return 0;
> @@ -1122,6 +1178,29 @@ exit_loop:
>      if (s->bits_per_pixel <= 4)
>          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_colour_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 */
>      if (s->last_picture.f->data[0]) {
>          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)
>          pdst->x_offset = psrc->x_offset;
>          pdst->y_offset = psrc->y_offset;
>          pdst->has_trns = psrc->has_trns;
> +        memcpy(pdst->transparent_colour_be, psrc->transparent_colour_be,
> sizeof(pdst->transparent_colour_be));
>
>          pdst->dispose_op = psrc->dispose_op;
>
> --
> 2.5.0
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>


More information about the ffmpeg-devel mailing list