X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavcodec%2Fqdrw.c;h=b7493e4da7b8aef9f597b666126b0eb5eb3456b8;hb=1da2a20763ae9ca579d5fd20763065871ddf6311;hp=2dc059e555da95344519b995c3d1d76c941e5556;hpb=ec6402b7c595c3ceed6d1b8c1b75c6aa8336e052;p=ffmpeg diff --git a/libavcodec/qdrw.c b/libavcodec/qdrw.c index 2dc059e555d..b7493e4da7b 100644 --- a/libavcodec/qdrw.c +++ b/libavcodec/qdrw.c @@ -1,6 +1,7 @@ /* * QuickDraw (qdrw) codec * Copyright (c) 2004 Konstantin Shishkov + * Copyright (c) 2015 Vittorio Giovara * * This file is part of Libav. * @@ -22,142 +23,290 @@ /** * @file * Apple QuickDraw codec. + * https://developer.apple.com/legacy/library/documentation/mac/QuickDraw/QuickDraw-461.html */ +#include "libavutil/common.h" #include "libavutil/intreadwrite.h" #include "avcodec.h" +#include "bytestream.h" +#include "internal.h" -typedef struct QdrawContext{ - AVCodecContext *avctx; - AVFrame pic; -} QdrawContext; +enum QuickdrawOpcodes { + PACKBITSRECT = 0x0098, + PACKBITSRGN, + DIRECTBITSRECT, + DIRECTBITSRGN, -static int decode_frame(AVCodecContext *avctx, - void *data, int *data_size, - AVPacket *avpkt) + EOP = 0x00FF, +}; + +static int parse_palette(AVCodecContext *avctx, GetByteContext *gbc, + uint32_t *pal, int colors) { - const uint8_t *buf = avpkt->data; - int buf_size = avpkt->size; - QdrawContext * const a = avctx->priv_data; - AVFrame * const p= (AVFrame*)&a->pic; - uint8_t* outdata; - int colors; int i; - uint32_t *pal; - int r, g, b; - - if(p->data[0]) - avctx->release_buffer(avctx, p); - - p->reference= 0; - if(avctx->get_buffer(avctx, p) < 0){ - av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n"); - return -1; - } - p->pict_type= AV_PICTURE_TYPE_I; - p->key_frame= 1; - - outdata = a->pic.data[0]; - buf += 0x68; /* jump to palette */ - colors = AV_RB32(buf); - buf += 4; - - if(colors < 0 || colors > 256) { - av_log(avctx, AV_LOG_ERROR, "Error color count - %i(0x%X)\n", colors, colors); - return -1; - } - - pal = (uint32_t*)p->data[1]; for (i = 0; i <= colors; i++) { - unsigned int idx; - idx = AV_RB16(buf); /* color index */ - buf += 2; - + uint8_t r, g, b; + unsigned int idx = bytestream2_get_be16(gbc); /* color index */ if (idx > 255) { - av_log(avctx, AV_LOG_ERROR, "Palette index out of range: %u\n", idx); - buf += 6; + av_log(avctx, AV_LOG_WARNING, + "Palette index out of range: %u\n", idx); + bytestream2_skip(gbc, 6); continue; } - r = *buf++; - buf++; - g = *buf++; - buf++; - b = *buf++; - buf++; - pal[idx] = (r << 16) | (g << 8) | b; + r = bytestream2_get_byte(gbc); + bytestream2_skip(gbc, 1); + g = bytestream2_get_byte(gbc); + bytestream2_skip(gbc, 1); + b = bytestream2_get_byte(gbc); + bytestream2_skip(gbc, 1); + pal[idx] = (0xFFU << 24) | (r << 16) | (g << 8) | b; } - p->palette_has_changed = 1; + return 0; +} + +static int decode_rle(AVCodecContext *avctx, AVFrame *p, GetByteContext *gbc, + int step) +{ + int i, j; + int offset = avctx->width * step; + uint8_t *outdata = p->data[0]; - buf += 18; /* skip unneeded data */ for (i = 0; i < avctx->height; i++) { int size, left, code, pix; - const uint8_t *next; - uint8_t *out; - int tsize = 0; + 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 */ - out = outdata; - size = AV_RB16(buf); /* size of packed line */ - buf += 2; - left = size; - next = buf + size; while (left > 0) { - code = *buf++; + code = bytestream2_get_byte(gbc); if (code & 0x80 ) { /* run */ - pix = *buf++; - if ((out + (257 - code)) > (outdata + a->pic.linesize[0])) - break; - memset(out, pix, 257 - code); - out += 257 - code; - tsize += 257 - code; - left -= 2; + pix = bytestream2_get_byte(gbc); + for (j = 0; j < 257 - code; j++) { + out[pos] = pix; + pos += step; + if (pos >= offset) { + pos -= offset; + pos++; + } + } + left -= 2; } else { /* copy */ - if ((out + code) > (outdata + a->pic.linesize[0])) - break; - memcpy(out, buf, code + 1); - out += code + 1; - buf += code + 1; - left -= 2 + code; - tsize += code + 1; + for (j = 0; j < code + 1; j++) { + out[pos] = bytestream2_get_byte(gbc); + pos += step; + if (pos >= offset) { + pos -= offset; + pos++; + } + } + left -= 2 + code; } } - buf = next; - outdata += a->pic.linesize[0]; + outdata += p->linesize[0]; } + return 0; +} - *data_size = sizeof(AVFrame); - *(AVFrame*)data = a->pic; +static int decode_frame(AVCodecContext *avctx, + void *data, int *got_frame, + AVPacket *avpkt) +{ + AVFrame * const p = data; + GetByteContext gbc; + int colors; + int w, h, ret; - return buf_size; -} + bytestream2_init(&gbc, avpkt->data, avpkt->size); -static av_cold int decode_init(AVCodecContext *avctx){ -// QdrawContext * const a = avctx->priv_data; + /* PICT images start with a 512 bytes empty header */ + if (bytestream2_peek_be32(&gbc) == 0) + bytestream2_skip(&gbc, 512); - avctx->pix_fmt= PIX_FMT_PAL8; + /* smallest PICT header */ + if (bytestream2_get_bytes_left(&gbc) < 40) { + av_log(avctx, AV_LOG_ERROR, "Frame is too small %d\n", + bytestream2_get_bytes_left(&gbc)); + return AVERROR_INVALIDDATA; + } - return 0; -} + bytestream2_skip(&gbc, 6); + h = bytestream2_get_be16(&gbc); + w = bytestream2_get_be16(&gbc); -static av_cold int decode_end(AVCodecContext *avctx){ - QdrawContext * const a = avctx->priv_data; - AVFrame *pic = &a->pic; + ret = ff_set_dimensions(avctx, w, h); + if (ret < 0) + return ret; - if (pic->data[0]) - avctx->release_buffer(avctx, pic); + /* version 1 is identified by 0x1101 + * it uses byte-aligned opcodes rather than word-aligned */ + if (bytestream2_get_be32(&gbc) != 0x001102FF) { + avpriv_request_sample(avctx, "QuickDraw version 1"); + return AVERROR_PATCHWELCOME; + } - return 0; + bytestream2_skip(&gbc, 26); + + while (bytestream2_get_bytes_left(&gbc) >= 4) { + int bppcnt, bpp; + int rowbytes, pack_type; + int opcode = bytestream2_get_be16(&gbc); + + switch(opcode) { + case PACKBITSRECT: + case PACKBITSRGN: + av_log(avctx, AV_LOG_DEBUG, "Parsing Packbit opcode\n"); + + bytestream2_skip(&gbc, 30); + bppcnt = bytestream2_get_be16(&gbc); /* cmpCount */ + bpp = bytestream2_get_be16(&gbc); /* cmpSize */ + + 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 { + av_log(avctx, AV_LOG_ERROR, + "Invalid pixel format (bppcnt %d bpp %d) in Packbit\n", + bppcnt, bpp); + return AVERROR_INVALIDDATA; + } + + /* jump to palette */ + bytestream2_skip(&gbc, 18); + colors = bytestream2_get_be16(&gbc); + + if (colors < 0 || colors > 256) { + av_log(avctx, AV_LOG_ERROR, + "Error color count - %i(0x%X)\n", colors, colors); + return AVERROR_INVALIDDATA; + } + if (bytestream2_get_bytes_left(&gbc) < (colors + 1) * 8) { + av_log(avctx, AV_LOG_ERROR, "Palette is too small %d\n", + 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"); + return ret; + } + + parse_palette(avctx, &gbc, (uint32_t *)p->data[1], colors); + p->palette_has_changed = 1; + + /* jump to image data */ + bytestream2_skip(&gbc, 18); + + if (opcode == PACKBITSRGN) { + bytestream2_skip(&gbc, 2 + 8); /* size + rect */ + avpriv_report_missing_feature(avctx, "Packbit mask region"); + } + + ret = decode_rle(avctx, p, &gbc, bppcnt); + if (ret < 0) + return ret; + *got_frame = 1; + break; + case DIRECTBITSRECT: + case DIRECTBITSRGN: + av_log(avctx, AV_LOG_DEBUG, "Parsing Directbit opcode\n"); + + bytestream2_skip(&gbc, 4); + rowbytes = bytestream2_get_be16(&gbc) & 0x3FFF; + if (rowbytes <= 250) { + avpriv_report_missing_feature(avctx, "Short rowbytes"); + return AVERROR_PATCHWELCOME; + } + + bytestream2_skip(&gbc, 10); + pack_type = bytestream2_get_be16(&gbc); + + bytestream2_skip(&gbc, 16); + bppcnt = bytestream2_get_be16(&gbc); /* cmpCount */ + bpp = bytestream2_get_be16(&gbc); /* cmpSize */ + + 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 == 4 && bpp == 8) { + avctx->pix_fmt = AV_PIX_FMT_ARGB; + } else { + av_log(avctx, AV_LOG_ERROR, + "Invalid pixel format (bppcnt %d bpp %d) in Directbit\n", + bppcnt, bpp); + return AVERROR_INVALIDDATA; + } + + /* set packing when default is selected */ + if (pack_type == 0) + pack_type = bppcnt; + + if (pack_type != 3 && pack_type != 4) { + 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"); + return ret; + } + + /* jump to data */ + bytestream2_skip(&gbc, 30); + + if (opcode == DIRECTBITSRGN) { + bytestream2_skip(&gbc, 2 + 8); /* size + rect */ + avpriv_report_missing_feature(avctx, "DirectBit mask region"); + } + + ret = decode_rle(avctx, p, &gbc, bppcnt); + if (ret < 0) + return ret; + *got_frame = 1; + break; + default: + av_log(avctx, AV_LOG_TRACE, "Unknown 0x%04X opcode\n", opcode); + break; + } + /* exit the loop when a known pixel block has been found */ + if (*got_frame) { + int eop, trail; + + /* re-align to a word */ + bytestream2_skip(&gbc, bytestream2_get_bytes_left(&gbc) % 2); + + eop = bytestream2_get_be16(&gbc); + trail = bytestream2_get_bytes_left(&gbc); + if (eop != EOP) + av_log(avctx, AV_LOG_WARNING, + "Missing end of picture opcode (found 0x%04X)\n", eop); + if (trail) + av_log(avctx, AV_LOG_WARNING, "Got %d trailing bytes\n", trail); + break; + } + } + + if (*got_frame) { + p->pict_type = AV_PICTURE_TYPE_I; + p->key_frame = 1; + + return avpkt->size; + } else { + av_log(avctx, AV_LOG_ERROR, "Frame contained no usable data\n"); + + return AVERROR_INVALIDDATA; + } } AVCodec ff_qdraw_decoder = { .name = "qdraw", + .long_name = NULL_IF_CONFIG_SMALL("Apple QuickDraw"), .type = AVMEDIA_TYPE_VIDEO, - .id = CODEC_ID_QDRAW, - .priv_data_size = sizeof(QdrawContext), - .init = decode_init, - .close = decode_end, + .id = AV_CODEC_ID_QDRAW, .decode = decode_frame, - .capabilities = CODEC_CAP_DR1, - .long_name = NULL_IF_CONFIG_SMALL("Apple QuickDraw"), + .capabilities = AV_CODEC_CAP_DR1, };