New comit of SDL2
[supertux.git] / src / SDL2 / IMG_pnm.c
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 /*
23  * PNM (portable anymap) image loader:
24  *
25  * Supports: PBM, PGM and PPM, ASCII and binary formats
26  * (PBM and PGM are loaded as 8bpp surfaces)
27  * Does not support: maximum component value > 255
28  */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <ctype.h>
33 #include <string.h>
34
35 #include "SDL_image.h"
36
37 #ifdef LOAD_PNM
38
39 /* See if an image is contained in a data source */
40 int IMG_isPNM(SDL_RWops *src)
41 {
42     Sint64 start;
43     int is_PNM;
44     char magic[2];
45
46     if ( !src )
47         return 0;
48     start = SDL_RWtell(src);
49     is_PNM = 0;
50     if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
51         /*
52          * PNM magic signatures:
53          * P1   PBM, ascii format
54          * P2   PGM, ascii format
55          * P3   PPM, ascii format
56          * P4   PBM, binary format
57          * P5   PGM, binary format
58          * P6   PPM, binary format
59          * P7   PAM, a general wrapper for PNM data
60          */
61         if ( magic[0] == 'P' && magic[1] >= '1' && magic[1] <= '6' ) {
62             is_PNM = 1;
63         }
64     }
65     SDL_RWseek(src, start, RW_SEEK_SET);
66     return(is_PNM);
67 }
68
69 /* read a non-negative integer from the source. return -1 upon error */
70 static int ReadNumber(SDL_RWops *src)
71 {
72     int number;
73     unsigned char ch;
74
75     /* Initialize return value */
76     number = 0;
77
78     /* Skip leading whitespace */
79     do {
80         if ( ! SDL_RWread(src, &ch, 1, 1) ) {
81             return(0);
82         }
83         /* Eat comments as whitespace */
84         if ( ch == '#' ) {  /* Comment is '#' to end of line */
85             do {
86                 if ( ! SDL_RWread(src, &ch, 1, 1) ) {
87                     return -1;
88                 }
89             } while ( (ch != '\r') && (ch != '\n') );
90         }
91     } while ( isspace(ch) );
92
93     /* Add up the number */
94     do {
95         number *= 10;
96         number += ch-'0';
97
98         if ( !SDL_RWread(src, &ch, 1, 1) ) {
99             return -1;
100         }
101     } while ( isdigit(ch) );
102
103     return(number);
104 }
105
106 SDL_Surface *IMG_LoadPNM_RW(SDL_RWops *src)
107 {
108     Sint64 start;
109     SDL_Surface *surface = NULL;
110     int width, height;
111     int maxval, y, bpl;
112     Uint8 *row;
113     Uint8 *buf = NULL;
114     char *error = NULL;
115     Uint8 magic[2];
116     int ascii;
117     enum { PBM, PGM, PPM, PAM } kind;
118
119 #define ERROR(s) do { error = (s); goto done; } while(0)
120
121     if ( !src ) {
122         /* The error message has been set in SDL_RWFromFile */
123         return NULL;
124     }
125     start = SDL_RWtell(src);
126
127     SDL_RWread(src, magic, 2, 1);
128     kind = magic[1] - '1';
129     ascii = 1;
130     if(kind >= 3) {
131         ascii = 0;
132         kind -= 3;
133     }
134
135     width = ReadNumber(src);
136     height = ReadNumber(src);
137     if(width <= 0 || height <= 0)
138         ERROR("Unable to read image width and height");
139
140     if(kind != PBM) {
141         maxval = ReadNumber(src);
142         if(maxval <= 0 || maxval > 255)
143             ERROR("unsupported PNM format");
144     } else
145         maxval = 255;   /* never scale PBMs */
146
147     /* binary PNM allows just a single character of whitespace after
148        the last parameter, and we've already consumed it */
149
150     if(kind == PPM) {
151         /* 24-bit surface in R,G,B byte order */
152         surface = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 24,
153 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
154                        0x000000ff, 0x0000ff00, 0x00ff0000,
155 #else
156                        0x00ff0000, 0x0000ff00, 0x000000ff,
157 #endif
158                        0);
159     } else {
160         /* load PBM/PGM as 8-bit indexed images */
161         surface = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 8,
162                        0, 0, 0, 0);
163     }
164     if ( surface == NULL )
165         ERROR("Out of memory");
166     bpl = width * surface->format->BytesPerPixel;
167     if(kind == PGM) {
168         SDL_Color *c = surface->format->palette->colors;
169         int i;
170         for(i = 0; i < 256; i++)
171             c[i].r = c[i].g = c[i].b = i;
172         surface->format->palette->ncolors = 256;
173     } else if(kind == PBM) {
174         /* for some reason PBM has 1=black, 0=white */
175         SDL_Color *c = surface->format->palette->colors;
176         c[0].r = c[0].g = c[0].b = 255;
177         c[1].r = c[1].g = c[1].b = 0;
178         surface->format->palette->ncolors = 2;
179         bpl = (width + 7) >> 3;
180         buf = (Uint8 *)SDL_malloc(bpl);
181         if(buf == NULL)
182             ERROR("Out of memory");
183     }
184
185     /* Read the image into the surface */
186     row = (Uint8 *)surface->pixels;
187     for(y = 0; y < height; y++) {
188         if(ascii) {
189             int i;
190             if(kind == PBM) {
191                 for(i = 0; i < width; i++) {
192                     Uint8 ch;
193                     do {
194                         if(!SDL_RWread(src, &ch,
195                                    1, 1))
196                                ERROR("file truncated");
197                         ch -= '0';
198                     } while(ch > 1);
199                     row[i] = ch;
200                 }
201             } else {
202                 for(i = 0; i < bpl; i++) {
203                     int c;
204                     c = ReadNumber(src);
205                     if(c < 0)
206                         ERROR("file truncated");
207                     row[i] = c;
208                 }
209             }
210         } else {
211             Uint8 *dst = (kind == PBM) ? buf : row;
212             if(!SDL_RWread(src, dst, bpl, 1))
213                 ERROR("file truncated");
214             if(kind == PBM) {
215                 /* expand bitmap to 8bpp */
216                 int i;
217                 for(i = 0; i < width; i++) {
218                     int bit = 7 - (i & 7);
219                     row[i] = (buf[i >> 3] >> bit) & 1;
220                 }
221             }
222         }
223         if(maxval < 255) {
224             /* scale up to full dynamic range (slow) */
225             int i;
226             for(i = 0; i < bpl; i++)
227                 row[i] = row[i] * 255 / maxval;
228         }
229         row += surface->pitch;
230     }
231 done:
232     SDL_free(buf);
233     if(error) {
234         SDL_RWseek(src, start, RW_SEEK_SET);
235         if ( surface ) {
236             SDL_FreeSurface(surface);
237             surface = NULL;
238         }
239         IMG_SetError(error);
240     }
241     return(surface);
242 }
243
244 #else
245
246 /* See if an image is contained in a data source */
247 int IMG_isPNM(SDL_RWops *src)
248 {
249     return(0);
250 }
251
252 /* Load a PNM type image from an SDL datasource */
253 SDL_Surface *IMG_LoadPNM_RW(SDL_RWops *src)
254 {
255     return(NULL);
256 }
257
258 #endif /* LOAD_PNM */