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