IMG_tga.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 11 Sep 2017 23:42:09 -0700
changeset 501 64a10bd1598c
parent 496 6332f9425dcc
child 528 7f9e88f4b45e
permissions -rw-r--r--
Fixed bug 2840 - Wrong colors in TGA files from GTA2

Amit Jain

Swapping of bits happened for SDL_LIL_ENDIAN system which is wrong. It should be for SDL_BIG_ENDIAN system.
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@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@368
    47
    Uint8 infolen;      /* length of info field */
slouken@368
    48
    Uint8 has_cmap;     /* 1 if image has colormap, 0 otherwise */
slouken@0
    49
    Uint8 type;
slouken@0
    50
slouken@368
    51
    Uint8 cmap_start[2];    /* index of first colormap entry */
slouken@368
    52
    Uint8 cmap_len[2];      /* number of entries in colormap */
slouken@368
    53
    Uint8 cmap_bits;        /* bits per colormap entry */
slouken@0
    54
slouken@368
    55
    Uint8 yorigin[2];       /* image origin (ignored here) */
slouken@0
    56
    Uint8 xorigin[2];
slouken@368
    57
    Uint8 width[2];     /* image size */
slouken@0
    58
    Uint8 height[2];
slouken@368
    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@368
    72
#define TGA_INTERLEAVE_MASK 0xc0
slouken@368
    73
#define TGA_INTERLEAVE_NONE 0x00
slouken@368
    74
#define TGA_INTERLEAVE_2WAY 0x40
slouken@368
    75
#define TGA_INTERLEAVE_4WAY 0x80
slouken@0
    76
slouken@368
    77
#define TGA_ORIGIN_MASK     0x30
slouken@368
    78
#define TGA_ORIGIN_LEFT     0x00
slouken@368
    79
#define TGA_ORIGIN_RIGHT    0x10
slouken@368
    80
#define TGA_ORIGIN_LOWER    0x00
slouken@368
    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@471
   114
    if (!SDL_RWread(src, &hdr, sizeof(hdr), 1)) {
slouken@118
   115
        error = "Error reading TGA data";
slouken@471
   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@471
   121
        rle = 1;
slouken@471
   122
        /* fallthrough */
slouken@0
   123
    case TGA_TYPE_INDEXED:
slouken@471
   124
        if (!hdr.has_cmap || hdr.pixel_bits != 8 || ncols > 256)
slouken@471
   125
            goto unsupported;
slouken@471
   126
        indexed = 1;
slouken@471
   127
        break;
slouken@0
   128
slouken@0
   129
    case TGA_TYPE_RLE_RGB:
slouken@471
   130
        rle = 1;
slouken@471
   131
        /* fallthrough */
slouken@0
   132
    case TGA_TYPE_RGB:
slouken@471
   133
        indexed = 0;
slouken@471
   134
        break;
slouken@0
   135
slouken@0
   136
    case TGA_TYPE_RLE_BW:
slouken@471
   137
        rle = 1;
slouken@471
   138
        /* fallthrough */
slouken@0
   139
    case TGA_TYPE_BW:
slouken@471
   140
        if (hdr.pixel_bits != 8)
slouken@471
   141
            goto unsupported;
slouken@471
   142
        /* Treat greyscale as 8bpp indexed images */
slouken@471
   143
        indexed = grey = 1;
slouken@471
   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@471
   154
        if (!indexed) {
slouken@471
   155
                goto unsupported;
slouken@471
   156
        }
slouken@471
   157
        break;
slouken@0
   158
slouken@0
   159
    case 15:
slouken@0
   160
    case 16:
slouken@471
   161
        /* 15 and 16bpp both seem to use 5 bits/plane. The extra alpha bit
slouken@471
   162
           is ignored for now. */
slouken@471
   163
        rmask = 0x7c00;
slouken@471
   164
        gmask = 0x03e0;
slouken@471
   165
        bmask = 0x001f;
slouken@471
   166
        break;
slouken@0
   167
slouken@0
   168
    case 32:
slouken@471
   169
        alpha = 1;
slouken@471
   170
        /* fallthrough */
slouken@0
   171
    case 24:
slouken@389
   172
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
slouken@368
   173
        {
slouken@368
   174
        int s = alpha ? 0 : 8;
slouken@368
   175
        amask = 0x000000ff >> s;
slouken@368
   176
        rmask = 0x0000ff00 >> s;
slouken@368
   177
        gmask = 0x00ff0000 >> s;
slouken@368
   178
        bmask = 0xff000000 >> s;
slouken@368
   179
        }
aschiffler@343
   180
#else
slouken@368
   181
        amask = alpha ? 0xff000000 : 0;
slouken@368
   182
        rmask = 0x00ff0000;
slouken@368
   183
        gmask = 0x0000ff00;
slouken@368
   184
        bmask = 0x000000ff;
aschiffler@343
   185
#endif
slouken@471
   186
        break;
slouken@0
   187
slouken@0
   188
    default:
slouken@117
   189
        goto unsupported;
slouken@0
   190
    }
