slouken@libsdl.org
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "SDL_endian.h"
#include "SDL_image.h"
#ifdef LOAD_TGA
<f91-men@nada.kth.se>
<f91-men@nada.kth.se>
<f91-men@nada.kth.se>
struct TGAheader {
Uint8 infolen;
Uint8 has_cmap;
Uint8 type;
Uint8 cmap_start[2];
Uint8 cmap_len[2];
Uint8 cmap_bits;
Uint8 yorigin[2];
Uint8 xorigin[2];
Uint8 width[2];
Uint8 height[2];
Uint8 pixel_bits;
Uint8 flags;
};
enum tga_type {
TGA_TYPE_INDEXED = 1,
TGA_TYPE_RGB = 2,
TGA_TYPE_BW = 3,
TGA_TYPE_RLE_INDEXED = 9,
TGA_TYPE_RLE_RGB = 10,
TGA_TYPE_RLE_BW = 11
};
#define TGA_INTERLEAVE_MASK 0xc0
#define TGA_INTERLEAVE_NONE 0x00
#define TGA_INTERLEAVE_2WAY 0x40
#define TGA_INTERLEAVE_4WAY 0x80
#define TGA_ORIGIN_MASK 0x30
#define TGA_ORIGIN_LEFT 0x00
#define TGA_ORIGIN_RIGHT 0x10
#define TGA_ORIGIN_LOWER 0x00
#define TGA_ORIGIN_UPPER 0x20
#define LE16(p) ((p)[0] + ((p)[1] << 8))
#define SETLE16(p, v) ((p)[0] = (v), (p)[1] = (v) >> 8)
static void unsupported(void)
{
IMG_SetError("unsupported TGA format");
}
SDL_Surface *IMG_LoadTGA_RW(SDL_RWops *src)
{
struct TGAheader hdr;
int rle = 0;
int alpha = 0;
int indexed = 0;
int grey = 0;
int ckey = -1;
int ncols, w, h;
SDL_Surface *img;
Uint32 rmask, gmask, bmask, amask;
Uint8 *dst;
int i;
int bpp;
int lstep;
Uint32 pixel;
int count, rep;
if ( !src ) {
return NULL;
}
if(!SDL_RWread(src, &hdr, sizeof(hdr), 1))
goto error;
ncols = LE16(hdr.cmap_len);
switch(hdr.type) {
case TGA_TYPE_RLE_INDEXED:
rle = 1;
case TGA_TYPE_INDEXED:
if(!hdr.has_cmap || hdr.pixel_bits != 8 || ncols > 256)
goto error;
indexed = 1;
break;
case TGA_TYPE_RLE_RGB:
rle = 1;
case TGA_TYPE_RGB:
indexed = 0;
break;
case TGA_TYPE_RLE_BW:
rle = 1;
case TGA_TYPE_BW:
if(hdr.pixel_bits != 8)
goto error;
indexed = grey = 1;
break;
default:
unsupported();
return NULL;
}
bpp = (hdr.pixel_bits + 7) >> 3;
rmask = gmask = bmask = amask = 0;
switch(hdr.pixel_bits) {
case 8:
if(!indexed) {
unsupported();
return NULL;
}
break;
case 15:
case 16:
rmask = 0x7c00;
gmask = 0x03e0;
bmask = 0x001f;
break;
case 32:
alpha = 1;
case 24:
if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
int s = alpha ? 0 : 8;
amask = 0x000000ff >> s;
rmask = 0x0000ff00 >> s;
gmask = 0x00ff0000 >> s;
bmask = 0xff000000 >> s;
} else {
amask = alpha ? 0xff000000 : 0;
rmask = 0x00ff0000;
gmask = 0x0000ff00;
bmask = 0x000000ff;
}
break;
default:
unsupported();
return NULL;
}
if((hdr.flags & TGA_INTERLEAVE_MASK) != TGA_INTERLEAVE_NONE
|| hdr.flags & TGA_ORIGIN_RIGHT) {
unsupported();
return NULL;
}
SDL_RWseek(src, hdr.infolen, SEEK_CUR);
w = LE16(hdr.width);
h = LE16(hdr.height);
img = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h,
bpp * 8,
rmask, gmask, bmask, amask);
if(hdr.has_cmap) {
int palsiz = ncols * ((hdr.cmap_bits + 7) >> 3);
if(indexed && !grey) {
Uint8 *pal = malloc(palsiz), *p = pal;
SDL_Color *colors = img->format->palette->colors;
img->format->palette->ncolors = ncols;
SDL_RWread(src, pal, palsiz, 1);
for(i = 0; i < ncols; i++) {
switch(hdr.cmap_bits) {
case 15:
case 16:
{
Uint16 c = p[0] + (p[1] << 8);
p += 2;
colors[i].r = (c >> 7) & 0xf8;
colors[i].g = (c >> 2) & 0xf8;
colors[i].b = c << 3;
}
break;
case 24:
case 32:
colors[i].b = *p++;
colors[i].g = *p++;
colors[i].r = *p++;
if(hdr.cmap_bits == 32 && *p++ < 128)
ckey = i;
break;
}
}
free(pal);
if(ckey >= 0)
SDL_SetColorKey(img, SDL_SRCCOLORKEY, ckey);
} else {
SDL_RWseek(src, palsiz, SEEK_CUR);
}
}
if(grey) {
SDL_Color *colors = img->format->palette->colors;
for(i = 0; i < 256; i++)
colors[i].r = colors[i].g = colors[i].b = i;
img->format->palette->ncolors = 256;
}
if(hdr.flags & TGA_ORIGIN_UPPER) {
lstep = img->pitch;
dst = img->pixels;
} else {
lstep = -img->pitch;
dst = (Uint8 *)img->pixels + (h - 1) * img->pitch;
}
count = rep = 0;
for(i = 0; i < h; i++) {
if(rle) {
int x = 0;
for(;;) {
Uint8 c;
if(count) {
int n = count;
if(n > w - x)
n = w - x;
SDL_RWread(src, dst + x * bpp, n * bpp, 1);
count -= n;
x += n;
if(x == w)
break;
} else if(rep) {
int n = rep;
if(n > w - x)
n = w - x;
rep -= n;
while(n--) {
memcpy(dst + x * bpp, &pixel, bpp);
x++;
}
if(x == w)
break;
}
SDL_RWread(src, &c, 1, 1);
if(c & 0x80) {
SDL_RWread(src, &pixel, bpp, 1);
rep = (c & 0x7f) + 1;
} else {
count = c + 1;
}
}
} else {
SDL_RWread(src, dst, w * bpp, 1);
}
if(SDL_BYTEORDER == SDL_BIG_ENDIAN && bpp == 2) {
int x;
Uint16 *p = (Uint16 *)dst;
for(x = 0; x < w; x++)
p[x] = SDL_Swap16(p[x]);
}
dst += lstep;
}
return img;
error:
IMG_SetError("Error reading TGA data");
return NULL;
}
#else
SDL_Surface *IMG_LoadTGA_RW(SDL_RWops *src)
{
return(NULL);
}
#endif