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