slouken@0
   191
slouken@471
   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@368
   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@368
   202
                   bpp * 8,
slouken@368
   203
                   rmask, gmask, bmask, amask);
slouken@471
   204
    if (img == NULL) {
slouken@118
   205
        error = "Out of memory";
slouken@118
   206
        goto error;
slouken@118
   207
    }
slouken@0
   208
slouken@471
   209
    if (hdr.has_cmap) {
slouken@471
   210
        int palsiz = ncols * ((hdr.cmap_bits + 7) >> 3);
slouken@471
   211
        if (indexed && !grey) {
slouken@471
   212
            Uint8 *pal = (Uint8 *)SDL_malloc(palsiz), *p = pal;
slouken@471
   213
            SDL_Color *colors = img->format->palette->colors;
slouken@471
   214
            img->format->palette->ncolors = ncols;
slouken@471
   215
            SDL_RWread(src, pal, palsiz, 1);
slouken@471
   216
            for(i = 0; i < ncols; i++) {
slouken@471
   217
                switch(hdr.cmap_bits) {
slouken@471
   218
                case 15:
slouken@471
   219
                case 16:
slouken@471
   220
                    {
slouken@471
   221
                    Uint16 c = p[0] + (p[1] << 8);
slouken@471
   222
                    p += 2;
slouken@471
   223
                    colors[i].r = (c >> 7) & 0xf8;
slouken@471
   224
                    colors[i].g = (c >> 2) & 0xf8;
slouken@471
   225
                    colors[i].b = c << 3;
slouken@471
   226
                    }
slouken@471
   227
                    break;
slouken@471
   228
                case 24:
slouken@471
   229
                case 32:
slouken@471
   230
                    colors[i].b = *p++;
slouken@471
   231
                    colors[i].g = *p++;
slouken@471
   232
                    colors[i].r = *p++;
slouken@471
   233
                    if (hdr.cmap_bits == 32 && *p++ < 128)
slouken@471
   234
                    ckey = i;
slouken@471
   235
                    break;
slouken@471
   236
                }
slouken@368
   237
            }
slouken@471
   238
            SDL_free(pal);
slouken@471
   239
            if (ckey >= 0)
slouken@471
   240
                SDL_SetColorKey(img, SDL_TRUE, ckey);
slouken@471
   241
        } else {
slouken@471
   242
            /* skip unneeded colormap */
slouken@471
   243
            SDL_RWseek(src, palsiz, RW_SEEK_CUR);
slouken@368
   244
        }
slouken@0
   245
    }
slouken@0
   246
slouken@471
   247
    if (grey) {
slouken@471
   248
        SDL_Color *colors = img->format->palette->colors;
slouken@471
   249
        for(i = 0; i < 256; i++)
slouken@471
   250
            colors[i].r = colors[i].g = colors[i].b = i;
slouken@471
   251
        img->format->palette->ncolors = 256;
slouken@0
   252
    }
