aboutsummaryrefslogblamecommitdiffstats
path: root/libavcodec/iff.c
blob: 33cf2e3a947858a420474157880ffb774ab0fc71 (plain) (tree)
1
2
3
4
5
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
  
                                                       
                                                 
                                                                 
                                  

















                                                                               
        
                                                       
   
                   
                               
 
                       
                     
                    
 






                               
                           
                   
                  
                       
                                                                                   
                                                                     
                                                              
                                                              
                                                    
                                                        



                                                                                                     
                                                                              
                                                  

                        
                  
             
 
                                                         














                                                         
























                                                 





























                                                       



                                                                           

                                                                   
                                                                  
 
                                     
                 
                                                                                
                                                                         
                                           
                                                                                 


                                              
                                                                                   
                                           
                
                                   
                                                         

                                                                                    
                                     
         

                                                           
                                   
                                                                                      
     



                                                                          
                                                            

             




                                                                               
                                                                   
                                                      
                                                      
                                     
                          
                        




                                                                     
 











                                                                  
                               



                                                                                           
                                                 
                                         
                                                    







                                                                  
                                       












                                                                       
         
                        
                                             
                                                
                                                                                    
                                           


                                       
                         




                                                    

                                                   
                                          
                                         
                                                  
                                          
                                                                                            
                                           



                                                                                          
                                                                                                            





                                                                                         













                                                                                             
                                                                
                          
                                                                                        
 
                                                                                      

                                       
                                          
                                                                                                                                         







                                                                                            
                                                                               



                                                        
                                                                                                  



                                                 




                                                                                                 
             


                                                                                     




             

                                                    
                           
                             
                           
                      

             
                                                     
                                     
            
                                            




                                                                             
                                                              
                                                                                                           
                                                    
                                                            
                                              
                                                                   
                                               
                                                                   
                                                     
                                                   

                                                            
                                                                              

                                            

                                   
 
                                                                           
                   
                                                                                               
                                                                                          
                               
 
                                          



                                                                                  
                                                    
                                   
     

                                                
 
             

   


                                             
                                         
                                                                                   
 
                                            
                     
                                                                         
               
        
                                                 
                 
                         


                                              

                                
                                         
   
                                                                                     
 





                                             
                                       


                              
                    
                         
 



















                                                                            
                                                      









                               






                                                                   
   
                                             




                                                                          
   
                                                     
                                             
 
               
                                                                      
                        
                                                      
                         
                                                                                     
                                                         
                                  
                                                     
                                                              



                        


                                                                                 
                                
 




































































                                                                                
                                  







                                                
                                                                 




















                                                                                              
                                    















                                                                                              
                                    

     































                                                                                                                  
                                          













                                                                
















































                                                                                                                                        



















































                                                                                               
                                                                                  
                                                                             
































                                                                      



                                                                       








                                                                  



                                                                                               













                                                                      
                 
























                                                   
                                                                                      
                                                                                                
                           
                 
 



                                                                                               


                                                                  


                                               



                                            
                                             




















                                                                                               
                                                                                 
 
                                                                                
                                                                                            
                                   
                         
 

                                                                      


                                                    



                                                
                                      













































































































































































































































































































                                                                                    




























                                                                      
                                                                 




















































                                                                                                                                
                                                                 






















                                                                                                                                



































                                                                             
                                                                                                
                                                         



                                                                                                                  
                                                         






                                                          
                                                             









                                                          

                                             
                                                                                                                                                


                                              
                                                   
 
                                              

                                         
                                            
                      
                                
                                   
 
                                                   
                                                 
                                                   
                   
                     
 
                                     
                                               
                                                        
                                            
                                                                             
                       
                                                               
                                                                 
                       
     
                
 
                                                                                    
                                              
                                                       
     
                             
             
                                                            
                                                                                          
                                                                              
                                                          
                                                                          
                                                                               

                                                                                          
                 
                                                           
                                                                              
                                                     
                                                                           





                                                                                                     
                                                                                                 

                                          
                                                                   
                                                                              
                                                                  
                                                                       

                                                                  
                                                      
                                                                                       
                 
             
                                                                                  
                                                                              
                                                                                          
                                                     
                                                                           



                                                                                          
                 
                                                           
                                                                           



                                                                                                 
                                                                                                 
                 
                                        
                                                     
                                                                           
                                                                               
                                                                                 
                                            
                 
             
                                                                              
                                                                                          
                                                                      
                                                                           



                                                                               
                                                                           
                                                                                
                                                                                                 

                                          
                                      
         
              

                                                                           
                                                                                          
                                             
                                                     
                                                                           
                                                              



                                                                             
                                                                            
                 
                                                                                                
                                                     
                                                                           
                                                                            
                                                                             
                                                                                     
                                                                                                    
                 
                                                           
                                             
                                                     
                                                                           
                                                              



                                                                             
                                                                                   
                                                                                                 
                 
                                                     
                                                                           
                                                              
                                                                             
                                                                                         
                     
                 
             
                                                                              
                                                                                          
                                                     
                                                                           
                                                                    
                                                     
                                                                           
                                                                                                 

                                          
                                                                               
                                                  
                                                                                                                  
                                          


























                                                                                                     
         
              











                                                                                                 
             
                                                                                                
                                                                                             
                                                                                                      
                                                                                             

                                      
             
                                                            
                                                  
                                                                                                                            



                                          


                                                                                                      
               
                                                                                                                

















                                                                                                         










                                                                                                         


                                                                                                    
                                  
     
                                                                                    

                                                        









                                                                                      
                                                    




                                                                           
                                                 


























                                                                                             
                                                        
         







                                             
 
                   
 

                    
                           
                               
                            
                                                                                    
                                         
                                           

                                         
                                   
                                                
                                       
  
      
/*
 * 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
 *
 * This file is part of FFmpeg.
 *
 * 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.
 *
 * 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 FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/**
 * @file
 * IFF ACBM/ANIM/DEEP/ILBM/PBM/RGB8/RGBN bitmap decoder
 */

#include <stdint.h>

#include "libavutil/imgutils.h"

#include "bytestream.h"
#include "avcodec.h"
#include "internal.h"
#include "mathops.h"

// TODO: masking bits
typedef enum {
    MASK_NONE,
    MASK_HAS_MASK,
    MASK_HAS_TRANSPARENT_COLOR,
    MASK_LASSO
} mask_type;

typedef struct IffContext {
    AVFrame *frame;
    int planesize;
    uint8_t * planebuf;
    uint8_t * ham_buf;      ///< temporary buffer for planar to chunky conversation
    uint32_t *ham_palbuf;   ///< HAM decode table
    uint32_t *mask_buf;     ///< temporary buffer for palette indices
    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
    unsigned  transparency; ///< TODO: transparency color index in palette
    unsigned  masking;      ///< TODO: masking method used
    int init; // 1 if buffer and palette data already initialized, 0 otherwise
    int16_t   tvdc[16];     ///< TVDC lookup table
    GetByteContext gb;
    uint8_t *video[2];
    unsigned video_size;
    uint32_t *pal;
} IffContext;

#define LUT8_PART(plane, v)                             \
    AV_LE2NE64C(UINT64_C(0x0000000)<<32 | v) << plane,  \
    AV_LE2NE64C(UINT64_C(0x1000000)<<32 | v) << plane,  \
    AV_LE2NE64C(UINT64_C(0x0010000)<<32 | v) << plane,  \
    AV_LE2NE64C(UINT64_C(0x1010000)<<32 | v) << plane,  \
    AV_LE2NE64C(UINT64_C(0x0000100)<<32 | v) << plane,  \
    AV_LE2NE64C(UINT64_C(0x1000100)<<32 | v) << plane,  \
    AV_LE2NE64C(UINT64_C(0x0010100)<<32 | v) << plane,  \
    AV_LE2NE64C(UINT64_C(0x1010100)<<32 | v) << plane,  \
    AV_LE2NE64C(UINT64_C(0x0000001)<<32 | v) << plane,  \
    AV_LE2NE64C(UINT64_C(0x1000001)<<32 | v) << plane,  \
    AV_LE2NE64C(UINT64_C(0x0010001)<<32 | v) << plane,  \
    AV_LE2NE64C(UINT64_C(0x1010001)<<32 | v) << plane,  \
    AV_LE2NE64C(UINT64_C(0x0000101)<<32 | v) << plane,  \
    AV_LE2NE64C(UINT64_C(0x1000101)<<32 | v) << plane,  \
    AV_LE2NE64C(UINT64_C(0x0010101)<<32 | v) << plane,  \
    AV_LE2NE64C(UINT64_C(0x1010101)<<32 | v) << plane

#define LUT8(plane) {                           \
    LUT8_PART(plane, 0x0000000),                \
    LUT8_PART(plane, 0x1000000),                \
    LUT8_PART(plane, 0x0010000),                \
    LUT8_PART(plane, 0x1010000),                \
    LUT8_PART(plane, 0x0000100),                \
    LUT8_PART(plane, 0x1000100),                \
    LUT8_PART(plane, 0x0010100),                \
    LUT8_PART(plane, 0x1010100),                \
    LUT8_PART(plane, 0x0000001),                \
    LUT8_PART(plane, 0x1000001),                \
    LUT8_PART(plane, 0x0010001),                \
    LUT8_PART(plane, 0x1010001),                \
    LUT8_PART(plane, 0x0000101),                \
    LUT8_PART(plane, 0x1000101),                \
    LUT8_PART(plane, 0x0010101),                \
    LUT8_PART(plane, 0x1010101),                \
}

