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