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