// 8 planes * 8-bit mask
static const uint64_t plane8_lut[8][256] = {
    LUT8(0), LUT8(1), LUT8(2), LUT8(3),
    LUT8(4), LUT8(5), LUT8(6), LUT8(7),
};

#define LUT32(plane) {                                \
             0,          0,          0,          0,   \
             0,          0,          0, 1 << plane,   \
             0,          0, 1 << plane,          0,   \
             0,          0, 1 << plane, 1 << plane,   \
             0, 1 << plane,          0,          0,   \
             0, 1 << plane,          0, 1 << plane,   \
             0, 1 << plane, 1 << plane,          0,   \
             0, 1 << plane, 1 << plane, 1 << plane,   \
    1 << plane,          0,          0,          0,   \
    1 << plane,          0,          0, 1 << plane,   \
    1 << plane,          0, 1 << plane,          0,   \
    1 << plane,          0, 1 << plane, 1 << plane,   \
    1 << plane, 1 << plane,          0,          0,   \
    1 << plane, 1 << plane,          0, 1 << plane,   \
    1 << plane, 1 << plane, 1 << plane,          0,   \
    1 << plane, 1 << plane, 1 << plane, 1 << plane,   \
}

// 32 planes * 4-bit mask * 4 lookup tables each
static const uint32_t plane32_lut[32][16*4] = {
    LUT32( 0), LUT32( 1), LUT32( 2), LUT32( 3),
    LUT32( 4), LUT32( 5), LUT32( 6), LUT32( 7),
    LUT32( 8), LUT32( 9), LUT32(10), LUT32(11),
    LUT32(12), LUT32(13), LUT32(14), LUT32(15),
    LUT32(16), LUT32(17), LUT32(18), LUT32(19),
    LUT32(20), LUT32(21), LUT32(22), LUT32(23),
    LUT32(24), LUT32(25), LUT32(26), LUT32(27),
    LUT32(28), LUT32(29), LUT32(30), LUT32(31),
};

// Gray to RGB, required for palette table of grayscale images with bpp < 8
static av_always_inline uint32_t gray2rgb(const uint32_t x) {
    return x << 16 | x << 8 | x;
}

/**
 * Convert CMAP buffer (stored in extradata) to lavc palette format
 */
static int cmap_read_palette(AVCodecContext *avctx, uint32_t *pal)
{
    IffContext *s = avctx->priv_data;
    int count, i;
    const uint8_t *const palette = avctx->extradata + AV_RB16(avctx->extradata);
    int palette_size = avctx->extradata_size - AV_RB16(avctx->extradata);

    if (avctx->bits_per_coded_sample > 8) {
        av_log(avctx, AV_LOG_ERROR, "bits_per_coded_sample > 8 not supported\n");
        return AVERROR_INVALIDDATA;
    }

    count = 1 << avctx->bits_per_coded_sample;
    // If extradata is smaller than actually needed, fill the remaining with black.
    count = FFMIN(palette_size / 3, count);
    if (count) {
        for (i = 0; i < count; i++)
            pal[i] = 0xFF000000 | AV_RB24(palette + i*3);
        if (s->flags && count >= 32) { // EHB
            for (i = 0; i < 32; i++)
                pal[i + 32] = 0xFF000000 | (AV_RB24(palette + i*3) & 0xFEFEFE) >> 1;
            count = FFMAX(count, 64);
        }
    } else { // Create gray-scale color palette for bps < 8
        count = 1 << avctx->bits_per_coded_sample;

        for (i = 0; i < count; i++)
            pal[i] = 0xFF000000 | gray2rgb((i * 255) >> avctx->bits_per_coded_sample);
    }
    if (s->masking == MASK_HAS_MASK) {
        memcpy(pal + (1 << avctx->bits_per_coded_sample), pal, count * 4);
        for (i = 0; i < count; i++)
            pal[i] &= 0xFFFFFF;
    } else if (s->masking == MASK_HAS_TRANSPARENT_COLOR &&
        s->transparency < 1 << avctx->bits_per_coded_sample)
        pal[s->transparency] &= 0xFFFFFF;
    return 0;
}

/**
 * Extracts the IFF extra context and updates internal
 * decoder structures.
 *
 * @param avctx the AVCodecContext where to extract extra context to
 * @param avpkt the AVPacket to extract extra context from or NULL to use avctx
 * @return >= 0 in case of success, a negative error code otherwise
 */
static int extract_header(AVCodecContext *const avctx,
                          const AVPacket *const avpkt)
{
    IffContext *s = avctx->priv_data;
    const uint8_t *buf;
    unsigned buf_size = 0;
    int i, palette_size;

    if (avctx->extradata_size < 2) {
        av_log(avctx, AV_LOG_ERROR, "not enough extradata\n");
        return AVERROR_INVALIDDATA;
    }
    palette_size = avctx->extradata_size - AV_RB16(avctx->extradata);

    if (avpkt && avctx->codec_tag == MKTAG('A', 'N', 'I', 'M')) {
        uint32_t chunk_id;
        uint64_t data_size;
        GetByteContext *gb = &s->gb;

        bytestream2_skip(gb, 4);
        while (bytestream2_get_bytes_left(gb) >= 1) {
            chunk_id  = bytestream2_get_le32(gb);
            data_size = bytestream2_get_be32(gb);

            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);
                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') ||
                       chunk_id == MKTAG('B', 'O', 'D', 'Y')) {
                if (chunk_id == MKTAG('B','O','D','Y'))
                    s->compression &= 0xFF;
                break;
            } else if (chunk_id == MKTAG('C', 'M', 'A', 'P')) {
                int count = data_size / 3;
                uint32_t *pal = s->pal;

                if (count > 256)
                    return AVERROR_INVALIDDATA;
                if (s->ham) {
                    for (i = 0; i < count; i++)
                        pal[i] = 0xFF000000 | bytestream2_get_le24(gb);
                } else {
                    for (i = 0; i < count; i++)
                        pal[i] = 0xFF000000 | bytestream2_get_be24(gb);
                }
                bytestream2_skip(gb, data_size & 1);
            } else {
                bytestream2_skip(gb, data_size + (data_size&1));
            }
        }
    } else if (!avpkt) {
        buf = avctx->extradata;
        buf_size = bytestream_get_be16(&buf);
        if (buf_size <= 1 || palette_size < 0) {
            av_log(avctx, AV_LOG_ERROR,
                   "Invalid palette size received: %u -> palette data offset: %d\n",
                   buf_size, palette_size);
            return AVERROR_INVALIDDATA;
        }
    }

    if (buf_size >= 41) {
        s->compression  = bytestream_get_byte(&buf);
        s->bpp          = bytestream_get_byte(&buf);
        s->ham          = bytestream_get_byte(&buf);
        s->flags        = bytestream_get_byte(&buf);
        s->transparency = bytestream_get_be16(&buf);
        s->masking      = bytestream_get_byte(&buf);
        for (i = 0; i < 16; i++)
            s->tvdc[i] = bytestream_get_be16(&buf);

        if (s->masking == MASK_HAS_MASK) {
            if (s->bpp >= 8 && !s->ham) {
                avctx->pix_fmt = AV_PIX_FMT_RGB32;
                av_freep(&s->mask_buf);
                av_freep(&s->mask_palbuf);
                s->mask_buf = av_malloc((s->planesize * 32) + AV_INPUT_BUFFER_PADDING_SIZE);
                if (!s->mask_buf)
                    return AVERROR(ENOMEM);
                if (s->bpp > 16) {
                    av_log(avctx, AV_LOG_ERROR, "bpp %d too large for palette\n", s->bpp);
                    av_freep(&s->mask_buf);
                    return AVERROR(ENOMEM);
                }
                s->mask_palbuf = av_malloc((2 << s->bpp) * sizeof(uint32_t) + AV_INPUT_BUFFER_PADDING_SIZE);
                if (!s->mask_palbuf) {
                    av_freep(&s->mask_buf);
                    return AVERROR(ENOMEM);
                }
            }
            s->bpp++;
        } else if (s->masking != MASK_NONE && s->masking != MASK_HAS_TRANSPARENT_COLOR) {
            av_log(avctx, AV_LOG_ERROR, "Masking not supported\n");
            return AVERROR_PATCHWELCOME;
        }
        if (!s->bpp || s->bpp > 32) {
            av_log(avctx, AV_LOG_ERROR, "Invalid number of bitplanes: %u\n", s->bpp);
            return AVERROR_INVALIDDATA;
        } else if (s->ham >= 8) {
            av_log(avctx, AV_LOG_ERROR, "Invalid number of hold bits for HAM: %u\n", s->ham);
            return AVERROR_INVALIDDATA;
        }

        av_freep(&s->ham_buf);
        av_freep(&s->ham_palbuf);

        if (s->ham) {
            int i, count = FFMIN(palette_size / 3, 1 << s->ham);
            int ham_count;
            const uint8_t *const palette = avctx->extradata + AV_RB16(avctx->extradata);

            s->ham_buf = av_malloc((s->planesize * 8) + AV_INPUT_BUFFER_PADDING_SIZE);
            if (!s->ham_buf)
                return AVERROR(ENOMEM);

            ham_count = 8 * (1 << s->ham);
            s->ham_palbuf = av_malloc((ham_count << !!(s->masking == MASK_HAS_MASK)) * sizeof (uint32_t) + AV_INPUT_BUFFER_PADDING_SIZE);
            if (!s->ham_palbuf) {
                av_freep(&s->ham_buf);
                return AVERROR(ENOMEM);
            }

            if (count) { // HAM with color palette attached
                // prefill with black and palette and set HAM take direct value mask to zero
                memset(s->ham_palbuf, 0, (1 << s->ham) * 2 * sizeof (uint32_t));
                for (i=0; i < count; i++) {
                    s->ham_palbuf[i*2+1] = 0xFF000000 | AV_RL24(palette + i*3);
                }
                count = 1 << s->ham;
            } else { // HAM with grayscale color palette
                count = 1 << s->ham;
                for (i=0; i < count; i++) {
                    s->ham_palbuf[i*2]   = 0xFF000000; // take direct color value from palette
                    s->ham_palbuf[i*2+1] = 0xFF000000 | av_le2ne32(gray2rgb((i * 255) >> s->ham));
                }
            }
            for (i=0; i < count; i++) {
                uint32_t tmp = i << (8 - s->ham);
                tmp |= tmp >> s->ham;
                s->ham_palbuf[(i+count)*2]     = 0xFF00FFFF; // just modify blue color component
                s->ham_palbuf[(i+count*2)*2]   = 0xFFFFFF00; // just modify red color component
                s->ham_palbuf[(i+count*3)*2]   = 0xFFFF00FF; // just modify green color component
                s->ham_palbuf[(i+count)*2+1]   = 0xFF000000 | tmp << 16;
                s->ham_palbuf[(i+count*2)*2+1] = 0xFF000000 | tmp;
                s->ham_palbuf[(i+count*3)*2+1] = 0xFF000000 | tmp << 8;
            }
            if (s->masking == MASK_HAS_MASK) {
                for (i = 0; i < ham_count; i++)
                    s->ham_palbuf[(1 << s->bpp) + i] = s->ham_palbuf[i] | 0xFF000000;
            }
        }
    }

    return 0;
}

