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