slouken@0
   253
slouken@471
   254
    if (hdr.flags & TGA_ORIGIN_UPPER) {
slouken@368
   255
        lstep = img->pitch;
slouken@368
   256
        dst = (Uint8 *)img->pixels;
slouken@0
   257
    } else {
slouken@368
   258
        lstep = -img->pitch;
slouken@368
   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@471
   266
        if (rle) {
slouken@471
   267
            int x = 0;
slouken@471
   268
            for(;;) {
slouken@471
   269
                Uint8 c;
slouken@0
   270
slouken@471
   271
                if (count) {
slouken@471
   272
                    int n = count;
slouken@471
   273
                    if (n > w - x)
slouken@471
   274
                        n = w - x;
slouken@471
   275
                    SDL_RWread(src, dst + x * bpp, n * bpp, 1);
slouken@471
   276
                    count -= n;
slouken@471
   277
                    x += n;
slouken@471
   278
                    if (x == w)
slouken@471
   279
                        break;
slouken@471
   280
                } else if (rep) {
slouken@471
   281
                    int n = rep;
slouken@471
   282
                    if (n > w - x)
slouken@471
   283
                        n = w - x;
slouken@471
   284
                    rep -= n;
slouken@471
   285
                    while (n--) {
slouken@471
   286
                        SDL_memcpy(dst + x * bpp, &pixel, bpp);
slouken@471
   287
                        x++;
slouken@471
   288
                    }
slouken@471
   289
                    if (x == w)
slouken@471
   290
                        break;
slouken@471
   291
                }
slouken@471
   292
slouken@471
   293
                SDL_RWread(src, &c, 1, 1);
slouken@471
   294
                if (c & 0x80) {
slouken@471
   295
                    SDL_RWread(src, &pixel, bpp, 1);
slouken@471
   296
                    rep = (c & 0x7f) + 1;
slouken@471
   297
                } else {
slouken@471
   298
                    count = c + 1;
slouken@471
   299
                }
slouken@368
   300
            }
slouken@471
   301
        } else {
slouken@471
   302
            SDL_RWread(src, dst, w * bpp, 1);
slouken@368
   303
        }
slouken@501
   304
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
slouken@471
   305
        if (bpp == 2) {
slouken@471
   306
            /* swap byte order */
slouken@471
   307
            int x;
slouken@471
   308
            Uint16 *p = (Uint16 *)dst;
slouken@471
   309
            for(x = 0; x < w; x++)
slouken@471
   310
            p[x] = SDL_Swap16(p[x]);
slouken@368
   311
        }
aschiffler@343
   312
#endif
slouken@471
   313
        dst += lstep;
slouken@0
   314
    }
slouken@0
   315
    return img;
slouken@0
   316
slouken@118
   317
unsupported:
slouken@118
   318
    error = "Unsupported TGA format";
slouken@118
   319
slouken@0
   320
error:
slouken@236
   321
    SDL_RWseek(src, start, RW_SEEK_SET);
slouken@118
   322
    if ( img ) {
slouken@118
   323
        SDL_FreeSurface(img);
slouken@118
   324
    }
slouken@451
   325
    IMG_SetError("%s", error);
slouken@117
   326
    return NULL;
slouken@0
   327
}
slouken@0
   328
slouken@0
   329
#else
slouken@0
   330
slouken@0
   331
/* dummy TGA load routine */
slouken@0
   332
SDL_Surface *IMG_LoadTGA_RW(SDL_RWops *src)
slouken@0
   333
{
slouken@368
   334
    return(NULL);
slouken@0
   335
}
slouken@0
   336
slouken@0
   337
#endif /* LOAD_TGA */
slouken@250
   338
slouken@250
   339
#endif /* !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) */