static av_cold int decode_end(AVCodecContext *avctx)
{
    IffContext *s = avctx->priv_data;
    av_freep(&s->planebuf);
    av_freep(&s->ham_buf);
    av_freep(&s->ham_palbuf);
    av_freep(&s->video[0]);
    av_freep(&s->video[1]);
    av_freep(&s->pal);
    return 0;
}

static av_cold int decode_init(AVCodecContext *avctx)
{
    IffContext *s = avctx->priv_data;
    int err;

    if (avctx->bits_per_coded_sample <= 8) {
        int palette_size;

        if (avctx->extradata_size >= 2)
            palette_size = avctx->extradata_size - AV_RB16(avctx->extradata);
        else
            palette_size = 0;
        avctx->pix_fmt = (avctx->bits_per_coded_sample < 8) ||
                         (avctx->extradata_size >= 2 && palette_size) ? AV_PIX_FMT_PAL8 : AV_PIX_FMT_GRAY8;
    } else if (avctx->bits_per_coded_sample <= 32) {
        if (avctx->codec_tag == MKTAG('R', 'G', 'B', '8')) {
            avctx->pix_fmt = AV_PIX_FMT_RGB32;
        } else if (avctx->codec_tag == MKTAG('R', 'G', 'B', 'N')) {
            avctx->pix_fmt = AV_PIX_FMT_RGB444;
        } else if (avctx->codec_tag != MKTAG('D', 'E', 'E', 'P')) {
            if (avctx->bits_per_coded_sample == 24) {
                avctx->pix_fmt = AV_PIX_FMT_0BGR32;
            } else if (avctx->bits_per_coded_sample == 32) {
                avctx->pix_fmt = AV_PIX_FMT_BGR32;
            } else {
                avpriv_request_sample(avctx, "unknown bits_per_coded_sample");
                return AVERROR_PATCHWELCOME;
            }
        }
    } else {
        return AVERROR_INVALIDDATA;
    }

    if ((err = av_image_check_size(avctx->width, avctx->height, 0, avctx)))
        return err;
    s->planesize = FFALIGN(avctx->width, 16) >> 3; // Align plane size in bits to word-boundary
    s->planebuf  = av_malloc(s->planesize * avctx->height + AV_INPUT_BUFFER_PADDING_SIZE);
    if (!s->planebuf)
        return AVERROR(ENOMEM);

    s->bpp = avctx->bits_per_coded_sample;

    if (avctx->codec_tag == MKTAG('A', 'N', 'I', 'M')) {
        s->video_size = FFALIGN(avctx->width, 2) * avctx->height * s->bpp;
        s->video[0] = av_calloc(FFALIGN(avctx->width, 2) * avctx->height, s->bpp);
        s->video[1] = av_calloc(FFALIGN(avctx->width, 2) * avctx->height, s->bpp);
        s->pal = av_calloc(256, sizeof(*s->pal));
        if (!s->video[0] || !s->video[1] || !s->pal)
            return AVERROR(ENOMEM);
    }

    if ((err = extract_header(avctx, NULL)) < 0)
        return err;

    return 0;
}

/**
 * Decode interleaved plane buffer up to 8bpp
 * @param dst Destination buffer
 * @param buf Source buffer
 * @param buf_size
 * @param plane plane number to decode as
 */
static void decodeplane8(uint8_t *dst, const uint8_t *buf, int buf_size, int plane)
{
    const uint64_t *lut = plane8_lut[plane];
    if (plane >= 8) {
        av_log(NULL, AV_LOG_WARNING, "Ignoring extra planes beyond 8\n");
        return;
    }
    do {
        uint64_t v = AV_RN64A(dst) | lut[*buf++];
        AV_WN64A(dst, v);
        dst += 8;
    } while (--buf_size);
}

/**
 * Decode interleaved plane buffer up to 24bpp
 * @param dst Destination buffer
 * @param buf Source buffer
 * @param buf_size
 * @param plane plane number to decode as
 */
static void decodeplane32(uint32_t *dst, const uint8_t *buf, int buf_size, int plane)
{
    const uint32_t *lut = plane32_lut[plane];
    do {
        unsigned mask = (*buf >> 2) & ~3;
        dst[0] |= lut[mask++];
        dst[1] |= lut[mask++];
        dst[2] |= lut[mask++];
        dst[3] |= lut[mask];
        mask    = (*buf++ << 2) & 0x3F;
        dst[4] |= lut[mask++];
        dst[5] |= lut[mask++];
        dst[6] |= lut[mask++];
        dst[7] |= lut[mask];
        dst    += 8;
    } while (--buf_size);
}

#define DECODE_HAM_PLANE32(x)       \
    first       = buf[x] << 1;      \
    second      = buf[(x)+1] << 1;  \
    delta      &= pal[first++];     \
    delta      |= pal[first];       \
    dst[x]      = delta;            \
    delta      &= pal[second++];    \
    delta      |= pal[second];      \
    dst[(x)+1]  = delta

/**
 * Converts one line of HAM6/8-encoded chunky buffer to 24bpp.
 *
 * @param dst the destination 24bpp buffer
 * @param buf the source 8bpp chunky buffer
 * @param pal the HAM decode table
 * @param buf_size the plane size in bytes
 */
static void decode_ham_plane32(uint32_t *dst, const uint8_t  *buf,
                               const uint32_t *const pal, unsigned buf_size)
{
    uint32_t delta = pal[1]; /* first palette entry */
    do {
        uint32_t first, second;
        DECODE_HAM_PLANE32(0);
        DECODE_HAM_PLANE32(2);
        DECODE_HAM_PLANE32(4);
        DECODE_HAM_PLANE32(6);
        buf += 8;
        dst += 8;
    } while (--buf_size);
}

static void lookup_pal_indicies(uint32_t *dst, const uint32_t *buf,
                         const uint32_t *const pal, unsigned width)
{
    do {
        *dst++ = pal[*buf++];
    } while (--width);
}

/**
 * Decode one complete byterun1 encoded line.
 *
 * @param dst the destination buffer where to store decompressed bitstream
 * @param dst_size the destination plane size in bytes
 * @param buf the source byterun1 compressed bitstream
 * @param buf_end the EOF of source byterun1 compressed bitstream
 * @return number of consumed bytes in byterun1 compressed bitstream
 */
