/*
* PNG image format
- * Copyright (c) 2003 Fabrice Bellard.
+ * Copyright (c) 2003 Fabrice Bellard
*
- * This file is part of FFmpeg.
+ * This file is part of Libav.
*
- * FFmpeg is free software; you can redistribute it and/or
+ * Libav is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * FFmpeg is distributed in the hope that it will be useful,
+ * Libav is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with FFmpeg; if not, write to the Free Software
+ * License along with Libav; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "libavutil/imgutils.h"
#include "avcodec.h"
#include "bytestream.h"
#include "png.h"
const uint8_t *bytestream;
const uint8_t *bytestream_start;
const uint8_t *bytestream_end;
- AVFrame picture;
+ AVFrame picture1, picture2;
+ AVFrame *current_picture, *last_picture;
int state;
int width, height;
else if(bpp == 2) UNROLL1(2, op)\
else if(bpp == 3) UNROLL1(3, op)\
else if(bpp == 4) UNROLL1(4, op)\
+ else {\
+ for (; i < size; i += bpp) {\
+ int j;\
+ for (j = 0; j < bpp; j++)\
+ dst[i+j] = op(dst[i+j-bpp], src[i+j], last[i+j]);\
+ }\
+ }
/* NOTE: 'dst' can be equal to 'last' */
static void png_filter_row(DSPContext *dsp, uint8_t *dst, int filter_type,
}
}
-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;
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)
{
if (s->color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
png_filter_row(&s->dsp, s->tmp_row, s->crow_buf[0], s->crow_buf + 1,
s->last_row, s->row_size, s->bpp);
- 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 */
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;
static int decode_frame(AVCodecContext *avctx,
void *data, int *data_size,
- const uint8_t *buf, int buf_size)
+ AVPacket *avpkt)
{
+ const uint8_t *buf = avpkt->data;
+ int buf_size = avpkt->size;
PNGDecContext * const s = avctx->priv_data;
AVFrame *picture = data;
- AVFrame * const p= (AVFrame*)&s->picture;
+ AVFrame *p;
+ uint8_t *crow_buf_base = NULL;
uint32_t tag, length;
int ret, crc;
+ FFSWAP(AVFrame *, s->current_picture, s->last_picture);
+ avctx->coded_frame= s->current_picture;
+ p = s->current_picture;
+
s->bytestream_start=
s->bytestream= buf;
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=
if (length > 0x7fffffff)
goto fail;
tag32 = bytestream_get_be32(&s->bytestream);
- tag = bswap_32(tag32);
-#ifdef DEBUG
- av_log(avctx, AV_LOG_DEBUG, "png: tag=%c%c%c%c length=%u\n",
- (tag & 0xff),
- ((tag >> 8) & 0xff),
- ((tag >> 16) & 0xff),
- ((tag >> 24) & 0xff), length);
-#endif
+ tag = av_bswap32(tag32);
+ av_dlog(avctx, "png: tag=%c%c%c%c length=%u\n",
+ (tag & 0xff),
+ ((tag >> 8) & 0xff),
+ ((tag >> 16) & 0xff),
+ ((tag >> 24) & 0xff), length);
switch(tag) {
case MKTAG('I', 'H', 'D', 'R'):
if (length != 13)
goto fail;
s->width = bytestream_get_be32(&s->bytestream);
s->height = bytestream_get_be32(&s->bytestream);
- if(avcodec_check_dimensions(avctx, s->width, s->height)){
+ if(av_image_check_size(s->width, s->height, 0, avctx)){
s->width= s->height= 0;
goto fail;
}
s->interlace_type = *s->bytestream++;
crc = bytestream_get_be32(&s->bytestream);
s->state |= PNG_IHDR;
-#ifdef DEBUG
- av_log(avctx, AV_LOG_DEBUG, "width=%d height=%d depth=%d color_type=%d compression_type=%d filter_type=%d interlace_type=%d\n",
- s->width, s->height, s->bit_depth, s->color_type,
- s->compression_type, s->filter_type, s->interlace_type);
-#endif
+ av_dlog(avctx, "width=%d height=%d depth=%d color_type=%d compression_type=%d filter_type=%d interlace_type=%d\n",
+ s->width, s->height, s->bit_depth, s->color_type,
+ s->compression_type, s->filter_type, s->interlace_type);
break;
case MKTAG('I', 'D', 'A', 'T'):
if (!(s->state & PNG_IHDR))
} else if (s->bit_depth == 16 &&
s->color_type == PNG_COLOR_TYPE_GRAY) {
avctx->pix_fmt = PIX_FMT_GRAY16BE;
+ } else if (s->bit_depth == 16 &&
+ s->color_type == PNG_COLOR_TYPE_RGB) {
+ avctx->pix_fmt = PIX_FMT_RGB48BE;
} else if (s->bit_depth == 1 &&
s->color_type == PNG_COLOR_TYPE_GRAY) {
avctx->pix_fmt = PIX_FMT_MONOBLACK;
} else if (s->color_type == PNG_COLOR_TYPE_PALETTE) {
avctx->pix_fmt = PIX_FMT_PAL8;
+ } else if (s->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ avctx->pix_fmt = PIX_FMT_Y400A;
} else {
goto fail;
}
av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
goto fail;
}
- p->pict_type= FF_I_TYPE;
+ p->pict_type= AV_PICTURE_TYPE_I;
p->key_frame= 1;
p->interlaced_frame = !!s->interlace_type;
s->width);
s->crow_size = s->pass_row_size + 1;
}
-#ifdef DEBUG
- av_log(avctx, AV_LOG_DEBUG, "row_size=%d crow_size =%d\n",
- s->row_size, s->crow_size);
-#endif
+ av_dlog(avctx, "row_size=%d crow_size =%d\n",
+ s->row_size, s->crow_size);
s->image_buf = p->data[0];
s->image_linesize = p->linesize[0];
/* copy the palette if needed */
goto fail;
}
/* compressed row */
- s->crow_buf = av_malloc(s->row_size + 1);
- if (!s->crow_buf)
+ crow_buf_base = av_malloc(s->row_size + 16);
+ if (!crow_buf_base)
goto fail;
+
+ /* we want crow_buf+1 to be 16-byte aligned */
+ s->crow_buf = crow_buf_base + 15;
s->zstream.avail_out = s->crow_size;
s->zstream.next_out = s->crow_buf;
}
}
}
exit_loop:
- *picture= *(AVFrame*)&s->picture;
- *data_size = sizeof(AVPicture);
+ /* handle p-frames only if a predecessor frame is available */
+ if(s->last_picture->data[0] != NULL) {
+ if(!(avpkt->flags & AV_PKT_FLAG_KEY)) {
+ int i, j;
+ uint8_t *pd = s->current_picture->data[0];
+ uint8_t *pd_last = s->last_picture->data[0];
+
+ for(j=0; j < s->height; j++) {
+ for(i=0; i < s->width * s->bpp; i++) {
+ pd[i] += pd_last[i];
+ }
+ pd += s->image_linesize;
+ pd_last += s->image_linesize;
+ }
+ }
+ }
+
+ *picture= *s->current_picture;
+ *data_size = sizeof(AVFrame);
ret = s->bytestream - s->bytestream_start;
the_end:
inflateEnd(&s->zstream);
- av_freep(&s->crow_buf);
+ av_free(crow_buf_base);
+ s->crow_buf = NULL;
av_freep(&s->last_row);
av_freep(&s->tmp_row);
return ret;
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;
+ s->current_picture = &s->picture1;
+ s->last_picture = &s->picture2;
+ avcodec_get_frame_defaults(&s->picture1);
+ avcodec_get_frame_defaults(&s->picture2);
dsputil_init(&s->dsp, avctx);
return 0;
}
-AVCodec png_decoder = {
+static av_cold int png_dec_end(AVCodecContext *avctx)
+{
+ PNGDecContext *s = avctx->priv_data;
+
+ if (s->picture1.data[0])
+ avctx->release_buffer(avctx, &s->picture1);
+ if (s->picture2.data[0])
+ avctx->release_buffer(avctx, &s->picture2);
+
+ return 0;
+}
+
+AVCodec ff_png_decoder = {
"png",
- CODEC_TYPE_VIDEO,
+ AVMEDIA_TYPE_VIDEO,
CODEC_ID_PNG,
sizeof(PNGDecContext),
png_dec_init,
NULL,
- NULL, //decode_end,
+ png_dec_end,
decode_frame,
- 0 /*CODEC_CAP_DR1*/ /*| CODEC_CAP_DRAW_HORIZ_BAND*/,
- NULL
+ CODEC_CAP_DR1 /*| CODEC_CAP_DRAW_HORIZ_BAND*/,
+ NULL,
+ .max_lowres = 5,
+ .long_name = NULL_IF_CONFIG_SMALL("PNG image"),
};