]> git.sesse.net Git - ffmpeg/blobdiff - libavcodec/pngdec.c
Include config.h from huffyuvdsp.h.
[ffmpeg] / libavcodec / pngdec.c
index 8199bd823671e3a49b30d1a85949fc08f0eb4f91..ee6a2baf9df9a8525a82a4411d5e7addac3f2d37 100644 (file)
@@ -671,47 +671,170 @@ static int decode_idat_chunk(AVCodecContext *avctx, PNGDecContext *s,
     return 0;
 }
 
-static int decode_frame_png(AVCodecContext *avctx,
-                        void *data, int *got_frame,
-                        AVPacket *avpkt)
+static int decode_plte_chunk(AVCodecContext *avctx, PNGDecContext *s,
+                             uint32_t length)
 {
-    PNGDecContext *const s = avctx->priv_data;
-    const uint8_t *buf     = avpkt->data;
-    int buf_size           = avpkt->size;
-    AVFrame *p;
-    AVDictionary *metadata  = NULL;
-    uint32_t tag, length;
-    int64_t sig;
-    int ret;
+    int n, i, r, g, b;
 
-    ff_thread_release_buffer(avctx, &s->last_picture);
-    FFSWAP(ThreadFrame, s->picture, s->last_picture);
-    p = s->picture.f;
+    if ((length % 3) != 0 || length > 256 * 3)
+        return AVERROR_INVALIDDATA;
+    /* read the palette */
+    n = length / 3;
+    for (i = 0; i < n; i++) {
+        r = bytestream2_get_byte(&s->gb);
+        g = bytestream2_get_byte(&s->gb);
+        b = bytestream2_get_byte(&s->gb);
+        s->palette[i] = (0xFFU << 24) | (r << 16) | (g << 8) | b;
+    }
+    for (; i < 256; i++)
+        s->palette[i] = (0xFFU << 24);
+    s->state |= PNG_PLTE;
+    bytestream2_skip(&s->gb, 4);     /* crc */
 
-    bytestream2_init(&s->gb, buf, buf_size);
+    return 0;
+}
 
-    /* check signature */
-    sig = bytestream2_get_be64(&s->gb);
-    if (sig != PNGSIG &&
-        sig != MNGSIG) {
-        av_log(avctx, AV_LOG_ERROR, "Missing png signature\n");
+static int decode_trns_chunk(AVCodecContext *avctx, PNGDecContext *s,
+                             uint32_t length)
+{
+    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))
         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 */
 
-    s->y = s->state = 0;
+    return 0;
+}
 
