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