/*
- * IFF ACBM/ANIM/DEEP/ILBM/PBM bitmap decoder
+ * IFF ACBM/ANIM/DEEP/ILBM/PBM/RGB8/RGBN bitmap decoder
* Copyright (c) 2010 Peter Ross <pross@xvid.org>
* Copyright (c) 2010 Sebastian Vater <cdgs.basty@googlemail.com>
* Copyright (c) 2016 Paul B Mahol
/**
* @file
- * IFF ACBM/ANIM/DEEP/ILBM/PBM bitmap decoder
+ * IFF ACBM/ANIM/DEEP/ILBM/PBM/RGB8/RGBN bitmap decoder
*/
#include <stdint.h>
uint32_t *mask_palbuf; ///< masking palette table
unsigned compression; ///< delta compression method used
unsigned is_short; ///< short compression method used
+ unsigned is_interlaced;///< video is interlaced
+ unsigned is_brush; ///< video is in ANBR format
unsigned bpp; ///< bits per plane to decode (differs from bits_per_coded_sample if HAM)
unsigned ham; ///< 0 if non-HAM or number of hold bits (6 for bpp > 6, 4 otherwise)
unsigned flags; ///< 1 for EHB, 0 is no extra half darkening
if (chunk_id == MKTAG('B', 'M', 'H', 'D')) {
bytestream2_skip(gb, data_size + (data_size & 1));
} else if (chunk_id == MKTAG('A', 'N', 'H', 'D')) {
+ unsigned extra;
if (data_size < 40)
return AVERROR_INVALIDDATA;
s->compression = (bytestream2_get_byte(gb) << 8) | (s->compression & 0xFF);
bytestream2_skip(gb, 19);
- s->is_short = !(bytestream2_get_be32(gb) & 1);
+ extra = bytestream2_get_be32(gb);
+ s->is_short = !(extra & 1);
+ s->is_brush = extra == 2;
+ s->is_interlaced = !!(extra & 0x40);
data_size -= 24;
bytestream2_skip(gb, data_size + (data_size & 1));
} else if (chunk_id == MKTAG('D', 'L', 'T', 'A') ||
static void decode_byte_vertical_delta(uint8_t *dst,
const uint8_t *buf, const uint8_t *buf_end,
- int w, int bpp, int dst_size)
+ int w, int xor, int bpp, int dst_size)
{
int ncolumns = ((w + 15) / 16) * 2;
int dstpitch = ncolumns * bpp;
while (opcode) {
bytestream2_seek_p(&pb, ofsdst, SEEK_SET);
- bytestream2_put_byte(&pb, x);
+ if (xor && ofsdst < dst_size) {
+ bytestream2_put_byte(&pb, dst[ofsdst] ^ x);
+ } else {
+ bytestream2_put_byte(&pb, x);
+ }
ofsdst += dstpitch;
opcode--;
}
while (opcode) {
bytestream2_seek_p(&pb, ofsdst, SEEK_SET);
- bytestream2_put_byte(&pb, bytestream2_get_byte(&gb));
+ if (xor && ofsdst < dst_size) {
+ bytestream2_put_byte(&pb, dst[ofsdst] ^ bytestream2_get_byte(&gb));
+ } else {
+ bytestream2_put_byte(&pb, bytestream2_get_byte(&gb));
+ }
ofsdst += dstpitch;
opcode--;
}
}
}
+static void decode_delta_d(uint8_t *dst,
+ const uint8_t *buf, const uint8_t *buf_end,
+ int w, int flag, int bpp, int dst_size)
+{
+ int planepitch = FFALIGN(w, 16) >> 3;
+ int pitch = planepitch * bpp;
+ int planepitch_byte = (w + 7) / 8;
+ unsigned entries, ofssrc;
+ GetByteContext gb, ptrs;
+ PutByteContext pb;
+ int k;
+
+ if (buf_end - buf <= 4 * bpp)
+ return;
+
+ bytestream2_init_writer(&pb, dst, dst_size);
+ bytestream2_init(&ptrs, buf, bpp * 4);
+
+ for (k = 0; k < bpp; k++) {
+ ofssrc = bytestream2_get_be32(&ptrs);
+
+ if (!ofssrc)
+ continue;
+
+ if (ofssrc >= buf_end - buf)
+ continue;
+
+ bytestream2_init(&gb, buf + ofssrc, buf_end - (buf + ofssrc));
+
+ entries = bytestream2_get_be32(&gb);
+ while (entries && bytestream2_get_bytes_left(&gb) >= 8) {
+ int32_t opcode = bytestream2_get_be32(&gb);
+ unsigned offset = bytestream2_get_be32(&gb);
+
+ bytestream2_seek_p(&pb, (offset / planepitch_byte) * pitch + (offset % planepitch_byte) + k * planepitch, SEEK_SET);
+ if (opcode >= 0) {
+ uint32_t x = bytestream2_get_be32(&gb);
+ while (opcode && bytestream2_get_bytes_left_p(&pb) > 0) {
+ bytestream2_put_be32(&pb, x);
+ bytestream2_skip_p(&pb, pitch - 4);
+ opcode--;
+ }
+ } else {
+ opcode = -opcode;
+ while (opcode && bytestream2_get_bytes_left(&gb) > 0) {
+ bytestream2_put_be32(&pb, bytestream2_get_be32(&gb));
+ bytestream2_skip_p(&pb, pitch - 4);
+ opcode--;
+ }
+ }
+ entries--;
+ }
+ }
+}
+
+static void decode_delta_e(uint8_t *dst,
+ const uint8_t *buf, const uint8_t *buf_end,
+ int w, int flag, int bpp, int dst_size)
+{
+ int planepitch = FFALIGN(w, 16) >> 3;
+ int pitch = planepitch * bpp;
+ int planepitch_byte = (w + 7) / 8;
+ unsigned entries, ofssrc;
+ GetByteContext gb, ptrs;
+ PutByteContext pb;
+ int k;
+
+ if (buf_end - buf <= 4 * bpp)
+ return;
+
+ bytestream2_init_writer(&pb, dst, dst_size);
+ bytestream2_init(&ptrs, buf, bpp * 4);
+
+ for (k = 0; k < bpp; k++) {
+ ofssrc = bytestream2_get_be32(&ptrs);
+
+ if (!ofssrc)
+ continue;
+
+ if (ofssrc >= buf_end - buf)
+ continue;
+
+ bytestream2_init(&gb, buf + ofssrc, buf_end - (buf + ofssrc));
+
+ entries = bytestream2_get_be16(&gb);
+ while (entries && bytestream2_get_bytes_left(&gb) >= 6) {
+ int16_t opcode = bytestream2_get_be16(&gb);
+ unsigned offset = bytestream2_get_be32(&gb);
+
+ bytestream2_seek_p(&pb, (offset / planepitch_byte) * pitch + (offset % planepitch_byte) + k * planepitch, SEEK_SET);
+ if (opcode >= 0) {
+ uint16_t x = bytestream2_get_be16(&gb);
+ while (opcode && bytestream2_get_bytes_left_p(&pb) > 0) {
+ bytestream2_put_be16(&pb, x);
+ bytestream2_skip_p(&pb, pitch - 2);
+ opcode--;
+ }
+ } else {
+ opcode = -opcode;
+ while (opcode && bytestream2_get_bytes_left(&gb) > 0) {
+ bytestream2_put_be16(&pb, bytestream2_get_be16(&gb));
+ bytestream2_skip_p(&pb, pitch - 2);
+ opcode--;
+ }
+ }
+ entries--;
+ }
+ }
+}
+
static void decode_delta_l(uint8_t *dst,
const uint8_t *buf, const uint8_t *buf_end,
int w, int flag, int bpp, int dst_size)
static int unsupported(AVCodecContext *avctx)
{
IffContext *s = avctx->priv_data;
- avpriv_request_sample(avctx, "bitmap (compression 0x%0x, bpp %i, ham %i)", s->compression, s->bpp, s->ham);
+ avpriv_request_sample(avctx, "bitmap (compression 0x%0x, bpp %i, ham %i, interlaced %i)", s->compression, s->bpp, s->ham, s->is_interlaced);
return AVERROR_INVALIDDATA;
}
}
} else if (avctx->codec_tag == MKTAG('I', 'L', 'B', 'M') || // interleaved
avctx->codec_tag == MKTAG('A', 'N', 'I', 'M')) {
+ if (avctx->codec_tag == MKTAG('A', 'N', 'I', 'M'))
+ memcpy(s->video[0], buf, FFMIN(buf_end - buf, s->video_size));
if (avctx->pix_fmt == AV_PIX_FMT_PAL8 || avctx->pix_fmt == AV_PIX_FMT_GRAY8) {
- if (avctx->codec_tag == MKTAG('A', 'N', 'I', 'M'))
- memcpy(s->video[0], buf, FFMIN(buf_end - buf, s->video_size));
for (y = 0; y < avctx->height; y++) {
uint8_t *row = &frame->data[0][y * frame->linesize[0]];
memset(row, 0, avctx->width);
break;
case 0x500:
case 0x501:
- decode_byte_vertical_delta(s->video[0], buf, buf_end, avctx->width, s->bpp, s->video_size);
+ decode_byte_vertical_delta(s->video[0], buf, buf_end, avctx->width, s->is_brush, s->bpp, s->video_size);
break;
case 0x700:
case 0x701:
case 0x4a01:
decode_delta_j(s->video[0], buf, buf_end, avctx->width, avctx->height, s->bpp, s->video_size);
break;
+ case 0x6400:
+ case 0x6401:
+ if (s->is_interlaced)
+ return unsupported(avctx);
+ decode_delta_d(s->video[0], buf, buf_end, avctx->width, s->is_interlaced, s->bpp, s->video_size);
+ break;
+ case 0x6500:
+ case 0x6501:
+ if (s->is_interlaced)
+ return unsupported(avctx);
+ decode_delta_e(s->video[0], buf, buf_end, avctx->width, s->is_interlaced, s->bpp, s->video_size);
+ break;
case 0x6c00:
case 0x6c01:
decode_delta_l(s->video[0], buf, buf_end, avctx->width, s->is_short, s->bpp, s->video_size);
return unsupported(avctx);
}
- FFSWAP(uint8_t *, s->video[0], s->video[1]);
- FFSWAP(uint32_t *, s->pal[0], s->pal[1]);
+ if (!s->is_brush) {
+ FFSWAP(uint8_t *, s->video[0], s->video[1]);
+ FFSWAP(uint32_t *, s->pal[0], s->pal[1]);
+ }
}
if (avpkt->flags & AV_PKT_FLAG_KEY) {
#if CONFIG_IFF_ILBM_DECODER
AVCodec ff_iff_ilbm_decoder = {
.name = "iff",
- .long_name = NULL_IF_CONFIG_SMALL("IFF ACBM/ANIM/DEEP/ILBM/PBM"),
+ .long_name = NULL_IF_CONFIG_SMALL("IFF ACBM/ANIM/DEEP/ILBM/PBM/RGB8/RGBN"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_IFF_ILBM,
.priv_data_size = sizeof(IffContext),