diff options
Diffstat (limited to 'libavcodec/qdrw.c')
-rw-r--r-- | libavcodec/qdrw.c | 254 |
1 files changed, 231 insertions, 23 deletions
diff --git a/libavcodec/qdrw.c b/libavcodec/qdrw.c index b7493e4..82cc528 100644 --- a/libavcodec/qdrw.c +++ b/libavcodec/qdrw.c @@ -3,20 +3,20 @@ * Copyright (c) 2004 Konstantin Shishkov * Copyright (c) 2015 Vittorio Giovara * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg 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. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -33,10 +33,13 @@ #include "internal.h" enum QuickdrawOpcodes { + CLIP = 0x0001, PACKBITSRECT = 0x0098, PACKBITSRGN, DIRECTBITSRECT, DIRECTBITSRGN, + SHORTCOMMENT = 0x00A0, + LONGCOMMENT, EOP = 0x00FF, }; @@ -55,6 +58,8 @@ static int parse_palette(AVCodecContext *avctx, GetByteContext *gbc, bytestream2_skip(gbc, 6); continue; } + if (avctx->pix_fmt != AV_PIX_FMT_PAL8) + return AVERROR_INVALIDDATA; r = bytestream2_get_byte(gbc); bytestream2_skip(gbc, 1); g = bytestream2_get_byte(gbc); @@ -66,6 +71,148 @@ static int parse_palette(AVCodecContext *avctx, GetByteContext *gbc, return 0; } +static int decode_rle_bpp2(AVCodecContext *avctx, AVFrame *p, GetByteContext *gbc) +{ + int offset = avctx->width; + uint8_t *outdata = p->data[0]; + int i, j; + + for (i = 0; i < avctx->height; i++) { + int size, left, code, pix; + uint8_t *out = outdata; + int pos = 0; + + /* size of packed line */ + if (offset / 4 > 200) + size = left = bytestream2_get_be16(gbc); + else + size = left = bytestream2_get_byte(gbc); + if (bytestream2_get_bytes_left(gbc) < size) + return AVERROR_INVALIDDATA; + + /* decode line */ + while (left > 0) { + code = bytestream2_get_byte(gbc); + if (code & 0x80 ) { /* run */ + pix = bytestream2_get_byte(gbc); + for (j = 0; j < 257 - code; j++) { + if (pos < offset) + out[pos++] = (pix & 0xC0) >> 6; + if (pos < offset) + out[pos++] = (pix & 0x30) >> 4; + if (pos < offset) + out[pos++] = (pix & 0x0C) >> 2; + if (pos < offset) + out[pos++] = (pix & 0x03); + } + left -= 2; + } else { /* copy */ + for (j = 0; j < code + 1; j++) { + pix = bytestream2_get_byte(gbc); + if (pos < offset) + out[pos++] = (pix & 0xC0) >> 6; + if (pos < offset) + out[pos++] = (pix & 0x30) >> 4; + if (pos < offset) + out[pos++] = (pix & 0x0C) >> 2; + if (pos < offset) + out[pos++] = (pix & 0x03); + } + left -= 1 + (code + 1); + } + } + outdata += p->linesize[0]; + } + return 0; +} + +static int decode_rle_bpp4(AVCodecContext *avctx, AVFrame *p, GetByteContext *gbc) +{ + int offset = avctx->width; + uint8_t *outdata = p->data[0]; + int i, j; + + for (i = 0; i < avctx->height; i++) { + int size, left, code, pix; + uint8_t *out = outdata; + int pos = 0; + + /* size of packed line */ + size = left = bytestream2_get_be16(gbc); + if (bytestream2_get_bytes_left(gbc) < size) + return AVERROR_INVALIDDATA; + + /* decode line */ + while (left > 0) { + code = bytestream2_get_byte(gbc); + if (code & 0x80 ) { /* run */ + pix = bytestream2_get_byte(gbc); + for (j = 0; j < 257 - code; j++) { + if (pos < offset) + out[pos++] = (pix & 0xF0) >> 4; + if (pos < offset) + out[pos++] = pix & 0xF; + } + left -= 2; + } else { /* copy */ + for (j = 0; j < code + 1; j++) { + pix = bytestream2_get_byte(gbc); + if (pos < offset) + out[pos++] = (pix & 0xF0) >> 4; + if (pos < offset) + out[pos++] = pix & 0xF; + } + left -= 1 + (code + 1); + } + } + outdata += p->linesize[0]; + } + return 0; +} + +static int decode_rle16(AVCodecContext *avctx, AVFrame *p, GetByteContext *gbc) +{ + int offset = avctx->width; + uint8_t *outdata = p->data[0]; + int i, j; + + for (i = 0; i < avctx->height; i++) { + int size, left, code, pix; + uint16_t *out = (uint16_t *)outdata; + int pos = 0; + + /* size of packed line */ + size = left = bytestream2_get_be16(gbc); + if (bytestream2_get_bytes_left(gbc) < size) + return AVERROR_INVALIDDATA; + + /* decode line */ + while (left > 0) { + code = bytestream2_get_byte(gbc); + if (code & 0x80 ) { /* run */ + pix = bytestream2_get_be16(gbc); + for (j = 0; j < 257 - code; j++) { + if (pos < offset) { + out[pos++] = pix; + } + } + left -= 3; + } else { /* copy */ + for (j = 0; j < code + 1; j++) { + if (pos < offset) { + out[pos++] = bytestream2_get_be16(gbc); + } else { + bytestream2_skip(gbc, 2); + } + } + left -= 1 + (code + 1) * 2; + } + } + outdata += p->linesize[0]; + } + return 0; +} + static int decode_rle(AVCodecContext *avctx, AVFrame *p, GetByteContext *gbc, int step) { @@ -89,9 +236,10 @@ static int decode_rle(AVCodecContext *avctx, AVFrame *p, GetByteContext *gbc, if (code & 0x80 ) { /* run */ pix = bytestream2_get_byte(gbc); for (j = 0; j < 257 - code; j++) { - out[pos] = pix; + if (pos < offset) + out[pos] = pix; pos += step; - if (pos >= offset) { + if (pos >= offset && step > 1) { pos -= offset; pos++; } @@ -99,9 +247,11 @@ static int decode_rle(AVCodecContext *avctx, AVFrame *p, GetByteContext *gbc, left -= 2; } else { /* copy */ for (j = 0; j < code + 1; j++) { - out[pos] = bytestream2_get_byte(gbc); + pix = bytestream2_get_byte(gbc); + if (pos < offset) + out[pos] = pix; pos += step; - if (pos >= offset) { + if (pos >= offset && step > 1) { pos -= offset; pos++; } @@ -114,6 +264,29 @@ static int decode_rle(AVCodecContext *avctx, AVFrame *p, GetByteContext *gbc, return 0; } +static int check_header(const char *buf, int buf_size) +{ + unsigned w, h, v0, v1; + + if (buf_size < 40) + return 0; + + w = AV_RB16(buf+6); + h = AV_RB16(buf+8); + v0 = AV_RB16(buf+10); + v1 = AV_RB16(buf+12); + + if (!w || !h) + return 0; + + if (v0 == 0x1101) + return 1; + if (v0 == 0x0011 && v1 == 0x02FF) + return 2; + return 0; +} + + static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPacket *avpkt) @@ -122,13 +295,16 @@ static int decode_frame(AVCodecContext *avctx, GetByteContext gbc; int colors; int w, h, ret; + int ver; bytestream2_init(&gbc, avpkt->data, avpkt->size); - - /* PICT images start with a 512 bytes empty header */ - if (bytestream2_peek_be32(&gbc) == 0) + if ( bytestream2_get_bytes_left(&gbc) >= 552 + && check_header(gbc.buffer + 512, bytestream2_get_bytes_left(&gbc) - 512) + ) bytestream2_skip(&gbc, 512); + ver = check_header(gbc.buffer, bytestream2_get_bytes_left(&gbc)); + /* smallest PICT header */ if (bytestream2_get_bytes_left(&gbc) < 40) { av_log(avctx, AV_LOG_ERROR, "Frame is too small %d\n", @@ -146,12 +322,15 @@ static int decode_frame(AVCodecContext *avctx, /* version 1 is identified by 0x1101 * it uses byte-aligned opcodes rather than word-aligned */ - if (bytestream2_get_be32(&gbc) != 0x001102FF) { + if (ver == 1) { avpriv_request_sample(avctx, "QuickDraw version 1"); return AVERROR_PATCHWELCOME; + } else if (ver != 2) { + avpriv_request_sample(avctx, "QuickDraw version unknown (%X)", bytestream2_get_be32(&gbc)); + return AVERROR_PATCHWELCOME; } - bytestream2_skip(&gbc, 26); + bytestream2_skip(&gbc, 4+26); while (bytestream2_get_bytes_left(&gbc) >= 4) { int bppcnt, bpp; @@ -159,6 +338,9 @@ static int decode_frame(AVCodecContext *avctx, int opcode = bytestream2_get_be16(&gbc); switch(opcode) { + case CLIP: + bytestream2_skip(&gbc, 10); + break; case PACKBITSRECT: case PACKBITSRGN: av_log(avctx, AV_LOG_DEBUG, "Parsing Packbit opcode\n"); @@ -170,6 +352,10 @@ static int decode_frame(AVCodecContext *avctx, av_log(avctx, AV_LOG_DEBUG, "bppcount %d bpp %d\n", bppcnt, bpp); if (bppcnt == 1 && bpp == 8) { avctx->pix_fmt = AV_PIX_FMT_PAL8; + } else if (bppcnt == 1 && (bpp == 4 || bpp == 2)) { + avctx->pix_fmt = AV_PIX_FMT_PAL8; + } else if (bppcnt == 3 && bpp == 5) { + avctx->pix_fmt = AV_PIX_FMT_RGB555; } else { av_log(avctx, AV_LOG_ERROR, "Invalid pixel format (bppcnt %d bpp %d) in Packbit\n", @@ -191,12 +377,12 @@ static int decode_frame(AVCodecContext *avctx, bytestream2_get_bytes_left(&gbc)); return AVERROR_INVALIDDATA; } - if ((ret = ff_get_buffer(avctx, p, 0)) < 0) { - av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n"); + if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; - } - parse_palette(avctx, &gbc, (uint32_t *)p->data[1], colors); + ret = parse_palette(avctx, &gbc, (uint32_t *)p->data[1], colors); + if (ret < 0) + return ret; p->palette_has_changed = 1; /* jump to image data */ @@ -207,7 +393,14 @@ static int decode_frame(AVCodecContext *avctx, avpriv_report_missing_feature(avctx, "Packbit mask region"); } - ret = decode_rle(avctx, p, &gbc, bppcnt); + if (avctx->pix_fmt == AV_PIX_FMT_RGB555) + ret = decode_rle16(avctx, p, &gbc); + else if (bpp == 2) + ret = decode_rle_bpp2(avctx, p, &gbc); + else if (bpp == 4) + ret = decode_rle_bpp4(avctx, p, &gbc); + else + ret = decode_rle(avctx, p, &gbc, bppcnt); if (ret < 0) return ret; *got_frame = 1; @@ -223,7 +416,15 @@ static int decode_frame(AVCodecContext *avctx, return AVERROR_PATCHWELCOME; } - bytestream2_skip(&gbc, 10); + bytestream2_skip(&gbc, 4); + h = bytestream2_get_be16(&gbc); + w = bytestream2_get_be16(&gbc); + bytestream2_skip(&gbc, 2); + + ret = ff_set_dimensions(avctx, w, h); + if (ret < 0) + return ret; + pack_type = bytestream2_get_be16(&gbc); bytestream2_skip(&gbc, 16); @@ -233,6 +434,8 @@ static int decode_frame(AVCodecContext *avctx, av_log(avctx, AV_LOG_DEBUG, "bppcount %d bpp %d\n", bppcnt, bpp); if (bppcnt == 3 && bpp == 8) { avctx->pix_fmt = AV_PIX_FMT_RGB24; + } else if (bppcnt == 3 && bpp == 5 || bppcnt == 2 && bpp == 8) { + avctx->pix_fmt = AV_PIX_FMT_RGB555; } else if (bppcnt == 4 && bpp == 8) { avctx->pix_fmt = AV_PIX_FMT_ARGB; } else { @@ -250,10 +453,8 @@ static int decode_frame(AVCodecContext *avctx, avpriv_request_sample(avctx, "Pack type %d", pack_type); return AVERROR_PATCHWELCOME; } - if ((ret = ff_get_buffer(avctx, p, 0)) < 0) { - av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n"); + if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; - } /* jump to data */ bytestream2_skip(&gbc, 30); @@ -263,11 +464,18 @@ static int decode_frame(AVCodecContext *avctx, avpriv_report_missing_feature(avctx, "DirectBit mask region"); } - ret = decode_rle(avctx, p, &gbc, bppcnt); + if (avctx->pix_fmt == AV_PIX_FMT_RGB555) + ret = decode_rle16(avctx, p, &gbc); + else + ret = decode_rle(avctx, p, &gbc, bppcnt); if (ret < 0) return ret; *got_frame = 1; break; + case LONGCOMMENT: + bytestream2_get_be16(&gbc); + bytestream2_skip(&gbc, bytestream2_get_be16(&gbc)); + break; default: av_log(avctx, AV_LOG_TRACE, "Unknown 0x%04X opcode\n", opcode); break; |