static int decode_byterun(uint8_t *dst, int dst_size,
                          GetByteContext *gb)
{
    unsigned x;
    for (x = 0; x < dst_size && bytestream2_get_bytes_left(gb) > 0;) {
        unsigned length;
        const int8_t value = bytestream2_get_byte(gb);
        if (value >= 0) {
            length = FFMIN3(value + 1, dst_size - x, bytestream2_get_bytes_left(gb));
            bytestream2_get_buffer(gb, dst + x, length);
            if (length < value + 1)
                bytestream2_skip(gb, value + 1 - length);
        } else if (value > -128) {
            length = FFMIN(-value + 1, dst_size - x);
            memset(dst + x, bytestream2_get_byte(gb), length);
        } else { // noop
            continue;
        }
        x += length;
    }
    if (x < dst_size) {
        av_log(NULL, AV_LOG_WARNING, "decode_byterun ended before plane size\n");
        memset(dst+x, 0, dst_size - x);
    }
    return bytestream2_tell(gb);
}

static int decode_byterun2(uint8_t *dst, int height, int line_size,
                           GetByteContext *gb)
{
    GetByteContext cmds;
    unsigned count;
    int i, y_pos = 0, x_pos = 0;

    if (bytestream2_get_be32(gb) != MKBETAG('V', 'D', 'A', 'T'))
        return 0;

    bytestream2_skip(gb, 4);
    count = bytestream2_get_be16(gb) - 2;
    if (bytestream2_get_bytes_left(gb) < count)
        return 0;

    bytestream2_init(&cmds, gb->buffer, count);
    bytestream2_skip(gb, count);

    for (i = 0; i < count && x_pos < line_size; i++) {
        int8_t cmd = bytestream2_get_byte(&cmds);
        int l, r;

        if (cmd == 0) {
            l = bytestream2_get_be16(gb);
            while (l-- > 0 && x_pos < line_size) {
                dst[x_pos + y_pos   * line_size    ] = bytestream2_get_byte(gb);
                dst[x_pos + y_pos++ * line_size + 1] = bytestream2_get_byte(gb);
                if (y_pos >= height) {
                    y_pos  = 0;
                    x_pos += 2;
                }
            }
        } else if (cmd < 0) {
            l = -cmd;
            while (l-- > 0 && x_pos < line_size) {
                dst[x_pos + y_pos   * line_size    ] = bytestream2_get_byte(gb);
                dst[x_pos + y_pos++ * line_size + 1] = bytestream2_get_byte(gb);
                if (y_pos >= height) {
                    y_pos  = 0;
                    x_pos += 2;
                }
            }
        } else if (cmd == 1) {
            l = bytestream2_get_be16(gb);
            r = bytestream2_get_be16(gb);
            while (l-- > 0 && x_pos < line_size) {
                dst[x_pos + y_pos   * line_size    ] = r >> 8;
                dst[x_pos + y_pos++ * line_size + 1] = r & 0xFF;
                if (y_pos >= height) {
                    y_pos  = 0;
                    x_pos += 2;
                }
            }
        } else {
            l = cmd;
            r = bytestream2_get_be16(gb);
            while (l-- > 0 && x_pos < line_size) {
                dst[x_pos + y_pos   * line_size    ] = r >> 8;
                dst[x_pos + y_pos++ * line_size + 1] = r & 0xFF;
                if (y_pos >= height) {
                    y_pos  = 0;
                    x_pos += 2;
                }
            }
        }
    }

    return bytestream2_tell(gb);
}

#define DECODE_RGBX_COMMON(type) \
    if (!length) { \
        length = bytestream2_get_byte(gb); \
        if (!length) { \
            length = bytestream2_get_be16(gb); \
            if (!length) \
                return; \
        } \
    } \
    for (i = 0; i < length; i++) { \
        *(type *)(dst + y*linesize + x * sizeof(type)) = pixel; \
        x += 1; \
        if (x >= width) { \
            y += 1; \
            if (y >= height) \
                return; \
            x = 0; \
        } \
    }

/**
 * Decode RGB8 buffer
 * @param[out] dst Destination buffer
 * @param width Width of destination buffer (pixels)
 * @param height Height of destination buffer (pixels)
 * @param linesize Line size of destination buffer (bytes)
 */
static void decode_rgb8(GetByteContext *gb, uint8_t *dst, int width, int height, int linesize)
{
    int x = 0, y = 0, i, length;
    while (bytestream2_get_bytes_left(gb) >= 4) {
        uint32_t pixel = 0xFF000000 | bytestream2_get_be24(gb);
        length = bytestream2_get_byte(gb) & 0x7F;
        DECODE_RGBX_COMMON(uint32_t)
    }
}

/**
 * Decode RGBN buffer
 * @param[out] dst Destination buffer
 * @param width Width of destination buffer (pixels)
 * @param height Height of destination buffer (pixels)
 * @param linesize Line size of destination buffer (bytes)
 */
static void decode_rgbn(GetByteContext *gb, uint8_t *dst, int width, int height, int linesize)
{
    int x = 0, y = 0, i, length;
    while (bytestream2_get_bytes_left(gb) >= 2) {
        uint32_t pixel = bytestream2_get_be16u(gb);
        length = pixel & 0x7;
        pixel >>= 4;
        DECODE_RGBX_COMMON(uint16_t)
    }
}

/**
 * Decode DEEP RLE 32-bit buffer
 * @param[out] dst Destination buffer
 * @param[in] src Source buffer
 * @param src_size Source buffer size (bytes)
 * @param width Width of destination buffer (pixels)
 * @param height Height of destination buffer (pixels)
 * @param linesize Line size of destination buffer (bytes)
 */
static void decode_deep_rle32(uint8_t *dst, const uint8_t *src, int src_size, int width, int height, int linesize)
{
    const uint8_t *src_end = src + src_size;
    int x = 0, y = 0, i;
    while (src + 5 <= src_end) {
        int opcode;
        opcode = *(int8_t *)src++;
        if (opcode >= 0) {
            int size = opcode + 1;
            for (i = 0; i < size; i++) {
                int length = FFMIN(size - i, width);
                memcpy(dst + y*linesize + x * 4, src, length * 4);
                src += length * 4;
                x += length;
                i += length;
                if (x >= width) {
                    x = 0;
                    y += 1;
                    if (y >= height)
                        return;
                }
            }
        } else {
            int size = -opcode + 1;
            uint32_t pixel = AV_RN32(src);
            for (i = 0; i < size; i++) {
                *(uint32_t *)(dst + y*linesize + x * 4) = pixel;
                x += 1;
                if (x >= width) {
                    x = 0;
                    y += 1;
                    if (y >= height)
                        return;
                }
            }
            src += 4;
        }
    }
}

/**
 * Decode DEEP TVDC 32-bit buffer
 * @param[out] dst Destination buffer
 * @param[in] src Source buffer
 * @param src_size Source buffer size (bytes)
 * @param width Width of destination buffer (pixels)
 * @param height Height of destination buffer (pixels)
 * @param linesize Line size of destination buffer (bytes)
 * @param[int] tvdc TVDC lookup table
 */
static void decode_deep_tvdc32(uint8_t *dst, const uint8_t *src, int src_size, int width, int height, int linesize, const int16_t *tvdc)
{
    int x = 0, y = 0, plane = 0;
    int8_t pixel = 0;
    int i, j;

    for (i = 0; i < src_size * 2;) {
#define GETNIBBLE ((i & 1) ?  (src[i>>1] & 0xF) : (src[i>>1] >> 4))
        int d = tvdc[GETNIBBLE];
        i++;
        if (d) {
            pixel += d;
            dst[y * linesize + x*4 + plane] = pixel;
            x++;
        } else {
            if (i >= src_size * 2)
                return;
            d = GETNIBBLE + 1;
            i++;
            d = FFMIN(d, width - x);
            for (j = 0; j < d; j++) {
                dst[y * linesize + x*4 + plane] = pixel;
                x++;
            }
        }
        if (x >= width) {
            plane++;
            if (plane >= 4) {
                y++;
                if (y >= height)
                    return;
                plane = 0;
            }
            x = 0;
            pixel = 0;
            i = (i + 1) & ~1;
        }
    }
}

static void decode_short_horizontal_delta(uint8_t *dst,
                                          const uint8_t *buf, const uint8_t *buf_end,
                                          int w, int bpp, int dst_size)
{
    int planepitch = FFALIGN(w, 16) >> 3;
    int pitch = planepitch * bpp;
    GetByteContext ptrs, gb;
    PutByteContext pb;
    unsigned ofssrc, pos;
    int i, k;

    bytestream2_init(&ptrs, buf, buf_end - buf);
    bytestream2_init_writer(&pb, dst, dst_size);

    for (k = 0; k < bpp; k++) {
        ofssrc = bytestream2_get_be32(&ptrs);
        pos = 0;

        if (!ofssrc)
            continue;

        if (ofssrc >= buf_end - buf)
            continue;

        bytestream2_init(&gb, buf + ofssrc, buf_end - (buf + ofssrc));
        while (bytestream2_peek_be16(&gb) != 0xFFFF && bytestream2_get_bytes_left(&gb) > 3) {
            int16_t offset = bytestream2_get_be16(&gb);
            unsigned noffset;

            if (offset >= 0) {
                unsigned data = bytestream2_get_be16(&gb);

                pos += offset * 2;
                noffset = (pos / planepitch) * pitch + (pos % planepitch) + k * planepitch;
                bytestream2_seek_p(&pb, noffset, SEEK_SET);
                bytestream2_put_be16(&pb, data);
            } else {
                uint16_t count = bytestream2_get_be16(&gb);

                pos += 2 * -(offset + 2);
                for (i = 0; i < count; i++) {
                    uint16_t data = bytestream2_get_be16(&gb);

                    pos += 2;
                    noffset = (pos / planepitch) * pitch + (pos % planepitch) + k * planepitch;
                    bytestream2_seek_p(&pb, noffset, SEEK_SET);
                    bytestream2_put_be16(&pb, data);
                }
            }
        }
    }
}

