Commit c2eec3df authored by Bobby Bingham's avatar Bobby Bingham

targa: support 2-way and 4-way interleaved files

Fixes ticket #701
Signed-off-by: 's avatarBobby Bingham <uhmmmm@gmail.com>
parent 3d9cdfdc
...@@ -33,17 +33,35 @@ typedef struct TargaContext { ...@@ -33,17 +33,35 @@ typedef struct TargaContext {
int compression_type; int compression_type;
} TargaContext; } TargaContext;
static uint8_t *advance_line(uint8_t *start, uint8_t *line,
int stride, int *y, int h, int interleave)
{
*y += interleave;
if (*y < h) {
return line + interleave * stride;
} else {
*y = (*y + 1) & (interleave - 1);
if (*y) {
return start + *y * stride;
} else {
return NULL;
}
}
}
static int targa_decode_rle(AVCodecContext *avctx, TargaContext *s, static int targa_decode_rle(AVCodecContext *avctx, TargaContext *s,
uint8_t *dst, int w, int h, int stride, int bpp) uint8_t *start, int w, int h, int stride,
int bpp, int interleave)
{ {
int x, y; int x, y;
int depth = (bpp + 1) >> 3; int depth = (bpp + 1) >> 3;
int type, count; int type, count;
int diff; uint8_t *line = start;
uint8_t *dst = line;
diff = stride - w * depth; x = y = count = 0;
x = y = 0; while (dst) {
while (y < h) {
if (bytestream2_get_bytes_left(&s->gb) <= 0) { if (bytestream2_get_bytes_left(&s->gb) <= 0) {
av_log(avctx, AV_LOG_ERROR, av_log(avctx, AV_LOG_ERROR,
"Ran ouf of data before end-of-image\n"); "Ran ouf of data before end-of-image\n");
...@@ -52,12 +70,6 @@ static int targa_decode_rle(AVCodecContext *avctx, TargaContext *s, ...@@ -52,12 +70,6 @@ static int targa_decode_rle(AVCodecContext *avctx, TargaContext *s,
type = bytestream2_get_byteu(&s->gb); type = bytestream2_get_byteu(&s->gb);
count = (type & 0x7F) + 1; count = (type & 0x7F) + 1;
type &= 0x80; type &= 0x80;
if(x + count > (h - y) * w){
av_log(avctx, AV_LOG_ERROR,
"Packet went out of bounds: position (%i,%i) size %i\n",
x, y, count);
return AVERROR_INVALIDDATA;
}
if (!type) { if (!type) {
do { do {
int n = FFMIN(count, w - x); int n = FFMIN(count, w - x);
...@@ -67,10 +79,9 @@ static int targa_decode_rle(AVCodecContext *avctx, TargaContext *s, ...@@ -67,10 +79,9 @@ static int targa_decode_rle(AVCodecContext *avctx, TargaContext *s,
x += n; x += n;
if (x == w) { if (x == w) {
x = 0; x = 0;
y++; dst = line = advance_line(start, line, stride, &y, h, interleave);
dst += diff;
} }
} while (count > 0); } while (dst && count > 0);
} else { } else {
uint8_t tmp[4]; uint8_t tmp[4];
bytestream2_get_buffer(&s->gb, tmp, depth); bytestream2_get_buffer(&s->gb, tmp, depth);
...@@ -84,12 +95,17 @@ static int targa_decode_rle(AVCodecContext *avctx, TargaContext *s, ...@@ -84,12 +95,17 @@ static int targa_decode_rle(AVCodecContext *avctx, TargaContext *s,
} while (--n); } while (--n);
if (x == w) { if (x == w) {
x = 0; x = 0;
y++; dst = line = advance_line(start, line, stride, &y, h, interleave);
dst += diff;
} }
} while (count > 0); } while (dst && count > 0);
} }
} }
if(count) {
av_log(avctx, AV_LOG_ERROR, "Packet went out of bounds\n");
return AVERROR_INVALIDDATA;
}
return 0; return 0;
} }
...@@ -104,6 +120,7 @@ static int decode_frame(AVCodecContext *avctx, ...@@ -104,6 +120,7 @@ static int decode_frame(AVCodecContext *avctx,
int stride; int stride;
int idlen, pal, compr, y, w, h, bpp, flags; int idlen, pal, compr, y, w, h, bpp, flags;
int first_clr, colors, csize; int first_clr, colors, csize;
int interleave;
bytestream2_init(&s->gb, avpkt->data, avpkt->size); bytestream2_init(&s->gb, avpkt->data, avpkt->size);
...@@ -174,6 +191,9 @@ static int decode_frame(AVCodecContext *avctx, ...@@ -174,6 +191,9 @@ static int decode_frame(AVCodecContext *avctx,
stride = -p->linesize[0]; stride = -p->linesize[0];
} }
interleave = flags & TGA_INTERLEAVE2 ? 2 :
flags & TGA_INTERLEAVE4 ? 4 : 1;
if(colors){ if(colors){
int pal_size, pal_sample_size; int pal_size, pal_sample_size;
if((colors + first_clr) > 256){ if((colors + first_clr) > 256){
...@@ -231,20 +251,24 @@ static int decode_frame(AVCodecContext *avctx, ...@@ -231,20 +251,24 @@ static int decode_frame(AVCodecContext *avctx,
memset(p->data[0], 0, p->linesize[0] * h); memset(p->data[0], 0, p->linesize[0] * h);
} else { } else {
if(compr & TGA_RLE){ if(compr & TGA_RLE){
int res = targa_decode_rle(avctx, s, dst, w, h, stride, bpp); int res = targa_decode_rle(avctx, s, dst, w, h, stride, bpp, interleave);
if (res < 0) if (res < 0)
return res; return res;
} else { } else {
size_t img_size = w * ((bpp + 1) >> 3); size_t img_size = w * ((bpp + 1) >> 3);
uint8_t *line;
if (bytestream2_get_bytes_left(&s->gb) < img_size * h) { if (bytestream2_get_bytes_left(&s->gb) < img_size * h) {
av_log(avctx, AV_LOG_ERROR, av_log(avctx, AV_LOG_ERROR,
"Not enough data available for image\n"); "Not enough data available for image\n");
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
} }
for (y = 0; y < h; y++) {
bytestream2_get_bufferu(&s->gb, dst, img_size); line = dst;
dst += stride; y = 0;
} do {
bytestream2_get_bufferu(&s->gb, line, img_size);
line = advance_line(dst, line, stride, &y, h, interleave);
} while (line);
} }
} }
if(flags & TGA_RIGHTTOLEFT) { // right-to-left, needs horizontal flip if(flags & TGA_RIGHTTOLEFT) { // right-to-left, needs horizontal flip
......
...@@ -41,6 +41,8 @@ enum TargaCompr { ...@@ -41,6 +41,8 @@ enum TargaCompr {
enum TargaFlags { enum TargaFlags {
TGA_RIGHTTOLEFT = 0x10, // right-to-left (flipped horizontally) TGA_RIGHTTOLEFT = 0x10, // right-to-left (flipped horizontally)
TGA_TOPTOBOTTOM = 0x20, // top-to-bottom (NOT flipped vertically) TGA_TOPTOBOTTOM = 0x20, // top-to-bottom (NOT flipped vertically)
TGA_INTERLEAVE2 = 0x40, // 2-way interleave, odd then even lines
TGA_INTERLEAVE4 = 0x80, // 4-way interleave
}; };
#endif /* AVCODEC_TARGA_H */ #endif /* AVCODEC_TARGA_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment