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