IMG_pnm.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 22 Jan 2012 21:52:00 -0500
changeset 320 aa5d55b11751
parent 280 ec4ae96c100c
child 343 5bf0f0d6a74e
permissions -rw-r--r--
Updated for SDL 2.0
     1 /*
     2   SDL_image:  An example image loading library for use with SDL
     3   Copyright (C) 1997-2012 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 	int 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 	int 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 = malloc(bpl);
   181 		if(buf == NULL)
   182 			ERROR("Out of memory");
   183 	}
   184 
   185 	/* Read the image into the surface */
   186 	row = 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 	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 */