static void decode_byte_vertical_delta(uint8_t *dst,
                                       const uint8_t *buf, const uint8_t *buf_end,
                                       int w, int xor, int bpp, int dst_size)
{
    int ncolumns = ((w + 15) / 16) * 2;
    int dstpitch = ncolumns * bpp;
    unsigned ofsdst, ofssrc, opcode, x;
    GetByteContext ptrs, gb;
    PutByteContext pb;
    int i, j, k;

    bytestream2_init(&ptrs, buf, buf_end - buf);
    bytestream2_init_writer(&pb, dst, dst_size);

    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));
        for (j = 0; j < ncolumns; j++) {
            ofsdst = j + k * ncolumns;

            i = bytestream2_get_byte(&gb);
            while (i > 0) {
                opcode = bytestream2_get_byte(&gb);

                if (opcode == 0) {
                    opcode  = bytestream2_get_byte(&gb);
                    x = bytestream2_get_byte(&gb);

                    while (opcode) {
                        bytestream2_seek_p(&pb, ofsdst, SEEK_SET);
                        if (xor && ofsdst < dst_size) {
                            bytestream2_put_byte(&pb, dst[ofsdst] ^ x);
                        } else {
                            bytestream2_put_byte(&pb, x);
                        }
                        ofsdst += dstpitch;
                        opcode--;
                    }
                } else if (opcode < 0x80) {
                    ofsdst += opcode * dstpitch;
                } else {
                    opcode &= 0x7f;

                    while (opcode) {
                        bytestream2_seek_p(&pb, ofsdst, SEEK_SET);
                        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--;
                    }
                }
                i--;
            }
        }
    }
}

static void decode_delta_j(uint8_t *dst,
                           const uint8_t *buf, const uint8_t *buf_end,
                           int w, int h, int bpp, int dst_size)
{
    int32_t pitch;
    uint8_t *ptr;
    uint32_t type, flag, cols, groups, rows, bytes;
    uint32_t offset;
    int planepitch_byte = (w + 7) / 8;
    int planepitch = ((w + 15) / 16) * 2;
    int kludge_j, b, g, r, d;
    GetByteContext gb;

    pitch = planepitch * bpp;
    kludge_j = w < 320 ? (320 - w) / 8 / 2 : 0;

    bytestream2_init(&gb, buf, buf_end - buf);

    while (bytestream2_get_bytes_left(&gb) >= 2) {
        type = bytestream2_get_be16(&gb);

        switch (type) {
        case 0:
            return;
        case 1:
            flag   = bytestream2_get_be16(&gb);
            cols   = bytestream2_get_be16(&gb);
            groups = bytestream2_get_be16(&gb);

            for (g = 0; g < groups; g++) {
                offset = bytestream2_get_be16(&gb);

                if (cols * bpp == 0 || bytestream2_get_bytes_left(&gb) < cols * bpp) {
                    av_log(NULL, AV_LOG_ERROR, "cols*bpp is invalid (%"PRId32"*%d)", cols, bpp);
                    return;
                }

                if (kludge_j)
                    offset = ((offset / (320 / 8)) * pitch) + (offset % (320 / 8)) - kludge_j;
                else
                    offset = ((offset / planepitch_byte) * pitch) + (offset % planepitch_byte);

                for (b = 0; b < cols; b++) {
                    for (d = 0; d < bpp; d++) {
                        uint8_t value = bytestream2_get_byte(&gb);

                        if (offset >= dst_size)
                            return;
                        ptr = dst + offset;

                        if (flag)
                            ptr[0] ^= value;
                        else
                            ptr[0]  = value;

                        offset += planepitch;
                    }
                }
                if ((cols * bpp) & 1)
                    bytestream2_skip(&gb, 1);
            }
            break;
        case 2:
            flag   = bytestream2_get_be16(&gb);
            rows   = bytestream2_get_be16(&gb);
            bytes  = bytestream2_get_be16(&gb);
            groups = bytestream2_get_be16(&gb);

            for (g = 0; g < groups; g++) {
                offset = bytestream2_get_be16(&gb);

                if (kludge_j)
                    offset = ((offset / (320 / 8)) * pitch) + (offset % (320/ 8)) - kludge_j;
                else
                    offset = ((offset / planepitch_byte) * pitch) + (offset % planepitch_byte);

                for (r = 0; r < rows; r++) {
                    for (d = 0; d < bpp; d++) {
                        unsigned noffset = offset + (r * pitch) + d * planepitch;

                        if (!bytes || bytestream2_get_bytes_left(&gb) < bytes) {
                            av_log(NULL, AV_LOG_ERROR, "bytes %"PRId32" is invalid", bytes);
                            return;
                        }

                        for (b = 0; b < bytes; b++) {
                            uint8_t value = bytestream2_get_byte(&gb);

                            if (noffset >= dst_size)
                                return;
                            ptr = dst + noffset;

                            if (flag)
                                ptr[0] ^= value;
                            else
                                ptr[0]  = value;

                            noffset++;
                        }
                    }
                }
                if ((rows * bytes * bpp) & 1)
                    bytestream2_skip(&gb, 1);
            }
            break;
        default:
            return;
        }
    }
}

static void decode_short_vertical_delta(uint8_t *dst,
                                        const uint8_t *buf, const uint8_t *buf_end,
                                        int w, int bpp, int dst_size)
{
    int ncolumns = (w + 15) >> 4;
    int dstpitch = ncolumns * bpp * 2;
    unsigned ofsdst, ofssrc, ofsdata, opcode, x;
    GetByteContext ptrs, gb, dptrs, dgb;
    PutByteContext pb;
    int i, j, k;

    if (buf_end - buf <= 64)
        return;

    bytestream2_init(&ptrs, buf, buf_end - buf);
    bytestream2_init(&dptrs, buf + 32, (buf_end - buf) - 32);
    bytestream2_init_writer(&pb, dst, dst_size);

    for (k = 0; k < bpp; k++) {
        ofssrc = bytestream2_get_be32(&ptrs);
        ofsdata = bytestream2_get_be32(&dptrs);

        if (!ofssrc)
            continue;

        if (ofssrc >= buf_end - buf)
            return;

        if (ofsdata >= buf_end - buf)
            return;

        bytestream2_init(&gb, buf + ofssrc, buf_end - (buf + ofssrc));
        bytestream2_init(&dgb, buf + ofsdata, buf_end - (buf + ofsdata));
        for (j = 0; j < ncolumns; j++) {
            ofsdst = (j + k * ncolumns) * 2;

            i = bytestream2_get_byte(&gb);
            while (i > 0) {
                opcode = bytestream2_get_byte(&gb);

                if (opcode == 0) {
                    opcode = bytestream2_get_byte(&gb);
                    x = bytestream2_get_be16(&dgb);

                    while (opcode) {
                        bytestream2_seek_p(&pb, ofsdst, SEEK_SET);
                        bytestream2_put_be16(&pb, x);
                        ofsdst += dstpitch;
                        opcode--;
                    }
                } else if (opcode < 0x80) {
                    ofsdst += opcode * dstpitch;
                } else {
                    opcode &= 0x7f;

                    while (opcode) {
                        bytestream2_seek_p(&pb, ofsdst, SEEK_SET);
                        bytestream2_put_be16(&pb, bytestream2_get_be16(&dgb));
                        ofsdst += dstpitch;
                        opcode--;
                    }
                }
                i--;
            }
        }
    }
}

