]> git.sesse.net Git - ffmpeg/blobdiff - libavcodec/pngdec.c
Check available size before writing in decode_frame()
[ffmpeg] / libavcodec / pngdec.c
index b68ea4db103bb332ee0ecf4b432a95971a55eb09..77ac0d155db1f09c6484f535ab127aeb38690fa7 100644 (file)
@@ -21,6 +21,7 @@
 #include "avcodec.h"
 #include "bytestream.h"
 #include "png.h"
+#include "dsputil.h"
 
 /* TODO:
  * - add 2, 4 and 16 bit depth support
 //#define DEBUG
 
 typedef struct PNGDecContext {
-    uint8_t *bytestream;
-    uint8_t *bytestream_start;
-    uint8_t *bytestream_end;
+    DSPContext dsp;
+
+    const uint8_t *bytestream;
+    const uint8_t *bytestream_start;
+    const uint8_t *bytestream_end;
     AVFrame picture;
 
     int state;
@@ -129,12 +132,60 @@ static void png_put_interlaced_row(uint8_t *dst, int width,
     }
 }
 
-/* XXX: optimize */
+void ff_add_png_paeth_prediction(uint8_t *dst, uint8_t *src, uint8_t *top, int w, int bpp)
+{
+    int i;
+    for(i = 0; i < w; i++) {
+        int a, b, c, p, pa, pb, pc;
+
+        a = dst[i - bpp];
+        b = top[i];
+        c = top[i - bpp];
+
+        p = b - c;
+        pc = a - c;
+
+        pa = abs(p);
+        pb = abs(pc);
+        pc = abs(p + pc);
+
+        if (pa <= pb && pa <= pc)
+            p = a;
+        else if (pb <= pc)
+            p = b;
+        else
+            p = c;
+        dst[i] = p + src[i];
+    }
+}
+
+#define UNROLL1(bpp, op) {\
+                 r = dst[0];\
+    if(bpp >= 2) g = dst[1];\
+    if(bpp >= 3) b = dst[2];\
+    if(bpp >= 4) a = dst[3];\
+    for(; i < size; i+=bpp) {\
+        dst[i+0] = r = op(r, src[i+0], last[i+0]);\
+        if(bpp == 1) continue;\
+        dst[i+1] = g = op(g, src[i+1], last[i+1]);\
+        if(bpp == 2) continue;\
+        dst[i+2] = b = op(b, src[i+2], last[i+2]);\
+        if(bpp == 3) continue;\
+        dst[i+3] = a = op(a, src[i+3], last[i+3]);\
+    }\
+}
+
+#define UNROLL_FILTER(op)\
+         if(bpp == 1) UNROLL1(1, op)\
+    else if(bpp == 2) UNROLL1(2, op)\
+    else if(bpp == 3) UNROLL1(3, op)\
+    else if(bpp == 4) UNROLL1(4, op)\
+
 /* NOTE: 'dst' can be equal to 'last' */
