|
|
|
@ -144,6 +144,7 @@ static av_cold int flic_decode_init(AVCodecContext *avctx) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
switch (depth) { |
|
|
|
|
case 1 : avctx->pix_fmt = AV_PIX_FMT_MONOBLACK; break; |
|
|
|
|
case 8 : avctx->pix_fmt = AV_PIX_FMT_PAL8; break; |
|
|
|
|
case 15 : avctx->pix_fmt = AV_PIX_FMT_RGB555; break; |
|
|
|
|
case 16 : avctx->pix_fmt = AV_PIX_FMT_RGB565; break; |
|
|
|
@ -162,6 +163,198 @@ static av_cold int flic_decode_init(AVCodecContext *avctx) |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int flic_decode_frame_1BPP(AVCodecContext *avctx, |
|
|
|
|
AVFrame *rframe, int *got_frame, |
|
|
|
|
const uint8_t *buf, int buf_size) |
|
|
|
|
{ |
|
|
|
|
FlicDecodeContext *s = avctx->priv_data; |
|
|
|
|
|
|
|
|
|
GetByteContext g2; |
|
|
|
|
ptrdiff_t pixel_ptr; |
|
|
|
|
|
|
|
|
|
unsigned int frame_size; |
|
|
|
|
int num_chunks; |
|
|
|
|
|
|
|
|
|
unsigned int chunk_size; |
|
|
|
|
int chunk_type; |
|
|
|
|
|
|
|
|
|
int i, j, ret, direction; |
|
|
|
|
|
|
|
|
|
int lines; |
|
|
|
|
int compressed_lines; |
|
|
|
|
int starting_line; |
|
|
|
|
int line_packets; |
|
|
|
|
ptrdiff_t y_ptr; |
|
|
|
|
int byte_run; |
|
|
|
|
int pixel_skip; |
|
|
|
|
int pixel_countdown; |
|
|
|
|
unsigned char *pixels; |
|
|
|
|
ptrdiff_t pixel_limit; |
|
|
|
|
|
|
|
|
|
bytestream2_init(&g2, buf, buf_size); |
|
|
|
|
|
|
|
|
|
if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) |
|
|
|
|
return ret; |
|
|
|
|
|
|
|
|
|
direction = s->frame->linesize[0] > 0; |
|
|
|
|
pixels = s->frame->data[0]; |
|
|
|
|
pixel_limit = s->avctx->height * s->frame->linesize[0]; |
|
|
|
|
if (buf_size < 16 || buf_size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE) |
|
|
|
|
return AVERROR_INVALIDDATA; |
|
|
|
|
frame_size = bytestream2_get_le32(&g2); |
|
|
|
|
if (frame_size > buf_size) |
|
|
|
|
frame_size = buf_size; |
|
|
|
|
bytestream2_skip(&g2, 2); /* skip the magic number */ |
|
|
|
|
num_chunks = bytestream2_get_le16(&g2); |
|
|
|
|
bytestream2_skip(&g2, 8); /* skip padding */ |
|
|
|
|
|
|
|
|
|
if (frame_size < 16) |
|
|
|
|
return AVERROR_INVALIDDATA; |
|
|
|
|
|
|
|
|
|
frame_size -= 16; |
|
|
|
|
|
|
|
|
|
/* iterate through the chunks */ |
|
|
|
|
while ((frame_size >= 6) && (num_chunks > 0) && |
|
|
|
|
bytestream2_get_bytes_left(&g2) >= 4) { |
|
|
|
|
int stream_ptr_after_chunk; |
|
|
|
|
chunk_size = bytestream2_get_le32(&g2); |
|
|
|
|
if (chunk_size > frame_size) { |
|
|
|
|
av_log(avctx, AV_LOG_WARNING, |
|
|
|
|
"Invalid chunk_size = %u > frame_size = %u\n", chunk_size, frame_size); |
|
|
|
|
chunk_size = frame_size; |
|
|
|
|
} |
|
|
|
|
stream_ptr_after_chunk = bytestream2_tell(&g2) - 4 + chunk_size; |
|
|
|
|
|
|
|
|
|
chunk_type = bytestream2_get_le16(&g2); |
|
|
|
|
|
|
|
|
|
switch (chunk_type) { |
|
|
|
|
case FLI_BRUN: |
|
|
|
|
/* Byte run compression: This chunk type only occurs in the first
|
|
|
|
|
* FLI frame and it will update the entire frame. */ |
|
|
|
|
y_ptr = 0; |
|
|
|
|
for (lines = 0; lines < s->avctx->height; lines++) { |
|
|
|
|
pixel_ptr = y_ptr; |
|
|
|
|
/* disregard the line packets; instead, iterate through all
|
|
|
|
|
* pixels on a row */ |
|
|
|
|
bytestream2_skip(&g2, 1); |
|
|
|
|
pixel_countdown = (s->avctx->width + 7) >> 3; |
|
|
|
|
while (pixel_countdown > 0) { |
|
|
|
|
if (bytestream2_tell(&g2) + 1 > stream_ptr_after_chunk) |
|
|
|
|
break; |
|
|
|
|
byte_run = sign_extend(bytestream2_get_byte(&g2), 8); |
|
|
|
|
if (!byte_run) { |
|
|
|
|
av_log(avctx, AV_LOG_ERROR, "Invalid byte run value.\n"); |
|
|
|
|
return AVERROR_INVALIDDATA; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (byte_run > 0) { |
|
|
|
|
int value = bytestream2_get_byte(&g2); |
|
|
|
|
CHECK_PIXEL_PTR(byte_run); |
|
|
|
|
for (j = 0; j < byte_run; j++) { |
|
|
|
|
pixels[pixel_ptr++] = value; |
|
|
|
|
pixel_countdown--; |
|
|
|
|
if (pixel_countdown < 0) |
|
|
|
|
av_log(avctx, AV_LOG_ERROR, "pixel_countdown < 0 (%d) at line %d\n", |
|
|
|
|
pixel_countdown, lines); |
|
|
|
|
} |
|
|
|
|
} else { /* copy bytes if byte_run < 0 */ |
|
|
|
|
byte_run = -byte_run; |
|
|
|
|
CHECK_PIXEL_PTR(byte_run); |
|
|
|
|
if (bytestream2_tell(&g2) + byte_run > stream_ptr_after_chunk) |
|
|
|
|
break; |
|
|
|
|
for (j = 0; j < byte_run; j++) { |
|
|
|
|
pixels[pixel_ptr++] = bytestream2_get_byte(&g2); |
|
|
|
|
pixel_countdown--; |
|
|
|
|
if (pixel_countdown < 0) |
|
|
|
|
av_log(avctx, AV_LOG_ERROR, "pixel_countdown < 0 (%d) at line %d\n", |
|
|
|
|
pixel_countdown, lines); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
y_ptr += s->frame->linesize[0]; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case FLI_LC: |
|
|
|
|
/* line compressed */ |
|
|
|
|
starting_line = bytestream2_get_le16(&g2); |
|
|
|
|
if (starting_line >= s->avctx->height) |
|
|
|
|
return AVERROR_INVALIDDATA; |
|
|
|
|
y_ptr = 0; |
|
|
|
|
y_ptr += starting_line * s->frame->linesize[0]; |
|
|
|
|
|
|
|
|
|
compressed_lines = bytestream2_get_le16(&g2); |
|
|
|
|
while (compressed_lines > 0) { |
|
|
|
|
pixel_ptr = y_ptr; |
|
|
|
|
CHECK_PIXEL_PTR(0); |
|
|
|
|
pixel_countdown = (s->avctx->width + 7) >> 3; |
|
|
|
|
if (bytestream2_tell(&g2) + 1 > stream_ptr_after_chunk) |
|
|
|
|
break; |
|
|
|
|
line_packets = bytestream2_get_byte(&g2); |
|
|
|
|
if (line_packets > 0) { |
|
|
|
|
for (i = 0; i < line_packets; i++) { |
|
|
|
|
/* account for the skip bytes */ |
|
|
|
|
if (bytestream2_tell(&g2) + 1 > stream_ptr_after_chunk) |
|
|
|
|
break; |
|
|
|
|
pixel_skip = bytestream2_get_byte(&g2); |
|
|
|
|
pixel_ptr += pixel_skip; |
|
|
|
|
pixel_countdown -= pixel_skip; |
|
|
|
|
byte_run = sign_extend(bytestream2_get_byte(&g2),8); |
|
|
|
|
if (byte_run > 0) { |
|
|
|
|
CHECK_PIXEL_PTR(byte_run); |
|
|
|
|
if (bytestream2_tell(&g2) + byte_run > stream_ptr_after_chunk) |
|
|
|
|
break; |
|
|
|
|
for (j = 0; j < byte_run; j++, pixel_countdown--) { |
|
|
|
|
pixels[pixel_ptr++] = bytestream2_get_byte(&g2); |
|
|
|
|
} |
|
|
|
|
} else if (byte_run < 0) { |
|
|
|
|
int value = bytestream2_get_byte(&g2); |
|
|
|
|
byte_run = -byte_run; |
|
|
|
|
CHECK_PIXEL_PTR(byte_run); |
|
|
|
|
for (j = 0; j < byte_run; j++, pixel_countdown--) { |
|
|
|
|
pixels[pixel_ptr++] = value; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
y_ptr += s->frame->linesize[0]; |
|
|
|
|
compressed_lines--; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
av_log(avctx, AV_LOG_ERROR, "Unrecognized chunk type: %d\n", chunk_type); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (stream_ptr_after_chunk - bytestream2_tell(&g2) >= 0) { |
|
|
|
|
bytestream2_skip(&g2, stream_ptr_after_chunk - bytestream2_tell(&g2)); |
|
|
|
|
} else { |
|
|
|
|
av_log(avctx, AV_LOG_ERROR, "Chunk overread\n"); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
frame_size -= chunk_size; |
|
|
|
|
num_chunks--; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* by the end of the chunk, the stream ptr should equal the frame
|
|
|
|
|
* size (minus 1 or 2, possibly); if it doesn't, issue a warning */ |
|
|
|
|
if (bytestream2_get_bytes_left(&g2) > 2) |
|
|
|
|
av_log(avctx, AV_LOG_ERROR, "Processed FLI chunk where chunk size = %d " \
|
|
|
|
|
"and final chunk ptr = %d\n", buf_size, |
|
|
|
|
buf_size - bytestream2_get_bytes_left(&g2)); |
|
|
|
|
|
|
|
|
|
if ((ret = av_frame_ref(rframe, s->frame)) < 0) |
|
|
|
|
return ret; |
|
|
|
|
|
|
|
|
|
*got_frame = 1; |
|
|
|
|
|
|
|
|
|
return buf_size; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int flic_decode_frame_8BPP(AVCodecContext *avctx, |
|
|
|
|
AVFrame *rframe, int *got_frame, |
|
|
|
|
const uint8_t *buf, int buf_size) |
|
|
|
@ -1092,7 +1285,10 @@ static int flic_decode_frame(AVCodecContext *avctx, AVFrame *frame, |
|
|
|
|
{ |
|
|
|
|
const uint8_t *buf = avpkt->data; |
|
|
|
|
int buf_size = avpkt->size; |
|
|
|
|
if (avctx->pix_fmt == AV_PIX_FMT_PAL8) { |
|
|
|
|
if (avctx->pix_fmt == AV_PIX_FMT_MONOBLACK) { |
|
|
|
|
return flic_decode_frame_1BPP(avctx, frame, got_frame, |
|
|
|
|
buf, buf_size); |
|
|
|
|
} else if (avctx->pix_fmt == AV_PIX_FMT_PAL8) { |
|
|
|
|
return flic_decode_frame_8BPP(avctx, frame, got_frame, |
|
|
|
|
buf, buf_size); |
|
|
|
|
} else if ((avctx->pix_fmt == AV_PIX_FMT_RGB555) || |
|
|
|
|