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