static void decode_long_vertical_delta(uint8_t *dst,
                                       const uint8_t *buf, const uint8_t *buf_end,
                                       int w, int bpp, int dst_size)
{
    int ncolumns = (w + 31) >> 5;
    int dstpitch = ((w + 15) / 16 * 2) * bpp;
    unsigned ofsdst, ofssrc, ofsdata, opcode, x;
    GetByteContext ptrs, gb, dptrs, dgb;
    PutByteContext pb;
    int i, j, k, h;

    if (buf_end - buf <= 64)
        return;

    h = (((w + 15) / 16 * 2) != ((w + 31) / 32 * 4)) ? 1 : 0;
    bytestream2_init(&ptrs, buf, buf_end - buf);
    bytestream2_init(&dptrs, buf + 32, (buf_end - buf) - 32);
    bytestream2_init_writer(&pb, dst, dst_size);

    for (k = 0; k < bpp; k++) {
        ofssrc = bytestream2_get_be32(&ptrs);
        ofsdata = bytestream2_get_be32(&dptrs);

        if (!ofssrc)
            continue;

        if (ofssrc >= buf_end - buf)
            return;

        if (ofsdata >= buf_end - buf)
            return;

        bytestream2_init(&gb, buf + ofssrc, buf_end - (buf + ofssrc));
        bytestream2_init(&dgb, buf + ofsdata, buf_end - (buf + ofsdata));
        for (j = 0; j < ncolumns; j++) {
            ofsdst = (j + k * ncolumns) * 4 - h * (2 * k);

            i = bytestream2_get_byte(&gb);
            while (i > 0) {
                opcode = bytestream2_get_byte(&gb);

                if (opcode == 0) {
                    opcode = bytestream2_get_byte(&gb);
                    if (h && (j == (ncolumns - 1))) {
                        x = bytestream2_get_be16(&dgb);
                        bytestream2_skip(&dgb, 2);
                    } else {
                        x = bytestream2_get_be32(&dgb);
                    }

                    while (opcode) {
                        bytestream2_seek_p(&pb, ofsdst, SEEK_SET);
                        if (h && (j == (ncolumns - 1))) {
                            bytestream2_put_be16(&pb, x);
                        } else {
                            bytestream2_put_be32(&pb, x);
                        }
                        ofsdst += dstpitch;
                        opcode--;
                    }
                } else if (opcode < 0x80) {
                    ofsdst += opcode * dstpitch;
                } else {
                    opcode &= 0x7f;

                    while (opcode) {
                        bytestream2_seek_p(&pb, ofsdst, SEEK_SET);
                        if (h && (j == (ncolumns - 1))) {
                            bytestream2_put_be16(&pb, bytestream2_get_be16(&dgb));
                            bytestream2_skip(&dgb, 2);
                        } else {
                            bytestream2_put_be32(&pb, bytestream2_get_be32(&dgb));
                        }
                        ofsdst += dstpitch;
                        opcode--;
                    }
                }
                i--;
            }
        }
    }
}

static void decode_short_vertical_delta2(uint8_t *dst,
                                         const uint8_t *buf, const uint8_t *buf_end,
                                         int w, int bpp, int dst_size)
{
    int ncolumns = (w + 15) >> 4;
    int dstpitch = ncolumns * bpp * 2;
    unsigned ofsdst, ofssrc, opcode, x;
    GetByteContext ptrs, gb;
    PutByteContext pb;
    int i, j, k;

    bytestream2_init(&ptrs, buf, buf_end - buf);
    bytestream2_init_writer(&pb, dst, dst_size);

    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));
        for (j = 0; j < ncolumns; j++) {
            ofsdst = (j + k * ncolumns) * 2;

            i = bytestream2_get_be16(&gb);
            while (i > 0 && bytestream2_get_bytes_left(&gb) > 4) {
                opcode = bytestream2_get_be16(&gb);

                if (opcode == 0) {
                    opcode = bytestream2_get_be16(&gb);
                    x = bytestream2_get_be16(&gb);

                    while (opcode && bytestream2_get_bytes_left_p(&pb) > 1) {
                        bytestream2_seek_p(&pb, ofsdst, SEEK_SET);
                        bytestream2_put_be16(&pb, x);
                        ofsdst += dstpitch;
                        opcode--;
                    }
                } else if (opcode < 0x8000) {
                    ofsdst += opcode * dstpitch;
                } else {
                    opcode &= 0x7fff;

                    while (opcode && bytestream2_get_bytes_left(&gb) > 1 &&
                           bytestream2_get_bytes_left_p(&pb) > 1) {
                        bytestream2_seek_p(&pb, ofsdst, SEEK_SET);
                        bytestream2_put_be16(&pb, bytestream2_get_be16(&gb));
                        ofsdst += dstpitch;
                        opcode--;
                    }
                }
                i--;
            }
        }
    }
}

static void decode_long_vertical_delta2(uint8_t *dst,
                                        const uint8_t *buf, const uint8_t *buf_end,
                                        int w, int bpp, int dst_size)
{
    int ncolumns = (w + 31) >> 5;
    int dstpitch = ((w + 15) / 16 * 2) * bpp;
    unsigned ofsdst, ofssrc, opcode, x;
    unsigned skip = 0x80000000, mask = skip - 1;
    GetByteContext ptrs, gb;
    PutByteContext pb;
    int i, j, k, h;

    h = (((w + 15) / 16 * 2) != ((w + 31) / 32 * 4)) ? 1 : 0;
    bytestream2_init(&ptrs, buf, buf_end - buf);
    bytestream2_init_writer(&pb, dst, dst_size);

    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));
        for (j = 0; j < ncolumns; j++) {
            ofsdst = (j + k * ncolumns) * 4 - h * (2 * k);

            if (h && (j == (ncolumns - 1))) {
                skip = 0x8000;
                mask = skip - 1;
            }

            i = bytestream2_get_be32(&gb);
            while (i > 0 && bytestream2_get_bytes_left(&gb) > 4) {
                opcode = bytestream2_get_be32(&gb);

                if (opcode == 0) {
                    if (h && (j == ncolumns - 1)) {
                        opcode = bytestream2_get_be16(&gb);
                        x = bytestream2_get_be16(&gb);
                    } else {
                        opcode = bytestream2_get_be32(&gb);
                        x = bytestream2_get_be32(&gb);
                    }

                    while (opcode && bytestream2_get_bytes_left_p(&pb) > 1) {
                        bytestream2_seek_p(&pb, ofsdst, SEEK_SET);
                        if (h && (j == ncolumns - 1))
                            bytestream2_put_be16(&pb, x);
                        else
                            bytestream2_put_be32(&pb, x);
                        ofsdst += dstpitch;
                        opcode--;
                    }
                } else if (opcode < skip) {
                    ofsdst += opcode * dstpitch;
                } else {
                    opcode &= mask;

                    while (opcode && bytestream2_get_bytes_left(&gb) > 1 &&
                           bytestream2_get_bytes_left_p(&pb) > 1) {
                        bytestream2_seek_p(&pb, ofsdst, SEEK_SET);
                        if (h && (j == ncolumns - 1)) {
                            bytestream2_put_be16(&pb, bytestream2_get_be16(&gb));
                        } else {
                            bytestream2_put_be32(&pb, bytestream2_get_be32(&gb));
                        }
                        ofsdst += dstpitch;
                        opcode--;
                    }
                }
                i--;
            }
        }
    }
}

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)
{
    GetByteContext off0, off1, dgb, ogb;
    PutByteContext pb;
    unsigned poff0, poff1;
    int i, k, dstpitch;
    int planepitch_byte = (w + 7) / 8;
    int planepitch = ((w + 15) / 16) * 2;
    int pitch = planepitch * bpp;

    if (buf_end - buf <= 64)
        return;

    bytestream2_init(&off0, buf, buf_end - buf);
    bytestream2_init(&off1, buf + 32, buf_end - (buf + 32));
    bytestream2_init_writer(&pb, dst, dst_size);

    dstpitch = flag ? (((w + 7) / 8) * bpp): 2;

    for (k = 0; k < bpp; k++) {
        poff0 = bytestream2_get_be32(&off0);
        poff1 = bytestream2_get_be32(&off1);

        if (!poff0)
            continue;

        if (2LL * poff0 >= buf_end - buf)
            return;

        if (2LL * poff1 >= buf_end - buf)
            return;

        bytestream2_init(&dgb, buf + 2 * poff0, buf_end - (buf + 2 * poff0));
        bytestream2_init(&ogb, buf + 2 * poff1, buf_end - (buf + 2 * poff1));

        while (bytestream2_peek_be16(&ogb) != 0xFFFF && bytestream2_get_bytes_left(&ogb) >= 4) {
            uint32_t offset = bytestream2_get_be16(&ogb);
            int16_t cnt = bytestream2_get_be16(&ogb);
            uint16_t data;

            offset = ((2 * offset) / planepitch_byte) * pitch + ((2 * offset) % planepitch_byte) + k * planepitch;
            if (cnt < 0) {
                if (bytestream2_get_bytes_left(&dgb) < 2)
                    break;
                bytestream2_seek_p(&pb, offset, SEEK_SET);
                cnt = -cnt;
                data = bytestream2_get_be16(&dgb);
                for (i = 0; i < cnt; i++) {
                    bytestream2_put_be16(&pb, data);
                    bytestream2_skip_p(&pb, dstpitch - 2);
                }
            } else {
                if (bytestream2_get_bytes_left(&dgb) < 2*cnt)
                    break;
                bytestream2_seek_p(&pb, offset, SEEK_SET);
                for (i = 0; i < cnt; i++) {
                    data = bytestream2_get_be16(&dgb);
                    bytestream2_put_be16(&pb, data);
                    bytestream2_skip_p(&pb, dstpitch - 2);
                }
            }
        }
    }
}

static int unsupported(AVCodecContext *avctx)
{
    IffContext *s = avctx->priv_data;
    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;
}

