2002-09-10 22:59:52 +02:00
|
|
|
/* author: Tilman Sauerbeck <tsauerbeck@users.sourceforge.net>
|
|
|
|
* based on: XreaL's x_r_img_tga.* (http://www.sourceforge.net/projects/xreal/)
|
|
|
|
* libtarga.*
|
|
|
|
* xli's tga.*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include "mp_msg.h"
|
|
|
|
|
|
|
|
#include "bswap.h"
|
|
|
|
#include "postproc/rgb2rgb.h"
|
|
|
|
#include "libvo/fastmemcpy.h"
|
|
|
|
|
|
|
|
#include "vd_internal.h"
|
|
|
|
|
|
|
|
static vd_info_t info =
|
|
|
|
{
|
|
|
|
"TGA Images decoder",
|
|
|
|
"mtga",
|
2002-09-15 15:29:00 +02:00
|
|
|
"Tilman Sauerbeck, A'rpi",
|
2002-09-10 22:59:52 +02:00
|
|
|
"Tilman Sauerbeck",
|
|
|
|
"only 24bpp and 32bpp RGB targa files support so far"
|
|
|
|
};
|
|
|
|
|
|
|
|
LIBVD_EXTERN(mtga)
|
|
|
|
|
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
TGA_NO_DATA,
|
|
|
|
TGA_UNCOMP_PALETTED,
|
|
|
|
TGA_UNCOMP_TRUECOLOR,
|
|
|
|
TGA_UNCOMP_GRAYSCALE,
|
|
|
|
TGA_RLE_PALETTED = 9,
|
|
|
|
TGA_RLE_TRUECOLOR,
|
|
|
|
TGA_RLE_GRAYSCALE
|
|
|
|
} TGAImageType;
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
unsigned char id_len;
|
|
|
|
unsigned short img_type;
|
|
|
|
|
|
|
|
unsigned short width;
|
|
|
|
unsigned short height;
|
|
|
|
|
|
|
|
unsigned char bpp;
|
|
|
|
unsigned char origin; /* 0 = lower left, 1 = upper left */
|
2002-09-14 00:13:49 +02:00
|
|
|
unsigned short start_row;
|
|
|
|
unsigned short increment;
|
2002-09-10 22:59:52 +02:00
|
|
|
} TGAInfo;
|
|
|
|
|
|
|
|
static unsigned int out_fmt = 0;
|
|
|
|
|
|
|
|
static int last_w = -1;
|
|
|
|
static int last_h = -1;
|
|
|
|
static int last_c = -1;
|
|
|
|
|
2002-09-14 00:13:49 +02:00
|
|
|
|
2002-09-10 22:59:52 +02:00
|
|
|
/* to set/get/query special features/parameters */
|
|
|
|
static int control(sh_video_t *sh, int cmd, void *arg, ...)
|
|
|
|
{
|
2002-09-14 00:13:49 +02:00
|
|
|
switch (cmd)
|
|
|
|
{
|
|
|
|
case VDCTRL_QUERY_FORMAT:
|
2002-09-15 15:29:00 +02:00
|
|
|
if (*((int *) arg) == out_fmt) return CONTROL_TRUE;
|
|
|
|
return CONTROL_FALSE;
|
2002-09-14 00:13:49 +02:00
|
|
|
}
|
2002-09-15 15:29:00 +02:00
|
|
|
return CONTROL_UNKNOWN;
|
2002-09-10 22:59:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* init driver */
|
|
|
|
static int init(sh_video_t *sh)
|
|
|
|
{
|
2002-09-14 00:13:49 +02:00
|
|
|
sh->context = (TGAInfo *) calloc(1, sizeof(TGAInfo));
|
2002-09-10 22:59:52 +02:00
|
|
|
last_w = -1;
|
2002-09-14 00:13:49 +02:00
|
|
|
|
2002-09-10 22:59:52 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* uninit driver */
|
|
|
|
static void uninit(sh_video_t *sh)
|
|
|
|
{
|
2002-09-14 00:13:49 +02:00
|
|
|
TGAInfo *info = sh->context;
|
|
|
|
free(info);
|
|
|
|
return;
|
2002-09-10 22:59:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* decode a runlength-encoded tga */
|
2002-09-15 15:29:00 +02:00
|
|
|
static void decode_rle_tga(TGAInfo *info, unsigned char *data, mp_image_t *mpi)
|
2002-09-10 22:59:52 +02:00
|
|
|
{
|
2002-09-15 15:29:00 +02:00
|
|
|
int row, col, replen, i, num_bytes = info->bpp / 8;
|
2002-09-10 22:59:52 +02:00
|
|
|
unsigned char repetitions, packet_header, *final;
|
|
|
|
|
2002-09-14 00:13:49 +02:00
|
|
|
/* see line 207 to see why this loop is set up like this */
|
|
|
|
for (row = info->start_row; (!info->origin && row) || (info->origin && row < info->height); row += info->increment)
|
2002-09-10 22:59:52 +02:00
|
|
|
{
|
2002-09-15 15:29:00 +02:00
|
|
|
final = mpi->planes[0] + mpi->stride[0] * row;
|
2002-09-14 00:13:49 +02:00
|
|
|
|
|
|
|
for (col = 0; col < info->width; col += repetitions)
|
2002-09-10 22:59:52 +02:00
|
|
|
{
|
|
|
|
packet_header = *data++;
|
2002-09-15 15:29:00 +02:00
|
|
|
repetitions = (1 + (packet_header & 0x7f));
|
|
|
|
replen = repetitions * num_bytes;
|
2002-09-10 22:59:52 +02:00
|
|
|
|
2002-09-14 00:13:49 +02:00
|
|
|
if (packet_header & 0x80) /* runlength encoded packet */
|
2002-09-10 22:59:52 +02:00
|
|
|
{
|
2002-09-14 00:13:49 +02:00
|
|
|
memcpy(final, data, num_bytes);
|
|
|
|
|
2002-09-15 15:29:00 +02:00
|
|
|
// Note: this will be slow when DR to vram!
|
|
|
|
i=num_bytes;
|
|
|
|
while(2*i<=replen){
|
|
|
|
memcpy(final+i,final,i);
|
|
|
|
i*=2;
|
|
|
|
}
|
|
|
|
memcpy(final+i,final,replen-i);
|
|
|
|
data += num_bytes;
|
2002-09-10 22:59:52 +02:00
|
|
|
}
|
|
|
|
else /* raw packet */
|
2002-09-15 15:29:00 +02:00
|
|
|
{
|
|
|
|
memcpy(final, data, replen);
|
|
|
|
data += replen;
|
|
|
|
}
|
|
|
|
|
|
|
|
final += replen;
|
2002-09-14 00:13:49 +02:00
|
|
|
}
|
2002-09-10 22:59:52 +02:00
|
|
|
}
|
2002-09-14 00:13:49 +02:00
|
|
|
|
2002-09-10 22:59:52 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-09-15 15:29:00 +02:00
|
|
|
static void decode_uncompressed_tga(TGAInfo *info, unsigned char *data, mp_image_t *mpi)
|
2002-09-10 22:59:52 +02:00
|
|
|
{
|
|
|
|
unsigned char *final;
|
2002-09-15 15:29:00 +02:00
|
|
|
int row, num_bytes = info->bpp / 8;
|
2002-09-14 00:13:49 +02:00
|
|
|
|
|
|
|
/* see line 207 to see why this loop is set up like this */
|
|
|
|
for (row = info->start_row; (!info->origin && row) || (info->origin && row < info->height); row += info->increment)
|
2002-09-10 22:59:52 +02:00
|
|
|
{
|
2002-09-15 15:29:00 +02:00
|
|
|
final = mpi->planes[0] + mpi->stride[0] * row;
|
2002-09-14 00:13:49 +02:00
|
|
|
memcpy(final, data, info->width * num_bytes);
|
|
|
|
data += info->width * num_bytes;
|
2002-09-10 22:59:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-09-15 15:29:00 +02:00
|
|
|
static short read_tga_header(unsigned char *buf, TGAInfo *info)
|
2002-09-10 22:59:52 +02:00
|
|
|
{
|
2002-09-15 15:29:00 +02:00
|
|
|
info->id_len = buf[0];
|
2002-09-10 22:59:52 +02:00
|
|
|
|
2002-09-15 15:29:00 +02:00
|
|
|
info->img_type = buf[2];
|
2002-09-10 22:59:52 +02:00
|
|
|
|
|
|
|
/* targa data is always stored in little endian byte order */
|
2002-09-15 15:29:00 +02:00
|
|
|
info->width = le2me_16(*(unsigned short *) &buf[12]);
|
|
|
|
info->height = le2me_16(*(unsigned short *) &buf[14]);
|
2002-09-10 22:59:52 +02:00
|
|
|
|
2002-09-15 15:29:00 +02:00
|
|
|
info->bpp = buf[16];
|
2002-09-10 22:59:52 +02:00
|
|
|
|
2002-09-15 15:29:00 +02:00
|
|
|
info->origin = (buf[17] & 0x20) >> 5;
|
2002-09-14 00:13:49 +02:00
|
|
|
|
2002-09-10 22:59:52 +02:00
|
|
|
/* FIXME check for valid targa data */
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* decode a frame */
|
|
|
|
static mp_image_t *decode(sh_video_t *sh, void *raw, int len, int flags)
|
|
|
|
{
|
2002-09-14 00:13:49 +02:00
|
|
|
TGAInfo *info = sh->context;
|
2002-09-10 22:59:52 +02:00
|
|
|
unsigned char *data = raw;
|
|
|
|
mp_image_t *mpi;
|
|
|
|
|
|
|
|
|
|
|
|
if (len <= 0)
|
|
|
|
return NULL; /* skip frame */
|
|
|
|
|
2002-09-15 15:29:00 +02:00
|
|
|
read_tga_header(data, info); /* read information about the file */
|
2002-09-10 22:59:52 +02:00
|
|
|
|
2002-09-14 00:13:49 +02:00
|
|
|
if (info->bpp == 24)
|
|
|
|
out_fmt = IMGFMT_BGR24;
|
|
|
|
else if (info->bpp == 32)
|
|
|
|
out_fmt = IMGFMT_BGR32;
|
2002-09-10 22:59:52 +02:00
|
|
|
else
|
|
|
|
{
|
2002-09-15 15:29:00 +02:00
|
|
|
mp_msg(MSGT_DECVIDEO, MSGL_INFO, "Unsupported TGA type! depth=%d\n",info->bpp);
|
2002-09-10 22:59:52 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2002-09-14 00:13:49 +02:00
|
|
|
if (info->img_type != TGA_UNCOMP_TRUECOLOR && info->img_type != TGA_RLE_TRUECOLOR) /* not a true color image */
|
2002-09-10 22:59:52 +02:00
|
|
|
{
|
2002-09-14 00:13:49 +02:00
|
|
|
mp_msg(MSGT_DECVIDEO, MSGL_INFO, "Unsupported TGA type: %i!\n", info->img_type);
|
2002-09-10 22:59:52 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2002-09-14 00:13:49 +02:00
|
|
|
/* if img.origin is 0, we decode from bottom to top. if it's 1, we decode from top to bottom */
|
|
|
|
info->start_row = (info->origin) ? 0 : info->height - 1;
|
|
|
|
info->increment = (info->origin) ? 1 : -1;
|
|
|
|
|
2002-09-10 22:59:52 +02:00
|
|
|
/* set data to the beginning of the image data */
|
2002-09-14 00:13:49 +02:00
|
|
|
data += 18 + info->id_len;
|
2002-09-10 22:59:52 +02:00
|
|
|
|
|
|
|
/* (re)init libvo if image parameters changed (width/height/colorspace) */
|
2002-09-14 00:13:49 +02:00
|
|
|
if (last_w != info->width || last_h != info->height || last_c != out_fmt)
|
2002-09-10 22:59:52 +02:00
|
|
|
{
|
2002-09-14 00:13:49 +02:00
|
|
|
last_w = info->width;
|
|
|
|
last_h = info->height;
|
2002-09-10 22:59:52 +02:00
|
|
|
last_c = out_fmt;
|
|
|
|
|
2002-09-14 00:13:49 +02:00
|
|
|
if (!out_fmt || !mpcodecs_config_vo(sh, info->width, info->height, out_fmt))
|
2002-09-10 22:59:52 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2002-09-14 00:13:49 +02:00
|
|
|
if (!(mpi = mpcodecs_get_image(sh, MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE, info->width, info->height)))
|
2002-09-10 22:59:52 +02:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* finally decode the image */
|
2002-09-14 00:13:49 +02:00
|
|
|
if (info->img_type == TGA_UNCOMP_TRUECOLOR)
|
2002-09-15 15:29:00 +02:00
|
|
|
decode_uncompressed_tga(info, data, mpi);
|
2002-09-14 00:13:49 +02:00
|
|
|
else if (info->img_type == TGA_RLE_TRUECOLOR)
|
2002-09-15 15:29:00 +02:00
|
|
|
decode_rle_tga(info, data, mpi);
|
|
|
|
// else
|
|
|
|
// mpi = NULL;
|
2002-09-10 22:59:52 +02:00
|
|
|
|
|
|
|
return mpi;
|
|
|
|
}
|
|
|
|
|