IMG_tga.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 15 Jun 2014 13:16:42 -0700
changeset 451 48116d511e5d
parent 389 1ff9cd8cf18b
child 471 c5078fe7c32c
permissions -rw-r--r--
Fixed format warnings: format not a string literal and no format arguments
     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_BIG_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("%s", 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) */