static int decode_frame(AVCodecContext *avctx,
                        void *data, int *got_frame,
                        AVPacket *avpkt)
{
    IffContext *s          = avctx->priv_data;
    AVFrame *frame         = data;
    const uint8_t *buf     = avpkt->data;
    int buf_size           = avpkt->size;
    const uint8_t *buf_end = buf + buf_size;
    int y, plane, res;
    GetByteContext *gb = &s->gb;
    const AVPixFmtDescriptor *desc;

    bytestream2_init(gb, avpkt->data, avpkt->size);

    if ((res = extract_header(avctx, avpkt)) < 0)
        return res;

    if ((res = ff_get_buffer(avctx, frame, 0)) < 0)
        return res;
    s->frame = frame;

    buf      += bytestream2_tell(gb);
    buf_size -= bytestream2_tell(gb);
    desc = av_pix_fmt_desc_get(avctx->pix_fmt);

    if (!s->init && avctx->bits_per_coded_sample <= 8 &&
        avctx->pix_fmt == AV_PIX_FMT_PAL8) {
        if ((res = cmap_read_palette(avctx, (uint32_t *)frame->data[1])) < 0)
            return res;
    } else if (!s->init && avctx->bits_per_coded_sample <= 8 &&
               avctx->pix_fmt == AV_PIX_FMT_RGB32) {
        if ((res = cmap_read_palette(avctx, s->mask_palbuf)) < 0)
            return res;
    }
    s->init = 1;

    if (s->compression <= 0xff && (avctx->codec_tag == MKTAG('A', 'N', 'I', 'M'))) {
        if (avctx->pix_fmt == AV_PIX_FMT_PAL8)
            memcpy(s->pal, s->frame->data[1], 256 * 4);
    }

    switch (s->compression) {
    case 0x0:
        if (avctx->codec_tag == MKTAG('A', 'C', 'B', 'M')) {
            if (avctx->pix_fmt == AV_PIX_FMT_PAL8 || avctx->pix_fmt == AV_PIX_FMT_GRAY8) {
                memset(frame->data[0], 0, avctx->height * frame->linesize[0]);
                for (plane = 0; plane < s->bpp; plane++) {
                    for (y = 0; y < avctx->height && buf < buf_end; y++) {
                        uint8_t *row = &frame->data[0][y * frame->linesize[0]];
                        decodeplane8(row, buf, FFMIN(s->planesize, buf_end - buf), plane);
                        buf += s->planesize;
                    }
                }
            } else if (s->ham) { // HAM to AV_PIX_FMT_BGR32
                memset(frame->data[0], 0, avctx->height * frame->linesize[0]);
                for (y = 0; y < avctx->height; y++) {
                    uint8_t *row = &frame->data[0][y * frame->linesize[0]];
                    memset(s->ham_buf, 0, s->planesize * 8);
                    for (plane = 0; plane < s->bpp; plane++) {
                        const uint8_t * start = buf + (plane * avctx->height + y) * s->planesize;
                        if (start >= buf_end)
                            break;
                        decodeplane8(s->ham_buf, start, FFMIN(s->planesize, buf_end - start), plane);
                    }
                    decode_ham_plane32((uint32_t *)row, s->ham_buf, s->ham_palbuf, s->planesize);
                }
            } else
                return unsupported(avctx);
        } else if (avctx->codec_tag == MKTAG('D', 'E', 'E', 'P')) {
            int raw_width = avctx->width * (av_get_bits_per_pixel(desc) >> 3);
            int x;
            for (y = 0; y < avctx->height && buf < buf_end; y++) {
                uint8_t *row = &frame->data[0][y * frame->linesize[0]];
                memcpy(row, buf, FFMIN(raw_width, buf_end - buf));
                buf += raw_width;
                if (avctx->pix_fmt == AV_PIX_FMT_BGR32) {
                    for (x = 0; x < avctx->width; x++)
                        row[4 * x + 3] = row[4 * x + 3] & 0xF0 | (row[4 * x + 3] >> 4);
                }
            }
        } 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) {
                for (y = 0; y < avctx->height; y++) {
                    uint8_t *row = &frame->data[0][y * frame->linesize[0]];
                    memset(row, 0, avctx->width);
                    for (plane = 0; plane < s->bpp && buf < buf_end; plane++) {
                        decodeplane8(row, buf, FFMIN(s->planesize, buf_end - buf), plane);
                        buf += s->planesize;
                    }
                }
            } else if (s->ham) { // HAM to AV_PIX_FMT_BGR32
                for (y = 0; y < avctx->height; y++) {
                    uint8_t *row = &frame->data[0][y * frame->linesize[0]];
                    memset(s->ham_buf, 0, s->planesize * 8);
                    for (plane = 0; plane < s->bpp && buf < buf_end; plane++) {
                        decodeplane8(s->ham_buf, buf, FFMIN(s->planesize, buf_end - buf), plane);
                        buf += s->planesize;
                    }
                    decode_ham_plane32((uint32_t *)row, s->ham_buf, s->ham_palbuf, s->planesize);
                }
            } else { // AV_PIX_FMT_BGR32
                for (y = 0; y < avctx->height; y++) {
                    uint8_t *row = &frame->data[0][y * frame->linesize[0]];
                    memset(row, 0, avctx->width << 2);
                    for (plane = 0; plane < s->bpp && buf < buf_end; plane++) {
                        decodeplane32((uint32_t *)row, buf,
                                      FFMIN(s->planesize, buf_end - buf), plane);
                        buf += s->planesize;
                    }
                }
            }
        } else if (avctx->codec_tag == MKTAG('P', 'B', 'M', ' ')) { // IFF-PBM
            if (avctx->pix_fmt == AV_PIX_FMT_PAL8 || avctx->pix_fmt == AV_PIX_FMT_GRAY8) {
                for (y = 0; y < avctx->height && buf_end > buf; y++) {
                    uint8_t *row = &frame->data[0][y * frame->linesize[0]];
                    memcpy(row, buf, FFMIN(avctx->width, buf_end - buf));
                    buf += avctx->width + (avctx->width % 2); // padding if odd
                }
            } else if (s->ham) { // IFF-PBM: HAM to AV_PIX_FMT_BGR32
                for (y = 0; y < avctx->height && buf_end > buf; y++) {
                    uint8_t *row = &frame->data[0][y * frame->linesize[0]];
                    memcpy(s->ham_buf, buf, FFMIN(avctx->width, buf_end - buf));
                    buf += avctx->width + (avctx->width & 1); // padding if odd
                    decode_ham_plane32((uint32_t *)row, s->ham_buf, s->ham_palbuf, s->planesize);
                }
            } else
                return unsupported(avctx);
        } else {
            return unsupported(avctx);
        }
        break;
    case 0x1:
        if (avctx->codec_tag == MKTAG('I', 'L', 'B', 'M') || // interleaved
            avctx->codec_tag == MKTAG('A', 'N', 'I', 'M')) {
            if (avctx->pix_fmt == AV_PIX_FMT_PAL8 || avctx->pix_fmt == AV_PIX_FMT_GRAY8) {
                uint8_t *video = s->video[0];

                for (y = 0; y < avctx->height; y++) {
                    uint8_t *row = &frame->data[0][y * frame->linesize[0]];
                    memset(row, 0, avctx->width);
                    for (plane = 0; plane < s->bpp; plane++) {
                        buf += decode_byterun(s->planebuf, s->planesize, gb);
                        if (avctx->codec_tag == MKTAG('A', 'N', 'I', 'M')) {
                            memcpy(video, s->planebuf, s->planesize);
                            video += s->planesize;
                        }
                        decodeplane8(row, s->planebuf, s->planesize, plane);
                    }
                }
            } else if (avctx->bits_per_coded_sample <= 8) { //8-bit (+ mask) to AV_PIX_FMT_BGR32
                for (y = 0; y < avctx->height; y++) {
                    uint8_t *row = &frame->data[0][y * frame->linesize[0]];
                    memset(s->mask_buf, 0, avctx->width * sizeof(uint32_t));
                    for (plane = 0; plane < s->bpp; plane++) {
                        buf += decode_byterun(s->planebuf, s->planesize, gb);
                        decodeplane32(s->mask_buf, s->planebuf, s->planesize, plane);
                    }
                    lookup_pal_indicies((uint32_t *)row, s->mask_buf, s->mask_palbuf, avctx->width);
                }
            } else if (s->ham) { // HAM to AV_PIX_FMT_BGR32
                uint8_t *video = s->video[0];
                for (y = 0; y < avctx->height; y++) {
                    uint8_t *row = &frame->data[0][y * frame->linesize[0]];
                    memset(s->ham_buf, 0, s->planesize * 8);
                    for (plane = 0; plane < s->bpp; plane++) {
                        buf += decode_byterun(s->planebuf, s->planesize, gb);
                        if (avctx->codec_tag == MKTAG('A', 'N', 'I', 'M')) {
                            memcpy(video, s->planebuf, s->planesize);
                            video += s->planesize;
                        }
                        decodeplane8(s->ham_buf, s->planebuf, s->planesize, plane);
                    }
                    decode_ham_plane32((uint32_t *)row, s->ham_buf, s->ham_palbuf, s->planesize);
                }
            } else { // AV_PIX_FMT_BGR32
                for (y = 0; y < avctx->height; y++) {
                    uint8_t *row = &frame->data[0][y * frame->linesize[0]];
                    memset(row, 0, avctx->width << 2);
                    for (plane = 0; plane < s->bpp; plane++) {
                        buf += decode_byterun(s->planebuf, s->planesize, gb);
                        decodeplane32((uint32_t *)row, s->planebuf, s->planesize, plane);
                    }
                }
            }
        } else if (avctx->codec_tag == MKTAG('P', 'B', 'M', ' ')) { // IFF-PBM
            if (avctx->pix_fmt == AV_PIX_FMT_PAL8 || avctx->pix_fmt == AV_PIX_FMT_GRAY8) {
                for (y = 0; y < avctx->height; y++) {
                    uint8_t *row = &frame->data[0][y * frame->linesize[0]];
                    buf += decode_byterun(row, avctx->width, gb);
                }
            } else if (s->ham) { // IFF-PBM: HAM to AV_PIX_FMT_BGR32
                for (y = 0; y < avctx->height; y++) {
                    uint8_t *row = &frame->data[0][y * frame->linesize[0]];
                    buf += decode_byterun(s->ham_buf, avctx->width, gb);
                    decode_ham_plane32((uint32_t *)row, s->ham_buf, s->ham_palbuf, s->planesize);
                }
            } else
                return unsupported(avctx);
        } else if (avctx->codec_tag == MKTAG('D', 'E', 'E', 'P')) { // IFF-DEEP
            if (av_get_bits_per_pixel(desc) == 32)
                decode_deep_rle32(frame->data[0], buf, buf_size, avctx->width, avctx->height, frame->linesize[0]);
            else
                return unsupported(avctx);
        } else if (avctx->codec_tag == MKTAG('A', 'C', 'B', 'M')) {
            if (avctx->pix_fmt == AV_PIX_FMT_PAL8 || avctx->pix_fmt == AV_PIX_FMT_GRAY8) {
                memset(frame->data[0], 0, avctx->height * frame->linesize[0]);
                for (plane = 0; plane < s->bpp; plane++) {
                    for (y = 0; y < avctx->height && buf < buf_end; y++) {
                        uint8_t *row = &frame->data[0][y * frame->linesize[0]];
                        decodeplane8(row, buf, FFMIN(s->planesize, buf_end - buf), plane);
                        buf += s->planesize;
                    }
                }
            } else if (s->ham) { // HAM to AV_PIX_FMT_BGR32
                memset(frame->data[0], 0, avctx->height * frame->linesize[0]);
                for (y = 0; y < avctx->height; y++) {
                    uint8_t *row = &frame->data[0][y * frame->linesize[0]];
                    memset(s->ham_buf, 0, s->planesize * 8);
                    for (plane = 0; plane < s->bpp; plane++) {
                        const uint8_t * start = buf + (plane * avctx->height + y) * s->planesize;
                        if (start >= buf_end)
                            break;
                        decodeplane8(s->ham_buf, start, FFMIN(s->planesize, buf_end - start), plane);
                    }
                    decode_ham_plane32((uint32_t *)row, s->ham_buf, s->ham_palbuf, s->planesize);
                }
            } else {
                return unsupported(avctx);
            }
        } else {
            return unsupported(avctx);
        }
        break;
    case 0x2:
        if (avctx->codec_tag == MKTAG('I', 'L', 'B', 'M') && avctx->pix_fmt == AV_PIX_FMT_PAL8) {
            for (plane = 0; plane < s->bpp; plane++) {
                decode_byterun2(s->planebuf, avctx->height, s->planesize, gb);
                for (y = 0; y < avctx->height; y++) {
                    uint8_t *row = &frame->data[0][y * frame->linesize[0]];
                    decodeplane8(row, s->planebuf + s->planesize * y, s->planesize, plane);
                }
            }
        } else {
            return unsupported(avctx);
        }
        break;
    case 0x4:
        if (avctx->codec_tag == MKTAG('R', 'G', 'B', '8') && avctx->pix_fmt == AV_PIX_FMT_RGB32)
            decode_rgb8(gb, frame->data[0], avctx->width, avctx->height, frame->linesize[0]);
        else if (avctx->codec_tag == MKTAG('R', 'G', 'B', 'N') && avctx->pix_fmt == AV_PIX_FMT_RGB444)
            decode_rgbn(gb, frame->data[0], avctx->width, avctx->height, frame->linesize[0]);
        else
            return unsupported(avctx);
        break;
    case 0x5:
        if (avctx->codec_tag == MKTAG('D', 'E', 'E', 'P')) {
            if (av_get_bits_per_pixel(desc) == 32)
                decode_deep_tvdc32(frame->data[0], buf, buf_size, avctx->width, avctx->height, frame->linesize[0], s->tvdc);
            else
                return unsupported(avctx);
        } else
            return unsupported(avctx);
        break;
    case 0x300:
    case 0x301:
        decode_short_horizontal_delta(s->video[0], buf, buf_end, avctx->width, s->bpp, s->video_size);
        break;
    case 0x500:
    case 0x501:
        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:
        if (s->is_short)
            decode_short_vertical_delta(s->video[0], buf, buf_end, avctx->width, s->bpp, s->video_size);
        else
            decode_long_vertical_delta(s->video[0], buf, buf_end, avctx->width, s->bpp, s->video_size);
        break;
    case 0x800:
    case 0x801:
        if (s->is_short)
            decode_short_vertical_delta2(s->video[0], buf, buf_end, avctx->width, s->bpp, s->video_size);
        else
            decode_long_vertical_delta2(s->video[0], buf, buf_end, avctx->width, s->bpp, s->video_size);
        break;
    case 0x4a00:
    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);
        break;
    default:
        return unsupported(avctx);
    }

    if (s->compression <= 0xff && (avctx->codec_tag == MKTAG('A', 'N', 'I', 'M'))) {
        memcpy(s->video[1], s->video[0], s->video_size);
    }

    if (s->compression > 0xff) {
        if (avctx->pix_fmt == AV_PIX_FMT_PAL8 || avctx->pix_fmt == AV_PIX_FMT_GRAY8) {
            buf = s->video[0];
            for (y = 0; y < avctx->height; y++) {
                uint8_t *row = &frame->data[0][y * frame->linesize[0]];
                memset(row, 0, avctx->width);
                for (plane = 0; plane < s->bpp; plane++) {
                    decodeplane8(row, buf, s->planesize, plane);
                    buf += s->planesize;
                }
            }
            memcpy(frame->data[1], s->pal, 256 * 4);
        } else if (s->ham) {
            int i, count = 1 << s->ham;

            buf = s->video[0];
            memset(s->ham_palbuf, 0, (1 << s->ham) * 2 * sizeof(uint32_t));
            for (i = 0; i < count; i++) {
                s->ham_palbuf[i*2+1] = s->pal[i];
            }
            for (i = 0; i < count; i++) {
                uint32_t tmp = i << (8 - s->ham);
                tmp |= tmp >> s->ham;
                s->ham_palbuf[(i+count)*2]     = 0xFF00FFFF;
                s->ham_palbuf[(i+count*2)*2]   = 0xFFFFFF00;
                s->ham_palbuf[(i+count*3)*2]   = 0xFFFF00FF;
                s->ham_palbuf[(i+count)*2+1]   = 0xFF000000 | tmp << 16;
                s->ham_palbuf[(i+count*2)*2+1] = 0xFF000000 | tmp;
                s->ham_palbuf[(i+count*3)*2+1] = 0xFF000000 | tmp << 8;
            }
            if (s->masking == MASK_HAS_MASK) {
                for (i = 0; i < 8 * (1 << s->ham); i++)
                    s->ham_palbuf[(1 << s->bpp) + i] = s->ham_palbuf[i] | 0xFF000000;
            }
            for (y = 0; y < avctx->height; y++) {
                uint8_t *row = &frame->data[0][y * frame->linesize[0]];
                memset(s->ham_buf, 0, s->planesize * 8);
                for (plane = 0; plane < s->bpp; plane++) {
                    decodeplane8(s->ham_buf, buf, s->planesize, plane);
                    buf += s->planesize;
                }
                decode_ham_plane32((uint32_t *)row, s->ham_buf, s->ham_palbuf, s->planesize);
            }
        } else {
            return unsupported(avctx);
        }

        if (!s->is_brush) {
            FFSWAP(uint8_t *, s->video[0], s->video[1]);
        }
    }

    if (avpkt->flags & AV_PKT_FLAG_KEY) {
        frame->key_frame = 1;
        frame->pict_type = AV_PICTURE_TYPE_I;
    } else {
        frame->key_frame = 0;
        frame->pict_type = AV_PICTURE_TYPE_P;
    }

    *got_frame = 1;

    return buf_size;
}

#if CONFIG_IFF_ILBM_DECODER
AVCodec ff_iff_ilbm_decoder = {
    .name           = "iff",
    .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),
    .init           = decode_init,
    .close          = decode_end,
    .decode         = decode_frame,
    .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP,
    .capabilities   = AV_CODEC_CAP_DR1,
};
#endif