]> git.sesse.net Git - ffmpeg/blobdiff - libavcodec/iff.c
avcodec/sheervideo: add interlaced YCbCr(A) 4:2:2:4 8-bit support
[ffmpeg] / libavcodec / iff.c
index 9d7dce5596eb2925abe9b0c307ddb2f5fe86ea54..96ecd8a91abfb95fd502d0484646a2fc82cc1039 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -23,7 +23,7 @@
 
 /**
  * @file
- * IFF ACBM/ANIM/DEEP/ILBM/PBM bitmap decoder
+ * IFF ACBM/ANIM/DEEP/ILBM/PBM/RGB8/RGBN bitmap decoder
  */
 
 #include <stdint.h>
@@ -52,6 +52,8 @@ typedef struct IffContext {
     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
@@ -221,12 +223,16 @@ static int extract_header(AVCodecContext *const avctx,
             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') ||
@@ -759,7 +765,7 @@ static void decode_short_horizontal_delta(uint8_t *dst,
 
 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;
@@ -794,7 +800,11 @@ static void decode_byte_vertical_delta(uint8_t *dst,
 
                     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--;
                     }
@@ -805,7 +815,11 @@ static void decode_byte_vertical_delta(uint8_t *dst,
 
                     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--;
                     }
@@ -1213,6 +1227,116 @@ static void decode_long_vertical_delta2(uint8_t *dst,
     }
 }
 
+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)
@@ -1279,7 +1403,7 @@ static void decode_delta_l(uint8_t *dst,
 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;
 }
 
@@ -1366,9 +1490,9 @@ static int decode_frame(AVCodecContext *avctx,
             }
         } 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);
@@ -1513,7 +1637,7 @@ static int decode_frame(AVCodecContext *avctx,
         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:
@@ -1533,6 +1657,18 @@ static int decode_frame(AVCodecContext *avctx,
     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);
@@ -1593,8 +1729,10 @@ static int decode_frame(AVCodecContext *avctx,
             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) {
@@ -1613,7 +1751,7 @@ static int decode_frame(AVCodecContext *avctx,
 #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),