/* SDL_image: An example image loading library for use with SDL Copyright (C) 1999-2004 Sam Lantinga This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Sam Lantinga slouken@libsdl.org */ /* $Id$ */ #include #include #include #include "SDL_endian.h" #include "SDL_image.h" #ifdef LOAD_TGA /* * A TGA loader for the SDL library * Supports: Reading 8, 15, 16, 24 and 32bpp images, with alpha or colourkey, * uncompressed or RLE encoded. * * 2000-06-10 Mattias Engdegård : initial version * 2000-06-26 Mattias Engdegård : read greyscale TGAs * 2000-08-09 Mattias Engdegård : alpha inversion removed */ struct TGAheader { Uint8 infolen; /* length of info field */ Uint8 has_cmap; /* 1 if image has colormap, 0 otherwise */ Uint8 type; Uint8 cmap_start[2]; /* index of first colormap entry */ Uint8 cmap_len[2]; /* number of entries in colormap */ Uint8 cmap_bits; /* bits per colormap entry */ Uint8 yorigin[2]; /* image origin (ignored here) */ Uint8 xorigin[2]; Uint8 width[2]; /* image size */ Uint8 height[2]; Uint8 pixel_bits; /* bits/pixel */ 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 /* read/write unaligned little-endian 16-bit ints */ #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"); } /* Load a TGA type image from an SDL datasource */ 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(!SDL_RWread(src, &hdr, sizeof(hdr), 1)) goto error; ncols = LE16(hdr.cmap_len); switch(hdr.type) { case TGA_TYPE_RLE_INDEXED: rle = 1; /* fallthrough */ 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; /* fallthrough */ case TGA_TYPE_RGB: indexed = 0; break; case TGA_TYPE_RLE_BW: rle = 1; /* fallthrough */ case TGA_TYPE_BW: if(hdr.pixel_bits != 8) goto error; /* Treat greyscale as 8bpp indexed images */ 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: /* 15 and 16bpp both seem to use 5 bits/plane. The extra alpha bit is ignored for now. */ rmask = 0x7c00; gmask = 0x03e0; bmask = 0x001f; break; case 32: alpha = 1; /* fallthrough */ 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); /* skip info field */ 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 { /* skip unneeded colormap */ 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; } /* The RLE decoding code is slightly convoluted since we can't rely on spans not to wrap across scan lines */ 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) { /* swap byte order */ 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 /* dummy TGA load routine */ SDL_Surface *IMG_LoadTGA_RW(SDL_RWops *src) { return(NULL); } #endif /* LOAD_TGA */