mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-12-23 12:43:46 +02:00
a247ac640d
Given that the AVCodec.next pointer has now been removed, most of the AVCodecs are not modified at all any more and can therefore be made const (as this patch does); the only exceptions are the very few codecs for external libraries that have a init_static_data callback. Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@gmail.com> Signed-off-by: James Almer <jamrial@gmail.com>
1919 lines
71 KiB
C
1919 lines
71 KiB
C
/*
|
|
* 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, 1U << plane, \
|
|
0, 0, 1U << plane, 0, \
|
|
0, 0, 1U << plane, 1U << plane, \
|
|
0, 1U << plane, 0, 0, \
|
|
0, 1U << plane, 0, 1U << plane, \
|
|
0, 1U << plane, 1U << plane, 0, \
|
|
0, 1U << plane, 1U << plane, 1U << plane, \
|
|
1U << plane, 0, 0, 0, \
|
|
1U << plane, 0, 0, 1U << plane, \
|
|
1U << plane, 0, 1U << plane, 0, \
|
|
1U << plane, 0, 1U << plane, 1U << plane, \
|
|
1U << plane, 1U << plane, 0, 0, \
|
|
1U << plane, 1U << plane, 0, 1U << plane, \
|
|
1U << plane, 1U << plane, 1U << plane, 0, \
|
|
1U << plane, 1U << plane, 1U << plane, 1U << 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) {
|
|
if ((1 << avctx->bits_per_coded_sample) < count) {
|
|
avpriv_request_sample(avctx, "overlapping mask");
|
|
return AVERROR_PATCHWELCOME;
|
|
}
|
|
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->ham) {
|
|
if (s->bpp > 8) {
|
|
av_log(avctx, AV_LOG_ERROR, "Invalid number of hold bits for HAM: %u\n", s->ham);
|
|
return AVERROR_INVALIDDATA;
|
|
} else if (s->ham != (s->bpp > 6 ? 6 : 4)) {
|
|
av_log(avctx, AV_LOG_ERROR, "Invalid number of hold bits for HAM: %u, BPP: %u\n", s->ham, s->bpp);
|
|
return AVERROR_INVALIDDATA;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
if (s->video_size && s->planesize * s->bpp * avctx->height > s->video_size)
|
|
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);
|
|
int extra_space = 1;
|
|
|
|
if (avctx->codec_tag == MKTAG('P', 'B', 'M', ' ') && s->ham == 4)
|
|
extra_space = 4;
|
|
|
|
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(extra_space * (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->mask_buf);
|
|
av_freep(&s->mask_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;
|
|
if (!s->video_size)
|
|
return AVERROR_INVALIDDATA;
|
|
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;
|
|
if (plane >= 8) {
|
|
av_log(NULL, AV_LOG_WARNING, "Ignoring extra planes beyond 8\n");
|
|
return;
|
|
}
|
|
lut = plane8_lut[plane];
|
|
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_end - src >= 5) {
|
|
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 - x);
|
|
if (src_end - src < length * 4)
|
|
return;
|
|
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);
|
|
}
|
|
|
|
if (ofsdst + (opcode - 1LL) * dstpitch > bytestream2_size_p(&pb))
|
|
return;
|
|
|
|
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);
|
|
}
|
|
|
|
if (ofsdst + (opcode - 1LL) * dstpitch > bytestream2_size_p(&pb))
|
|
return;
|
|
|
|
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);
|
|
if (entries * 8LL > bytestream2_get_bytes_left(&gb))
|
|
return;
|
|
|
|
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);
|
|
if (opcode && 4 + (opcode - 1LL) * pitch > bytestream2_get_bytes_left_p(&pb))
|
|
continue;
|
|
while (opcode && bytestream2_get_bytes_left_p(&pb) > 0) {
|
|
bytestream2_put_be32(&pb, x);
|
|
bytestream2_skip_p(&pb, pitch - 4);
|
|
opcode--;
|
|
}
|
|
} else {
|
|
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 - (s->masking == MASK_HAS_MASK) &&
|
|
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
|
|
const 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
|