IMG_pnm.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 04 Feb 2006 23:39:03 +0000
changeset 121 1bf9c0c87374
parent 119 336c49447e0d
child 154 201cc5c1b373
permissions -rw-r--r--
Updated copyright information
     1 /*
     2     SDL_image:  An example image loading library for use with SDL
     3     Copyright (C) 1997-2006 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 
    23 /*
    24  * PNM (portable anymap) image loader:
    25  *
    26  * Supports: PBM, PGM and PPM, ASCII and binary formats
    27  * (PBM and PGM are loaded as 8bpp surfaces)
    28  * Does not support: maximum component value > 255
    29  */
    30 
    31 #include <stdio.h>
    32 #include <stdlib.h>
    33 #include <ctype.h>
    34 #include <string.h>
    35 
    36 #include "SDL_image.h"
    37 
    38 #ifdef LOAD_PNM
    39 
    40 /* See if an image is contained in a data source */
    41 int IMG_isPNM(SDL_RWops *src)
    42 {
    43 	int start;
    44 	int is_PNM;
    45 	char magic[2];
    46 
    47 	start = SDL_RWtell(src);
    48 	is_PNM = 0;
    49 	if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
    50 		/*
    51 		 * PNM magic signatures:
    52 		 * P1	PBM, ascii format
    53 		 * P2	PGM, ascii format
    54 		 * P3	PPM, ascii format
    55 		 * P4	PBM, binary format
    56 		 * P5	PGM, binary format
    57 		 * P6	PPM, binary format
    58 		 * P7	PAM, a general wrapper for PNM data
    59 		 */
    60 		if ( magic[0] == 'P' && magic[1] >= '1' && magic[1] <= '6' ) {
    61 			is_PNM = 1;
    62 		}
    63 	}
    64 	SDL_RWseek(src, start, SEEK_SET);
    65 	return(is_PNM);
    66 }
    67 
    68 /* read a non-negative integer from the source. return -1 upon error */
    69 static int ReadNumber(SDL_RWops *src)
    70 {
    71 	int number;
    72 	unsigned char ch;
    73 
    74 	/* Initialize return value */
    75 	number = 0;
    76 
    77 	/* Skip leading whitespace */
    78 	do {
    79 		if ( ! SDL_RWread(src, &ch, 1, 1) ) {
    80 			return(0);
    81 		}
    82 		/* Eat comments as whitespace */
    83 		if ( ch == '#' ) {  /* Comment is '#' to end of line */
    84 			do {
    85 				if ( ! SDL_RWread(src, &ch, 1, 1) ) {
    86 					return -1;
    87 				}
    88 			} while ( (ch != '\r') && (ch != '\n') );
    89 		}
    90 	} while ( isspace(ch) );
    91 
    92 	/* Add up the number */
    93 	do {
    94 		number *= 10;
    95 		number += ch-'0';
    96 
    97 		if ( !SDL_RWread(src, &ch, 1, 1) ) {
    98 			return -1;
    99 		}
   100 	} while ( isdigit(ch) );
   101 
   102 	return(number);
   103 }
   104 
   105 SDL_Surface *IMG_LoadPNM_RW(SDL_RWops *src)
   106 {
   107 	int start;
   108 	SDL_Surface *surface = NULL;
   109 	int width, height;
   110 	int maxval, y, bpl;
   111 	Uint8 *row;
   112 	Uint8 *buf = NULL;
   113 	char *error = NULL;
   114 	Uint8 magic[2];
   115 	int ascii;
   116 	enum { PBM, PGM, PPM, PAM } kind;
   117 
   118 #define ERROR(s) do { error = (s); goto done; } while(0)
   119 
   120 	if ( !src ) {
   121 		/* The error message has been set in SDL_RWFromFile */
   122 		return NULL;
   123 	}
   124 	start = SDL_RWtell(src);
   125 
   126 	SDL_RWread(src, magic, 2, 1);
   127 	kind = magic[1] - '1';
   128 	ascii = 1;
   129 	if(kind >= 3) {
   130 		ascii = 0;
   131 		kind -= 3;
   132 	}
   133 
   134 	width = ReadNumber(src);
   135 	height = ReadNumber(src);
   136 	if(width <= 0 || height <= 0)
   137 		ERROR("Unable to read image width and height");
   138 
   139 	if(kind != PBM) {
   140 		maxval = ReadNumber(src);
   141 		if(maxval <= 0 || maxval > 255)
   142 			ERROR("unsupported PNM format");
   143 	} else
   144 		maxval = 255;	/* never scale PBMs */
   145 
   146 	/* binary PNM allows just a single character of whitespace after
   147 	   the last parameter, and we've already consumed it */
   148 
   149 	if(kind == PPM) {
   150 		/* 24-bit surface in R,G,B byte order */
   151 		surface = SDL_AllocSurface(SDL_SWSURFACE, width, height, 24,
   152 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
   153 					   0x000000ff, 0x0000ff00, 0x00ff0000,
   154 #else
   155 					   0x00ff0000, 0x0000ff00, 0x000000ff,
   156 #endif
   157 					   0);
   158 	} else {
   159 		/* load PBM/PGM as 8-bit indexed images */
   160 		surface = SDL_AllocSurface(SDL_SWSURFACE, width, height, 8,
   161 					   0, 0, 0, 0);
   162 	}
   163 	if ( surface == NULL )
   164 		ERROR("Out of memory");
   165 	bpl = width * surface->format->BytesPerPixel;
   166 	if(kind == PGM) {
   167 		SDL_Color *c = surface->format->palette->colors;
   168 		int i;
   169 		for(i = 0; i < 256; i++)
   170 			c[i].r = c[i].g = c[i].b = i;
   171 		surface->format->palette->ncolors = 256;
   172 	} else if(kind == PBM) {
   173 		/* for some reason PBM has 1=black, 0=white */
   174 		SDL_Color *c = surface->format->palette->colors;
   175 		c[0].r = c[0].g = c[0].b = 255;
   176 		c[1].r = c[1].g = c[1].b = 0;
   177 		surface->format->palette->ncolors = 2;
   178 		bpl = (width + 7) >> 3;
   179 		buf = malloc(bpl);
   180 		if(buf == NULL)
   181 			ERROR("Out of memory");
   182 	}
   183 
   184 	/* Read the image into the surface */
   185 	row = surface->pixels;
   186 	for(y = 0; y < height; y++) {
   187 		if(ascii) {
   188 			int i;
   189 			if(kind == PBM) {
   190 				for(i = 0; i < width; i++) {
   191 					Uint8 ch;
   192 					do {
   193 						if(!SDL_RWread(src, &ch,
   194 							       1, 1))
   195 						       ERROR("file truncated");
   196 						ch -= '0';
   197 					} while(ch > 1);
   198 					row[i] = ch;
   199 				}
   200 			} else {
   201 				for(i = 0; i < bpl; i++) {
   202 					int c;
   203 					c = ReadNumber(src);
   204 					if(c < 0)
   205 						ERROR("file truncated");
   206 					row[i] = c;
   207 				}
   208 			}
   209 		} else {
   210 			Uint8 *dst = (kind == PBM) ? buf : row;
   211 			if(!SDL_RWread(src, dst, bpl, 1))
   212 				ERROR("file truncated");
   213 			if(kind == PBM) {
   214 				/* expand bitmap to 8bpp */
   215 				int i;
   216 				for(i = 0; i < width; i++) {
   217 					int bit = 7 - (i & 7);
   218 					row[i] = (buf[i >> 3] >> bit) & 1;
   219 				}
   220 			}
   221 		}
   222 		if(maxval < 255) {
   223 			/* scale up to full dynamic range (slow) */
   224 			int i;
   225 			for(i = 0; i < bpl; i++)
   226 				row[i] = row[i] * 255 / maxval;
   227 		}
   228 		row += surface->pitch;
   229 	}
   230 done:
   231 	free(buf);
   232 	if(error) {
   233 		SDL_RWseek(src, start, SEEK_SET);
   234 		if ( surface ) {
   235 			SDL_FreeSurface(surface);
   236 			surface = NULL;
   237 		}
   238 		IMG_SetError(error);
   239 	}
   240 	return(surface);
   241 }
   242 
   243 #else
   244 
   245 /* See if an image is contained in a data source */
   246 int IMG_isPNM(SDL_RWops *src)
   247 {
   248 	return(0);
   249 }
   250 
   251 /* Load a PNM type image from an SDL datasource */
   252 SDL_Surface *IMG_LoadPNM_RW(SDL_RWops *src)
   253 {
   254 	return(NULL);
   255 }
   256 
   257 #endif /* LOAD_PNM */