-static void png_filter_row(uint8_t *dst, int filter_type,
+static void png_filter_row(DSPContext *dsp, uint8_t *dst, int filter_type,
                            uint8_t *src, uint8_t *last, int size, int bpp)
 {
-    int i, p;
+    int i, p, r, g, b, a;
 
     switch(filter_type) {
     case PNG_FILTER_VALUE_NONE:
@@ -144,59 +195,46 @@ static void png_filter_row(uint8_t *dst, int filter_type,
         for(i = 0; i < bpp; i++) {
             dst[i] = src[i];
         }
-        for(i = bpp; i < size; i++) {
-            p = dst[i - bpp];
-            dst[i] = p + src[i];
+        if(bpp == 4) {
+            p = *(int*)dst;
+            for(; i < size; i+=bpp) {
+                int s = *(int*)(src+i);
+                p = ((s&0x7f7f7f7f) + (p&0x7f7f7f7f)) ^ ((s^p)&0x80808080);
+                *(int*)(dst+i) = p;
+            }
+        } else {
+#define OP_SUB(x,s,l) x+s
+            UNROLL_FILTER(OP_SUB);
         }
         break;
     case PNG_FILTER_VALUE_UP:
-        for(i = 0; i < size; i++) {
-            p = last[i];
-            dst[i] = p + src[i];
-        }
+        dsp->add_bytes_l2(dst, src, last, size);
         break;
     case PNG_FILTER_VALUE_AVG:
         for(i = 0; i < bpp; i++) {
             p = (last[i] >> 1);
             dst[i] = p + src[i];
         }
-        for(i = bpp; i < size; i++) {
-            p = ((dst[i - bpp] + last[i]) >> 1);
-            dst[i] = p + src[i];
-        }
+#define OP_AVG(x,s,l) (((x + l) >> 1) + s) & 0xff
+        UNROLL_FILTER(OP_AVG);
         break;
     case PNG_FILTER_VALUE_PAETH:
         for(i = 0; i < bpp; i++) {
             p = last[i];
             dst[i] = p + src[i];
         }
-        for(i = bpp; i < size; i++) {
-            int a, b, c, pa, pb, pc;
-
-            a = dst[i - bpp];
-            b = last[i];
-            c = last[i - bpp];
-
-            p = b - c;
-            pc = a - c;
-
-            pa = abs(p);
-            pb = abs(pc);
-            pc = abs(p + pc);
-
-            if (pa <= pb && pa <= pc)
-                p = a;
-            else if (pb <= pc)
-                p = b;
-            else
-                p = c;
-            dst[i] = p + src[i];
+        if(bpp > 1 && size > 4) {
+            // would write off the end of the array if we let it process the last pixel with bpp=3
+            int w = bpp==4 ? size : size-3;
+            dsp->add_png_paeth_prediction(dst+i, src+i, last+i, w-i, bpp);
+            i = w;
         }
+        ff_add_png_paeth_prediction(dst+i, src+i, last+i, size-i, bpp);
         break;
     }
 }
 
-static void convert_to_rgb32(uint8_t *dst, const uint8_t *src, int width)
+static av_always_inline void convert_to_rgb32_loco(uint8_t *dst, const uint8_t *src, int width, int loco)
 {
     int j;
     unsigned int r, g, b, a;
@@ -206,12 +244,34 @@ static void convert_to_rgb32(uint8_t *dst, const uint8_t *src, int width)
         g = src[1];
         b = src[2];
         a = src[3];
+        if(loco) {
+            r = (r+g)&0xff;
+            b = (b+g)&0xff;
+        }
         *(uint32_t *)dst = (a << 24) | (r << 16) | (g << 8) | b;
         dst += 4;
         src += 4;
     }
 }
 
+static void convert_to_rgb32(uint8_t *dst, const uint8_t *src, int width, int loco)
+{
+    if(loco)
+        convert_to_rgb32_loco(dst, src, width, 1);
+    else
+        convert_to_rgb32_loco(dst, src, width, 0);
+}
+
+static void deloco_rgb24(uint8_t *dst, int size)
+{
+    int i;
+    for(i=0; i<size; i+=3) {
+        int g = dst[i+1];
+        dst[i+0] += g;
+        dst[i+2] += g;
+    }
+}
+
 /* process exactly one decompressed row */
 static void png_handle_row(PNGDecContext *s)
 {
@@ -222,10 +282,10 @@ static void png_handle_row(PNGDecContext *s)
         ptr = s->image_buf + s->image_linesize * s->y;
         /* need to swap bytes correctly for RGB_ALPHA */
         if (s->color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
-            png_filter_row(s->tmp_row, s->crow_buf[0], s->crow_buf + 1,
+            png_filter_row(&s->dsp, s->tmp_row, s->crow_buf[0], s->crow_buf + 1,
                            s->last_row, s->row_size, s->bpp);
-            memcpy(s->last_row, s->tmp_row, s->row_size);
-            convert_to_rgb32(ptr, s->tmp_row, s->width);
+            convert_to_rgb32(ptr, s->tmp_row, s->width, s->filter_type == PNG_FILTER_TYPE_LOCO);
+            FFSWAP(uint8_t*, s->last_row, s->tmp_row);
         } else {
             /* in normal case, we avoid one copy */
             if (s->y == 0)
@@ -233,12 +293,19 @@ static void png_handle_row(PNGDecContext *s)
             else
                 last_row = ptr - s->image_linesize;
 
-            png_filter_row(ptr, s->crow_buf[0], s->crow_buf + 1,
+            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->color_type == PNG_COLOR_TYPE_RGB && s->y > 0)
+            deloco_rgb24(ptr - s->image_linesize, s->row_size);
         s->y++;
         if (s->y == s->height) {
             s->state |= PNG_ALLIMAGE;
+            if (s->filter_type == PNG_FILTER_TYPE_LOCO &&
+                s->color_type == PNG_COLOR_TYPE_RGB)
+                deloco_rgb24(ptr, s->row_size);
         }
     } else {
         got_line = 0;
@@ -249,9 +316,9 @@ static void png_handle_row(PNGDecContext *s)
                    wait for the next one */
                 if (got_line)
                     break;
-                png_filter_row(s->tmp_row, s->crow_buf[0], s->crow_buf + 1,
+                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);
-                memcpy(s->last_row, s->tmp_row, s->pass_row_size);
+                FFSWAP(uint8_t*, s->last_row, s->tmp_row);
                 got_line = 1;
             }
             if ((png_pass_dsp_ymask[s->pass] << (s->y & 7)) & 0x80) {
@@ -312,11 +379,11 @@ static int png_decode_idat(PNGDecContext *s, int length)
 
 static int decode_frame(AVCodecContext *avctx,
                         void *data, int *data_size,
-                        uint8_t *buf, int buf_size)
+                        const uint8_t *buf, int buf_size)
 {
     PNGDecContext * const s = avctx->priv_data;
     AVFrame *picture = data;
-    AVFrame * const p= (AVFrame*)&s->picture;
+    AVFrame * const p= &s->picture;
     uint32_t tag, length;
     int ret, crc;
 
@@ -325,7 +392,8 @@ static int decode_frame(AVCodecContext *avctx,
     s->bytestream_end= buf + buf_size;
 
     /* check signature */
-    if (memcmp(s->bytestream, ff_pngsig, 8) != 0)
+    if (memcmp(s->bytestream, ff_pngsig, 8) != 0 &&
+        memcmp(s->bytestream, ff_mngsig, 8) != 0)
         return -1;
     s->bytestream+= 8;
     s->y=
@@ -514,8 +582,8 @@ static int decode_frame(AVCodecContext *avctx,
         }
     }
  exit_loop:
-    *picture= *(AVFrame*)&s->picture;
-    *data_size = sizeof(AVPicture);
+    *picture= s->picture;
+    *data_size = sizeof(AVFrame);
 
     ret = s->bytestream - s->bytestream_start;
  the_end:
@@ -529,11 +597,12 @@ static int decode_frame(AVCodecContext *avctx,
     goto the_end;
 }
 
-static int png_dec_init(AVCodecContext *avctx){
+static av_cold int png_dec_init(AVCodecContext *avctx){
     PNGDecContext *s = avctx->priv_data;
 
-    avcodec_get_frame_defaults((AVFrame*)&s->picture);
-    avctx->coded_frame= (AVFrame*)&s->picture;
+    avcodec_get_frame_defaults(&s->picture);
+    avctx->coded_frame= &s->picture;
+    dsputil_init(&s->dsp, avctx);
 
     return 0;
 }
@@ -548,5 +617,6 @@ AVCodec png_decoder = {
     NULL, //decode_end,
     decode_frame,
     0 /*CODEC_CAP_DR1*/ /*| CODEC_CAP_DRAW_HORIZ_BAND*/,
-    NULL
+    NULL,
+    .long_name = NULL_IF_CONFIG_SMALL("PNG image"),
 };