-    /* init the zlib */
-    s->zstream.zalloc = ff_png_zalloc;
-    s->zstream.zfree  = ff_png_zfree;
-    s->zstream.opaque = NULL;
-    ret = inflateInit(&s->zstream);
-    if (ret != Z_OK) {
-        av_log(avctx, AV_LOG_ERROR, "inflateInit returned error %d\n", ret);
-        return AVERROR_EXTERNAL;
+static void handle_small_bpp(PNGDecContext *s, AVFrame *p)
+{
+    if (s->bits_per_pixel == 1 && s->color_type == PNG_COLOR_TYPE_PALETTE) {
+        int i, j, k;
+        uint8_t *pd = p->data[0];
+        for (j = 0; j < s->height; j++) {
+            i = s->width / 8;
+            for (k = 7; k >= 1; k--)
+                if ((s->width&7) >= k)
+                    pd[8*i + k - 1] = (pd[i]>>8-k) & 1;
+            for (i--; i >= 0; i--) {
+                pd[8*i + 7]=  pd[i]     & 1;
+                pd[8*i + 6]= (pd[i]>>1) & 1;
+                pd[8*i + 5]= (pd[i]>>2) & 1;
+                pd[8*i + 4]= (pd[i]>>3) & 1;
+                pd[8*i + 3]= (pd[i]>>4) & 1;
+                pd[8*i + 2]= (pd[i]>>5) & 1;
+                pd[8*i + 1]= (pd[i]>>6) & 1;
+                pd[8*i + 0]=  pd[i]>>7;
+            }
+            pd += s->image_linesize;
+        }
+    } else if (s->bits_per_pixel == 2) {
+        int i, j;
+        uint8_t *pd = p->data[0];
+        for (j = 0; j < s->height; j++) {
+            i = s->width / 4;
+            if (s->color_type == PNG_COLOR_TYPE_PALETTE) {
+                if ((s->width&3) >= 3) pd[4*i + 2]= (pd[i] >> 2) & 3;
+                if ((s->width&3) >= 2) pd[4*i + 1]= (pd[i] >> 4) & 3;
+                if ((s->width&3) >= 1) pd[4*i + 0]=  pd[i] >> 6;
+                for (i--; i >= 0; i--) {
+                    pd[4*i + 3]=  pd[i]     & 3;
+                    pd[4*i + 2]= (pd[i]>>2) & 3;
+                    pd[4*i + 1]= (pd[i]>>4) & 3;
+                    pd[4*i + 0]=  pd[i]>>6;
+                }
+            } else {
+                if ((s->width&3) >= 3) pd[4*i + 2]= ((pd[i]>>2) & 3)*0x55;
+                if ((s->width&3) >= 2) pd[4*i + 1]= ((pd[i]>>4) & 3)*0x55;
+                if ((s->width&3) >= 1) pd[4*i + 0]= ( pd[i]>>6     )*0x55;
+                for (i--; i >= 0; i--) {
+                    pd[4*i + 3]= ( pd[i]     & 3)*0x55;
+                    pd[4*i + 2]= ((pd[i]>>2) & 3)*0x55;
+                    pd[4*i + 1]= ((pd[i]>>4) & 3)*0x55;
+                    pd[4*i + 0]= ( pd[i]>>6     )*0x55;
+                }
+            }
+            pd += s->image_linesize;
+        }
+    } else if (s->bits_per_pixel == 4) {
+        int i, j;
+        uint8_t *pd = p->data[0];
+        for (j = 0; j < s->height; j++) {
+            i = s->width/2;
+            if (s->color_type == PNG_COLOR_TYPE_PALETTE) {
+                if (s->width&1) pd[2*i+0]= pd[i]>>4;
+                for (i--; i >= 0; i--) {
+                    pd[2*i + 1] = pd[i] & 15;
+                    pd[2*i + 0] = pd[i] >> 4;
+                }
+            } else {
+                if (s->width & 1) pd[2*i + 0]= (pd[i] >> 4) * 0x11;
+                for (i--; i >= 0; i--) {
+                    pd[2*i + 1] = (pd[i] & 15) * 0x11;
+                    pd[2*i + 0] = (pd[i] >> 4) * 0x11;
+                }
+            }
+            pd += s->image_linesize;
+        }
     }
+}
+
+static int decode_fctl_chunk(AVCodecContext *avctx, PNGDecContext *s,
+                             uint32_t length)
+{
+    uint32_t sequence_number, width, height, x_offset, y_offset;
+
+    if (length != 26)
+        return AVERROR_INVALIDDATA;
+
+    sequence_number = bytestream2_get_be32(&s->gb);
+    width           = bytestream2_get_be32(&s->gb);
+    height          = bytestream2_get_be32(&s->gb);
+    x_offset        = bytestream2_get_be32(&s->gb);
+    y_offset        = bytestream2_get_be32(&s->gb);
+    bytestream2_skip(&s->gb, 10); /* delay_num  (2)
+                                   * delay_den  (2)
+                                   * dispose_op (1)
+                                   * blend_op   (1)
+                                   * crc        (4)
+                                   */
+
+    if (width != s->width || height != s->height ||
+        x_offset != 0 || y_offset != 0) {
+        if (sequence_number == 0)
+            return AVERROR_INVALIDDATA;
+        avpriv_request_sample(avctx, "non key frames");
+        return AVERROR_PATCHWELCOME;
+    }
+
+    return 0;
+}
+
+static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s,
+                               AVFrame *p, AVPacket *avpkt)
+{
+    AVDictionary *metadata  = NULL;
+    uint32_t tag, length;
+    int decode_next_dat = 0;
+    int ret = AVERROR_INVALIDDATA;
+
     for (;;) {
-        if (bytestream2_get_bytes_left(&s->gb) <= 0) {
-            av_log(avctx, AV_LOG_ERROR, "%d bytes left\n", bytestream2_get_bytes_left(&s->gb));
+        length = bytestream2_get_bytes_left(&s->gb);
+        if (length <= 0) {
+            if (avctx->codec_id == AV_CODEC_ID_APNG && length == 0) {
+                if (!(s->state & PNG_IDAT))
+                    return 0;
+                else
+                    goto exit_loop;
+            }
+            av_log(avctx, AV_LOG_ERROR, "%d bytes left\n", length);
             if (   s->state & PNG_ALLIMAGE
                 && avctx->strict_std_compliance <= FF_COMPLIANCE_NORMAL)
                 goto exit_loop;
@@ -719,7 +842,7 @@ static int decode_frame_png(AVCodecContext *avctx,
         }
 
         length = bytestream2_get_be32(&s->gb);
-        if (length > 0x7fffffff || length > bytestream2_get_bytes_left(&s->gb))  {
+        if (length > 0x7fffffff || length > bytestream2_get_bytes_left(&s->gb)) {
             av_log(avctx, AV_LOG_ERROR, "chunk too big\n");
             goto fail;
         }
@@ -739,46 +862,35 @@ static int decode_frame_png(AVCodecContext *avctx,
             if (decode_phys_chunk(avctx, s) < 0)
                 goto fail;
             break;
+        case MKTAG('f', 'c', 'T', 'L'):
+            if (avctx->codec_id != AV_CODEC_ID_APNG)
+                goto skip_tag;
+            if ((ret = decode_fctl_chunk(avctx, s, length)) < 0)
+                goto fail;
+            decode_next_dat = 1;
+            break;
+        case MKTAG('f', 'd', 'A', 'T'):
+            if (avctx->codec_id != AV_CODEC_ID_APNG)
+                goto skip_tag;
+            if (!decode_next_dat)
+                goto fail;
+            bytestream2_get_be32(&s->gb);
+            length -= 4;
+            /* fallthrough */
         case MKTAG('I', 'D', 'A', 'T'):
+            if (avctx->codec_id == AV_CODEC_ID_APNG && !decode_next_dat)
+                goto skip_tag;
             if (decode_idat_chunk(avctx, s, length, p) < 0)
                 goto fail;
             break;
         case MKTAG('P', 'L', 'T', 'E'):
-        {
-            int n, i, r, g, b;
-
-            if ((length % 3) != 0 || length > 256 * 3)
+            if (decode_plte_chunk(avctx, s, length) < 0)
                 goto skip_tag;
-            /* read the palette */
-            n = length / 3;
-            for (i = 0; i < n; i++) {
-                r = bytestream2_get_byte(&s->gb);
-                g = bytestream2_get_byte(&s->gb);
-                b = bytestream2_get_byte(&s->gb);
-                s->palette[i] = (0xFFU << 24) | (r << 16) | (g << 8) | b;
-            }
-            for (; i < 256; i++)
-                s->palette[i] = (0xFFU << 24);
-            s->state |= PNG_PLTE;
-            bytestream2_skip(&s->gb, 4);     /* crc */
-        }
-        break;
+            break;
         case MKTAG('t', 'R', 'N', '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 (decode_trns_chunk(avctx, s, length) < 0)
                 goto skip_tag;
-            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 */
-        }
-        break;
+            break;
         case MKTAG('t', 'E', 'X', 't'):
             if (decode_text_chunk(s, length, 0, &metadata) < 0)
                 av_log(avctx, AV_LOG_WARNING, "Broken tEXt chunk\n");
@@ -806,77 +918,8 @@ skip_tag:
     }
 exit_loop:
 
-    if (s->bits_per_pixel == 1 && s->color_type == PNG_COLOR_TYPE_PALETTE){
-        int i, j, k;
-        uint8_t *pd = p->data[0];
-        for (j = 0; j < s->height; j++) {
-            i = s->width / 8;
-            for (k = 7; k >= 1; k--)
-                if ((s->width&7) >= k)
-                    pd[8*i + k - 1] = (pd[i]>>8-k) & 1;
-            for (i--; i >= 0; i--) {
-                pd[8*i + 7]=  pd[i]     & 1;
-                pd[8*i + 6]= (pd[i]>>1) & 1;
-                pd[8*i + 5]= (pd[i]>>2) & 1;
-                pd[8*i + 4]= (pd[i]>>3) & 1;
-                pd[8*i + 3]= (pd[i]>>4) & 1;
-                pd[8*i + 2]= (pd[i]>>5) & 1;
-                pd[8*i + 1]= (pd[i]>>6) & 1;
-                pd[8*i + 0]=  pd[i]>>7;
-            }
-            pd += s->image_linesize;
-        }
-    }
-    if (s->bits_per_pixel == 2){
-        int i, j;
-        uint8_t *pd = p->data[0];
-        for (j = 0; j < s->height; j++) {
-            i = s->width / 4;
-            if (s->color_type == PNG_COLOR_TYPE_PALETTE){
-                if ((s->width&3) >= 3) pd[4*i + 2]= (pd[i] >> 2) & 3;
-                if ((s->width&3) >= 2) pd[4*i + 1]= (pd[i] >> 4) & 3;
-                if ((s->width&3) >= 1) pd[4*i + 0]=  pd[i] >> 6;
-                for (i--; i >= 0; i--) {
-                    pd[4*i + 3]=  pd[i]     & 3;
-                    pd[4*i + 2]= (pd[i]>>2) & 3;
-                    pd[4*i + 1]= (pd[i]>>4) & 3;
-                    pd[4*i + 0]=  pd[i]>>6;
-                }
-            } else {
-                if ((s->width&3) >= 3) pd[4*i + 2]= ((pd[i]>>2) & 3)*0x55;
-                if ((s->width&3) >= 2) pd[4*i + 1]= ((pd[i]>>4) & 3)*0x55;
-                if ((s->width&3) >= 1) pd[4*i + 0]= ( pd[i]>>6     )*0x55;
-                for (i--; i >= 0; i--) {
-                    pd[4*i + 3]= ( pd[i]     & 3)*0x55;
-                    pd[4*i + 2]= ((pd[i]>>2) & 3)*0x55;
-                    pd[4*i + 1]= ((pd[i]>>4) & 3)*0x55;
-                    pd[4*i + 0]= ( pd[i]>>6     )*0x55;
-                }
-            }
-            pd += s->image_linesize;
-        }
-    }
-    if (s->bits_per_pixel == 4){
-        int i, j;
-        uint8_t *pd = p->data[0];
-        for (j = 0; j < s->height; j++) {
-            i = s->width/2;
-            if (s->color_type == PNG_COLOR_TYPE_PALETTE){
-                if (s->width&1) pd[2*i+0]= pd[i]>>4;
-                for (i--; i >= 0; i--) {
-                pd[2*i + 1] = pd[i] & 15;
-                pd[2*i + 0] = pd[i] >> 4;
-            }
-            } else {
-                if (s->width & 1) pd[2*i + 0]= (pd[i] >> 4) * 0x11;
-                for (i--; i >= 0; i--) {
-                    pd[2*i + 1] = (pd[i] & 15) * 0x11;
-                    pd[2*i + 0] = (pd[i] >> 4) * 0x11;
-                }
-            }
-            pd += s->image_linesize;
-        }
-    }
+    if (s->bits_per_pixel <= 4)
+        handle_small_bpp(s, p);
 
     /* handle p-frames only if a predecessor frame is available */
     if (s->last_picture.f->data[0]) {
@@ -903,6 +946,54 @@ exit_loop:
 
     av_frame_set_metadata(p, metadata);
     metadata   = NULL;
+    return 0;
+
+fail:
+    av_dict_free(&metadata);
+    ff_thread_report_progress(&s->picture, INT_MAX, 0);
+    return ret;
+}
+
+#if CONFIG_PNG_DECODER
+static int decode_frame_png(AVCodecContext *avctx,
+                        void *data, int *got_frame,
+                        AVPacket *avpkt)
+{
+    PNGDecContext *const s = avctx->priv_data;
+    const uint8_t *buf     = avpkt->data;
+    int buf_size           = avpkt->size;
+    AVFrame *p;
+    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 */
+    sig = bytestream2_get_be64(&s->gb);
+    if (sig != PNGSIG &&
+        sig != MNGSIG) {
+        av_log(avctx, AV_LOG_ERROR, "Missing png signature\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    s->y = s->state = 0;
+
+    /* init the zlib */
+    s->zstream.zalloc = ff_png_zalloc;
+    s->zstream.zfree  = ff_png_zfree;
+    s->zstream.opaque = NULL;
+    ret = inflateInit(&s->zstream);
+    if (ret != Z_OK) {
+        av_log(avctx, AV_LOG_ERROR, "inflateInit returned error %d\n", ret);
+        return AVERROR_EXTERNAL;
+    }
+
+    if ((ret = decode_frame_common(avctx, s, p, avpkt)) < 0)
+        goto the_end;
 
     if ((ret = av_frame_ref(data, s->picture.f)) < 0)
         return ret;
@@ -914,12 +1005,64 @@ the_end:
     inflateEnd(&s->zstream);
     s->crow_buf = NULL;
     return ret;
-fail:
-    av_dict_free(&metadata);
-    ff_thread_report_progress(&s->picture, INT_MAX, 0);
-    ret = AVERROR_INVALIDDATA;
-    goto the_end;
 }
+#endif
+
+#if CONFIG_APNG_DECODER
+static int decode_frame_apng(AVCodecContext *avctx,
+                        void *data, int *got_frame,
+                        AVPacket *avpkt)
+{
+    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;
+
+    if (!(s->state & PNG_IHDR)) {
+        if (!avctx->extradata_size)
+            return AVERROR_INVALIDDATA;
+
+        /* only init fields, there is no zlib use in extradata */
+        s->zstream.zalloc = ff_png_zalloc;
+        s->zstream.zfree  = ff_png_zfree;
+
+        bytestream2_init(&s->gb, avctx->extradata, avctx->extradata_size);
+        if ((ret = decode_frame_common(avctx, s, p, avpkt)) < 0)
+            goto end;
+    }
+
+    /* reset state for a new frame */
+    if ((ret = inflateInit(&s->zstream)) != Z_OK) {
+        av_log(avctx, AV_LOG_ERROR, "inflateInit returned error %d\n", ret);
+        ret = AVERROR_EXTERNAL;
+        goto end;
+    }
+    s->y = 0;
+    s->state &= ~(PNG_IDAT | PNG_ALLIMAGE);
+    bytestream2_init(&s->gb, avpkt->data, avpkt->size);
+    if ((ret = decode_frame_common(avctx, s, p, avpkt)) < 0)
+        goto end;
+
+    if (!(s->state & PNG_ALLIMAGE))
+        av_log(avctx, AV_LOG_WARNING, "Frame did not contain a complete image\n");
+    if (!(s->state & (PNG_ALLIMAGE|PNG_IDAT))) {
+        ret = AVERROR_INVALIDDATA;
+        goto end;
+    }
+    if ((ret = av_frame_ref(data, s->picture.f)) < 0)
+        goto end;
+
+    *got_frame = 1;
+    ret = bytestream2_tell(&s->gb);
+
+end:
+    inflateEnd(&s->zstream);
+    return ret;
+}
+#endif
 
 static int update_thread_context(AVCodecContext *dst, const AVCodecContext *src)
 {
@@ -972,6 +1115,23 @@ static av_cold int png_dec_end(AVCodecContext *avctx)
     return 0;
 }
 
+#if CONFIG_APNG_DECODER
+AVCodec ff_apng_decoder = {
+    .name           = "apng",
+    .long_name      = NULL_IF_CONFIG_SMALL("APNG (Animated Portable Network Graphics) image"),
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = AV_CODEC_ID_APNG,
+    .priv_data_size = sizeof(PNGDecContext),
+    .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   = CODEC_CAP_DR1 | CODEC_CAP_FRAME_THREADS /*| CODEC_CAP_DRAW_HORIZ_BAND*/,
+};
+#endif
+
+#if CONFIG_PNG_DECODER
 AVCodec ff_png_decoder = {
     .name           = "png",
     .long_name      = NULL_IF_CONFIG_SMALL("PNG (Portable Network Graphics) image"),
@@ -985,3 +1145,4 @@ AVCodec ff_png_decoder = {
     .update_thread_context = ONLY_IF_THREADS_ENABLED(update_thread_context),
     .capabilities   = CODEC_CAP_DR1 | CODEC_CAP_FRAME_THREADS /*| CODEC_CAP_DRAW_HORIZ_BAND*/,
 };
+#endif