IMG_pcx.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 08 Nov 2009 17:13:36 +0000
changeset 236 cce1251de477
parent 216 7714c2bb6302
child 280 ec4ae96c100c
permissions -rw-r--r--
Use RW_SEEK constants for RWseek()
     1 /*
     2     SDL_image:  An example image loading library for use with SDL
     3     Copyright (C) 1997-2009 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  * PCX file reader:
    25  * Supports:
    26  *  1..4 bits/pixel in multiplanar format (1 bit/plane/pixel)
    27  *  8 bits/pixel in single-planar format (8 bits/plane/pixel)
    28  *  24 bits/pixel in 3-plane format (8 bits/plane/pixel)
    29  *
    30  * (The <8bpp formats are expanded to 8bpp surfaces)
    31  *
    32  * Doesn't support:
    33  *  single-planar packed-pixel formats other than 8bpp
    34  *  4-plane 32bpp format with a fourth "intensity" plane
    35  */
    36 #include <stdio.h>
    37 #include <stdlib.h>
    38 
    39 #include "SDL_endian.h"
    40 
    41 #include "SDL_image.h"
    42 
    43 #ifdef LOAD_PCX
    44 
    45 struct PCXheader {
    46 	Uint8 Manufacturer;
    47 	Uint8 Version;
    48 	Uint8 Encoding;
    49 	Uint8 BitsPerPixel;
    50 	Sint16 Xmin, Ymin, Xmax, Ymax;
    51 	Sint16 HDpi, VDpi;
    52 	Uint8 Colormap[48];
    53 	Uint8 Reserved;
    54 	Uint8 NPlanes;
    55 	Sint16 BytesPerLine;
    56 	Sint16 PaletteInfo;
    57 	Sint16 HscreenSize;
    58 	Sint16 VscreenSize;
    59 	Uint8 Filler[54];
    60 };
    61 
    62 /* See if an image is contained in a data source */
    63 int IMG_isPCX(SDL_RWops *src)
    64 {
    65 	int start;
    66 	int is_PCX;
    67 	const int ZSoft_Manufacturer = 10;
    68 	const int PC_Paintbrush_Version = 5;
    69 	const int PCX_Uncompressed_Encoding = 0;
    70 	const int PCX_RunLength_Encoding = 1;
    71 	struct PCXheader pcxh;
    72 
    73 	if ( !src )
    74 		return 0;
    75 	start = SDL_RWtell(src);
    76 	is_PCX = 0;
    77 	if ( SDL_RWread(src, &pcxh, sizeof(pcxh), 1) == 1 ) {
    78 		if ( (pcxh.Manufacturer == ZSoft_Manufacturer) &&
    79 		     (pcxh.Version == PC_Paintbrush_Version) &&
    80 		     (pcxh.Encoding == PCX_RunLength_Encoding ||
    81 		      pcxh.Encoding == PCX_Uncompressed_Encoding) ) {
    82 			is_PCX = 1;
    83 		}
    84 	}
    85 	SDL_RWseek(src, start, RW_SEEK_SET);
    86 	return(is_PCX);
    87 }
    88 
    89 /* Load a PCX type image from an SDL datasource */
    90 SDL_Surface *IMG_LoadPCX_RW(SDL_RWops *src)
    91 {
    92 	int start;
    93 	struct PCXheader pcxh;
    94 	Uint32 Rmask;
    95 	Uint32 Gmask;
    96 	Uint32 Bmask;
    97 	Uint32 Amask;
    98 	SDL_Surface *surface = NULL;
    99 	int width, height;
   100 	int y, bpl;
   101 	Uint8 *row, *buf = NULL;
   102 	char *error = NULL;
   103 	int bits, src_bits;
   104 
   105 	if ( !src ) {
   106 		/* The error message has been set in SDL_RWFromFile */
   107 		return NULL;
   108 	}
   109 	start = SDL_RWtell(src);
   110 
   111 	if ( ! SDL_RWread(src, &pcxh, sizeof(pcxh), 1) ) {
   112 		error = "file truncated";
   113 		goto done;
   114 	}
   115 	pcxh.Xmin = SDL_SwapLE16(pcxh.Xmin);
   116 	pcxh.Ymin = SDL_SwapLE16(pcxh.Ymin);
   117 	pcxh.Xmax = SDL_SwapLE16(pcxh.Xmax);
   118 	pcxh.Ymax = SDL_SwapLE16(pcxh.Ymax);
   119 	pcxh.BytesPerLine = SDL_SwapLE16(pcxh.BytesPerLine);
   120 
   121 	/* Create the surface of the appropriate type */
   122 	width = (pcxh.Xmax - pcxh.Xmin) + 1;
   123 	height = (pcxh.Ymax - pcxh.Ymin) + 1;
   124 	Rmask = Gmask = Bmask = Amask = 0;
   125 	src_bits = pcxh.BitsPerPixel * pcxh.NPlanes;
   126 	if((pcxh.BitsPerPixel == 1 && pcxh.NPlanes >= 1 && pcxh.NPlanes <= 4)
   127 	   || (pcxh.BitsPerPixel == 8 && pcxh.NPlanes == 1)) {
   128 		bits = 8;
   129 	} else if(pcxh.BitsPerPixel == 8 && pcxh.NPlanes == 3) {
   130 		bits = 24;
   131 		if ( SDL_BYTEORDER == SDL_LIL_ENDIAN ) {
   132 			Rmask = 0x000000FF;
   133 			Gmask = 0x0000FF00;
   134 			Bmask = 0x00FF0000;
   135 		} else {
   136 			Rmask = 0xFF0000;
   137 			Gmask = 0x00FF00;
   138 			Bmask = 0x0000FF;
   139 		}
   140 	} else {
   141 		error = "unsupported PCX format";
   142 		goto done;
   143 	}
   144 	surface = SDL_AllocSurface(SDL_SWSURFACE, width, height,
   145 				   bits, Rmask, Gmask, Bmask, Amask);
   146 	if ( surface == NULL )
   147 		goto done;
   148 
   149 	bpl = pcxh.NPlanes * pcxh.BytesPerLine;
   150 	if (bpl > surface->pitch) {
   151 		error = "bytes per line is too large (corrupt?)";
   152 	}
   153 	buf = malloc(bpl);
   154 	row = surface->pixels;
   155 	for ( y=0; y<surface->h; ++y ) {
   156 		/* decode a scan line to a temporary buffer first */
   157 		int i, count = 0;
   158 		Uint8 ch;
   159 		Uint8 *dst = (src_bits == 8) ? row : buf;
   160 		if ( pcxh.Encoding == 0 ) {
   161 			if(!SDL_RWread(src, dst, bpl, 1)) {
   162 				error = "file truncated";
   163 				goto done;
   164 			}
   165 		} else {
   166 			for(i = 0; i < bpl; i++) {
   167 				if(!count) {
   168 					if(!SDL_RWread(src, &ch, 1, 1)) {
   169 						error = "file truncated";
   170 						goto done;
   171 					}
   172 					if( (ch & 0xc0) == 0xc0) {
   173 						count = ch & 0x3f;
   174 						if(!SDL_RWread(src, &ch, 1, 1)) {
   175 							error = "file truncated";
   176 							goto done;
   177 						}
   178 					} else
   179 						count = 1;
   180 				}
   181 				dst[i] = ch;
   182 				count--;
   183 			}
   184 		}
   185 
   186 		if(src_bits <= 4) {
   187 			/* expand planes to 1 byte/pixel */
   188 			Uint8 *src = buf;
   189 			int plane;
   190 			for(plane = 0; plane < pcxh.NPlanes; plane++) {
   191 				int i, j, x = 0;
   192 				for(i = 0; i < pcxh.BytesPerLine; i++) {
   193 					Uint8 byte = *src++;
   194 					for(j = 7; j >= 0; j--) {
   195 						unsigned bit = (byte >> j) & 1;
   196 						/* skip padding bits */
   197 						if (i * 8 + j >= width)
   198 							continue;
   199 						row[x++] |= bit << plane;
   200 					}
   201 				}
   202 			}
   203  		} else if(src_bits == 24) {
   204 			/* de-interlace planes */
   205 			Uint8 *src = buf;
   206 			int plane;
   207 			for(plane = 0; plane < pcxh.NPlanes; plane++) {
   208 				int x;
   209 				dst = row + plane;
   210 				for(x = 0; x < width; x++) {
   211 					*dst = *src++;
   212 					dst += pcxh.NPlanes;
   213 				}
   214 			}
   215 		}
   216 
   217 		row += surface->pitch;
   218 	}
   219 
   220 	if(bits == 8) {
   221 		SDL_Color *colors = surface->format->palette->colors;
   222 		int nc = 1 << src_bits;
   223 		int i;
   224 
   225 		surface->format->palette->ncolors = nc;
   226 		if(src_bits == 8) {
   227 			Uint8 ch;
   228 			/* look for a 256-colour palette */
   229 			do {
   230 				if ( !SDL_RWread(src, &ch, 1, 1)) {
   231 					error = "file truncated";
   232 					goto done;
   233 				}
   234 			} while ( ch != 12 );
   235 
   236 			for(i = 0; i < 256; i++) {
   237 				SDL_RWread(src, &colors[i].r, 1, 1);
   238 				SDL_RWread(src, &colors[i].g, 1, 1);
   239 				SDL_RWread(src, &colors[i].b, 1, 1);
   240 			}
   241 		} else {
   242 			for(i = 0; i < nc; i++) {
   243 				colors[i].r = pcxh.Colormap[i * 3];
   244 				colors[i].g = pcxh.Colormap[i * 3 + 1];
   245 				colors[i].b = pcxh.Colormap[i * 3 + 2];
   246 			}
   247 		}
   248 	}
   249 
   250 done:
   251 	free(buf);
   252 	if ( error ) {
   253 		SDL_RWseek(src, start, RW_SEEK_SET);
   254 		if ( surface ) {
   255 			SDL_FreeSurface(surface);
   256 			surface = NULL;
   257 		}
   258 		IMG_SetError(error);
   259 	}
   260 	return(surface);
   261 }
   262 
   263 #else
   264 
   265 /* See if an image is contained in a data source */
   266 int IMG_isPCX(SDL_RWops *src)
   267 {
   268 	return(0);
   269 }
   270 
   271 /* Load a PCX type image from an SDL datasource */
   272 SDL_Surface *IMG_LoadPCX_RW(SDL_RWops *src)
   273 {
   274 	return(NULL);
   275 }
   276 
   277 #endif /* LOAD_PCX */