2010-07-31 06:52:47 +04:00
|
|
|
/*
|
|
|
|
* The Python Imaging Library.
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* decoder for Autodesk Animator FLI/FLC animations
|
|
|
|
*
|
|
|
|
* history:
|
2020-05-01 15:08:57 +03:00
|
|
|
* 97-01-03 fl Created
|
|
|
|
* 97-01-17 fl Added SS2 support (FLC)
|
2010-07-31 06:52:47 +04:00
|
|
|
*
|
|
|
|
* Copyright (c) Fredrik Lundh 1997.
|
|
|
|
* Copyright (c) Secret Labs AB 1997.
|
|
|
|
*
|
|
|
|
* See the README file for information on usage and redistribution.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "Imaging.h"
|
|
|
|
|
2021-01-03 06:17:51 +03:00
|
|
|
#define I16(ptr) ((ptr)[0] + ((ptr)[1] << 8))
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2021-01-03 06:17:51 +03:00
|
|
|
#define I32(ptr) ((ptr)[0] + ((ptr)[1] << 8) + ((ptr)[2] << 16) + ((ptr)[3] << 24))
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2021-01-03 06:17:51 +03:00
|
|
|
#define ERR_IF_DATA_OOB(offset) \
|
|
|
|
if ((data + (offset)) > ptr + bytes) { \
|
|
|
|
state->errcode = IMAGING_CODEC_OVERRUN; \
|
|
|
|
return -1; \
|
|
|
|
}
|
2020-05-01 15:08:57 +03:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
int
|
2021-01-03 06:17:51 +03:00
|
|
|
ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8 *buf, Py_ssize_t bytes) {
|
|
|
|
UINT8 *ptr;
|
2010-07-31 06:52:47 +04:00
|
|
|
int framesize;
|
2019-09-30 11:45:43 +03:00
|
|
|
int c, chunks, advance;
|
2010-07-31 06:52:47 +04:00
|
|
|
int l, lines;
|
|
|
|
int i, j, x = 0, y, ymax;
|
|
|
|
|
|
|
|
/* If not even the chunk size is present, we'd better leave */
|
|
|
|
|
2020-05-10 12:56:36 +03:00
|
|
|
if (bytes < 4) {
|
2020-05-01 15:08:57 +03:00
|
|
|
return 0;
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
/* We don't decode anything unless we have a full chunk in the
|
2020-01-02 07:23:36 +03:00
|
|
|
input buffer */
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
ptr = buf;
|
|
|
|
|
|
|
|
framesize = I32(ptr);
|
2020-05-10 12:56:36 +03:00
|
|
|
if (framesize < I32(ptr)) {
|
2020-05-01 15:08:57 +03:00
|
|
|
return 0;
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
/* Make sure this is a frame chunk. The Python driver takes
|
|
|
|
case of other chunk types. */
|
|
|
|
|
2020-01-02 07:23:36 +03:00
|
|
|
if (bytes < 8) {
|
|
|
|
state->errcode = IMAGING_CODEC_OVERRUN;
|
|
|
|
return -1;
|
|
|
|
}
|
2021-01-03 06:17:51 +03:00
|
|
|
if (I16(ptr + 4) != 0xF1FA) {
|
2020-05-01 15:08:57 +03:00
|
|
|
state->errcode = IMAGING_CODEC_UNKNOWN;
|
|
|
|
return -1;
|
2010-07-31 06:52:47 +04:00
|
|
|
}
|
|
|
|
|
2021-01-03 06:17:51 +03:00
|
|
|
chunks = I16(ptr + 6);
|
2010-07-31 06:52:47 +04:00
|
|
|
ptr += 16;
|
2019-09-30 11:45:43 +03:00
|
|
|
bytes -= 16;
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
/* Process subchunks */
|
|
|
|
for (c = 0; c < chunks; c++) {
|
2021-01-03 06:17:51 +03:00
|
|
|
UINT8 *data;
|
2020-05-01 15:08:57 +03:00
|
|
|
if (bytes < 10) {
|
|
|
|
state->errcode = IMAGING_CODEC_OVERRUN;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
data = ptr + 6;
|
2021-01-03 06:17:51 +03:00
|
|
|
switch (I16(ptr + 4)) {
|
|
|
|
case 4:
|
|
|
|
case 11:
|
2020-05-01 15:08:57 +03:00
|
|
|
/* FLI COLOR chunk */
|
|
|
|
break; /* ignored; handled by Python code */
|
|
|
|
case 7:
|
|
|
|
/* FLI SS2 chunk (word delta) */
|
|
|
|
/* OOB ok, we've got 4 bytes min on entry */
|
2021-01-03 06:17:51 +03:00
|
|
|
lines = I16(data);
|
|
|
|
data += 2;
|
2020-05-01 15:08:57 +03:00
|
|
|
for (l = y = 0; l < lines && y < state->ysize; l++, y++) {
|
2021-01-03 06:17:51 +03:00
|
|
|
UINT8 *local_buf = (UINT8 *)im->image[y];
|
2020-05-01 15:08:57 +03:00
|
|
|
int p, packets;
|
|
|
|
ERR_IF_DATA_OOB(2)
|
2021-01-03 06:17:51 +03:00
|
|
|
packets = I16(data);
|
|
|
|
data += 2;
|
2020-05-01 15:08:57 +03:00
|
|
|
while (packets & 0x8000) {
|
|
|
|
/* flag word */
|
|
|
|
if (packets & 0x4000) {
|
|
|
|
y += 65536 - packets; /* skip lines */
|
|
|
|
if (y >= state->ysize) {
|
|
|
|
state->errcode = IMAGING_CODEC_OVERRUN;
|
|
|
|
return -1;
|
|
|
|
}
|
2021-01-03 06:17:51 +03:00
|
|
|
local_buf = (UINT8 *)im->image[y];
|
2020-05-01 15:08:57 +03:00
|
|
|
} else {
|
|
|
|
/* store last byte (used if line width is odd) */
|
2021-01-03 06:17:51 +03:00
|
|
|
local_buf[state->xsize - 1] = (UINT8)packets;
|
2020-05-01 15:08:57 +03:00
|
|
|
}
|
|
|
|
ERR_IF_DATA_OOB(2)
|
2021-01-03 06:17:51 +03:00
|
|
|
packets = I16(data);
|
|
|
|
data += 2;
|
2020-05-01 15:08:57 +03:00
|
|
|
}
|
|
|
|
for (p = x = 0; p < packets; p++) {
|
|
|
|
ERR_IF_DATA_OOB(2)
|
|
|
|
x += data[0]; /* pixel skip */
|
|
|
|
if (data[1] >= 128) {
|
|
|
|
ERR_IF_DATA_OOB(4)
|
2021-01-03 06:17:51 +03:00
|
|
|
i = 256 - data[1]; /* run */
|
2020-05-10 12:56:36 +03:00
|
|
|
if (x + i + i > state->xsize) {
|
2020-05-01 15:08:57 +03:00
|
|
|
break;
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2020-05-01 15:08:57 +03:00
|
|
|
for (j = 0; j < i; j++) {
|
|
|
|
local_buf[x++] = data[2];
|
|
|
|
local_buf[x++] = data[3];
|
|
|
|
}
|
|
|
|
data += 2 + 2;
|
|
|
|
} else {
|
2021-01-03 06:17:51 +03:00
|
|
|
i = 2 * (int)data[1]; /* chunk */
|
2020-05-10 12:56:36 +03:00
|
|
|
if (x + i > state->xsize) {
|
2020-05-01 15:08:57 +03:00
|
|
|
break;
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2021-01-03 06:17:51 +03:00
|
|
|
ERR_IF_DATA_OOB(2 + i)
|
2020-05-01 15:08:57 +03:00
|
|
|
memcpy(local_buf + x, data + 2, i);
|
|
|
|
data += 2 + i;
|
|
|
|
x += i;
|
|
|
|
}
|
|
|
|
}
|
2020-05-10 12:56:36 +03:00
|
|
|
if (p < packets) {
|
2020-05-01 15:08:57 +03:00
|
|
|
break; /* didn't process all packets */
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2020-05-01 15:08:57 +03:00
|
|
|
}
|
|
|
|
if (l < lines) {
|
|
|
|
/* didn't process all lines */
|
|
|
|
state->errcode = IMAGING_CODEC_OVERRUN;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 12:
|
|
|
|
/* FLI LC chunk (byte delta) */
|
|
|
|
/* OOB Check ok, we have 4 bytes min here */
|
2021-01-03 06:17:51 +03:00
|
|
|
y = I16(data);
|
|
|
|
ymax = y + I16(data + 2);
|
|
|
|
data += 4;
|
2020-05-01 15:08:57 +03:00
|
|
|
for (; y < ymax && y < state->ysize; y++) {
|
2021-01-03 06:17:51 +03:00
|
|
|
UINT8 *out = (UINT8 *)im->image[y];
|
|
|
|
ERR_IF_DATA_OOB(1)
|
2020-05-01 15:08:57 +03:00
|
|
|
int p, packets = *data++;
|
|
|
|
for (p = x = 0; p < packets; p++, x += i) {
|
|
|
|
ERR_IF_DATA_OOB(2)
|
|
|
|
x += data[0]; /* skip pixels */
|
|
|
|
if (data[1] & 0x80) {
|
2021-01-03 06:17:51 +03:00
|
|
|
i = 256 - data[1]; /* run */
|
2020-05-10 12:56:36 +03:00
|
|
|
if (x + i > state->xsize) {
|
2020-05-01 15:08:57 +03:00
|
|
|
break;
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2020-05-01 15:08:57 +03:00
|
|
|
ERR_IF_DATA_OOB(3)
|
|
|
|
memset(out + x, data[2], i);
|
|
|
|
data += 3;
|
|
|
|
} else {
|
|
|
|
i = data[1]; /* chunk */
|
2020-05-10 12:56:36 +03:00
|
|
|
if (x + i > state->xsize) {
|
2020-05-01 15:08:57 +03:00
|
|
|
break;
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2021-01-03 06:17:51 +03:00
|
|
|
ERR_IF_DATA_OOB(2 + i)
|
2020-05-01 15:08:57 +03:00
|
|
|
memcpy(out + x, data + 2, i);
|
|
|
|
data += i + 2;
|
|
|
|
}
|
|
|
|
}
|
2020-05-10 12:56:36 +03:00
|
|
|
if (p < packets) {
|
2020-05-01 15:08:57 +03:00
|
|
|
break; /* didn't process all packets */
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2020-05-01 15:08:57 +03:00
|
|
|
}
|
|
|
|
if (y < ymax) {
|
|
|
|
/* didn't process all lines */
|
|
|
|
state->errcode = IMAGING_CODEC_OVERRUN;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 13:
|
|
|
|
/* FLI BLACK chunk */
|
2020-05-10 12:56:36 +03:00
|
|
|
for (y = 0; y < state->ysize; y++) {
|
2020-05-01 15:08:57 +03:00
|
|
|
memset(im->image[y], 0, state->xsize);
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2020-05-01 15:08:57 +03:00
|
|
|
break;
|
|
|
|
case 15:
|
|
|
|
/* FLI BRUN chunk */
|
|
|
|
/* OOB, ok, we've got 4 bytes min on entry */
|
|
|
|
for (y = 0; y < state->ysize; y++) {
|
2021-01-03 06:17:51 +03:00
|
|
|
UINT8 *out = (UINT8 *)im->image[y];
|
2020-05-01 15:08:57 +03:00
|
|
|
data += 1; /* ignore packetcount byte */
|
|
|
|
for (x = 0; x < state->xsize; x += i) {
|
|
|
|
ERR_IF_DATA_OOB(2)
|
|
|
|
if (data[0] & 0x80) {
|
|
|
|
i = 256 - data[0];
|
|
|
|
if (x + i > state->xsize) {
|
|
|
|
break; /* safety first */
|
|
|
|
}
|
2021-01-03 06:17:51 +03:00
|
|
|
ERR_IF_DATA_OOB(i + 1)
|
2020-05-01 15:08:57 +03:00
|
|
|
memcpy(out + x, data + 1, i);
|
|
|
|
data += i + 1;
|
|
|
|
} else {
|
|
|
|
i = data[0];
|
2020-05-10 12:56:36 +03:00
|
|
|
if (x + i > state->xsize) {
|
2020-05-01 15:08:57 +03:00
|
|
|
break; /* safety first */
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2020-05-01 15:08:57 +03:00
|
|
|
memset(out + x, data[1], i);
|
|
|
|
data += 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (x != state->xsize) {
|
|
|
|
/* didn't unpack whole line */
|
|
|
|
state->errcode = IMAGING_CODEC_OVERRUN;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 16:
|
|
|
|
/* COPY chunk */
|
2021-01-03 06:17:51 +03:00
|
|
|
if (state->xsize > bytes / state->ysize) {
|
2020-05-01 15:08:57 +03:00
|
|
|
/* not enough data for frame */
|
|
|
|
return ptr - buf; /* bytes consumed */
|
|
|
|
}
|
|
|
|
for (y = 0; y < state->ysize; y++) {
|
2021-01-03 06:17:51 +03:00
|
|
|
UINT8 *local_buf = (UINT8 *)im->image[y];
|
2020-05-01 15:08:57 +03:00
|
|
|
memcpy(local_buf, data, state->xsize);
|
|
|
|
data += state->xsize;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 18:
|
|
|
|
/* PSTAMP chunk */
|
|
|
|
break; /* ignored */
|
|
|
|
default:
|
|
|
|
/* unknown chunk */
|
|
|
|
/* printf("unknown FLI/FLC chunk: %d\n", I16(ptr+4)); */
|
|
|
|
state->errcode = IMAGING_CODEC_UNKNOWN;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
advance = I32(ptr);
|
2021-03-12 00:12:35 +03:00
|
|
|
if (advance == 0 ) {
|
2021-04-01 17:41:46 +03:00
|
|
|
// If there's no advance, we're in an infinite loop
|
2021-03-12 00:12:35 +03:00
|
|
|
state->errcode = IMAGING_CODEC_BROKEN;
|
|
|
|
return -1;
|
|
|
|
}
|
2020-05-01 15:08:57 +03:00
|
|
|
if (advance < 0 || advance > bytes) {
|
|
|
|
state->errcode = IMAGING_CODEC_OVERRUN;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
ptr += advance;
|
|
|
|
bytes -= advance;
|
2010-07-31 06:52:47 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return -1; /* end of frame */
|
|
|
|
}
|