IMG_tga.c
author Sam Lantinga <slouken@lokigames.com>
Thu, 10 Aug 2000 06:05:39 +0000
changeset 0 76be7dab668c
child 4 b1bb33e907f8
permissions -rw-r--r--
Initial revision
     1 /*
     2     IMGLIB:  An example image loading library for use with SDL
     3     Copyright (C) 1999  Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Library General Public
     7     License as published by the Free Software Foundation; either
     8     version 2 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Library General Public License for more details.
    14 
    15     You should have received a copy of the GNU Library General Public
    16     License along with this library; if not, write to the Free
    17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    18 
    19     Sam Lantinga
    20     5635-34 Springhouse Dr.
    21     Pleasanton, CA 94588 (USA)
    22     slouken@devolution.com
    23 */
    24 
    25 #include <stdio.h>
    26 #include <stdlib.h>
    27 
    28 #include "SDL_endian.h"
    29 
    30 #include "SDL_image.h"
    31 
    32 #ifdef LOAD_TGA
    33 
    34 /*
    35  * A TGA loader for the SDL library
    36  * Supports: Reading 8, 15, 16, 24 and 32bpp images, with alpha or colourkey,
    37  *           uncompressed or RLE encoded.
    38  *
    39  * 2000-06-10 Mattias Engdegård <f91-men@nada.kth.se>: initial version
    40  * 2000-06-26 Mattias Engdegård <f91-men@nada.kth.se>: read greyscale TGAs
    41  */
    42 
    43 extern int IMG_invert_alpha;
    44 
    45 struct TGAheader {
    46     Uint8 infolen;		/* length of info field */
    47     Uint8 has_cmap;		/* 1 if image has colormap, 0 otherwise */
    48     Uint8 type;
    49 
    50     Uint8 cmap_start[2];	/* index of first colormap entry */
    51     Uint8 cmap_len[2];		/* number of entries in colormap */
    52     Uint8 cmap_bits;		/* bits per colormap entry */
    53 
    54     Uint8 yorigin[2];		/* image origin (ignored here) */
    55     Uint8 xorigin[2];
    56     Uint8 width[2];		/* image size */
    57     Uint8 height[2];
    58     Uint8 pixel_bits;		/* bits/pixel */
    59     Uint8 flags;
    60 };
    61 
    62 enum tga_type {
    63     TGA_TYPE_INDEXED = 1,
    64     TGA_TYPE_RGB = 2,
    65     TGA_TYPE_BW = 3,
    66     TGA_TYPE_RLE_INDEXED = 9,
    67     TGA_TYPE_RLE_RGB = 10,
    68     TGA_TYPE_RLE_BW = 11
    69 };
    70 
    71 #define TGA_INTERLEAVE_MASK	0xc0
    72 #define TGA_INTERLEAVE_NONE	0x00
    73 #define TGA_INTERLEAVE_2WAY	0x40
    74 #define TGA_INTERLEAVE_4WAY	0x80
    75 
    76 #define TGA_ORIGIN_MASK		0x30
    77 #define TGA_ORIGIN_LEFT		0x00
    78 #define TGA_ORIGIN_RIGHT	0x10
    79 #define TGA_ORIGIN_LOWER	0x00
    80 #define TGA_ORIGIN_UPPER	0x20
    81 
    82 /* read/write unaligned little-endian 16-bit ints */
    83 #define LE16(p) ((p)[0] + ((p)[1] << 8))
    84 #define SETLE16(p, v) ((p)[0] = (v), (p)[1] = (v) >> 8)
    85 
    86 static void unsupported(void)
    87 {
    88     IMG_SetError("unsupported TGA format");
    89 }
    90 
    91 /* Load a TGA type image from an SDL datasource */
    92 SDL_Surface *IMG_LoadTGA_RW(SDL_RWops *src)
    93 {
    94     struct TGAheader hdr;
    95     int rle = 0;
    96     int alpha = 0;
    97     int indexed = 0;
    98     int grey = 0;
    99     int ckey = -1;
   100     int ncols, w, h;
   101     SDL_Surface *img;
   102     Uint32 rmask, gmask, bmask, amask;
   103     Uint8 *dst;
   104     int i;
   105     int bpp;
   106     int lstep;
   107     Uint32 pixel;
   108     int count, rep;
   109 
   110     if(!SDL_RWread(src, &hdr, sizeof(hdr), 1))
   111 	goto error;
   112     ncols = LE16(hdr.cmap_len);
   113     switch(hdr.type) {
   114     case TGA_TYPE_RLE_INDEXED:
   115 	rle = 1;
   116 	/* fallthrough */
   117     case TGA_TYPE_INDEXED:
   118 	if(!hdr.has_cmap || hdr.pixel_bits != 8 || ncols > 256)
   119 	    goto error;
   120 	indexed = 1;
   121 	break;
   122 
   123     case TGA_TYPE_RLE_RGB:
   124 	rle = 1;
   125 	/* fallthrough */
   126     case TGA_TYPE_RGB:
   127 	indexed = 0;
   128 	break;
   129 
   130     case TGA_TYPE_RLE_BW:
   131 	rle = 1;
   132 	/* fallthrough */
   133     case TGA_TYPE_BW:
   134 	if(hdr.pixel_bits != 8)
   135 	    goto error;
   136 	/* Treat greyscale as 8bpp indexed images */
   137 	indexed = grey = 1;
   138 	break;
   139 
   140     default:
   141         unsupported();
   142 	return NULL;
   143     }
   144 
   145     bpp = (hdr.pixel_bits + 7) >> 3;
   146     switch(hdr.pixel_bits) {
   147     case 8:
   148 	if(!indexed) {
   149 	    unsupported();
   150 	    return NULL;
   151 	}
   152 	rmask = gmask = bmask = amask = 0;
   153 	break;
   154 
   155     case 15:
   156     case 16:
   157 	/* 15 and 16bpp both seem to use 5 bits/plane. The extra alpha bit
   158 	   is ignored for now. */
   159 	amask = 0;
   160 	rmask = 0x7c00;
   161 	gmask = 0x03e0;
   162 	bmask = 0x001f;
   163 	break;
   164 
   165     case 32:
   166 	alpha = 1;
   167 	/* fallthrough */
   168     case 24:
   169 	if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
   170 	    int s = alpha ? 0 : 8;
   171 	    amask = 0x000000ff >> s;
   172 	    rmask = 0x0000ff00 >> s;
   173 	    gmask = 0x00ff0000 >> s;
   174 	    bmask = 0xff000000 >> s;
   175 	} else {
   176 	    amask = alpha ? 0xff000000 : 0;
   177 	    rmask = 0x00ff0000;
   178 	    gmask = 0x0000ff00;
   179 	    bmask = 0x000000ff;
   180 	}
   181 	break;
   182 
   183     default:
   184 	unsupported();
   185 	return NULL;
   186     }
   187 
   188     if((hdr.flags & TGA_INTERLEAVE_MASK) != TGA_INTERLEAVE_NONE
   189        || hdr.flags & TGA_ORIGIN_RIGHT) {
   190 	unsupported();
   191 	return NULL;
   192     }
   193     
   194     SDL_RWseek(src, hdr.infolen, SEEK_CUR); /* skip info field */
   195 
   196     w = LE16(hdr.width);
   197     h = LE16(hdr.height);
   198     img = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h,
   199 			       bpp * 8,
   200 			       rmask, gmask, bmask, amask);
   201 
   202     if(hdr.has_cmap) {
   203 	int palsiz = ncols * ((hdr.cmap_bits + 7) >> 3);
   204 	if(indexed && !grey) {
   205 	    Uint8 *pal = malloc(palsiz), *p = pal;
   206 	    SDL_Color *colors = img->format->palette->colors;
   207 	    img->format->palette->ncolors = ncols;
   208 	    SDL_RWread(src, pal, palsiz, 1);
   209 	    for(i = 0; i < ncols; i++) {
   210 		switch(hdr.cmap_bits) {
   211 		case 15:
   212 		case 16:
   213 		    {
   214 			Uint16 c = p[0] + (p[1] << 8);
   215 			p += 2;
   216 			colors[i].r = (c >> 7) & 0xf8;
   217 			colors[i].g = (c >> 2) & 0xf8;
   218 			colors[i].b = c << 3;
   219 		    }
   220 		    break;
   221 		case 24:
   222 		case 32:
   223 		    colors[i].b = *p++;
   224 		    colors[i].g = *p++;
   225 		    colors[i].r = *p++;
   226 		    if(hdr.cmap_bits == 32 && *p++ < 128)
   227 			ckey = i;
   228 		    break;
   229 		}
   230 	    }
   231 	    free(pal);
   232 	    if(ckey >= 0)
   233 		SDL_SetColorKey(img, SDL_SRCCOLORKEY, ckey);
   234 	} else {
   235 	    /* skip unneeded colormap */
   236 	    SDL_RWseek(src, palsiz, SEEK_CUR);
   237 	}
   238     }
   239 
   240     if(grey) {
   241 	SDL_Color *colors = img->format->palette->colors;
   242 	for(i = 0; i < 256; i++)
   243 	    colors[i].r = colors[i].g = colors[i].b = i;
   244 	img->format->palette->ncolors = 256;
   245     }
   246 
   247     if(hdr.flags & TGA_ORIGIN_UPPER) {
   248 	lstep = img->pitch;
   249 	dst = (Uint8 *)img->pixels;
   250     } else {
   251 	lstep = -img->pitch;
   252 	dst = (Uint8 *)img->pixels + (h - 1) * img->pitch;
   253     }
   254 
   255     /* The RLE decoding code is slightly convoluted since we can't rely on
   256        spans not to wrap across scan lines */
   257     count = rep = 0;
   258     for(i = 0; i < h; i++) {
   259 	if(rle) {
   260 	    int x = 0;
   261 	    for(;;) {
   262 		Uint8 c;
   263 
   264 		if(count) {
   265 		    int n = count;
   266 		    if(n > w - x)
   267 			n = w - x;
   268 		    SDL_RWread(src, dst + x * bpp, n * bpp, 1);
   269 		    count -= n;
   270 		    x += n;
   271 		    if(x == w)
   272 			break;
   273 		} else if(rep) {
   274 		    int n = rep;
   275 		    if(n > w - x)
   276 			n = w - x;
   277 		    rep -= n;
   278 		    while(n--) {
   279 			memcpy(dst + x * bpp, &pixel, bpp);
   280 			x++;
   281 		    }
   282 		    if(x == w)
   283 			break;
   284 		}
   285 
   286 		SDL_RWread(src, &c, 1, 1);
   287 		if(c & 0x80) {
   288 		    SDL_RWread(src, &pixel, bpp, 1);
   289 		    rep = (c & 0x7f) + 1;
   290 		} else {
   291 		    count = c + 1;
   292 		}
   293 	    }
   294 
   295 	} else {
   296 	    SDL_RWread(src, dst, w * bpp, 1);
   297 	}
   298 	if(SDL_BYTEORDER == SDL_BIG_ENDIAN && bpp == 2) {
   299 	    /* swap byte order */
   300 	    int x;
   301 	    Uint16 *p = (Uint16 *)dst;
   302 	    for(x = 0; x < w; x++)
   303 		p[x] = SDL_Swap16(p[x]);
   304 	}
   305 	if(alpha && !IMG_invert_alpha) {
   306 	    /* TGA stores alpha traditionally (0 transparent, 0xff opaque) */
   307 	    int x;
   308 	    for(x = 0; x < w; x++)
   309 		dst[x * 4 + 3] ^= 0xff;
   310 	}
   311 	dst += lstep;
   312     }
   313     if(alpha)
   314 	SDL_SetAlpha(img, SDL_SRCALPHA, 0);
   315     return img;
   316 
   317 error:
   318     IMG_SetError("Error reading TGA data");
   319     return NULL;
   320 }
   321 
   322 #else
   323 
   324 /* dummy TGA load routine */
   325 SDL_Surface *IMG_LoadTGA_RW(SDL_RWops *src)
   326 {
   327 	return(NULL);
   328 }
   329 
   330 #endif /* LOAD_TGA */